From 328032e2317e3ac4859196bbf12bdb71795302fe Mon Sep 17 00:00:00 2001 From: Alexandre Marie Date: Tue, 21 Jul 2020 14:45:14 +0200 Subject: New upstream version 0.13.0+dfsg --- silx/gui/plot/actions/PlotToolAction.py | 4 +- silx/gui/plot/actions/control.py | 35 +++- silx/gui/plot/actions/fit.py | 359 +++++++++++++++++++++++++------- silx/gui/plot/actions/histogram.py | 201 ++++++++++++++---- silx/gui/plot/actions/medfilt.py | 4 +- 5 files changed, 479 insertions(+), 124 deletions(-) (limited to 'silx/gui/plot/actions') diff --git a/silx/gui/plot/actions/PlotToolAction.py b/silx/gui/plot/actions/PlotToolAction.py index 77e8be2..fbb0b0f 100644 --- a/silx/gui/plot/actions/PlotToolAction.py +++ b/silx/gui/plot/actions/PlotToolAction.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2017 European Synchrotron Radiation Facility +# Copyright (c) 2004-2020 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -131,7 +131,7 @@ class PlotToolAction(PlotAction): return PlotAction.eventFilter(self, qobject, event) def _getToolWindow(self): - """Returns the window containg tohe tool. + """Returns the window containing the tool. It uses lazy loading to create this tool.. """ diff --git a/silx/gui/plot/actions/control.py b/silx/gui/plot/actions/control.py index e2fa6b1..ba69748 100755 --- a/silx/gui/plot/actions/control.py +++ b/silx/gui/plot/actions/control.py @@ -391,9 +391,8 @@ class ColormapAction(PlotAction): elif isinstance(image, items.ColormapMixIn): # Set dialog from active image colormap = image.getColormap() - data = image.getData(copy=False) # Set histogram and range if any - self._dialog.setData(data) + self._dialog.setItem(image) else: # No active image or active image is RGBA, @@ -401,8 +400,7 @@ class ColormapAction(PlotAction): scatter = self.plot._getActiveItem(kind='scatter') if scatter is not None: colormap = scatter.getColormap() - data = scatter.getValueData(copy=False) - self._dialog.setData(data) + self._dialog.setItem(scatter) else: # No active data image nor scatter, @@ -605,3 +603,32 @@ class ShowAxisAction(PlotAction): def _actionTriggered(self, checked=False): self.plot.setAxesDisplayed(checked) + +class ClosePolygonInteractionAction(PlotAction): + """QAction controlling closure of a polygon in draw interaction mode + if the :class:`.PlotWidget`. + + :param plot: :class:`.PlotWidget` instance on which to operate + :param parent: See :class:`QAction` + """ + + def __init__(self, plot, parent=None): + tooltip = 'Close the current polygon drawn' + PlotAction.__init__(self, + plot, + icon='add-shape-polygon', + text='Close the polygon', + tooltip=tooltip, + triggered=self._actionTriggered, + checkable=True, + parent=parent) + self.plot.sigInteractiveModeChanged.connect(self._modeChanged) + self._modeChanged(None) + + def _modeChanged(self, source): + mode = self.plot.getInteractiveMode() + enabled = "shape" in mode and mode["shape"] == "polygon" + self.setEnabled(enabled) + + def _actionTriggered(self, checked=False): + self.plot._eventHandler.validate() diff --git a/silx/gui/plot/actions/fit.py b/silx/gui/plot/actions/fit.py index 6fc5c75..f3c9e1c 100644 --- a/silx/gui/plot/actions/fit.py +++ b/silx/gui/plot/actions/fit.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2019 European Synchrotron Radiation Facility +# Copyright (c) 2004-2020 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -38,52 +38,43 @@ __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" __date__ = "10/10/2018" -from .PlotToolAction import PlotToolAction import logging + +import numpy + +from .PlotToolAction import PlotToolAction +from .. import items +from ....utils.deprecation import deprecated from silx.gui import qt from silx.gui.plot.ItemsSelectionDialog import ItemsSelectionDialog -from silx.gui.plot.items import Curve, Histogram _logger = logging.getLogger(__name__) -def _getUniqueCurve(plt): - """Get a single curve from the plot. - Get the active curve if any, else if a single curve is plotted - get it, else return None. +def _getUniqueCurveOrHistogram(plot): + """Returns unique :class:`Curve` or :class:`Histogram` in a `PlotWidget`. - :param plt: :class:`.PlotWidget` instance on which to operate + If there is an active curve, returns it, else return curve or histogram + only if alone in the plot. - :return: return value of plt.getActiveCurve(), or plt.getAllCurves()[0], - or None + :param PlotWidget plot: + :rtype: Union[None,~silx.gui.plot.items.Curve,~silx.gui.plot.items.Histogram] """ - curve = plt.getActiveCurve() + curve = plot.getActiveCurve() if curve is not None: return curve - curves = plt.getAllCurves() - if len(curves) == 0: - return None + histograms = [item for item in plot.getItems() + if isinstance(item, items.Histogram) and item.isVisible()] + curves = [item for item in plot.getItems() + if isinstance(item, items.Curve) and item.isVisible()] - if len(curves) == 1 and len(plt._getItems(kind='histogram')) == 0: + if len(histograms) == 1 and len(curves) == 0: + return histograms[0] + elif len(curves) == 1 and len(histograms) == 0: return curves[0] - - return None - - -def _getUniqueHistogram(plt): - """Return the histogram if there is a single histogram and no curve in - the plot. In all other cases, return None. - - :param plt: :class:`.PlotWidget` instance on which to operate - :return: histogram or None - """ - histograms = plt._getItems(kind='histogram') - if len(histograms) != 1: - return None - if plt.getAllCurves(just_legend=True): + else: return None - return histograms[0] class FitAction(PlotToolAction): @@ -93,78 +84,303 @@ class FitAction(PlotToolAction): :param plot: :class:`.PlotWidget` instance on which to operate :param parent: See :class:`QAction` """ + def __init__(self, plot, parent=None): + self.__item = None + self.__activeCurveSynchroEnabled = False + self.__range = 0, 1 + self.__rangeAutoUpdate = False + self.__x, self.__y = None, None # Data to fit + self.__curveParams = {} # Store curve parameters to use for fit result + self.__legend = None + super(FitAction, self).__init__( plot, icon='math-fit', text='Fit curve', tooltip='Open a fit dialog', parent=parent) + @property + @deprecated(replacement='getXRange()[0]', since_version='0.13.0') + def xmin(self): + return self.getXRange()[0] + + @property + @deprecated(replacement='getXRange()[1]', since_version='0.13.0') + def xmax(self): + return self.getXRange()[1] + + @property + @deprecated(replacement='getXData()', since_version='0.13.0') + def x(self): + return self.getXData() + + @property + @deprecated(replacement='getYData()', since_version='0.13.0') + def y(self): + return self.getYData() + + @property + @deprecated(since_version='0.13.0') + def xlabel(self): + return self.__curveParams.get('xlabel', None) + + @property + @deprecated(since_version='0.13.0') + def ylabel(self): + return self.__curveParams.get('ylabel', None) + + @property + @deprecated(since_version='0.13.0') + def legend(self): + return self.__legend + def _createToolWindow(self): # import done here rather than at module level to avoid circular import # FitWidget -> BackgroundWidget -> PlotWindow -> actions -> fit -> FitWidget from ...fit.FitWidget import FitWidget window = FitWidget(parent=self.plot) - window.setWindowFlags(qt.Qt.Window) + window.setWindowFlags(qt.Qt.Dialog) window.sigFitWidgetSignal.connect(self.handle_signal) return window def _connectPlot(self, window): - # Wait for the next iteration, else the plot is not yet initialized - # No curve available - qt.QTimer.singleShot(10, lambda: self._initFit(window)) + if self.isXRangeUpdatedOnZoom(): + self.__setAutoXRangeEnabled(True) + else: + plot = self.plot + if plot is None: + _logger.error("No associated PlotWidget") + return + self._setXRange(*plot.getXAxis().getLimits()) - def _initFit(self, window): - plot = self.plot - self.xlabel = plot.getXAxis().getLabel() - self.ylabel = plot.getYAxis().getLabel() - self.xmin, self.xmax = plot.getXAxis().getLimits() + if self.isFittedItemUpdatedFromActiveCurve(): + self.__setFittedItemAutoUpdateEnabled(True) + else: + # Wait for the next iteration, else the plot is not yet initialized + # No curve available + qt.QTimer.singleShot(10, self._initFit) - histo = _getUniqueHistogram(self.plot) - curve = _getUniqueCurve(self.plot) + def _disconnectPlot(self, window): + if self.isXRangeUpdatedOnZoom(): + self.__setAutoXRangeEnabled(False) - if histo is None and curve is None: + if self.isFittedItemUpdatedFromActiveCurve(): + self.__setFittedItemAutoUpdateEnabled(False) + + def _initFit(self): + plot = self.plot + if plot is None: + _logger.error("No associated PlotWidget") + return + + item = _getUniqueCurveOrHistogram(plot) + if item is None: # ambiguous case, we need to ask which plot item to fit - isd = ItemsSelectionDialog(parent=plot, plot=self.plot) + isd = ItemsSelectionDialog(parent=plot, plot=plot) isd.setWindowTitle("Select item to be fitted") isd.setItemsSelectionMode(qt.QTableWidget.SingleSelection) isd.setAvailableKinds(["curve", "histogram"]) isd.selectAllKinds() - result = isd.exec_() - if result and len(isd.getSelectedItems()) == 1: - item = isd.getSelectedItems()[0] + if not isd.exec_(): # Cancel + self._getToolWindow().setVisible(False) else: - return - elif histo is not None: - # presence of a unique histo and no curve - item = histo - elif curve is not None: - # presence of a unique or active curve - item = curve + selectedItems = isd.getSelectedItems() + item = selectedItems[0] if len(selectedItems) == 1 else None + + self._setXRange(*plot.getXAxis().getLimits()) + self._setFittedItem(item) + + def __updateFitWidget(self): + """Update the data/range used by the FitWidget""" + fitWidget = self._getToolWindow() + + item = self._getFittedItem() + xdata = self.getXData(copy=False) + ydata = self.getYData(copy=False) + if item is None or xdata is None or ydata is None: + fitWidget.setData(y=None) + fitWidget.setWindowTitle("No curve selected") + + else: + xmin, xmax = self.getXRange() + fitWidget.setData( + xdata, ydata, xmin=xmin, xmax=xmax) + fitWidget.setWindowTitle( + "Fitting " + item.getName() + + " on x range %f-%f" % (xmin, xmax)) + + # X Range management + + def getXRange(self): + """Returns the range on the X axis on which to perform the fit.""" + return self.__range + + def _setXRange(self, xmin, xmax): + """Set the range on which the fit is done. + + :param float xmin: + :param float xmax: + """ + range_ = float(xmin), float(xmax) + if self.__range != range_: + self.__range = range_ + self.__updateFitWidget() + + def __setAutoXRangeEnabled(self, enabled): + """Implement the change of update mode of the X range. + + :param bool enabled: + """ + plot = self.plot + if plot is None: + _logger.error("No associated PlotWidget") + return - self.legend = item.getLegend() + if enabled: + self._setXRange(*plot.getXAxis().getLimits()) + plot.getXAxis().sigLimitsChanged.connect(self._setXRange) + else: + plot.getXAxis().sigLimitsChanged.disconnect(self._setXRange) - if isinstance(item, Histogram): + def setXRangeUpdatedOnZoom(self, enabled): + """Set whether or not to update the X range on zoom change. + + :param bool enabled: + """ + if enabled != self.__rangeAutoUpdate: + self.__rangeAutoUpdate = enabled + if self._getToolWindow().isVisible(): + self.__setAutoXRangeEnabled(enabled) + + def isXRangeUpdatedOnZoom(self): + """Returns the current mode of fitted data X range update. + + :rtype: bool + """ + return self.__rangeAutoUpdate + + # Fitted item update + + def getXData(self, copy=True): + """Returns the X data used for the fit or None if undefined. + + :param bool copy: + True to get a copy of the data, False to get the internal data. + :rtype: Union[numpy.ndarray,None] + """ + return None if self.__x is None else numpy.array(self.__x, copy=copy) + + def getYData(self, copy=True): + """Returns the Y data used for the fit or None if undefined. + + :param bool copy: + True to get a copy of the data, False to get the internal data. + :rtype: Union[numpy.ndarray,None] + """ + return None if self.__y is None else numpy.array(self.__y, copy=copy) + + def _getFittedItem(self): + """Returns the current item used for the fit + + :rtype: Union[~silx.gui.plot.items.Curve,~silx.gui.plot.items.Histogram,None] + """ + return self.__item + + def _setFittedItem(self, item): + """Set the curve to use for fitting. + + :param Union[~silx.gui.plot.items.Curve,~silx.gui.plot.items.Histogram,None] item: + """ + plot = self.plot + if plot is None: + _logger.error("No associated PlotWidget") + + if plot is None or item is None: + self.__item = None + self.__curveParams = {} + self.__updateFitWidget() + return + + axis = item.getYAxis() if isinstance(item, items.YAxisMixIn) else 'left' + self.__curveParams = { + 'yaxis': axis, + 'xlabel': plot.getXAxis().getLabel(), + 'ylabel': plot.getYAxis(axis).getLabel(), + } + self.__legend = item.getName() + + if isinstance(item, items.Histogram): bin_edges = item.getBinEdgesData(copy=False) # take the middle coordinate between adjacent bin edges - self.x = (bin_edges[1:] + bin_edges[:-1]) / 2 - self.y = item.getValueData(copy=False) + self.__x = (bin_edges[1:] + bin_edges[:-1]) / 2 + self.__y = item.getValueData(copy=False) # else take the active curve, or else the unique curve - elif isinstance(item, Curve): - self.x = item.getXData(copy=False) - self.y = item.getYData(copy=False) + elif isinstance(item, items.Curve): + self.__x = item.getXData(copy=False) + self.__y = item.getYData(copy=False) + + self.__item = item + self.__updateFitWidget() + + def __activeCurveChanged(self, previous, current): + """Handle change of active curve in the PlotWidget + """ + if current is None: + self._setFittedItem(None) + else: + item = self.plot.getCurve(current) + self._setFittedItem(item) + + def __setFittedItemAutoUpdateEnabled(self, enabled): + """Implement the change of fitted item update mode + + :param bool enabled: + """ + plot = self.plot + if plot is None: + _logger.error("No associated PlotWidget") + return + + if enabled: + self._setFittedItem(plot.getActiveCurve()) + plot.sigActiveCurveChanged.connect(self.__activeCurveChanged) + + else: + plot.sigActiveCurveChanged.disconnect( + self.__activeCurveChanged) + + def setFittedItemUpdatedFromActiveCurve(self, enabled): + """Toggle fitted data synchronization with plot active curve. + + :param bool enabled: + """ + enabled = bool(enabled) + if enabled != self.__activeCurveSynchroEnabled: + self.__activeCurveSynchroEnabled = enabled + if self._getToolWindow().isVisible(): + self.__setFittedItemAutoUpdateEnabled(enabled) + + def isFittedItemUpdatedFromActiveCurve(self): + """Returns True if fitted data is synchronized with plot. + + :rtype: bool + """ + return self.__activeCurveSynchroEnabled - window.setData(self.x, self.y, - xmin=self.xmin, xmax=self.xmax) - window.setWindowTitle( - "Fitting " + self.legend + - " on x range %f-%f" % (self.xmin, self.xmax)) + # Handle fit completed def handle_signal(self, ddict): - x_fit = self.x[self.xmin <= self.x] - x_fit = x_fit[x_fit <= self.xmax] - fit_legend = "Fit <%s>" % self.legend + xdata = self.getXData(copy=False) + if xdata is None: + _logger.error("No reference data to display fit result for") + return + + xmin, xmax = self.getXRange() + x_fit = xdata[xmin <= xdata] + x_fit = x_fit[x_fit <= xmax] + fit_legend = "Fit <%s>" % self.__legend fit_curve = self.plot.getCurve(fit_legend) if ddict["event"] == "FitFinished": @@ -175,11 +391,12 @@ class FitAction(PlotToolAction): if fit_curve is None: self.plot.addCurve(x_fit, y_fit, fit_legend, - xlabel=self.xlabel, ylabel=self.ylabel, - resetzoom=False) + resetzoom=False, + **self.__curveParams) else: fit_curve.setData(x_fit, y_fit) fit_curve.setVisible(True) + fit_curve.setYAxis(self.__curveParams.get('yaxis', 'left')) if ddict["event"] in ["FitStarted", "FitFailed"]: if fit_curve is not None: diff --git a/silx/gui/plot/actions/histogram.py b/silx/gui/plot/actions/histogram.py index 3bb3e6a..f3e6370 100644 --- a/silx/gui/plot/actions/histogram.py +++ b/silx/gui/plot/actions/histogram.py @@ -37,16 +37,83 @@ __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __date__ = "10/10/2018" __license__ = "MIT" +import numpy +import logging +import weakref + from .PlotToolAction import PlotToolAction from silx.math.histogram import Histogramnd from silx.math.combo import min_max -import numpy -import logging from silx.gui import qt +from silx.gui.plot import items _logger = logging.getLogger(__name__) +class _LastActiveItem(qt.QObject): + + sigActiveItemChanged = qt.Signal(object, object) + """Emitted when the active plot item have changed""" + + def __init__(self, parent, plot): + assert plot is not None + super(_LastActiveItem, self).__init__(parent=parent) + self.__plot = weakref.ref(plot) + self.__item = None + item = self.__findActiveItem() + self.setActiveItem(item) + plot.sigActiveImageChanged.connect(self._activeImageChanged) + plot.sigActiveScatterChanged.connect(self._activeScatterChanged) + + def getPlotWidget(self): + return self.__plot() + + def __findActiveItem(self): + plot = self.getPlotWidget() + image = plot.getActiveImage() + if image is not None: + return image + scatter = plot.getActiveScatter() + if scatter is not None: + return scatter + + def getActiveItem(self): + if self.__item is None: + return None + item = self.__item() + if item is None: + self.__item = None + return item + + def setActiveItem(self, item): + previous = self.getActiveItem() + if previous is item: + return + if item is None: + self.__item = None + else: + self.__item = weakref.ref(item) + self.sigActiveItemChanged.emit(previous, item) + + def _activeImageChanged(self, previous, current): + """Handle active image change""" + plot = self.getPlotWidget() + item = plot.getImage(current) + if item is None: + self.setActiveItem(None) + elif isinstance(item, items.ImageBase): + self.setActiveItem(item) + else: + # Do not touch anything, which is consistent with silx v0.12 behavior + pass + + def _activeScatterChanged(self, previous, current): + """Handle active scatter change""" + plot = self.getPlotWidget() + item = plot.getScatter(current) + self.setActiveItem(item) + + class PixelIntensitiesHistoAction(PlotToolAction): """QAction to plot the pixels intensities diagram @@ -61,66 +128,110 @@ class PixelIntensitiesHistoAction(PlotToolAction): text='pixels intensity', tooltip='Compute image intensity distribution', parent=parent) - self._connectedToActiveImage = False + self._lastItemFilter = _LastActiveItem(self, plot) self._histo = None + self._item = None def _connectPlot(self, window): - if not self._connectedToActiveImage: - self.plot.sigActiveImageChanged.connect( - self._activeImageChanged) - self._connectedToActiveImage = True - self.computeIntensityDistribution() + self._lastItemFilter.sigActiveItemChanged.connect(self._activeItemChanged) + item = self._lastItemFilter.getActiveItem() + self._setSelectedItem(item) PlotToolAction._connectPlot(self, window) def _disconnectPlot(self, window): - if self._connectedToActiveImage: - self.plot.sigActiveImageChanged.disconnect( - self._activeImageChanged) - self._connectedToActiveImage = False + self._lastItemFilter.sigActiveItemChanged.disconnect(self._activeItemChanged) PlotToolAction._disconnectPlot(self, window) + self._setSelectedItem(None) - def _activeImageChanged(self, previous, legend): - """Handle active image change: toggle enabled toolbar, update curve""" + def _getSelectedItem(self): + item = self._item + if item is None: + return None + else: + return item() + + def _activeItemChanged(self, previous, current): if self._isWindowInUse(): + self._setSelectedItem(current) + + def _setSelectedItem(self, item): + if item is not None: + if not isinstance(item, (items.ImageBase, items.Scatter)): + # Filter out other things + return + + old = self._getSelectedItem() + if item is old: + return + if old is not None: + old.sigItemChanged.disconnect(self._itemUpdated) + if item is None: + self._item = None + else: + self._item = weakref.ref(item) + item.sigItemChanged.connect(self._itemUpdated) + self.computeIntensityDistribution() + + def _itemUpdated(self, event): + if event == items.ItemChangedType.DATA: self.computeIntensityDistribution() + def _cleanUp(self): + plot = self.getHistogramPlotWidget() + try: + plot.remove('pixel intensity', kind='item') + except Exception: + pass + def computeIntensityDistribution(self): """Get the active image and compute the image intensity distribution """ - activeImage = self.plot.getActiveImage() + item = self._getSelectedItem() - if activeImage is not None: - image = activeImage.getData(copy=False) - if image.ndim == 3: # RGB(A) images + if item is None: + self._cleanUp() + return + + if isinstance(item, items.ImageBase): + array = item.getData(copy=False) + if array.ndim == 3: # RGB(A) images _logger.info('Converting current image from RGB(A) to grayscale\ in order to compute the intensity distribution') - image = (image[:, :, 0] * 0.299 + - image[:, :, 1] * 0.587 + - image[:, :, 2] * 0.114) - - xmin, xmax = min_max(image, min_positive=False, finite=True) - nbins = min(1024, int(numpy.sqrt(image.size))) - data_range = xmin, xmax - - # bad hack: get 256 bins in the case we have a B&W - if numpy.issubdtype(image.dtype, numpy.integer): - if nbins > xmax - xmin: - nbins = xmax - xmin - - nbins = max(2, nbins) - - data = image.ravel().astype(numpy.float32) - histogram = Histogramnd(data, n_bins=nbins, histo_range=data_range) - assert len(histogram.edges) == 1 - self._histo = histogram.histo - edges = histogram.edges[0] - plot = self.getHistogramPlotWidget() - plot.addHistogram(histogram=self._histo, - edges=edges, - legend='pixel intensity', - fill=True, - color='#66aad7') - plot.resetZoom() + array = (array[:, :, 0] * 0.299 + + array[:, :, 1] * 0.587 + + array[:, :, 2] * 0.114) + elif isinstance(item, items.Scatter): + array = item.getValueData(copy=False) + else: + assert(False) + + if array.size == 0: + self._cleanUp() + return + + xmin, xmax = min_max(array, min_positive=False, finite=True) + nbins = min(1024, int(numpy.sqrt(array.size))) + data_range = xmin, xmax + + # bad hack: get 256 bins in the case we have a B&W + if numpy.issubdtype(array.dtype, numpy.integer): + if nbins > xmax - xmin: + nbins = xmax - xmin + + nbins = max(2, nbins) + + data = array.ravel().astype(numpy.float32) + histogram = Histogramnd(data, n_bins=nbins, histo_range=data_range) + assert len(histogram.edges) == 1 + self._histo = histogram.histo + edges = histogram.edges[0] + plot = self.getHistogramPlotWidget() + plot.addHistogram(histogram=self._histo, + edges=edges, + legend='pixel intensity', + fill=True, + color='#66aad7') + plot.resetZoom() def getHistogramPlotWidget(self): """Create the plot histogram if needed, otherwise create it diff --git a/silx/gui/plot/actions/medfilt.py b/silx/gui/plot/actions/medfilt.py index 276f970..f86a377 100644 --- a/silx/gui/plot/actions/medfilt.py +++ b/silx/gui/plot/actions/medfilt.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2018 European Synchrotron Radiation Facility +# Copyright (c) 2004-2020 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -89,7 +89,7 @@ class MedianFilterAction(PlotToolAction): self._legend = None else: self._originalImage = self.plot.getImage(self._activeImageLegend).getData(copy=False) - self._legend = self.plot.getImage(self._activeImageLegend).getLegend() + self._legend = self.plot.getImage(self._activeImageLegend).getName() def _updateFilter(self, kernelWidth, conditional=False): if self._originalImage is None: -- cgit v1.2.3