summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorAlexandre Marie <alexandre.marie@synchrotron-soleil.fr>2018-12-17 12:28:24 +0100
committerAlexandre Marie <alexandre.marie@synchrotron-soleil.fr>2018-12-17 12:28:24 +0100
commitcebdc9244c019224846cb8d2668080fe386a6adc (patch)
treeaedec55da0f9dd4fc4d6c7eb0f58489a956e2e8c /examples
parent159ef14fb9e198bb0066ea14e6b980f065de63dd (diff)
New upstream version 0.9.0+dfsg
Diffstat (limited to 'examples')
-rw-r--r--examples/__init__.py29
-rw-r--r--examples/blissPlot.py635
-rw-r--r--examples/colormapDialog.py2
-rw-r--r--examples/compareImages.py149
-rw-r--r--examples/plot3dSceneWindow.py10
-rw-r--r--examples/plotCurveLegendWidget.py154
-rw-r--r--examples/plotGL32.py46
-rw-r--r--examples/plotInteractiveImageROI.py9
-rw-r--r--examples/plotStats.py4
-rw-r--r--examples/plotTimeSeries.py33
-rwxr-xr-xexamples/simplewidget.py44
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():
"""