summaryrefslogtreecommitdiff
path: root/silx/gui/dialog/ColormapDialog.py
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@debian.org>2021-09-07 14:39:36 +0200
committerPicca Frédéric-Emmanuel <picca@debian.org>2021-09-07 14:39:36 +0200
commitd3194b1a9c4404ba93afac43d97172ab24c57098 (patch)
treea1604130e1401dc1cbd084518ed72869dc92b86f /silx/gui/dialog/ColormapDialog.py
parentb3bea947efa55d2c0f198b6c6795b3177be27f45 (diff)
New upstream version 0.15.2+dfsg
Diffstat (limited to 'silx/gui/dialog/ColormapDialog.py')
-rw-r--r--silx/gui/dialog/ColormapDialog.py178
1 files changed, 166 insertions, 12 deletions
diff --git a/silx/gui/dialog/ColormapDialog.py b/silx/gui/dialog/ColormapDialog.py
index 6b5d83b..ca7ee97 100644
--- a/silx/gui/dialog/ColormapDialog.py
+++ b/silx/gui/dialog/ColormapDialog.py
@@ -59,12 +59,9 @@ The updates of the colormap description are also available through the signal:
:attr:`ColormapDialog.sigColormapChanged`.
""" # noqa
-from __future__ import division
-
__authors__ = ["V.A. Sole", "T. Vincent", "H. Payno"]
__license__ = "MIT"
-__date__ = "27/11/2018"
-
+__date__ = "08/12/2020"
import enum
import logging
@@ -73,7 +70,7 @@ import numpy
from .. import qt
from .. import utils
-from ..colors import Colormap
+from ..colors import Colormap, cursorColorForColormap
from ..plot import PlotWidget
from ..plot.items.axis import Axis
from ..plot.items import BoundingRect
@@ -84,12 +81,14 @@ from silx.gui.plot import items
from silx.gui import icons
from silx.gui.qt import inspect as qtinspect
from silx.gui.widgets.ColormapNameComboBox import ColormapNameComboBox
+from silx.gui.widgets.WaitingPushButton import WaitingPushButton
from silx.math.histogram import Histogramnd
from silx.utils import deprecation
+from silx.gui.plot.items.roi import RectangleROI
+from silx.gui.plot.tools.roi import RegionOfInterestManager
_logger = logging.getLogger(__name__)
-
_colormapIconPreview = {}
@@ -510,6 +509,7 @@ class _ColormapHistogram(qt.QWidget):
:returns: Tuple{float, float}
"""
scale = self._plot.getXAxis().getScale()
+
def isDisplayable(pos):
if pos is None:
return False
@@ -770,7 +770,8 @@ class _ColormapHistogram(qt.QWidget):
else:
histogram = numpy.array(histogram, copy=True)
bin_edges = numpy.array(bin_edges, copy=True)
- norm_histogram = histogram / max(histogram)
+ with numpy.errstate(invalid='ignore'):
+ norm_histogram = histogram / numpy.nanmax(histogram)
self._plot.addHistogram(norm_histogram,
bin_edges,
legend="Data",
@@ -917,6 +918,27 @@ class ColormapDialog(qt.QDialog):
self._histoWidget.sigRangeMoving.connect(self._histogramRangeMoving)
self._histoWidget.sigRangeMoved.connect(self._histogramRangeMoved)
+ # Scale to buttons
+ self._visibleAreaButton = qt.QPushButton(self)
+ self._visibleAreaButton.setEnabled(False)
+ self._visibleAreaButton.setText("Visible Area")
+ self._visibleAreaButton.clicked.connect(
+ self._handleScaleToVisibleAreaClicked,
+ type=qt.Qt.QueuedConnection)
+
+ # Place-holder for selected area ROI manager
+ self._roiForColormapManager = None
+
+ self._selectedAreaButton = WaitingPushButton(self)
+ self._selectedAreaButton.setEnabled(False)
+ self._selectedAreaButton.setText("Selection")
+ self._selectedAreaButton.setIcon(icons.getQIcon("add-shape-rectangle"))
+ self._selectedAreaButton.setCheckable(True)
+ self._selectedAreaButton.setDisabledWhenWaiting(False)
+ self._selectedAreaButton.toggled.connect(
+ self._handleScaleToSelectionToggled,
+ type=qt.Qt.QueuedConnection)
+
# define modal buttons
types = qt.QDialogButtonBox.Ok | qt.QDialogButtonBox.Cancel
self._buttonsModal = qt.QDialogButtonBox(parent=self)
@@ -955,6 +977,16 @@ class ColormapDialog(qt.QDialog):
label.setToolTip("Mode for autoscale. Algorithm used to find range in auto scale.")
formLayout.addItem(qt.QSpacerItem(1, 1, qt.QSizePolicy.Fixed, qt.QSizePolicy.Fixed))
formLayout.addRow(label, autoScaleCombo)
+
+ layout = qt.QHBoxLayout()
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.addWidget(self._visibleAreaButton)
+ layout.addWidget(self._selectedAreaButton)
+ self._scaleToAreaGroup = qt.QGroupBox('Scale to:', self)
+ self._scaleToAreaGroup.setLayout(layout)
+ self._scaleToAreaGroup.setVisible(False)
+ formLayout.addRow(self._scaleToAreaGroup)
+
formLayout.addRow(self._buttonsModal)
formLayout.addRow(self._buttonsNonModal)
formLayout.setSizeConstraint(qt.QLayout.SetMinimumSize)
@@ -965,7 +997,9 @@ class ColormapDialog(qt.QDialog):
self.setTabOrder(self._minValue, self._maxValue)
self.setTabOrder(self._maxValue, self._autoButtons)
self.setTabOrder(self._autoButtons, self._autoScaleCombo)
- self.setTabOrder(self._autoScaleCombo, self._buttonsModal)
+ self.setTabOrder(self._autoScaleCombo, self._visibleAreaButton)
+ self.setTabOrder(self._visibleAreaButton, self._selectedAreaButton)
+ self.setTabOrder(self._selectedAreaButton, self._buttonsModal)
self.setTabOrder(self._buttonsModal, self._buttonsNonModal)
self.setFixedSize(self.sizeHint())
@@ -1094,9 +1128,9 @@ class ColormapDialog(qt.QDialog):
if data.ndim == 3: # RGB(A) images
_logger.info('Converting current image from RGB(A) to grayscale\
in order to compute the intensity distribution')
- data = (data[:, :, 0] * 0.299 +
- data[:, :, 1] * 0.587 +
- data[:, :, 2] * 0.114)
+ data = (data[:,:, 0] * 0.299 +
+ data[:,:, 1] * 0.587 +
+ data[:,:, 2] * 0.114)
# bad hack: get 256 continuous bins in the case we have a B&W
normalizeData = True
@@ -1141,7 +1175,7 @@ class ColormapDialog(qt.QDialog):
bins = histogram.edges[0]
if normalizeData:
if scale == Colormap.LOGARITHM:
- bins = 10**bins
+ bins = 10 ** bins
return histogram.histo, bins
def _getItem(self):
@@ -1173,6 +1207,7 @@ class ColormapDialog(qt.QDialog):
raise ValueError("Item %s is not supported" % item)
self._item = weakref.ref(item, self._itemAboutToFinalize)
finally:
+ self._syncScaleToButtonsEnabled()
self._dataRange = None
self._histogramData = None
self._invalidateData()
@@ -1194,6 +1229,7 @@ class ColormapDialog(qt.QDialog):
return
self._item = None
+ self._syncScaleToButtonsEnabled()
if data is None:
self._data = None
self._itemHolder = None
@@ -1311,6 +1347,55 @@ class ColormapDialog(qt.QDialog):
colormap.setVRange(xmin, xmax)
self._updateWidgetRange()
+ def setColormapRangeFromDataBounds(self, bounds):
+ """Set the range of the colormap from current item and rect.
+
+ If there is no ColormapMixIn item attached to the ColormapDialog,
+ nothing is done.
+
+ :param Union[List[float],None] bounds:
+ (xmin, xmax, ymin, ymax) Rectangular region in data space
+ """
+ if bounds is None:
+ return None # no-op
+
+ colormap = self.getColormap()
+ if colormap is None:
+ return # no-op
+
+ item = self._getItem()
+ if not isinstance(item, items.ColormapMixIn):
+ return None # no-op
+
+ data = item.getColormappedData(copy=False)
+
+ xmin, xmax, ymin, ymax = bounds
+
+ if isinstance(item, items.ImageBase):
+ ox, oy = item.getOrigin()
+ sx, sy = item.getScale()
+
+ ystart = max(0, int((ymin - oy) / sy))
+ ystop = max(0, int(numpy.ceil((ymax - oy) / sy)))
+ xstart = max(0, int((xmin - ox) / sx))
+ xstop = max(0, int(numpy.ceil((xmax - ox) / sx)))
+
+ subset = data[ystart:ystop, xstart:xstop]
+
+ elif isinstance(item, items.Scatter):
+ x = item.getXData(copy=False)
+ y = item.getYData(copy=False)
+ subset = data[
+ numpy.logical_and(
+ numpy.logical_and(xmin <= x, x <= xmax),
+ numpy.logical_and(ymin <= y, y <= ymax))]
+
+ if subset.size == 0:
+ return # no-op
+
+ vmin, vmax = colormap._computeAutoscaleRange(subset)
+ self._setColormapRange(vmin, vmax)
+
def _updateWidgetRange(self):
"""Update the colormap range displayed into the widget."""
xmin, xmax = self._getFiniteColormapRange()
@@ -1387,6 +1472,8 @@ class ColormapDialog(qt.QDialog):
if self._colormapChange.locked():
return
+ self._syncScaleToButtonsEnabled()
+
colormap = self.getColormap()
if colormap is None:
self._comboBoxColormap.setEnabled(False)
@@ -1591,6 +1678,73 @@ class ColormapDialog(qt.QDialog):
vmax = xmax
self._setColormapRange(vmin, vmax)
+ def _syncScaleToButtonsEnabled(self):
+ """Set the state of scale to buttons according to current item and colormap"""
+ colormap = self.getColormap()
+ enabled = self._item is not None and colormap is not None and colormap.isEditable()
+ self._scaleToAreaGroup.setVisible(enabled)
+ self._visibleAreaButton.setEnabled(enabled)
+ if not enabled:
+ self._selectedAreaButton.setChecked(False)
+ self._selectedAreaButton.setEnabled(enabled)
+
+ def _handleScaleToVisibleAreaClicked(self):
+ """Set colormap range from current item's visible area"""
+ item = self._getItem()
+ if item is None:
+ return # no-op
+
+ bounds = item.getVisibleBounds()
+ if bounds is None:
+ return # no-op
+
+ self.setColormapRangeFromDataBounds(bounds)
+
+ def _handleScaleToSelectionToggled(self, checked=False):
+ """Handle toggle of scale to selected are button"""
+ # Reset any previous ROI manager
+ if self._roiForColormapManager is not None:
+ self._roiForColormapManager.clear()
+ self._roiForColormapManager.stop()
+ self._roiForColormapManager = None
+
+ if not checked: # Reset button status
+ self._selectedAreaButton.setWaiting(False)
+ self._selectedAreaButton.setText("Selection")
+ return
+
+ item = self._getItem()
+ if item is None:
+ self._selectedAreaButton.setChecked(False)
+ return # no-op
+
+ plotWidget = item.getPlot()
+ if plotWidget is None:
+ self._selectedAreaButton.setChecked(False)
+ return # no-op
+
+ self._selectedAreaButton.setWaiting(True)
+ self._selectedAreaButton.setText("Draw Area...")
+
+ self._roiForColormapManager = RegionOfInterestManager(parent=plotWidget)
+ cmap = self.getColormap()
+ self._roiForColormapManager.setColor(
+ 'black' if cmap is None else cursorColorForColormap(cmap.getName()))
+ self._roiForColormapManager.sigInteractiveModeFinished.connect(
+ self.__roiInteractiveModeFinished)
+ self._roiForColormapManager.sigInteractiveRoiFinalized.connect(self.__roiFinalized)
+ self._roiForColormapManager.start(RectangleROI)
+
+ def __roiInteractiveModeFinished(self):
+ self._selectedAreaButton.setChecked(False)
+
+ def __roiFinalized(self, roi):
+ self._selectedAreaButton.setChecked(False)
+ if roi is not None:
+ ox, oy = roi.getOrigin()
+ width, height = roi.getSize()
+ self.setColormapRangeFromDataBounds((ox, ox+width, oy, oy+height))
+
def keyPressEvent(self, event):
"""Override key handling.