summaryrefslogtreecommitdiff
path: root/silx/gui/plot/actions/histogram.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/actions/histogram.py')
-rw-r--r--silx/gui/plot/actions/histogram.py201
1 files changed, 156 insertions, 45 deletions
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