diff options
Diffstat (limited to 'silx/gui/plot/PlotToolButtons.py')
-rw-r--r-- | silx/gui/plot/PlotToolButtons.py | 592 |
1 files changed, 0 insertions, 592 deletions
diff --git a/silx/gui/plot/PlotToolButtons.py b/silx/gui/plot/PlotToolButtons.py deleted file mode 100644 index 3970896..0000000 --- a/silx/gui/plot/PlotToolButtons.py +++ /dev/null @@ -1,592 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# 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 -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ###########################################################################*/ -"""This module provides a set of QToolButton to use with -:class:`~silx.gui.plot.PlotWidget`. - -The following QToolButton are available: - -- :class:`.AspectToolButton` -- :class:`.YAxisOriginToolButton` -- :class:`.ProfileToolButton` -- :class:`.SymbolToolButton` - -""" - -__authors__ = ["V. Valls", "H. Payno"] -__license__ = "MIT" -__date__ = "27/06/2017" - - -import functools -import logging -import weakref - -from .. import icons -from .. import qt -from ... import config - -from .items import SymbolMixIn, Scatter - - -_logger = logging.getLogger(__name__) - - -class PlotToolButton(qt.QToolButton): - """A QToolButton connected to a :class:`~silx.gui.plot.PlotWidget`. - """ - - def __init__(self, parent=None, plot=None): - super(PlotToolButton, self).__init__(parent) - self._plotRef = None - if plot is not None: - self.setPlot(plot) - - def plot(self): - """ - Returns the plot connected to the widget. - """ - return None if self._plotRef is None else self._plotRef() - - def setPlot(self, plot): - """ - Set the plot connected to the widget - - :param plot: :class:`.PlotWidget` instance on which to operate. - """ - previousPlot = self.plot() - - if previousPlot is plot: - return - if previousPlot is not None: - self._disconnectPlot(previousPlot) - - if plot is None: - self._plotRef = None - else: - self._plotRef = weakref.ref(plot) - self._connectPlot(plot) - - def _connectPlot(self, plot): - """ - Called when the plot is connected to the widget - - :param plot: :class:`.PlotWidget` instance - """ - pass - - def _disconnectPlot(self, plot): - """ - Called when the plot is disconnected from the widget - - :param plot: :class:`.PlotWidget` instance - """ - pass - - -class AspectToolButton(PlotToolButton): - """Tool button to switch keep aspect ratio of a plot""" - - STATE = None - """Lazy loaded states used to feed AspectToolButton""" - - def __init__(self, parent=None, plot=None): - if self.STATE is None: - self.STATE = {} - # dont keep ratio - self.STATE[False, "icon"] = icons.getQIcon('shape-ellipse-solid') - self.STATE[False, "state"] = "Aspect ratio is not kept" - self.STATE[False, "action"] = "Do no keep data aspect ratio" - # keep ratio - self.STATE[True, "icon"] = icons.getQIcon('shape-circle-solid') - self.STATE[True, "state"] = "Aspect ratio is kept" - self.STATE[True, "action"] = "Keep data aspect ratio" - - super(AspectToolButton, self).__init__(parent=parent, plot=plot) - - keepAction = self._createAction(True) - keepAction.triggered.connect(self.keepDataAspectRatio) - keepAction.setIconVisibleInMenu(True) - - dontKeepAction = self._createAction(False) - dontKeepAction.triggered.connect(self.dontKeepDataAspectRatio) - dontKeepAction.setIconVisibleInMenu(True) - - menu = qt.QMenu(self) - menu.addAction(keepAction) - menu.addAction(dontKeepAction) - self.setMenu(menu) - self.setPopupMode(qt.QToolButton.InstantPopup) - - def _createAction(self, keepAspectRatio): - icon = self.STATE[keepAspectRatio, "icon"] - text = self.STATE[keepAspectRatio, "action"] - return qt.QAction(icon, text, self) - - def _connectPlot(self, plot): - plot.sigSetKeepDataAspectRatio.connect(self._keepDataAspectRatioChanged) - self._keepDataAspectRatioChanged(plot.isKeepDataAspectRatio()) - - def _disconnectPlot(self, plot): - plot.sigSetKeepDataAspectRatio.disconnect(self._keepDataAspectRatioChanged) - - def keepDataAspectRatio(self): - """Configure the plot to keep the aspect ratio""" - plot = self.plot() - if plot is not None: - # This will trigger _keepDataAspectRatioChanged - plot.setKeepDataAspectRatio(True) - - def dontKeepDataAspectRatio(self): - """Configure the plot to not keep the aspect ratio""" - plot = self.plot() - if plot is not None: - # This will trigger _keepDataAspectRatioChanged - plot.setKeepDataAspectRatio(False) - - def _keepDataAspectRatioChanged(self, aspectRatio): - """Handle Plot set keep aspect ratio signal""" - icon, toolTip = self.STATE[aspectRatio, "icon"], self.STATE[aspectRatio, "state"] - self.setIcon(icon) - self.setToolTip(toolTip) - - -class YAxisOriginToolButton(PlotToolButton): - """Tool button to switch the Y axis orientation of a plot.""" - - STATE = None - """Lazy loaded states used to feed YAxisOriginToolButton""" - - def __init__(self, parent=None, plot=None): - if self.STATE is None: - self.STATE = {} - # is down - self.STATE[False, "icon"] = icons.getQIcon('plot-ydown') - self.STATE[False, "state"] = "Y-axis is oriented downward" - self.STATE[False, "action"] = "Orient Y-axis downward" - # keep ration - self.STATE[True, "icon"] = icons.getQIcon('plot-yup') - self.STATE[True, "state"] = "Y-axis is oriented upward" - self.STATE[True, "action"] = "Orient Y-axis upward" - - super(YAxisOriginToolButton, self).__init__(parent=parent, plot=plot) - - upwardAction = self._createAction(True) - upwardAction.triggered.connect(self.setYAxisUpward) - upwardAction.setIconVisibleInMenu(True) - - downwardAction = self._createAction(False) - downwardAction.triggered.connect(self.setYAxisDownward) - downwardAction.setIconVisibleInMenu(True) - - menu = qt.QMenu(self) - menu.addAction(upwardAction) - menu.addAction(downwardAction) - self.setMenu(menu) - self.setPopupMode(qt.QToolButton.InstantPopup) - - def _createAction(self, isUpward): - icon = self.STATE[isUpward, "icon"] - text = self.STATE[isUpward, "action"] - return qt.QAction(icon, text, self) - - def _connectPlot(self, plot): - yAxis = plot.getYAxis() - yAxis.sigInvertedChanged.connect(self._yAxisInvertedChanged) - self._yAxisInvertedChanged(yAxis.isInverted()) - - def _disconnectPlot(self, plot): - plot.getYAxis().sigInvertedChanged.disconnect(self._yAxisInvertedChanged) - - def setYAxisUpward(self): - """Configure the plot to use y-axis upward""" - plot = self.plot() - if plot is not None: - # This will trigger _yAxisInvertedChanged - plot.getYAxis().setInverted(False) - - def setYAxisDownward(self): - """Configure the plot to use y-axis downward""" - plot = self.plot() - if plot is not None: - # This will trigger _yAxisInvertedChanged - plot.getYAxis().setInverted(True) - - def _yAxisInvertedChanged(self, inverted): - """Handle Plot set y axis inverted signal""" - isUpward = not inverted - icon, toolTip = self.STATE[isUpward, "icon"], self.STATE[isUpward, "state"] - self.setIcon(icon) - self.setToolTip(toolTip) - - -class ProfileOptionToolButton(PlotToolButton): - """Button to define option on the profile""" - sigMethodChanged = qt.Signal(str) - - def __init__(self, parent=None, plot=None): - PlotToolButton.__init__(self, parent=parent, plot=plot) - - self.STATE = {} - # is down - self.STATE['sum', "icon"] = icons.getQIcon('math-sigma') - self.STATE['sum', "state"] = "Compute profile sum" - self.STATE['sum', "action"] = "Compute profile sum" - # keep ration - self.STATE['mean', "icon"] = icons.getQIcon('math-mean') - self.STATE['mean', "state"] = "Compute profile mean" - self.STATE['mean', "action"] = "Compute profile mean" - - self.sumAction = self._createAction('sum') - self.sumAction.triggered.connect(self.setSum) - self.sumAction.setIconVisibleInMenu(True) - self.sumAction.setCheckable(True) - self.sumAction.setChecked(True) - - self.meanAction = self._createAction('mean') - self.meanAction.triggered.connect(self.setMean) - self.meanAction.setIconVisibleInMenu(True) - self.meanAction.setCheckable(True) - - menu = qt.QMenu(self) - menu.addAction(self.sumAction) - menu.addAction(self.meanAction) - self.setMenu(menu) - self.setPopupMode(qt.QToolButton.InstantPopup) - self._method = 'mean' - self._update() - - def _createAction(self, method): - icon = self.STATE[method, "icon"] - text = self.STATE[method, "action"] - return qt.QAction(icon, text, self) - - def setSum(self): - self.setMethod('sum') - - def _update(self): - icon = self.STATE[self._method, "icon"] - toolTip = self.STATE[self._method, "state"] - self.setIcon(icon) - self.setToolTip(toolTip) - self.sumAction.setChecked(self._method == "sum") - self.meanAction.setChecked(self._method == "mean") - - def setMean(self): - self.setMethod('mean') - - def setMethod(self, method): - """Set the method to use. - - :param str method: Either 'sum' or 'mean' - """ - if method != self._method: - if method in ('sum', 'mean'): - self._method = method - self.sigMethodChanged.emit(self._method) - self._update() - else: - _logger.warning( - "Unsupported method '%s'. Setting ignored.", method) - - def getMethod(self): - """Returns the current method in use (See :meth:`setMethod`). - - :rtype: str - """ - return self._method - - -class ProfileToolButton(PlotToolButton): - """Button used in Profile3DToolbar to switch between 2D profile - and 1D profile.""" - STATE = None - """Lazy loaded states used to feed ProfileToolButton""" - - sigDimensionChanged = qt.Signal(int) - - def __init__(self, parent=None, plot=None): - if self.STATE is None: - self.STATE = { - (1, "icon"): icons.getQIcon('profile1D'), - (1, "state"): "1D profile is computed on visible image", - (1, "action"): "1D profile on visible image", - (2, "icon"): icons.getQIcon('profile2D'), - (2, "state"): "2D profile is computed, one 1D profile for each image in the stack", - (2, "action"): "2D profile on image stack"} - # Compute 1D profile - # Compute 2D profile - - super(ProfileToolButton, self).__init__(parent=parent, plot=plot) - - self._dimension = 1 - - profile1DAction = self._createAction(1) - profile1DAction.triggered.connect(self.computeProfileIn1D) - profile1DAction.setIconVisibleInMenu(True) - profile1DAction.setCheckable(True) - profile1DAction.setChecked(True) - self._profile1DAction = profile1DAction - - profile2DAction = self._createAction(2) - profile2DAction.triggered.connect(self.computeProfileIn2D) - profile2DAction.setIconVisibleInMenu(True) - profile2DAction.setCheckable(True) - self._profile2DAction = profile2DAction - - menu = qt.QMenu(self) - menu.addAction(profile1DAction) - menu.addAction(profile2DAction) - self.setMenu(menu) - self.setPopupMode(qt.QToolButton.InstantPopup) - menu.setTitle('Select profile dimension') - self.computeProfileIn1D() - - def _createAction(self, profileDimension): - icon = self.STATE[profileDimension, "icon"] - text = self.STATE[profileDimension, "action"] - return qt.QAction(icon, text, self) - - def _profileDimensionChanged(self, profileDimension): - """Update icon in toolbar, emit number of dimensions for profile""" - self.setIcon(self.STATE[profileDimension, "icon"]) - self.setToolTip(self.STATE[profileDimension, "state"]) - self._dimension = profileDimension - self.sigDimensionChanged.emit(profileDimension) - self._profile1DAction.setChecked(profileDimension == 1) - self._profile2DAction.setChecked(profileDimension == 2) - - def computeProfileIn1D(self): - self._profileDimensionChanged(1) - - def computeProfileIn2D(self): - self._profileDimensionChanged(2) - - def setDimension(self, dimension): - """Set the selected dimension""" - assert dimension in [1, 2] - if self._dimension == dimension: - return - if dimension == 1: - self.computeProfileIn1D() - elif dimension == 2: - self.computeProfileIn2D() - else: - _logger.warning("Unsupported dimension '%s'. Setting ignored.", dimension) - - def getDimension(self): - """Get the selected dimension. - - :rtype: int (1 or 2) - """ - return self._dimension - - -class _SymbolToolButtonBase(PlotToolButton): - """Base class for PlotToolButton setting marker and size. - - :param parent: See QWidget - :param plot: The `~silx.gui.plot.PlotWidget` to control - """ - - def __init__(self, parent=None, plot=None): - super(_SymbolToolButtonBase, self).__init__(parent=parent, plot=plot) - - def _addSizeSliderToMenu(self, menu): - """Add a slider to set size to the given menu - - :param QMenu menu: - """ - slider = qt.QSlider(qt.Qt.Horizontal) - slider.setRange(1, 20) - slider.setValue(int(config.DEFAULT_PLOT_SYMBOL_SIZE)) - slider.setTracking(False) - slider.valueChanged.connect(self._sizeChanged) - widgetAction = qt.QWidgetAction(menu) - widgetAction.setDefaultWidget(slider) - menu.addAction(widgetAction) - - def _addSymbolsToMenu(self, menu): - """Add symbols to the given menu - - :param QMenu menu: - """ - for marker, name in zip(SymbolMixIn.getSupportedSymbols(), - SymbolMixIn.getSupportedSymbolNames()): - action = qt.QAction(name, menu) - action.setCheckable(False) - action.triggered.connect( - functools.partial(self._markerChanged, marker)) - menu.addAction(action) - - def _sizeChanged(self, value): - """Manage slider value changed - - :param int value: Marker size - """ - plot = self.plot() - if plot is None: - return - - for item in plot.getItems(): - if isinstance(item, SymbolMixIn): - item.setSymbolSize(value) - - def _markerChanged(self, marker): - """Manage change of marker. - - :param str marker: Letter describing the marker - """ - plot = self.plot() - if plot is None: - return - - for item in plot.getItems(): - if isinstance(item, SymbolMixIn): - item.setSymbol(marker) - - -class SymbolToolButton(_SymbolToolButtonBase): - """A tool button with a drop-down menu to control symbol size and marker. - - :param parent: See QWidget - :param plot: The `~silx.gui.plot.PlotWidget` to control - """ - - def __init__(self, parent=None, plot=None): - super(SymbolToolButton, self).__init__(parent=parent, plot=plot) - - self.setToolTip('Set symbol size and marker') - self.setIcon(icons.getQIcon('plot-symbols')) - - menu = qt.QMenu(self) - self._addSizeSliderToMenu(menu) - menu.addSeparator() - self._addSymbolsToMenu(menu) - - self.setMenu(menu) - self.setPopupMode(qt.QToolButton.InstantPopup) - - -class ScatterVisualizationToolButton(_SymbolToolButtonBase): - """QToolButton to select the visualization mode of scatter plot - - :param parent: See QWidget - :param plot: The `~silx.gui.plot.PlotWidget` to control - """ - - def __init__(self, parent=None, plot=None): - super(ScatterVisualizationToolButton, self).__init__( - parent=parent, plot=plot) - - self.setToolTip( - 'Set scatter visualization mode, symbol marker and size') - self.setIcon(icons.getQIcon('eye')) - - menu = qt.QMenu(self) - - # Add visualization modes - - for mode in Scatter.supportedVisualizations(): - if mode is not Scatter.Visualization.BINNED_STATISTIC: - name = mode.value.capitalize() - action = qt.QAction(name, menu) - action.setCheckable(False) - action.triggered.connect( - functools.partial(self._visualizationChanged, mode, None)) - menu.addAction(action) - - if Scatter.Visualization.BINNED_STATISTIC in Scatter.supportedVisualizations(): - reductions = Scatter.supportedVisualizationParameterValues( - Scatter.VisualizationParameter.BINNED_STATISTIC_FUNCTION) - if reductions: - submenu = menu.addMenu('Binned Statistic') - for reduction in reductions: - name = reduction.capitalize() - action = qt.QAction(name, menu) - action.setCheckable(False) - action.triggered.connect(functools.partial( - self._visualizationChanged, - Scatter.Visualization.BINNED_STATISTIC, - {Scatter.VisualizationParameter.BINNED_STATISTIC_FUNCTION: reduction})) - submenu.addAction(action) - - submenu.addSeparator() - binsmenu = submenu.addMenu('N Bins') - - slider = qt.QSlider(qt.Qt.Horizontal) - slider.setRange(10, 1000) - slider.setValue(100) - slider.setTracking(False) - slider.valueChanged.connect(self._binningChanged) - widgetAction = qt.QWidgetAction(binsmenu) - widgetAction.setDefaultWidget(slider) - binsmenu.addAction(widgetAction) - - menu.addSeparator() - - submenu = menu.addMenu(icons.getQIcon('plot-symbols'), "Symbol") - self._addSymbolsToMenu(submenu) - - submenu = menu.addMenu(icons.getQIcon('plot-symbols'), "Symbol Size") - self._addSizeSliderToMenu(submenu) - - self.setMenu(menu) - self.setPopupMode(qt.QToolButton.InstantPopup) - - def _visualizationChanged(self, mode, parameters=None): - """Handle change of visualization mode. - - :param ScatterVisualizationMixIn.Visualization mode: - The visualization mode to use for scatter - :param Union[dict,None] parameters: - Dict of VisualizationParameter: parameter_value to set - with the visualization. - """ - plot = self.plot() - if plot is None: - return - - for item in plot.getItems(): - if isinstance(item, Scatter): - if parameters: - for parameter, value in parameters.items(): - item.setVisualizationParameter(parameter, value) - item.setVisualization(mode) - - def _binningChanged(self, value): - """Handle change of binning. - - :param int value: The number of bin on each dimension. - """ - plot = self.plot() - if plot is None: - return - - for item in plot.getItems(): - if isinstance(item, Scatter): - item.setVisualizationParameter( - Scatter.VisualizationParameter.BINNED_STATISTIC_SHAPE, - (value, value)) - item.setVisualization(Scatter.Visualization.BINNED_STATISTIC) |