diff options
author | Alexandre Marie <alexandre.marie@synchrotron-soleil.fr> | 2018-12-17 12:28:24 +0100 |
---|---|---|
committer | Alexandre Marie <alexandre.marie@synchrotron-soleil.fr> | 2018-12-17 12:28:24 +0100 |
commit | cebdc9244c019224846cb8d2668080fe386a6adc (patch) | |
tree | aedec55da0f9dd4fc4d6c7eb0f58489a956e2e8c /examples | |
parent | 159ef14fb9e198bb0066ea14e6b980f065de63dd (diff) |
New upstream version 0.9.0+dfsg
Diffstat (limited to 'examples')
-rw-r--r-- | examples/__init__.py | 29 | ||||
-rw-r--r-- | examples/blissPlot.py | 635 | ||||
-rw-r--r-- | examples/colormapDialog.py | 2 | ||||
-rw-r--r-- | examples/compareImages.py | 149 | ||||
-rw-r--r-- | examples/plot3dSceneWindow.py | 10 | ||||
-rw-r--r-- | examples/plotCurveLegendWidget.py | 154 | ||||
-rw-r--r-- | examples/plotGL32.py | 46 | ||||
-rw-r--r-- | examples/plotInteractiveImageROI.py | 9 | ||||
-rw-r--r-- | examples/plotStats.py | 4 | ||||
-rw-r--r-- | examples/plotTimeSeries.py | 33 | ||||
-rwxr-xr-x | examples/simplewidget.py | 44 |
11 files changed, 391 insertions, 724 deletions
diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..625d5fa --- /dev/null +++ b/examples/__init__.py @@ -0,0 +1,29 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-2018 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 package contains examples""" + +__authors__ = ["V. Valls"] +__license__ = "MIT" +__date__ = "26/07/2018" diff --git a/examples/blissPlot.py b/examples/blissPlot.py deleted file mode 100644 index 71c1fd3..0000000 --- a/examples/blissPlot.py +++ /dev/null @@ -1,635 +0,0 @@ -# coding: utf-8 - - -from __future__ import division, absolute_import, print_function, unicode_literals - - -import six - -from silx.gui import qt, icons -from silx.gui.plot.actions import PlotAction, mode -from silx.gui.plot import PlotWindow, PlotWidget -from silx.gui.plot.Colors import rgba - - -class DrawModeAction(PlotAction): - """Action that control drawing mode""" - - _MODES = { # shape: (icon, text, tooltip - 'rectangle': ('shape-rectangle', 'Rectangle selection', 'Select a rectangular region'), - 'line': ('shape-diagonal', 'Line selection', 'Select a line'), - 'hline': ('shape-horizontal', 'H. line selection', 'Select a horizontal line'), - 'vline': ('shape-vertical', 'V. line selection', 'Select a vertical line'), - 'polygon': ('shape-polygon', 'Polygon selection', 'Select a polygon'), - } - - def __init__(self, plot, parent=None): - self._shape = 'polygon' - self._label = None - self._color = 'black' - self._width = None - icon, text, tooltip = self._MODES[self._shape] - - super(DrawModeAction, self).__init__( - plot, icon=icon, text=text, - tooltip=tooltip, - triggered=self._actionTriggered, - checkable=True, parent=parent) - - # Listen to mode change - self.plot.sigInteractiveModeChanged.connect(self._modeChanged) - # Init the state - self._modeChanged(None) - - def _update(self): - if self.isChecked(): - self._actionTriggered() - - def setShape(self, shape): - self._shape = shape - icon, text, tooltip = self._MODES[self._shape] - self.setIcon(icons.getQIcon(icon)) - self.setText(text) - self.setToolTip(tooltip) - self._update() - - def getShape(self): - return self._shape - - def setColor(self, color): - self._color = rgba(color) - self._update() - - def getColor(self): - return qt.QColor.fromRgbF(*self._color) - - def setLabel(self, label): - self._label = label - self._update() - - def getLabel(self): - return self._label - - def setWidth(self, width): - self._width = width - self._update() - - def getWidth(self): - return self._width - - def _modeChanged(self, source): - modeDict = self.plot.getInteractiveMode() - old = self.blockSignals(True) - self.setChecked(modeDict['mode'] == 'draw' and - modeDict['shape'] == self._shape and - modeDict['label'] == self._label) - self.blockSignals(old) - - def _actionTriggered(self, checked=False): - self.plot.setInteractiveMode('draw', - source=self, - shape=self._shape, - color=self._color, - label=self._label, - width=self._width) - - -class ShapeSelector(qt.QObject): - """Handles the selection of a single shape in a PlotWidget - - :param parent: QObject's parent - """ - - selectionChanged = qt.Signal(tuple) - """Signal emitted whenever the selection has changed. - - It provides the selection. - """ - - selectionFinished = qt.Signal(tuple) - """Signal emitted when selection is terminated. - - It provides the selection. - """ - - def __init__(self, parent=None): - assert isinstance(parent, PlotWidget) - super(ShapeSelector, self).__init__(parent) - self._isSelectionRunning = False - self._selection = () - self._itemId = "%s-%s" % (self.__class__.__name__, id(self)) - - # Add a toolbar to plot - self._toolbar = qt.QToolBar('Selection') - self._modeAction = DrawModeAction(plot=parent) - self._modeAction.setLabel(self._itemId) - self._modeAction.setColor(rgba('red')) - toolButton = qt.QToolButton() - toolButton.setDefaultAction(self._modeAction) - toolButton.setToolButtonStyle(qt.Qt.ToolButtonTextBesideIcon) - self._toolbar.addWidget(toolButton) - - # Style - - def getColor(self): - """Returns the color used for the selection shape - - :rtype: QColor - """ - return self._modeAction.getColor() - - def setColor(self, color): - """Set the color used for the selection shape - - :param color: The color to use for selection shape as - either a color name, a QColor, a list of uint8 or float in [0, 1]. - """ - self._modeAction.setColor(color) - self._updateShape() - - # Control selection - - def getSelection(self): - """Returns selection control point coordinates - - Returns an empty tuple if there is no selection - - :return: Nx2 (x, y) coordinates or an empty tuple. - """ - return tuple(zip(*self._selection)) - - def _setSelection(self, x, y): - """Set the selection shape control points. - - Use :meth:`reset` to remove the selection. - - :param x: X coordinates of control points - :param y: Y coordinates of control points - """ - selection = x, y - if selection != self._selection: - self._selection = selection - self._updateShape() - self.selectionChanged.emit(self.getSelection()) - - def reset(self): - """Clear the rectangle selection""" - if self._selection: - self._selection = () - self._updateShape() - self.selectionChanged.emit(self.getSelection()) - - def start(self, shape): - """Start requiring user to select a rectangle - - :param str shape: The shape to select in: - 'rectangle', 'line', 'polygon', 'hline', 'vline' - """ - plot = self.parent() - if plot is None: - raise RuntimeError('No plot to perform selection') - - self.stop() - self.reset() - - assert shape in ('rectangle', 'line', 'polygon', 'hline', 'vline') - - self._modeAction.setShape(shape) - self._modeAction.trigger() # To set the interaction mode - - self._isSelectionRunning = True - - plot.sigPlotSignal.connect(self._handleDraw) - - self._toolbar.show() - plot.addToolBar(qt.Qt.BottomToolBarArea, self._toolbar) - - def stop(self): - """Stop shape selection""" - if not self._isSelectionRunning: - return - - plot = self.parent() - if plot is None: - return - - mode = plot.getInteractiveMode() - if mode['mode'] == 'draw' and mode['label'] == self._itemId: - plot.setInteractiveMode('zoom') # This disconnects draw handler - - plot.sigPlotSignal.disconnect(self._handleDraw) - - plot.removeToolBar(self._toolbar) - - self._isSelectionRunning = False - self.selectionFinished.emit(self.getSelection()) - - def _handleDraw(self, event): - """Handle shape drawing event""" - if (event['event'] == 'drawingFinished' and - event['parameters']['label'] == self._itemId): - self._setSelection(event['xdata'], event['ydata']) - self.stop() - - def _updateShape(self): - """Update shape on the plot""" - plot = self.parent() - if plot is not None: - if not self._selection: - plot.remove(legend=self._itemId, kind='item') - - else: - x, y = self._selection - shape = self._modeAction.getShape() - if shape == 'line': - shape = 'polylines' - - plot.addItem(x, y, - legend=self._itemId, - shape=shape, - color=rgba(self._modeAction.getColor()), - fill=False) - - - -class PointsSelector(qt.QObject): - """Handle selection of points in a PlotWidget""" - - selectionChanged = qt.Signal(tuple) - """Signal emitted whenever the selection has changed. - - It provides the selection. - """ - - selectionFinished = qt.Signal(tuple) - """Signal emitted when selection is terminated. - - It provides the selection. - """ - - - def __init__(self, parent): - assert isinstance(parent, PlotWidget) - super(PointsSelector, self).__init__(parent) - - self._isSelectionRunning = False - self._markersAndPos = [] - self._totalPoints = 0 - - def getSelection(self): - """Returns the selection""" - return tuple(pos for _, pos in self._markersAndPos) - - def eventFilter(self, obj, event): - """Event filter for plot hide and key event""" - if event.type() == qt.QEvent.Hide: - self.stop() - - elif event.type() == qt.QEvent.KeyPress: - if event.key() in (qt.Qt.Key_Delete, qt.Qt.Key_Backspace) or ( - event.key() == qt.Qt.Key_Z and event.modifiers() & qt.Qt.ControlModifier): - if len(self._markersAndPos) > 0: - plot = self.parent() - if plot is not None: - legend, _ = self._markersAndPos.pop() - plot.remove(legend=legend, kind='marker') - - self._updateStatusBar() - self.selectionChanged.emit(self.getSelection()) - return True # Stop further handling of those keys - - elif event.key() == qt.Qt.Key_Return: - self.stop() - return True # Stop further handling of those keys - - return super(PointsSelector, self).eventFilter(obj, event) - - def start(self, nbPoints=1): - """Start interactive selection of points - - :param int nbPoints: Number of points to select - """ - self.stop() - self.reset() - - plot = self.parent() - if plot is None: - raise RuntimeError('No plot to perform selection') - - self._totalPoints = nbPoints - self._isSelectionRunning = True - - plot.setInteractiveMode(mode='zoom') - self._handleInteractiveModeChanged(None) - plot.sigInteractiveModeChanged.connect( - self._handleInteractiveModeChanged) - - plot.installEventFilter(self) - - self._updateStatusBar() - - def stop(self): - """Stop interactive point selection""" - if not self._isSelectionRunning: - return - - plot = self.parent() - if plot is None: - return - - plot.removeEventFilter(self) - - plot.sigInteractiveModeChanged.disconnect( - self._handleInteractiveModeChanged) - - currentMode = plot.getInteractiveMode() - if currentMode['mode'] == 'zoom': # Stop handling mouse click - plot.sigPlotSignal.disconnect(self._handleSelect) - - plot.statusBar().clearMessage() - self._isSelectionRunning = False - self.selectionFinished.emit(self.getSelection()) - - def reset(self): - """Reset selected points""" - plot = self.parent() - if plot is None: - return - - for legend, _ in self._markersAndPos: - plot.remove(legend=legend, kind='marker') - self._markersAndPos = [] - self.selectionChanged.emit(self.getSelection()) - - def _updateStatusBar(self): - """Update status bar message""" - plot = self.parent() - if plot is None: - return - - msg = 'Select %d/%d input points' % (len(self._markersAndPos), - self._totalPoints) - - currentMode = plot.getInteractiveMode() - if currentMode['mode'] != 'zoom': - msg += ' (Use zoom mode to add/remove points)' - - plot.statusBar().showMessage(msg) - - def _handleSelect(self, event): - """Handle mouse events""" - if event['event'] == 'mouseClicked' and event['button'] == 'left': - plot = self.parent() - if plot is None: - return - - x, y = event['x'], event['y'] - - # Add marker - legend = "sx.ginput %d" % len(self._markersAndPos) - plot.addMarker( - x, y, - legend=legend, - text='%d' % len(self._markersAndPos), - color='red', - draggable=False) - - self._markersAndPos.append((legend, (x, y))) - self._updateStatusBar() - if len(self._markersAndPos) >= self._totalPoints: - self.stop() - - def _handleInteractiveModeChanged(self, source): - """Handle change of interactive mode in the plot - - :param source: Objects that triggered the mode change - """ - plot = self.parent() - if plot is None: - return - - mode = plot.getInteractiveMode() - if mode['mode'] == 'zoom': # Handle click events - plot.sigPlotSignal.connect(self._handleSelect) - else: # Do not handle click event - plot.sigPlotSignal.disconnect(self._handleSelect) - self._updateStatusBar() - - -# TODO refactor to make a selection by composition rather than inheritance... -class BlissPlot(PlotWindow): - """Plot with selection methods""" - - sigSelectionDone = qt.Signal(object) - """Signal emitted when the selection is done - - It provides the list of selected points - """ - - def __init__(self, parent=None, **kwargs): - super(BlissPlot, self).__init__(parent=parent, **kwargs) - self._selectionColor = rgba('red') - self._selectionMode = None - self._markers = [] - self._pointNames = () - - # Style - - def getColor(self): - """Returns the color used for selection markers - - :rtype: QColor - """ - return qt.QColor.fromRgbF(*self._selectionColor) - - def setColor(self, color): - """Set the markers used for selection - - :param color: The color to use for selection markers as - either a color name, a QColor, a list of uint8 or float in [0, 1]. - """ - self._selectionColor = rgba(color) - self._updateMarkers() # To apply color change - - # Marker helpers - - def _setSelectedPointMarker(self, x, y, index=None): - """Add/Update a marker for a point - - :param float x: X coord in plot - :param float y: Y coord in plot - :param int index: Index of point in points names to set - :return: corresponding marker legend - :rtype: str - """ - if index is None: - index = len(self._markers) - - name = self._pointNames[index] - legend = "BlissPlotSelection-%d" % index - - self.addMarker( - x, y, - legend=legend, - text=name, - color=self._selectionColor, - draggable=self._selectionMode is not None) - return legend - - def _updateMarkers(self): - """Update all markers to sync color/draggable""" - for index, (x, y) in enumerate(self.getSelectedPoints()): - self._setSelectedPointMarker(x, y, index) - - # Selection mode control - - def startPointSelection(self, points=1): - """Request the user to select a number of points - - :param points: - The number of points the user need to select (default: 1) - or a list of point names or a single name. - :type points: Union[int, List[str], str] - :return: A future to access the result - :rtype: concurrent.futures.Future - """ - self.stopSelection() - self.resetSelection() - - if isinstance(points, six.string_types): - points = [points] - elif isinstance(points, int): - points = [str(i) for i in range(points)] - - self._pointNames = points - - self._markers = [] - self._selectionMode = 'points' - - self.setInteractiveMode(mode='zoom') - self._handleInteractiveModeChanged(None) - self.sigInteractiveModeChanged.connect( - self._handleInteractiveModeChanged) - - def stopSelection(self): - """Stop current selection. - - Calling this method emits the selection through sigSelectionDone - and does not clear the selection. - """ - if self._selectionMode is not None: - currentMode = self.getInteractiveMode() - if currentMode['mode'] == 'zoom': # Stop handling mouse click - self.sigPlotSignal.disconnect(self._handleSelect) - - self.sigInteractiveModeChanged.disconnect( - self._handleInteractiveModeChanged) - - self._selectionMode = None - self.statusBar().showMessage('Selection done') - - self._updateMarkers() # To make them not draggable - - self.sigSelectionDone.emit(self.getSelectedPoints()) - - def getSelectedPoints(self): - """Returns list of currently selected points - - :rtype: tuple - """ - return tuple(self._getItem(kind='marker', legend=legend).getPosition() - for legend in self._markers) - - def resetSelection(self): - """Clear current selection""" - for legend in self._markers: - self.remove(legend, kind='marker') - self._markers = [] - - if self._selectionMode is not None: - self._updateStatusBar() - else: - self.statusBar().clearMessage() - - def _handleInteractiveModeChanged(self, source): - """Handle change of interactive mode in the plot - - :param source: Objects that triggered the mode change - """ - mode = self.getInteractiveMode() - if mode['mode'] == 'zoom': # Handle click events - self.sigPlotSignal.connect(self._handleSelect) - else: # Do not handle click event - self.sigPlotSignal.disconnect(self._handleSelect) - self._updateStatusBar() - - def _handleSelect(self, event): - """Handle mouse events""" - if event['event'] == 'mouseClicked' and event['button'] == 'left': - if len(self._markers) == len(self._pointNames): - return - - x, y = event['x'], event['y'] - legend = self._setSelectedPointMarker(x, y, len(self._markers)) - self._markers.append(legend) - self._updateStatusBar() - - def keyPressEvent(self, event): - """Handle keys for undo/done actions""" - if self._selectionMode is not None: - if event.key() in (qt.Qt.Key_Delete, qt.Qt.Key_Backspace) or ( - event.key() == qt.Qt.Key_Z and - event.modifiers() & qt.Qt.ControlModifier): - if len(self._markers) > 0: - legend = self._markers.pop() - self.remove(legend, kind='marker') - - self._updateStatusBar() - return # Stop processing the event - - elif event.key() == qt.Qt.Key_Return: - self.stopSelection() - return # Stop processing the event - - return super(BlissPlot, self).keyPressEvent(event) - - def _updateStatusBar(self): - """Update status bar message""" - if len(self._markers) < len(self._pointNames): - name = self._pointNames[len(self._markers)] - msg = 'Select point: %s (%d/%d)' % ( - name, len(self._markers), len(self._pointNames)) - else: - msg = 'Selection ready. Press Enter to validate' - - currentMode = self.getInteractiveMode() - if currentMode['mode'] != 'zoom': - msg += ' (Use zoom mode to add/edit points)' - - self.statusBar().showMessage(msg) - - -if __name__ == '__main__': - app = qt.QApplication([]) - - #plot = BlissPlot() - #plot.startPointSelection(('first', 'second', 'third')) - - def dumpChanged(selection): - print('selectionChanged', selection) - - def dumpFinished(selection): - print('selectionFinished', selection) - - plot = PlotWindow() - selector = ShapeSelector(plot) - #selector.start(shape='rectangle') - selector.selectionChanged.connect(dumpChanged) - selector.selectionFinished.connect(dumpFinished) - plot.show() - - points = PointsSelector(plot) - points.start(3) - points.selectionChanged.connect(dumpChanged) - points.selectionFinished.connect(dumpFinished) - #app.exec_() diff --git a/examples/colormapDialog.py b/examples/colormapDialog.py index 9ef6508..08e3fe8 100644 --- a/examples/colormapDialog.py +++ b/examples/colormapDialog.py @@ -149,7 +149,7 @@ class ColormapDialogExample(qt.QMainWindow): self.colorDialogs.append(newDialog) self.mainWidget.layout().addWidget(newDialog) - def removeColorDialog(self, dialog): + def removeColorDialog(self, dialog, result): self.colorDialogs.remove(dialog) def setNoColormap(self): diff --git a/examples/compareImages.py b/examples/compareImages.py new file mode 100644 index 0000000..94f68a0 --- /dev/null +++ b/examples/compareImages.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-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. +# +# ###########################################################################*/ +"""Example demonstrating the use of the widget CompareImages +""" + +import sys +import logging +import numpy +import argparse + +import silx.io +from silx.gui import qt +import silx.test.utils +from silx.gui.plot.CompareImages import CompareImages + +_logger = logging.getLogger(__name__) + +try: + import fabio +except ImportError: + _logger.debug("Backtrace", exc_info=True) + fabio = None + +try: + import PIL +except ImportError: + _logger.debug("Backtrace", exc_info=True) + PIL = None + + +def createTestData(): + data = numpy.arange(100 * 100) + data = (data % 100) / 5.0 + data = numpy.sin(data) + data1 = data.copy() + data1.shape = 100, 100 + data2 = silx.test.utils.add_gaussian_noise(data, stdev=0.1) + data2.shape = 100, 100 + return data1, data2 + + +def loadImage(filename): + try: + return silx.io.get_data(filename) + except Exception: + _logger.debug("Error while loading image with silx.io", exc_info=True) + + if fabio is None and PIL is None: + raise ImportError("fabio nor PIL are available") + + if fabio is not None: + try: + return fabio.open(filename).data + except Exception: + _logger.debug("Error while loading image with fabio", exc_info=True) + + if PIL is not None: + try: + return numpy.asarray(PIL.Image.open(filename)) + except Exception: + _logger.debug("Error while loading image with PIL", exc_info=True) + + raise Exception("Impossible to load '%s' with the available image libraries" % filename) + + +file_description = """ +Image data to compare (HDF5 file with path, EDF files, JPEG/PNG image files). +Data from HDF5 files can be accessed using dataset path and slicing as an URL: silx:../my_file.h5?path=/entry/data&slice=10 +EDF file frames also can can be accessed using URL: fabio:../my_file.edf?slice=10 +Using URL in command like usually have to be quoted: "URL". +""" + + +def createParser(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + 'files', + nargs=argparse.ZERO_OR_MORE, + help=file_description) + parser.add_argument( + '--debug', + dest="debug", + action="store_true", + default=False, + help='Set logging system in debug mode') + parser.add_argument( + '--testdata', + dest="testdata", + action="store_true", + default=False, + help='Use synthetic images to test the application') + parser.add_argument( + '--use-opengl-plot', + dest="use_opengl_plot", + action="store_true", + default=False, + help='Use OpenGL for plots (instead of matplotlib)') + return parser + + +if __name__ == "__main__": + parser = createParser() + options = parser.parse_args(sys.argv[1:]) + + if options.debug: + logging.root.setLevel(logging.DEBUG) + + if options.testdata: + _logger.info("Generate test data") + data1, data2 = createTestData() + else: + if len(options.files) != 2: + raise Exception("Expected 2 images to compare them") + data1 = loadImage(options.files[0]) + data2 = loadImage(options.files[1]) + + if options.use_opengl_plot: + backend = "gl" + else: + backend = "mpl" + + app = qt.QApplication([]) + window = CompareImages(backend=backend) + window.setData(data1, data2) + window.setVisible(True) + app.exec_() diff --git a/examples/plot3dSceneWindow.py b/examples/plot3dSceneWindow.py index 3b78109..cf6f209 100644 --- a/examples/plot3dSceneWindow.py +++ b/examples/plot3dSceneWindow.py @@ -51,6 +51,8 @@ import numpy from silx.gui import qt from silx.gui.plot3d.SceneWindow import SceneWindow, items +from silx.gui.plot3d.tools.PositionInfoWidget import PositionInfoWidget +from silx.gui.widgets.BoxLayoutDockWidget import BoxLayoutDockWidget SIZE = 1024 @@ -67,6 +69,14 @@ sceneWidget.setForegroundColor((1., 1., 1., 1.)) sceneWidget.setTextColor((0.1, 0.1, 0.1, 1.)) +# Add PositionInfoWidget to display picking info +positionInfo = PositionInfoWidget() +positionInfo.setSceneWidget(sceneWidget) +dock = BoxLayoutDockWidget() +dock.setWindowTitle("Selection Info") +dock.setWidget(positionInfo) +window.addDockWidget(qt.Qt.BottomDockWidgetArea, dock) + # 2D Image ### # Add a dummy RGBA image diff --git a/examples/plotCurveLegendWidget.py b/examples/plotCurveLegendWidget.py new file mode 100644 index 0000000..9209c58 --- /dev/null +++ b/examples/plotCurveLegendWidget.py @@ -0,0 +1,154 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2018 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 example illustrates the use of :class:`CurveLegendsWidget`. + +:class:`CurveLegendsWidget` display curves style and legend currently visible +in a :class:`~silx.gui.plot.PlotWidget` +""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "20/07/2018" + +import functools + +import numpy + +from silx.gui import qt +from silx.gui.plot import Plot1D +from silx.gui.plot.tools.CurveLegendsWidget import CurveLegendsWidget +from silx.gui.widgets.BoxLayoutDockWidget import BoxLayoutDockWidget + + +class MyCurveLegendsWidget(CurveLegendsWidget): + """Extension of CurveLegendWidget. + + This widget adds: + - Set a curve as active with a left click its the legend + - Adds a context menu with content specific to the hovered legend + + :param QWidget parent: See QWidget + """ + + def __init__(self, parent=None): + super(MyCurveLegendsWidget, self).__init__(parent) + + # Activate/Deactivate curve with left click on the legend widget + self.sigCurveClicked.connect(self._switchCurveActive) + + # Add a custom context menu + self.setContextMenuPolicy(qt.Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self._contextMenu) + + def _switchCurveActive(self, curve): + """Set a curve as active. + + This is called from the context menu and when a legend is clicked. + + :param silx.gui.plot.items.Curve curve: + """ + plot = curve.getPlot() + plot.setActiveCurve( + curve.getLegend() if curve != plot.getActiveCurve() else None) + + def _switchCurveVisibility(self, curve): + """Toggle the visibility of a curve + + :param silx.gui.plot.items.Curve curve: + """ + curve.setVisible(not curve.isVisible()) + + def _switchCurveYAxis(self, curve): + """Change the Y axis a curve is attached to. + + :param silx.gui.plot.items.Curve curve: + """ + yaxis = curve.getYAxis() + curve.setYAxis('left' if yaxis is 'right' else 'right') + + def _contextMenu(self, pos): + """Create a show the context menu. + + :param QPoint pos: Position in this widget + """ + curve = self.curveAt(pos) # Retrieve curve from hovered legend + if curve is not None: + menu = qt.QMenu() # Create the menu + + # Add an action to activate the curve + activeCurve = curve.getPlot().getActiveCurve() + menu.addAction('Unselect' if curve == activeCurve else 'Select', + functools.partial(self._switchCurveActive, curve)) + + # Add an action to switch the Y axis of a curve + yaxis = 'right' if curve.getYAxis() == 'left' else 'left' + menu.addAction('Map to %s' % yaxis, + functools.partial(self._switchCurveYAxis, curve)) + + # Add an action to show/hide the curve + menu.addAction('Hide' if curve.isVisible() else 'Show', + functools.partial(self._switchCurveVisibility, curve)) + + globalPosition = self.mapToGlobal(pos) + menu.exec_(globalPosition) + + +# First create the QApplication +app = qt.QApplication([]) + +# Create a plot and add some curves +window = Plot1D() +window.setWindowTitle("CurveLegendWidgets demo") + +x = numpy.linspace(-numpy.pi, numpy.pi, 100) +window.addCurve(x, 2. * numpy.random.random(100) - 1., + legend='random', + symbol='s', linestyle='--', + color='red') +window.addCurve(x, numpy.sin(x), + legend='sin', + symbol='o', linestyle=':', + color='blue') +window.addCurve(x, numpy.cos(x), + legend='cos', + symbol='', linestyle='-', + color='blue') + + +# Create a MyCurveLegendWidget associated to the plot +curveLegendsWidget = MyCurveLegendsWidget() +curveLegendsWidget.setPlotWidget(window) + +# Add the CurveLegendsWidget as a dock widget to the plot +dock = BoxLayoutDockWidget() +dock.setWindowTitle('Curve legends') +dock.setWidget(curveLegendsWidget) +window.addDockWidget(qt.Qt.RightDockWidgetArea, dock) + +# Show the plot and run the QApplication +window.setAttribute(qt.Qt.WA_DeleteOnClose) +window.show() + +app.exec_() diff --git a/examples/plotGL32.py b/examples/plotGL32.py deleted file mode 100644 index 474fbd2..0000000 --- a/examples/plotGL32.py +++ /dev/null @@ -1,46 +0,0 @@ -import datetime as dt -import numpy as np - -from PyQt5.QtWidgets import QApplication -from silx.gui.plot import Plot1D - -if 1: - BACKEND = 'gl' # this gives incorrect results -else: - BACKEND = 'mpl' # this works - -def makePlot1D(): - - # Make large value with a relatively small range by creating POSIX time stamps. - base = dt.datetime.today() - dates = [base - dt.timedelta(seconds=x) for x in range(0, 2500, 20)] - - x = np.array([d.timestamp() for d in dates], dtype=np.float64) - np.random.seed(seed=1) - y = np.random.random(x.shape) * 12 - 3 - - print('x range', np.nanmin(x), np.nanmax(x)) - print('y range', np.nanmin(y), np.nanmax(y)) - - plot1D = Plot1D(backend=BACKEND) - xAxis = plot1D.getXAxis() - - curve = plot1D.addCurve(x=x, y=y, legend='curve', symbol='o', fill=True) - - plot1D.addMarker(x=x[0], y=y[0], legend='marker', text='the marker', draggable=True) - plot1D.addYMarker(y[0], legend='hmarker', text='the H marker', draggable=True) - plot1D.addXMarker(x[0], legend='vmarker', text='the V marker', draggable=True) - - plot1D.show() - - return plot1D - - -def main(): - global app, plot - app = QApplication([]) - plot = makePlot1D() - app.exec_() - -if __name__ == "__main__": - main() diff --git a/examples/plotInteractiveImageROI.py b/examples/plotInteractiveImageROI.py index 6c5bc8d..d45bdf5 100644 --- a/examples/plotInteractiveImageROI.py +++ b/examples/plotInteractiveImageROI.py @@ -99,8 +99,17 @@ widget.setLayout(layout) layout.addWidget(roiToolbar) layout.addWidget(roiTable) +def roiDockVisibilityChanged(visible): + """Handle change of visibility of the roi dock widget + + If dock becomes hidden, ROI interaction is stopped. + """ + if not visible: + roiManager.stop() + dock = qt.QDockWidget('Image ROI') dock.setWidget(widget) +dock.visibilityChanged.connect(roiDockVisibilityChanged) plot.addTabbedDockWidget(dock) # Show the widget and start the application diff --git a/examples/plotStats.py b/examples/plotStats.py index 91a444e..fff7585 100644 --- a/examples/plotStats.py +++ b/examples/plotStats.py @@ -39,7 +39,7 @@ On this example we will: __authors__ = ["H. Payno"] __license__ = "MIT" -__date__ = "06/06/2018" +__date__ = "24/07/2018" from silx.gui import qt @@ -112,8 +112,6 @@ def main(): plot.getStatsWidget().setStats(stats) plot.getStatsWidget().parent().setVisible(True) - # Update the checkedbox cause we arre playing with the visibility - plot.getStatsAction().setChecked(True) plot.show() app.exec_() diff --git a/examples/plotTimeSeries.py b/examples/plotTimeSeries.py deleted file mode 100644 index 28100fe..0000000 --- a/examples/plotTimeSeries.py +++ /dev/null @@ -1,33 +0,0 @@ -import datetime as dt -import numpy as np - -from silx.gui import qt -from silx.gui.plot import Plot1D, TickMode - - -app = qt.QApplication([]) - -base = dt.datetime.today() -dates = [base - dt.timedelta(days=x) for x in range(0, 50)] - -x = np.array([d.timestamp() for d in dates], dtype=np.uint64) - -np.random.seed(seed=1) -y = np.random.random(x.shape) * 12 - 3 - -plot1D = Plot1D(backend='gl') -xAxis = plot1D.getXAxis() -xAxis.setTickMode(TickMode.TIME_SERIES) -xAxis.setTimeZone('UTC') - -def later(): - xAxis.setTimeZone(dt.timezone(dt.timedelta(hours=12))) - print('set time zone', xAxis.getTimeZone()) - -qt.QTimer.singleShot(4000, later) - -curve = plot1D.addCurve(x=x, y=y, legend='curve') -plot1D.show() - -app.exec_() - diff --git a/examples/simplewidget.py b/examples/simplewidget.py index 8bfe7d5..e952dc6 100755 --- a/examples/simplewidget.py +++ b/examples/simplewidget.py @@ -33,12 +33,17 @@ It shows the following widgets: __authors__ = ["V. Valls"] __license__ = "MIT" -__date__ = "12/01/2017" +__date__ = "02/08/2018" import sys +import functools +import numpy + from silx.gui import qt +from silx.gui.colors import Colormap from silx.gui.widgets.WaitingPushButton import WaitingPushButton from silx.gui.widgets.ThreadPoolPushButton import ThreadPoolPushButton +from silx.gui.widgets.RangeSlider import RangeSlider class SimpleWidgetExample(qt.QMainWindow): @@ -52,12 +57,17 @@ class SimpleWidgetExample(qt.QMainWindow): main_panel = qt.QWidget(self) main_panel.setLayout(qt.QVBoxLayout()) - main_panel.layout().addWidget(qt.QLabel("WaitingPushButton")) - main_panel.layout().addWidget(self.createWaitingPushButton()) - main_panel.layout().addWidget(self.createWaitingPushButton2()) + layout = main_panel.layout() + layout.addWidget(qt.QLabel("WaitingPushButton")) + layout.addWidget(self.createWaitingPushButton()) + layout.addWidget(self.createWaitingPushButton2()) + + layout.addWidget(qt.QLabel("ThreadPoolPushButton")) + layout.addWidget(self.createThreadPoolPushButton()) - main_panel.layout().addWidget(qt.QLabel("ThreadPoolPushButton")) - main_panel.layout().addWidget(self.createThreadPoolPushButton()) + layout.addWidget(qt.QLabel("RangeSlider")) + layout.addWidget(self.createRangeSlider()) + layout.addWidget(self.createRangeSliderWithBackground()) self.setCentralWidget(main_panel) @@ -79,6 +89,9 @@ class SimpleWidgetExample(qt.QMainWindow): print("Error") print(result) + def printEvent(self, eventName, *args): + print("Event %s: %s" % (eventName, args)) + def takesTimeToComputePow(self, a, b): qt.QThread.sleep(2) return a ** b @@ -90,6 +103,25 @@ class SimpleWidgetExample(qt.QMainWindow): widget.failed.connect(self.printError) return widget + def createRangeSlider(self): + widget = RangeSlider(self) + widget.setRange(0, 500) + widget.setValues(100, 400) + widget.sigValueChanged.connect(functools.partial(self.printEvent, "sigValueChanged")) + widget.sigPositionChanged.connect(functools.partial(self.printEvent, "sigPositionChanged")) + widget.sigPositionCountChanged.connect(functools.partial(self.printEvent, "sigPositionCountChanged")) + return widget + + def createRangeSliderWithBackground(self): + widget = RangeSlider(self) + widget.setRange(0, 500) + widget.setValues(100, 400) + background = numpy.sin(numpy.arange(250) / 250.0) + background[0], background[-1] = background[-1], background[0] + colormap = Colormap("viridis") + widget.setGroovePixmapFromProfile(background, colormap) + return widget + def main(): """ |