summaryrefslogtreecommitdiff
path: root/src/silx/gui/plot/actions/control.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/gui/plot/actions/control.py')
-rwxr-xr-xsrc/silx/gui/plot/actions/control.py694
1 files changed, 694 insertions, 0 deletions
diff --git a/src/silx/gui/plot/actions/control.py b/src/silx/gui/plot/actions/control.py
new file mode 100755
index 0000000..439985e
--- /dev/null
+++ b/src/silx/gui/plot/actions/control.py
@@ -0,0 +1,694 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2004-2019 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.
+#
+# ###########################################################################*/
+"""
+:mod:`silx.gui.plot.actions.control` provides a set of QAction relative to control
+of a :class:`.PlotWidget`.
+
+The following QAction are available:
+
+- :class:`ColormapAction`
+- :class:`CrosshairAction`
+- :class:`CurveStyleAction`
+- :class:`GridAction`
+- :class:`KeepAspectRatioAction`
+- :class:`PanWithArrowKeysAction`
+- :class:`ResetZoomAction`
+- :class:`ShowAxisAction`
+- :class:`XAxisLogarithmicAction`
+- :class:`XAxisAutoScaleAction`
+- :class:`YAxisInvertedAction`
+- :class:`YAxisLogarithmicAction`
+- :class:`YAxisAutoScaleAction`
+- :class:`ZoomBackAction`
+- :class:`ZoomInAction`
+- :class:`ZoomOutAction`
+"""
+
+from __future__ import division
+
+__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"]
+__license__ = "MIT"
+__date__ = "27/11/2020"
+
+from . import PlotAction
+import logging
+from silx.gui.plot import items
+from silx.gui.plot._utils import applyZoomToPlot as _applyZoomToPlot
+from silx.gui import qt
+from silx.gui import icons
+
+_logger = logging.getLogger(__name__)
+
+
+class ResetZoomAction(PlotAction):
+ """QAction controlling reset zoom on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(ResetZoomAction, self).__init__(
+ plot, icon='zoom-original', text='Reset Zoom',
+ tooltip='Auto-scale the graph',
+ triggered=self._actionTriggered,
+ checkable=False, parent=parent)
+ self._autoscaleChanged(True)
+ plot.getXAxis().sigAutoScaleChanged.connect(self._autoscaleChanged)
+ plot.getYAxis().sigAutoScaleChanged.connect(self._autoscaleChanged)
+
+ def _autoscaleChanged(self, enabled):
+ xAxis = self.plot.getXAxis()
+ yAxis = self.plot.getYAxis()
+ self.setEnabled(xAxis.isAutoScale() or yAxis.isAutoScale())
+
+ if xAxis.isAutoScale() and yAxis.isAutoScale():
+ tooltip = 'Auto-scale the graph'
+ elif xAxis.isAutoScale(): # And not Y axis
+ tooltip = 'Auto-scale the x-axis of the graph only'
+ elif yAxis.isAutoScale(): # And not X axis
+ tooltip = 'Auto-scale the y-axis of the graph only'
+ else: # no axis in autoscale
+ tooltip = 'Auto-scale the graph'
+ self.setToolTip(tooltip)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.resetZoom()
+
+
+class ZoomBackAction(PlotAction):
+ """QAction performing a zoom-back in :class:`.PlotWidget` limits history.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(ZoomBackAction, self).__init__(
+ plot, icon='zoom-back', text='Zoom Back',
+ tooltip='Zoom back the plot',
+ triggered=self._actionTriggered,
+ checkable=False, parent=parent)
+ self.setShortcutContext(qt.Qt.WidgetShortcut)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.getLimitsHistory().pop()
+
+
+class ZoomInAction(PlotAction):
+ """QAction performing a zoom-in on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(ZoomInAction, self).__init__(
+ plot, icon='zoom-in', text='Zoom In',
+ tooltip='Zoom in the plot',
+ triggered=self._actionTriggered,
+ checkable=False, parent=parent)
+ self.setShortcut(qt.QKeySequence.ZoomIn)
+ self.setShortcutContext(qt.Qt.WidgetShortcut)
+
+ def _actionTriggered(self, checked=False):
+ _applyZoomToPlot(self.plot, 1.1)
+
+
+class ZoomOutAction(PlotAction):
+ """QAction performing a zoom-out on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(ZoomOutAction, self).__init__(
+ plot, icon='zoom-out', text='Zoom Out',
+ tooltip='Zoom out the plot',
+ triggered=self._actionTriggered,
+ checkable=False, parent=parent)
+ self.setShortcut(qt.QKeySequence.ZoomOut)
+ self.setShortcutContext(qt.Qt.WidgetShortcut)
+
+ def _actionTriggered(self, checked=False):
+ _applyZoomToPlot(self.plot, 1. / 1.1)
+
+
+class XAxisAutoScaleAction(PlotAction):
+ """QAction controlling X axis autoscale on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(XAxisAutoScaleAction, self).__init__(
+ plot, icon='plot-xauto', text='X Autoscale',
+ tooltip='Enable x-axis auto-scale when checked.\n'
+ 'If unchecked, x-axis does not change when reseting zoom.',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.setChecked(plot.getXAxis().isAutoScale())
+ plot.getXAxis().sigAutoScaleChanged.connect(self.setChecked)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.getXAxis().setAutoScale(checked)
+ if checked:
+ self.plot.resetZoom()
+
+
+class YAxisAutoScaleAction(PlotAction):
+ """QAction controlling Y axis autoscale on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(YAxisAutoScaleAction, self).__init__(
+ plot, icon='plot-yauto', text='Y Autoscale',
+ tooltip='Enable y-axis auto-scale when checked.\n'
+ 'If unchecked, y-axis does not change when reseting zoom.',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.setChecked(plot.getYAxis().isAutoScale())
+ plot.getYAxis().sigAutoScaleChanged.connect(self.setChecked)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.getYAxis().setAutoScale(checked)
+ if checked:
+ self.plot.resetZoom()
+
+
+class XAxisLogarithmicAction(PlotAction):
+ """QAction controlling X axis log scale on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(XAxisLogarithmicAction, self).__init__(
+ plot, icon='plot-xlog', text='X Log. scale',
+ tooltip='Logarithmic x-axis when checked',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.axis = plot.getXAxis()
+ self.setChecked(self.axis.getScale() == self.axis.LOGARITHMIC)
+ self.axis.sigScaleChanged.connect(self._setCheckedIfLogScale)
+
+ def _setCheckedIfLogScale(self, scale):
+ self.setChecked(scale == self.axis.LOGARITHMIC)
+
+ def _actionTriggered(self, checked=False):
+ scale = self.axis.LOGARITHMIC if checked else self.axis.LINEAR
+ self.axis.setScale(scale)
+
+
+class YAxisLogarithmicAction(PlotAction):
+ """QAction controlling Y axis log scale on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(YAxisLogarithmicAction, self).__init__(
+ plot, icon='plot-ylog', text='Y Log. scale',
+ tooltip='Logarithmic y-axis when checked',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.axis = plot.getYAxis()
+ self.setChecked(self.axis.getScale() == self.axis.LOGARITHMIC)
+ self.axis.sigScaleChanged.connect(self._setCheckedIfLogScale)
+
+ def _setCheckedIfLogScale(self, scale):
+ self.setChecked(scale == self.axis.LOGARITHMIC)
+
+ def _actionTriggered(self, checked=False):
+ scale = self.axis.LOGARITHMIC if checked else self.axis.LINEAR
+ self.axis.setScale(scale)
+
+
+class GridAction(PlotAction):
+ """QAction controlling grid mode on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param str gridMode: The grid mode to use in 'both', 'major'.
+ See :meth:`.PlotWidget.setGraphGrid`
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, gridMode='both', parent=None):
+ assert gridMode in ('both', 'major')
+ self._gridMode = gridMode
+
+ super(GridAction, self).__init__(
+ plot, icon='plot-grid', text='Grid',
+ tooltip='Toggle grid (on/off)',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.setChecked(plot.getGraphGrid() is not None)
+ plot.sigSetGraphGrid.connect(self._gridChanged)
+
+ def _gridChanged(self, which):
+ """Slot listening for PlotWidget grid mode change."""
+ self.setChecked(which != 'None')
+
+ def _actionTriggered(self, checked=False):
+ self.plot.setGraphGrid(self._gridMode if checked else None)
+
+
+class CurveStyleAction(PlotAction):
+ """QAction controlling curve style on a :class:`.PlotWidget`.
+
+ It changes the default line and markers style which updates all
+ curves on the plot.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ super(CurveStyleAction, self).__init__(
+ plot, icon='plot-toggle-points', text='Curve style',
+ tooltip='Change curve line and markers style',
+ triggered=self._actionTriggered,
+ checkable=False, parent=parent)
+
+ def _actionTriggered(self, checked=False):
+ currentState = (self.plot.isDefaultPlotLines(),
+ self.plot.isDefaultPlotPoints())
+
+ if currentState == (False, False):
+ newState = True, False
+ else:
+ # line only, line and symbol, symbol only
+ states = (True, False), (True, True), (False, True)
+ newState = states[(states.index(currentState) + 1) % 3]
+
+ self.plot.setDefaultPlotLines(newState[0])
+ self.plot.setDefaultPlotPoints(newState[1])
+
+
+class ColormapAction(PlotAction):
+ """QAction opening a ColormapDialog to update the colormap.
+
+ Both the active image colormap and the default colormap are updated.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ self._dialog = None # To store an instance of ColormapDialog
+ super(ColormapAction, self).__init__(
+ plot, icon='colormap', text='Colormap',
+ tooltip="Change colormap",
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.plot.sigActiveImageChanged.connect(self._updateColormap)
+ self.plot.sigActiveScatterChanged.connect(self._updateColormap)
+
+ def setColorDialog(self, colorDialog):
+ """Set a specific color dialog instead of using the default dialog."""
+ assert(colorDialog is not None)
+ assert(self._dialog is None)
+ self._dialog = colorDialog
+ self._dialog.visibleChanged.connect(self._dialogVisibleChanged)
+ self.setChecked(self._dialog.isVisible())
+
+ @staticmethod
+ def _createDialog(parent):
+ """Create the dialog if not already existing
+
+ :parent QWidget parent: Parent of the new colormap
+ :rtype: ColormapDialog
+ """
+ from silx.gui.dialog.ColormapDialog import ColormapDialog
+ dialog = ColormapDialog(parent=parent)
+ dialog.setModal(False)
+ return dialog
+
+ def _actionTriggered(self, checked=False):
+ """Create a cmap dialog and update active image and default cmap."""
+ if self._dialog is None:
+ self._dialog = self._createDialog(self.plot)
+ self._dialog.visibleChanged.connect(self._dialogVisibleChanged)
+
+ # Run the dialog listening to colormap change
+ if checked is True:
+ self._updateColormap()
+ self._dialog.show()
+ else:
+ self._dialog.hide()
+
+ def _dialogVisibleChanged(self, isVisible):
+ self.setChecked(isVisible)
+
+ def _updateColormap(self):
+ if self._dialog is None:
+ return
+ image = self.plot.getActiveImage()
+
+ if isinstance(image, items.ColormapMixIn):
+ # Set dialog from active image
+ colormap = image.getColormap()
+ # Set histogram and range if any
+ self._dialog.setItem(image)
+
+ else:
+ # No active image or active image is RGBA,
+ # Check for active scatter plot
+ scatter = self.plot._getActiveItem(kind='scatter')
+ if scatter is not None:
+ colormap = scatter.getColormap()
+ self._dialog.setItem(scatter)
+
+ else:
+ # No active data image nor scatter,
+ # set dialog from default info
+ colormap = self.plot.getDefaultColormap()
+ # Reset histogram and range if any
+ self._dialog.setData(None)
+
+ self._dialog.setColormap(colormap)
+
+
+class ColorBarAction(PlotAction):
+ """QAction opening the ColorBarWidget of the specified plot.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ self._dialog = None # To store an instance of ColorBar
+ super(ColorBarAction, self).__init__(
+ plot, icon='colorbar', text='Colorbar',
+ tooltip="Show/Hide the colorbar",
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ colorBarWidget = self.plot.getColorBarWidget()
+ old = self.blockSignals(True)
+ self.setChecked(colorBarWidget.isVisibleTo(self.plot))
+ self.blockSignals(old)
+ colorBarWidget.sigVisibleChanged.connect(self._widgetVisibleChanged)
+
+ def _widgetVisibleChanged(self, isVisible):
+ """Callback when the colorbar `visible` property change."""
+ if self.isChecked() == isVisible:
+ return
+ self.setChecked(isVisible)
+
+ def _actionTriggered(self, checked=False):
+ """Create a cmap dialog and update active image and default cmap."""
+ colorBarWidget = self.plot.getColorBarWidget()
+ if not colorBarWidget.isHidden() == checked:
+ return
+ self.plot.getColorBarWidget().setVisible(checked)
+
+
+class KeepAspectRatioAction(PlotAction):
+ """QAction controlling aspect ratio on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ # Uses two images for checked/unchecked states
+ self._states = {
+ False: (icons.getQIcon('shape-circle-solid'),
+ "Keep data aspect ratio"),
+ True: (icons.getQIcon('shape-ellipse-solid'),
+ "Do no keep data aspect ratio")
+ }
+
+ icon, tooltip = self._states[plot.isKeepDataAspectRatio()]
+ super(KeepAspectRatioAction, self).__init__(
+ plot,
+ icon=icon,
+ text='Toggle keep aspect ratio',
+ tooltip=tooltip,
+ triggered=self._actionTriggered,
+ checkable=False,
+ parent=parent)
+ plot.sigSetKeepDataAspectRatio.connect(
+ self._keepDataAspectRatioChanged)
+
+ def _keepDataAspectRatioChanged(self, aspectRatio):
+ """Handle Plot set keep aspect ratio signal"""
+ icon, tooltip = self._states[aspectRatio]
+ self.setIcon(icon)
+ self.setToolTip(tooltip)
+
+ def _actionTriggered(self, checked=False):
+ # This will trigger _keepDataAspectRatioChanged
+ self.plot.setKeepDataAspectRatio(not self.plot.isKeepDataAspectRatio())
+
+
+class YAxisInvertedAction(PlotAction):
+ """QAction controlling Y orientation on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ # Uses two images for checked/unchecked states
+ self._states = {
+ False: (icons.getQIcon('plot-ydown'),
+ "Orient Y axis downward"),
+ True: (icons.getQIcon('plot-yup'),
+ "Orient Y axis upward"),
+ }
+
+ icon, tooltip = self._states[plot.getYAxis().isInverted()]
+ super(YAxisInvertedAction, self).__init__(
+ plot,
+ icon=icon,
+ text='Invert Y Axis',
+ tooltip=tooltip,
+ triggered=self._actionTriggered,
+ checkable=False,
+ parent=parent)
+ plot.getYAxis().sigInvertedChanged.connect(self._yAxisInvertedChanged)
+
+ def _yAxisInvertedChanged(self, inverted):
+ """Handle Plot set y axis inverted signal"""
+ icon, tooltip = self._states[inverted]
+ self.setIcon(icon)
+ self.setToolTip(tooltip)
+
+ def _actionTriggered(self, checked=False):
+ # This will trigger _yAxisInvertedChanged
+ yAxis = self.plot.getYAxis()
+ yAxis.setInverted(not yAxis.isInverted())
+
+
+class CrosshairAction(PlotAction):
+ """QAction toggling crosshair cursor on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param str color: Color to use to draw the crosshair
+ :param int linewidth: Width of the crosshair cursor
+ :param str linestyle: Style of line. See :meth:`.Plot.setGraphCursor`
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, color='black', linewidth=1, linestyle='-',
+ parent=None):
+ self.color = color
+ """Color used to draw the crosshair (str)."""
+
+ self.linewidth = linewidth
+ """Width of the crosshair cursor (int)."""
+
+ self.linestyle = linestyle
+ """Style of line of the cursor (str)."""
+
+ super(CrosshairAction, self).__init__(
+ plot, icon='crosshair', text='Crosshair Cursor',
+ tooltip='Enable crosshair cursor when checked',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.setChecked(plot.getGraphCursor() is not None)
+ plot.sigSetGraphCursor.connect(self.setChecked)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.setGraphCursor(checked,
+ color=self.color,
+ linestyle=self.linestyle,
+ linewidth=self.linewidth)
+
+
+class PanWithArrowKeysAction(PlotAction):
+ """QAction toggling pan with arrow keys on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+
+ super(PanWithArrowKeysAction, self).__init__(
+ plot, icon='arrow-keys', text='Pan with arrow keys',
+ tooltip='Enable pan with arrow keys when checked',
+ triggered=self._actionTriggered,
+ checkable=True, parent=parent)
+ self.setChecked(plot.isPanWithArrowKeys())
+ plot.sigSetPanWithArrowKeys.connect(self.setChecked)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.setPanWithArrowKeys(checked)
+
+
+class ShowAxisAction(PlotAction):
+ """QAction controlling axis visibility on a :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ tooltip = 'Show plot axis when checked, otherwise hide them'
+ PlotAction.__init__(self,
+ plot,
+ icon='axis',
+ text='show axis',
+ tooltip=tooltip,
+ triggered=self._actionTriggered,
+ checkable=True,
+ parent=parent)
+ self.setChecked(self.plot.isAxesDisplayed())
+ plot._sigAxesVisibilityChanged.connect(self.setChecked)
+
+ def _actionTriggered(self, checked=False):
+ self.plot.setAxesDisplayed(checked)
+
+
+class ClosePolygonInteractionAction(PlotAction):
+ """QAction controlling closure of a polygon in draw interaction mode
+ if the :class:`.PlotWidget`.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ tooltip = 'Close the current polygon drawn'
+ PlotAction.__init__(self,
+ plot,
+ icon='add-shape-polygon',
+ text='Close the polygon',
+ tooltip=tooltip,
+ triggered=self._actionTriggered,
+ checkable=True,
+ parent=parent)
+ self.plot.sigInteractiveModeChanged.connect(self._modeChanged)
+ self._modeChanged(None)
+
+ def _modeChanged(self, source):
+ mode = self.plot.getInteractiveMode()
+ enabled = "shape" in mode and mode["shape"] == "polygon"
+ self.setEnabled(enabled)
+
+ def _actionTriggered(self, checked=False):
+ self.plot._eventHandler.validate()
+
+
+class OpenGLAction(PlotAction):
+ """QAction controlling rendering of a :class:`.PlotWidget`.
+
+ For now it can enable or not the OpenGL backend.
+
+ :param plot: :class:`.PlotWidget` instance on which to operate
+ :param parent: See :class:`QAction`
+ """
+
+ def __init__(self, plot, parent=None):
+ # Uses two images for checked/unchecked states
+ self._states = {
+ "opengl": (icons.getQIcon('backend-opengl'),
+ "OpenGL rendering (fast)\nClick to disable OpenGL"),
+ "matplotlib": (icons.getQIcon('backend-opengl'),
+ "Matplotlib rendering (safe)\nClick to enable OpenGL"),
+ "unknown": (icons.getQIcon('backend-opengl'),
+ "Custom rendering")
+ }
+
+ name = self._getBackendName(plot)
+ self.__state = name
+ icon, tooltip = self._states[name]
+ super(OpenGLAction, self).__init__(
+ plot,
+ icon=icon,
+ text='Enable/disable OpenGL rendering',
+ tooltip=tooltip,
+ triggered=self._actionTriggered,
+ checkable=True,
+ parent=parent)
+
+ def _backendUpdated(self):
+ name = self._getBackendName(self.plot)
+ self.__state = name
+ icon, tooltip = self._states[name]
+ self.setIcon(icon)
+ self.setToolTip(tooltip)
+ self.setChecked(name == "opengl")
+
+ def _getBackendName(self, plot):
+ backend = plot.getBackend()
+ name = type(backend).__name__.lower()
+ if "opengl" in name:
+ return "opengl"
+ elif "matplotlib" in name:
+ return "matplotlib"
+ else:
+ return "unknown"
+
+ def _actionTriggered(self, checked=False):
+ plot = self.plot
+ name = self._getBackendName(self.plot)
+ if self.__state != name:
+ # THere is no event to know the backend was updated
+ # So here we check if there is a mismatch between the displayed state
+ # and the real state of the widget
+ self._backendUpdated()
+ return
+ if name != "opengl":
+ from silx.gui.utils import glutils
+ result = glutils.isOpenGLAvailable()
+ if not result:
+ qt.QMessageBox.critical(plot, "OpenGL rendering not available", result.error)
+ # Uncheck if needed
+ self._backendUpdated()
+ return
+ plot.setBackend("opengl")
+ else:
+ plot.setBackend("matplotlib")
+ self._backendUpdated()