summaryrefslogtreecommitdiff
path: root/silx/gui/plot/PlotWidget.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/PlotWidget.py')
-rw-r--r--silx/gui/plot/PlotWidget.py267
1 files changed, 267 insertions, 0 deletions
diff --git a/silx/gui/plot/PlotWidget.py b/silx/gui/plot/PlotWidget.py
new file mode 100644
index 0000000..5666d56
--- /dev/null
+++ b/silx/gui/plot/PlotWidget.py
@@ -0,0 +1,267 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2004-2017 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.
+#
+# ###########################################################################*/
+"""Qt widget providing Plot API for 1D and 2D data.
+
+This provides the plot API of :class:`silx.gui.plot.Plot.Plot` as a
+Qt widget.
+"""
+
+__authors__ = ["V.A. Sole", "T. Vincent"]
+__license__ = "MIT"
+__date__ = "22/02/2016"
+
+
+import logging
+
+from . import Plot
+
+from .. import qt
+
+
+_logger = logging.getLogger(__name__)
+
+
+class PlotWidget(qt.QMainWindow, Plot.Plot):
+ """Qt Widget providing a 1D/2D plot.
+
+ This widget is a QMainWindow.
+ It provides Qt signals for the Plot and add supports for panning
+ with arrow keys.
+
+ :param parent: The parent of this widget or None.
+ :param backend: The backend to use for the plot (default: matplotlib).
+ See :class:`.Plot` for the list of supported backend.
+ :type backend: str or :class:`BackendBase.BackendBase`
+ """
+
+ sigPlotSignal = qt.Signal(object)
+ """Signal for all events of the plot.
+
+ The signal information is provided as a dict.
+ See :class:`.Plot` for documentation of the content of the dict.
+ """
+
+ sigSetYAxisInverted = qt.Signal(bool)
+ """Signal emitted when Y axis orientation has changed"""
+
+ sigSetXAxisLogarithmic = qt.Signal(bool)
+ """Signal emitted when X axis scale has changed"""
+
+ sigSetYAxisLogarithmic = qt.Signal(bool)
+ """Signal emitted when Y axis scale has changed"""
+
+ sigSetXAxisAutoScale = qt.Signal(bool)
+ """Signal emitted when X axis autoscale has changed"""
+
+ sigSetYAxisAutoScale = qt.Signal(bool)
+ """Signal emitted when Y axis autoscale has changed"""
+
+ sigSetKeepDataAspectRatio = qt.Signal(bool)
+ """Signal emitted when plot keep aspect ratio has changed"""
+
+ sigSetGraphGrid = qt.Signal(str)
+ """Signal emitted when plot grid has changed"""
+
+ sigSetGraphCursor = qt.Signal(bool)
+ """Signal emitted when plot crosshair cursor has changed"""
+
+ sigSetPanWithArrowKeys = qt.Signal(bool)
+ """Signal emitted when pan with arrow keys has changed"""
+
+ sigContentChanged = qt.Signal(str, str, str)
+ """Signal emitted when the content of the plot is changed.
+
+ It provides 3 informations:
+
+ - action: The change of the plot: 'add' or 'remove'
+ - kind: The kind of primitive changed:
+ 'curve', 'image', 'scatter', 'histogram', 'item' or 'marker'
+ - legend: The legend of the primitive changed.
+ """
+
+ sigActiveCurveChanged = qt.Signal(object, object)
+ """Signal emitted when the active curve has changed.
+
+ It provides 2 informations:
+
+ - previous: The legend of the previous active curve or None
+ - legend: The legend of the new active curve or None if no curve is active
+ """
+
+ sigActiveImageChanged = qt.Signal(object, object)
+ """Signal emitted when the active image has changed.
+
+ It provides 2 informations:
+
+ - previous: The legend of the previous active image or None
+ - legend: The legend of the new active image or None if no image is active
+ """
+
+ sigActiveScatterChanged = qt.Signal(object, object)
+ """Signal emitted when the active Scatter has changed.
+
+ It provides following information:
+
+ - previous: The legend of the previous active scatter or None
+ - legend: The legend of the new active image or None if no image is active
+ """
+
+ sigInteractiveModeChanged = qt.Signal(object)
+ """Signal emitted when the interactive mode has changed
+
+ It provides the source as passed to :meth:`setInteractiveMode`.
+ """
+
+ def __init__(self, parent=None, backend=None,
+ legends=False, callback=None, **kw):
+
+ if kw:
+ _logger.warning(
+ 'deprecated: __init__ extra arguments: %s', str(kw))
+ if legends:
+ _logger.warning('deprecated: __init__ legend argument')
+ if callback:
+ _logger.warning('deprecated: __init__ callback argument')
+
+ self._panWithArrowKeys = True
+
+ qt.QMainWindow.__init__(self, parent)
+ if parent is not None:
+ # behave as a widget
+ self.setWindowFlags(qt.Qt.Widget)
+ else:
+ self.setWindowTitle('PlotWidget')
+
+ Plot.Plot.__init__(self, parent, backend=backend)
+
+ widget = self.getWidgetHandle()
+ if widget is not None:
+ self.setCentralWidget(widget)
+ else:
+ _logger.warning("Plot backend does not support widget")
+
+ self.setFocusPolicy(qt.Qt.StrongFocus)
+ self.setFocus(qt.Qt.OtherFocusReason)
+
+ def notify(self, event, **kwargs):
+ """Override :meth:`Plot.notify` to send Qt signals."""
+ eventDict = kwargs.copy()
+ eventDict['event'] = event
+ self.sigPlotSignal.emit(eventDict)
+
+ if event == 'setYAxisInverted':
+ self.sigSetYAxisInverted.emit(kwargs['state'])
+ elif event == 'setXAxisLogarithmic':
+ self.sigSetXAxisLogarithmic.emit(kwargs['state'])
+ elif event == 'setYAxisLogarithmic':
+ self.sigSetYAxisLogarithmic.emit(kwargs['state'])
+ elif event == 'setXAxisAutoScale':
+ self.sigSetXAxisAutoScale.emit(kwargs['state'])
+ elif event == 'setYAxisAutoScale':
+ self.sigSetYAxisAutoScale.emit(kwargs['state'])
+ elif event == 'setKeepDataAspectRatio':
+ self.sigSetKeepDataAspectRatio.emit(kwargs['state'])
+ elif event == 'setGraphGrid':
+ self.sigSetGraphGrid.emit(kwargs['which'])
+ elif event == 'setGraphCursor':
+ self.sigSetGraphCursor.emit(kwargs['state'])
+ elif event == 'contentChanged':
+ self.sigContentChanged.emit(
+ kwargs['action'], kwargs['kind'], kwargs['legend'])
+ elif event == 'activeCurveChanged':
+ self.sigActiveCurveChanged.emit(
+ kwargs['previous'], kwargs['legend'])
+ elif event == 'activeImageChanged':
+ self.sigActiveImageChanged.emit(
+ kwargs['previous'], kwargs['legend'])
+ elif event == 'activeScatterChanged':
+ self.sigActiveScatterChanged.emit(
+ kwargs['previous'], kwargs['legend'])
+ elif event == 'interactiveModeChanged':
+ self.sigInteractiveModeChanged.emit(kwargs['source'])
+ Plot.Plot.notify(self, event, **kwargs)
+
+ # Panning with arrow keys
+
+ def isPanWithArrowKeys(self):
+ """Returns whether or not panning the graph with arrow keys is enable.
+
+ See :meth:`setPanWithArrowKeys`.
+ """
+ return self._panWithArrowKeys
+
+ def setPanWithArrowKeys(self, pan=False):
+ """Enable/Disable panning the graph with arrow keys.
+
+ This grabs the keyboard.
+
+ :param bool pan: True to enable panning, False to disable.
+ """
+ pan = bool(pan)
+ panHasChanged = self._panWithArrowKeys != pan
+
+ self._panWithArrowKeys = pan
+ if not self._panWithArrowKeys:
+ self.setFocusPolicy(qt.Qt.NoFocus)
+ else:
+ self.setFocusPolicy(qt.Qt.StrongFocus)
+ self.setFocus(qt.Qt.OtherFocusReason)
+
+ if panHasChanged:
+ self.sigSetPanWithArrowKeys.emit(pan)
+
+ # Dict to convert Qt arrow key code to direction str.
+ _ARROWS_TO_PAN_DIRECTION = {
+ qt.Qt.Key_Left: 'left',
+ qt.Qt.Key_Right: 'right',
+ qt.Qt.Key_Up: 'up',
+ qt.Qt.Key_Down: 'down'
+ }
+
+ def keyPressEvent(self, event):
+ """Key event handler handling panning on arrow keys.
+
+ Overrides base class implementation.
+ """
+ key = event.key()
+ if self._panWithArrowKeys and key in self._ARROWS_TO_PAN_DIRECTION:
+ self.pan(self._ARROWS_TO_PAN_DIRECTION[key], factor=0.1)
+
+ # Send a mouse move event to the plot widget to take into account
+ # that even if mouse didn't move on the screen, it moved relative
+ # to the plotted data.
+ qapp = qt.QApplication.instance()
+ event = qt.QMouseEvent(
+ qt.QEvent.MouseMove,
+ self.getWidgetHandle().mapFromGlobal(qt.QCursor.pos()),
+ qt.Qt.NoButton,
+ qapp.mouseButtons(),
+ qapp.keyboardModifiers())
+ qapp.sendEvent(self.getWidgetHandle(), event)
+
+ else:
+ # Only call base class implementation when key is not handled.
+ # See QWidget.keyPressEvent for details.
+ super(PlotWidget, self).keyPressEvent(event)