From 16b799320054627246f3b2103560bece53cfc10c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 19 Jan 2026 13:51:40 -0600 Subject: [PATCH] Implementing blitting for colorbar indicators --- openmc_plotter/plotgui.py | 81 +++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/openmc_plotter/plotgui.py b/openmc_plotter/plotgui.py index 2f4b590..24b6c00 100644 --- a/openmc_plotter/plotgui.py +++ b/openmc_plotter/plotgui.py @@ -47,8 +47,15 @@ def __init__(self, model: PlotModel, parent, main_window): self.colorbar = None self.data_indicator = None self.tally_data_indicator = None + self.tally_colorbar = None + self.tally_image = None self.image = None + self._data_colorbar_bg = None + self._tally_colorbar_bg = None + self._last_tally_indicator_value = None + self._last_data_indicator_value = None + self.menu = QMenu(self) def enterEvent(self, event): @@ -538,6 +545,7 @@ def updatePixmap(self): linewidth=3., color='blue', clip_on=True) + self.data_indicator.set_animated(True) self.colorbar.ax.add_line(self.data_indicator) self.colorbar.ax.margins(0.0, 0.0) self.updateDataIndicatorVisibility() @@ -626,6 +634,7 @@ def updatePixmap(self): linewidth=3., color='blue', clip_on=True) + self.tally_data_indicator.set_animated(True) self.tally_colorbar.ax.add_line(self.tally_data_indicator) self.tally_colorbar.ax.margins(0.0, 0.0) @@ -655,6 +664,9 @@ def updatePixmap(self): self.ax.dataLim.y1 = data_bounds[3] self.draw() + self._cache_colorbar_backgrounds() + self._blit_indicator(self.data_indicator, self.colorbar) + self._blit_indicator(self.tally_data_indicator, self.tally_colorbar) return "Done" def current_view_data_bounds(self): @@ -740,21 +752,63 @@ def parseContoursLine(line): def updateColorbarScale(self): self.updatePixmap() + def _cache_colorbar_backgrounds(self): + """Cache colorbar backgrounds for fast indicator blitting.""" + self._data_colorbar_bg = None + self._tally_colorbar_bg = None + + if self.colorbar and self.data_indicator: + self._data_colorbar_bg = self.copy_from_bbox( + self.colorbar.ax.bbox) + + if self.tally_colorbar and self.tally_data_indicator: + self._tally_colorbar_bg = self.copy_from_bbox( + self.tally_colorbar.ax.bbox) + + def _blit_indicator(self, indicator, colorbar): + """Blit a single indicator line onto its colorbar if possible.""" + if colorbar is None or indicator is None: + return False + + if not indicator.get_visible(): + return False + + if colorbar is self.colorbar: + background = self._data_colorbar_bg + else: + background = self._tally_colorbar_bg + + if background is None: + return False + + self.restore_region(background) + colorbar.ax.draw_artist(indicator) + self.blit(colorbar.ax.bbox) + return True + def updateTallyDataIndicatorValue(self, y_val): cv = self.model.currentView if not cv.tallyDataVisible or not cv.tallyDataIndicator: return - if self.tally_data_indicator is not None: - data = self.tally_data_indicator.get_data() + if self.tally_data_indicator is not None and self.tally_image is not None: # use norm to get axis value if log scale if cv.tallyDataLogScale: y_val = self.tally_image.norm(y_val) + + # If indicator value hasn't changed, skip update + if self._last_tally_indicator_value == y_val: + return + self._last_tally_indicator_value = y_val + + data = self.tally_data_indicator.get_data() self.tally_data_indicator.set_data([data[0], [y_val, y_val]]) dl_color = invert_rgb(self.tally_image.get_cmap()(y_val), True) self.tally_data_indicator.set_c(dl_color) - self.draw() + + if not self._blit_indicator(self.tally_data_indicator, self.tally_colorbar): + self.draw_idle() def updateDataIndicatorValue(self, y_val): cv = self.model.currentView @@ -763,28 +817,39 @@ def updateDataIndicatorValue(self, y_val): not cv.data_indicator_enabled[cv.colorby]: return - if self.data_indicator: - data = self.data_indicator.get_data() + if self.data_indicator and self.image is not None: # use norm to get axis value if log scale if cv.color_scale_log[cv.colorby]: y_val = self.image.norm(y_val) + + # If indicator value hasn't changed, skip update + if self._last_data_indicator_value == y_val: + return + self._last_data_indicator_value = y_val + + data = self.data_indicator.get_data() self.data_indicator.set_data([data[0], [y_val, y_val]]) dl_color = invert_rgb(self.image.get_cmap()(y_val), True) self.data_indicator.set_c(dl_color) - self.draw() + + if not self._blit_indicator(self.data_indicator, self.colorbar): + self.draw_idle() def updateDataIndicatorVisibility(self): cv = self.model.currentView if self.data_indicator and cv.colorby in _MODEL_PROPERTIES: val = cv.data_indicator_enabled[cv.colorby] self.data_indicator.set_visible(val) - self.draw() + if not self._blit_indicator(self.data_indicator, self.colorbar): + self.draw_idle() def updateColorMap(self, colormap_name, property_type): if self.colorbar and property_type == self.model.activeView.colorby: self.image.set_cmap(colormap_name) self.figure.draw_without_rendering() self.draw() + self._cache_colorbar_backgrounds() + self._blit_indicator(self.data_indicator, self.colorbar) def updateColorMinMax(self, property_type): av = self.model.activeView @@ -795,6 +860,8 @@ def updateColorMinMax(self, property_type): (0.0, 0.0)) self.figure.draw_without_rendering() self.draw() + self._cache_colorbar_backgrounds() + self._blit_indicator(self.data_indicator, self.colorbar) class ColorDialog(QDialog):