summaryrefslogtreecommitdiff
path: root/silx/gui/plot/actions
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/actions')
-rw-r--r--silx/gui/plot/actions/PlotToolAction.py4
-rwxr-xr-xsilx/gui/plot/actions/control.py35
-rw-r--r--silx/gui/plot/actions/fit.py359
-rw-r--r--silx/gui/plot/actions/histogram.py201
-rw-r--r--silx/gui/plot/actions/medfilt.py4
5 files changed, 479 insertions, 124 deletions
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: