From f7bdc2acff3c13a6d632c28c4569690ab106eed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Picca=20Fr=C3=A9d=C3=A9ric-Emmanuel?= Date: Fri, 18 Aug 2017 14:48:52 +0200 Subject: Import Upstream version 0.5.0+dfsg --- silx/gui/plot/PlotWidget.py | 267 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 silx/gui/plot/PlotWidget.py (limited to 'silx/gui/plot/PlotWidget.py') 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) -- cgit v1.2.3