summaryrefslogtreecommitdiff
path: root/silx/gui/plot/PlotToolButtons.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/PlotToolButtons.py')
-rw-r--r--silx/gui/plot/PlotToolButtons.py592
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)