summaryrefslogtreecommitdiff
path: root/examples/blissPlot.py
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/blissPlot.py
parent159ef14fb9e198bb0066ea14e6b980f065de63dd (diff)
New upstream version 0.9.0+dfsg
Diffstat (limited to 'examples/blissPlot.py')
-rw-r--r--examples/blissPlot.py635
1 files changed, 0 insertions, 635 deletions
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_()