diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2021-09-07 14:39:36 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2021-09-07 14:39:36 +0200 |
commit | d3194b1a9c4404ba93afac43d97172ab24c57098 (patch) | |
tree | a1604130e1401dc1cbd084518ed72869dc92b86f /silx/gui/dialog/ColormapDialog.py | |
parent | b3bea947efa55d2c0f198b6c6795b3177be27f45 (diff) |
New upstream version 0.15.2+dfsg
Diffstat (limited to 'silx/gui/dialog/ColormapDialog.py')
-rw-r--r-- | silx/gui/dialog/ColormapDialog.py | 178 |
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. |