diff options
Diffstat (limited to 'silx/gui/plot/actions')
-rw-r--r-- | silx/gui/plot/actions/PlotAction.py | 78 | ||||
-rw-r--r-- | silx/gui/plot/actions/PlotToolAction.py | 150 | ||||
-rw-r--r-- | silx/gui/plot/actions/__init__.py | 42 | ||||
-rw-r--r-- | silx/gui/plot/actions/control.py | 604 | ||||
-rw-r--r-- | silx/gui/plot/actions/fit.py | 186 | ||||
-rw-r--r-- | silx/gui/plot/actions/histogram.py | 146 | ||||
-rw-r--r-- | silx/gui/plot/actions/io.py | 743 | ||||
-rw-r--r-- | silx/gui/plot/actions/medfilt.py | 147 | ||||
-rw-r--r-- | silx/gui/plot/actions/mode.py | 104 |
9 files changed, 0 insertions, 2200 deletions
diff --git a/silx/gui/plot/actions/PlotAction.py b/silx/gui/plot/actions/PlotAction.py deleted file mode 100644 index 2983775..0000000 --- a/silx/gui/plot/actions/PlotAction.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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. -# -# ###########################################################################*/ -""" -The class :class:`.PlotAction` help the creation of a qt.QAction associated -with a :class:`.PlotWidget`. -""" - -from __future__ import division - - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "03/01/2018" - - -import weakref -from silx.gui import icons -from silx.gui import qt - - -class PlotAction(qt.QAction): - """Base class for QAction that operates on a PlotWidget. - - :param plot: :class:`.PlotWidget` instance on which to operate. - :param icon: QIcon or str name of icon to use - :param str text: The name of this action to be used for menu label - :param str tooltip: The text of the tooltip - :param triggered: The callback to connect to the action's triggered - signal or None for no callback. - :param bool checkable: True for checkable action, False otherwise (default) - :param parent: See :class:`QAction`. - """ - - def __init__(self, plot, icon, text, tooltip=None, - triggered=None, checkable=False, parent=None): - assert plot is not None - self._plotRef = weakref.ref(plot) - - if not isinstance(icon, qt.QIcon): - # Try with icon as a string and load corresponding icon - icon = icons.getQIcon(icon) - - super(PlotAction, self).__init__(icon, text, parent) - - if tooltip is not None: - self.setToolTip(tooltip) - - self.setCheckable(checkable) - - if triggered is not None: - self.triggered[bool].connect(triggered) - - @property - def plot(self): - """The :class:`.PlotWidget` this action group is controlling.""" - return self._plotRef() diff --git a/silx/gui/plot/actions/PlotToolAction.py b/silx/gui/plot/actions/PlotToolAction.py deleted file mode 100644 index 77e8be2..0000000 --- a/silx/gui/plot/actions/PlotToolAction.py +++ /dev/null @@ -1,150 +0,0 @@ -# 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. -# -# ###########################################################################*/ -""" -The class :class:`.PlotToolAction` help the creation of a qt.QAction associating -a tool window with a :class:`.PlotWidget`. -""" - -from __future__ import division - - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "10/10/2018" - - -import weakref - -from .PlotAction import PlotAction -from silx.gui import qt - - -class PlotToolAction(PlotAction): - """Base class for QAction that maintain a tool window operating on a - PlotWidget.""" - - def __init__(self, plot, icon, text, tooltip=None, - triggered=None, checkable=False, parent=None): - PlotAction.__init__(self, - plot=plot, - icon=icon, - text=text, - tooltip=tooltip, - triggered=self._triggered, - parent=parent, - checkable=True) - self._previousGeometry = None - self._toolWindow = None - - def _triggered(self, checked): - """Update the plot of the histogram visibility status - - :param bool checked: status of the action button - """ - self._setToolWindowVisible(checked) - - def _setToolWindowVisible(self, visible): - """Set the tool window visible or hidden.""" - tool = self._getToolWindow() - if tool.isVisible() == visible: - # Nothing to do - return - - if visible: - self._connectPlot(tool) - tool.show() - if self._previousGeometry is not None: - # Restore the geometry - tool.setGeometry(self._previousGeometry) - else: - self._disconnectPlot(tool) - # Save the geometry - self._previousGeometry = tool.geometry() - tool.hide() - - def _connectPlot(self, window): - """Called if the tool is visible and have to be updated according to - event of the plot. - - :param qt.QWidget window: The tool window - """ - pass - - def _disconnectPlot(self, window): - """Called if the tool is not visible and dont have anymore to be updated - according to event of the plot. - - :param qt.QWidget window: The tool window - """ - pass - - def _isWindowInUse(self): - """Returns true if the tool window is currently in use.""" - if not self.isChecked(): - return False - return self._toolWindow is not None - - def _ownerVisibilityChanged(self, isVisible): - """Called when the visibility of the parent of the tool window changes - - :param bool isVisible: True if the parent became visible - """ - if self._isWindowInUse(): - self._setToolWindowVisible(isVisible) - - def eventFilter(self, qobject, event): - """Observe when the close event is emitted then - simply uncheck the action button - - :param qobject: the object observe - :param event: the event received by qobject - """ - if event.type() == qt.QEvent.Close: - if self._toolWindow is not None: - window = self._toolWindow() - self._previousGeometry = window.geometry() - window.hide() - self.setChecked(False) - - return PlotAction.eventFilter(self, qobject, event) - - def _getToolWindow(self): - """Returns the window containg tohe tool. - - It uses lazy loading to create this tool.. - """ - if self._toolWindow is None: - window = self._createToolWindow() - if self._previousGeometry is not None: - window.setGeometry(self._previousGeometry) - window.installEventFilter(self) - plot = self.plot - plot.sigVisibilityChanged.connect(self._ownerVisibilityChanged) - self._toolWindow = weakref.ref(window) - return self._toolWindow() - - def _createToolWindow(self): - """Create the tool window managing the plot.""" - raise NotImplementedError() diff --git a/silx/gui/plot/actions/__init__.py b/silx/gui/plot/actions/__init__.py deleted file mode 100644 index 930c728..0000000 --- a/silx/gui/plot/actions/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2017-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 provides a set of QAction to use with -:class:`~silx.gui.plot.PlotWidget` - -Those actions are useful to add menu items or toolbar items -that interact with a :class:`~silx.gui.plot.PlotWidget`. - -It provides a base class used to define new plot actions: -:class:`~silx.gui.plot.actions.PlotAction`. -""" - -__authors__ = ["H. Payno"] -__license__ = "MIT" -__date__ = "16/08/2017" - -from .PlotAction import PlotAction -from . import control -from . import mode -from . import io diff --git a/silx/gui/plot/actions/control.py b/silx/gui/plot/actions/control.py deleted file mode 100644 index 10df130..0000000 --- a/silx/gui/plot/actions/control.py +++ /dev/null @@ -1,604 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.control` provides a set of QAction relative to control -of a :class:`.PlotWidget`. - -The following QAction are available: - -- :class:`ColormapAction` -- :class:`CrosshairAction` -- :class:`CurveStyleAction` -- :class:`GridAction` -- :class:`KeepAspectRatioAction` -- :class:`PanWithArrowKeysAction` -- :class:`ResetZoomAction` -- :class:`XAxisLogarithmicAction` -- :class:`XAxisAutoScaleAction` -- :class:`YAxisInvertedAction` -- :class:`YAxisLogarithmicAction` -- :class:`YAxisAutoScaleAction` -- :class:`ZoomBackAction` -- :class:`ZoomInAction` -- :class:`ZoomOutAction` -- :class:'ShowAxisAction' -""" - -from __future__ import division - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "24/04/2018" - -from . import PlotAction -import logging -from silx.gui.plot import items -from silx.gui.plot._utils import applyZoomToPlot as _applyZoomToPlot -from silx.gui import qt -from silx.gui import icons - -_logger = logging.getLogger(__name__) - - -class ResetZoomAction(PlotAction): - """QAction controlling reset zoom on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(ResetZoomAction, self).__init__( - plot, icon='zoom-original', text='Reset Zoom', - tooltip='Auto-scale the graph', - triggered=self._actionTriggered, - checkable=False, parent=parent) - self._autoscaleChanged(True) - plot.getXAxis().sigAutoScaleChanged.connect(self._autoscaleChanged) - plot.getYAxis().sigAutoScaleChanged.connect(self._autoscaleChanged) - - def _autoscaleChanged(self, enabled): - xAxis = self.plot.getXAxis() - yAxis = self.plot.getYAxis() - self.setEnabled(xAxis.isAutoScale() or yAxis.isAutoScale()) - - if xAxis.isAutoScale() and yAxis.isAutoScale(): - tooltip = 'Auto-scale the graph' - elif xAxis.isAutoScale(): # And not Y axis - tooltip = 'Auto-scale the x-axis of the graph only' - elif yAxis.isAutoScale(): # And not X axis - tooltip = 'Auto-scale the y-axis of the graph only' - else: # no axis in autoscale - tooltip = 'Auto-scale the graph' - self.setToolTip(tooltip) - - def _actionTriggered(self, checked=False): - self.plot.resetZoom() - - -class ZoomBackAction(PlotAction): - """QAction performing a zoom-back in :class:`.PlotWidget` limits history. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(ZoomBackAction, self).__init__( - plot, icon='zoom-back', text='Zoom Back', - tooltip='Zoom back the plot', - triggered=self._actionTriggered, - checkable=False, parent=parent) - self.setShortcutContext(qt.Qt.WidgetShortcut) - - def _actionTriggered(self, checked=False): - self.plot.getLimitsHistory().pop() - - -class ZoomInAction(PlotAction): - """QAction performing a zoom-in on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(ZoomInAction, self).__init__( - plot, icon='zoom-in', text='Zoom In', - tooltip='Zoom in the plot', - triggered=self._actionTriggered, - checkable=False, parent=parent) - self.setShortcut(qt.QKeySequence.ZoomIn) - self.setShortcutContext(qt.Qt.WidgetShortcut) - - def _actionTriggered(self, checked=False): - _applyZoomToPlot(self.plot, 1.1) - - -class ZoomOutAction(PlotAction): - """QAction performing a zoom-out on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(ZoomOutAction, self).__init__( - plot, icon='zoom-out', text='Zoom Out', - tooltip='Zoom out the plot', - triggered=self._actionTriggered, - checkable=False, parent=parent) - self.setShortcut(qt.QKeySequence.ZoomOut) - self.setShortcutContext(qt.Qt.WidgetShortcut) - - def _actionTriggered(self, checked=False): - _applyZoomToPlot(self.plot, 1. / 1.1) - - -class XAxisAutoScaleAction(PlotAction): - """QAction controlling X axis autoscale on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(XAxisAutoScaleAction, self).__init__( - plot, icon='plot-xauto', text='X Autoscale', - tooltip='Enable x-axis auto-scale when checked.\n' - 'If unchecked, x-axis does not change when reseting zoom.', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.setChecked(plot.getXAxis().isAutoScale()) - plot.getXAxis().sigAutoScaleChanged.connect(self.setChecked) - - def _actionTriggered(self, checked=False): - self.plot.getXAxis().setAutoScale(checked) - if checked: - self.plot.resetZoom() - - -class YAxisAutoScaleAction(PlotAction): - """QAction controlling Y axis autoscale on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(YAxisAutoScaleAction, self).__init__( - plot, icon='plot-yauto', text='Y Autoscale', - tooltip='Enable y-axis auto-scale when checked.\n' - 'If unchecked, y-axis does not change when reseting zoom.', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.setChecked(plot.getYAxis().isAutoScale()) - plot.getYAxis().sigAutoScaleChanged.connect(self.setChecked) - - def _actionTriggered(self, checked=False): - self.plot.getYAxis().setAutoScale(checked) - if checked: - self.plot.resetZoom() - - -class XAxisLogarithmicAction(PlotAction): - """QAction controlling X axis log scale on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(XAxisLogarithmicAction, self).__init__( - plot, icon='plot-xlog', text='X Log. scale', - tooltip='Logarithmic x-axis when checked', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.axis = plot.getXAxis() - self.setChecked(self.axis.getScale() == self.axis.LOGARITHMIC) - self.axis.sigScaleChanged.connect(self._setCheckedIfLogScale) - - def _setCheckedIfLogScale(self, scale): - self.setChecked(scale == self.axis.LOGARITHMIC) - - def _actionTriggered(self, checked=False): - scale = self.axis.LOGARITHMIC if checked else self.axis.LINEAR - self.axis.setScale(scale) - - -class YAxisLogarithmicAction(PlotAction): - """QAction controlling Y axis log scale on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(YAxisLogarithmicAction, self).__init__( - plot, icon='plot-ylog', text='Y Log. scale', - tooltip='Logarithmic y-axis when checked', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.axis = plot.getYAxis() - self.setChecked(self.axis.getScale() == self.axis.LOGARITHMIC) - self.axis.sigScaleChanged.connect(self._setCheckedIfLogScale) - - def _setCheckedIfLogScale(self, scale): - self.setChecked(scale == self.axis.LOGARITHMIC) - - def _actionTriggered(self, checked=False): - scale = self.axis.LOGARITHMIC if checked else self.axis.LINEAR - self.axis.setScale(scale) - - -class GridAction(PlotAction): - """QAction controlling grid mode on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param str gridMode: The grid mode to use in 'both', 'major'. - See :meth:`.PlotWidget.setGraphGrid` - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, gridMode='both', parent=None): - assert gridMode in ('both', 'major') - self._gridMode = gridMode - - super(GridAction, self).__init__( - plot, icon='plot-grid', text='Grid', - tooltip='Toggle grid (on/off)', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.setChecked(plot.getGraphGrid() is not None) - plot.sigSetGraphGrid.connect(self._gridChanged) - - def _gridChanged(self, which): - """Slot listening for PlotWidget grid mode change.""" - self.setChecked(which != 'None') - - def _actionTriggered(self, checked=False): - self.plot.setGraphGrid(self._gridMode if checked else None) - - -class CurveStyleAction(PlotAction): - """QAction controlling curve style on a :class:`.PlotWidget`. - - It changes the default line and markers style which updates all - curves on the plot. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(CurveStyleAction, self).__init__( - plot, icon='plot-toggle-points', text='Curve style', - tooltip='Change curve line and markers style', - triggered=self._actionTriggered, - checkable=False, parent=parent) - - def _actionTriggered(self, checked=False): - currentState = (self.plot.isDefaultPlotLines(), - self.plot.isDefaultPlotPoints()) - - # line only, line and symbol, symbol only - states = (True, False), (True, True), (False, True) - newState = states[(states.index(currentState) + 1) % 3] - - self.plot.setDefaultPlotLines(newState[0]) - self.plot.setDefaultPlotPoints(newState[1]) - - -class ColormapAction(PlotAction): - """QAction opening a ColormapDialog to update the colormap. - - Both the active image colormap and the default colormap are updated. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - def __init__(self, plot, parent=None): - self._dialog = None # To store an instance of ColormapDialog - super(ColormapAction, self).__init__( - plot, icon='colormap', text='Colormap', - tooltip="Change colormap", - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.plot.sigActiveImageChanged.connect(self._updateColormap) - self.plot.sigActiveScatterChanged.connect(self._updateColormap) - - def setColorDialog(self, colorDialog): - """Set a specific color dialog instead of using the default dialog.""" - assert(colorDialog is not None) - assert(self._dialog is None) - self._dialog = colorDialog - self._dialog.visibleChanged.connect(self._dialogVisibleChanged) - self.setChecked(self._dialog.isVisible()) - - @staticmethod - def _createDialog(parent): - """Create the dialog if not already existing - - :parent QWidget parent: Parent of the new colormap - :rtype: ColormapDialog - """ - from silx.gui.dialog.ColormapDialog import ColormapDialog - dialog = ColormapDialog(parent=parent) - dialog.setModal(False) - return dialog - - def _actionTriggered(self, checked=False): - """Create a cmap dialog and update active image and default cmap.""" - if self._dialog is None: - self._dialog = self._createDialog(self.plot) - self._dialog.visibleChanged.connect(self._dialogVisibleChanged) - - # Run the dialog listening to colormap change - if checked is True: - self._dialog.show() - self._updateColormap() - else: - self._dialog.hide() - - def _dialogVisibleChanged(self, isVisible): - self.setChecked(isVisible) - - def _updateColormap(self): - if self._dialog is None: - return - image = self.plot.getActiveImage() - - if isinstance(image, items.ImageComplexData): - # Specific init for complex images - colormap = image.getColormap() - - mode = image.getVisualizationMode() - if mode in (items.ImageComplexData.Mode.AMPLITUDE_PHASE, - items.ImageComplexData.Mode.LOG10_AMPLITUDE_PHASE): - data = image.getData( - copy=False, mode=items.ImageComplexData.Mode.PHASE) - else: - data = image.getData(copy=False) - - # Set histogram and range if any - self._dialog.setData(data) - - elif isinstance(image, items.ColormapMixIn): - # Set dialog from active image - colormap = image.getColormap() - data = image.getData(copy=False) - # Set histogram and range if any - self._dialog.setData(data) - - else: - # No active image or active image is RGBA, - # Check for active scatter plot - scatter = self.plot._getActiveItem(kind='scatter') - if scatter is not None: - colormap = scatter.getColormap() - data = scatter.getValueData(copy=False) - self._dialog.setData(data) - - else: - # No active data image nor scatter, - # set dialog from default info - colormap = self.plot.getDefaultColormap() - # Reset histogram and range if any - self._dialog.setData(None) - - self._dialog.setColormap(colormap) - - -class ColorBarAction(PlotAction): - """QAction opening the ColorBarWidget of the specified plot. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - def __init__(self, plot, parent=None): - self._dialog = None # To store an instance of ColorBar - super(ColorBarAction, self).__init__( - plot, icon='colorbar', text='Colorbar', - tooltip="Show/Hide the colorbar", - triggered=self._actionTriggered, - checkable=True, parent=parent) - colorBarWidget = self.plot.getColorBarWidget() - old = self.blockSignals(True) - self.setChecked(colorBarWidget.isVisibleTo(self.plot)) - self.blockSignals(old) - colorBarWidget.sigVisibleChanged.connect(self._widgetVisibleChanged) - - def _widgetVisibleChanged(self, isVisible): - """Callback when the colorbar `visible` property change.""" - if self.isChecked() == isVisible: - return - self.setChecked(isVisible) - - def _actionTriggered(self, checked=False): - """Create a cmap dialog and update active image and default cmap.""" - colorBarWidget = self.plot.getColorBarWidget() - if not colorBarWidget.isHidden() == checked: - return - self.plot.getColorBarWidget().setVisible(checked) - - -class KeepAspectRatioAction(PlotAction): - """QAction controlling aspect ratio on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - # Uses two images for checked/unchecked states - self._states = { - False: (icons.getQIcon('shape-circle-solid'), - "Keep data aspect ratio"), - True: (icons.getQIcon('shape-ellipse-solid'), - "Do no keep data aspect ratio") - } - - icon, tooltip = self._states[plot.isKeepDataAspectRatio()] - super(KeepAspectRatioAction, self).__init__( - plot, - icon=icon, - text='Toggle keep aspect ratio', - tooltip=tooltip, - triggered=self._actionTriggered, - checkable=False, - parent=parent) - plot.sigSetKeepDataAspectRatio.connect( - self._keepDataAspectRatioChanged) - - def _keepDataAspectRatioChanged(self, aspectRatio): - """Handle Plot set keep aspect ratio signal""" - icon, tooltip = self._states[aspectRatio] - self.setIcon(icon) - self.setToolTip(tooltip) - - def _actionTriggered(self, checked=False): - # This will trigger _keepDataAspectRatioChanged - self.plot.setKeepDataAspectRatio(not self.plot.isKeepDataAspectRatio()) - - -class YAxisInvertedAction(PlotAction): - """QAction controlling Y orientation on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - # Uses two images for checked/unchecked states - self._states = { - False: (icons.getQIcon('plot-ydown'), - "Orient Y axis downward"), - True: (icons.getQIcon('plot-yup'), - "Orient Y axis upward"), - } - - icon, tooltip = self._states[plot.getYAxis().isInverted()] - super(YAxisInvertedAction, self).__init__( - plot, - icon=icon, - text='Invert Y Axis', - tooltip=tooltip, - triggered=self._actionTriggered, - checkable=False, - parent=parent) - plot.getYAxis().sigInvertedChanged.connect(self._yAxisInvertedChanged) - - def _yAxisInvertedChanged(self, inverted): - """Handle Plot set y axis inverted signal""" - icon, tooltip = self._states[inverted] - self.setIcon(icon) - self.setToolTip(tooltip) - - def _actionTriggered(self, checked=False): - # This will trigger _yAxisInvertedChanged - yAxis = self.plot.getYAxis() - yAxis.setInverted(not yAxis.isInverted()) - - -class CrosshairAction(PlotAction): - """QAction toggling crosshair cursor on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param str color: Color to use to draw the crosshair - :param int linewidth: Width of the crosshair cursor - :param str linestyle: Style of line. See :meth:`.Plot.setGraphCursor` - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, color='black', linewidth=1, linestyle='-', - parent=None): - self.color = color - """Color used to draw the crosshair (str).""" - - self.linewidth = linewidth - """Width of the crosshair cursor (int).""" - - self.linestyle = linestyle - """Style of line of the cursor (str).""" - - super(CrosshairAction, self).__init__( - plot, icon='crosshair', text='Crosshair Cursor', - tooltip='Enable crosshair cursor when checked', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.setChecked(plot.getGraphCursor() is not None) - plot.sigSetGraphCursor.connect(self.setChecked) - - def _actionTriggered(self, checked=False): - self.plot.setGraphCursor(checked, - color=self.color, - linestyle=self.linestyle, - linewidth=self.linewidth) - - -class PanWithArrowKeysAction(PlotAction): - """QAction toggling pan with arrow keys on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - - super(PanWithArrowKeysAction, self).__init__( - plot, icon='arrow-keys', text='Pan with arrow keys', - tooltip='Enable pan with arrow keys when checked', - triggered=self._actionTriggered, - checkable=True, parent=parent) - self.setChecked(plot.isPanWithArrowKeys()) - plot.sigSetPanWithArrowKeys.connect(self.setChecked) - - def _actionTriggered(self, checked=False): - self.plot.setPanWithArrowKeys(checked) - - -class ShowAxisAction(PlotAction): - """QAction controlling axis visibility on a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - tooltip = 'Show plot axis when checked, otherwise hide them' - PlotAction.__init__(self, - plot, - icon='axis', - text='show axis', - tooltip=tooltip, - triggered=self._actionTriggered, - checkable=True, - parent=parent) - self.setChecked(self.plot._backend.isAxesDisplayed()) - plot._sigAxesVisibilityChanged.connect(self.setChecked) - - def _actionTriggered(self, checked=False): - self.plot.setAxesDisplayed(checked) - diff --git a/silx/gui/plot/actions/fit.py b/silx/gui/plot/actions/fit.py deleted file mode 100644 index cb70733..0000000 --- a/silx/gui/plot/actions/fit.py +++ /dev/null @@ -1,186 +0,0 @@ -# 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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.fit` module provides actions relative to fit. - -The following QAction are available: - -- :class:`.FitAction` - -.. autoclass:`.FitAction` -""" - -from __future__ import division - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "10/10/2018" - -from .PlotToolAction import PlotToolAction -import logging -from silx.gui import qt -from silx.gui.plot.ItemsSelectionDialog import ItemsSelectionDialog -from silx.gui.plot.items import Curve, Histogram - -_logger = logging.getLogger(__name__) - - -def _getUniqueCurve(plt): - """Get a single curve from the plot. - Get the active curve if any, else if a single curve is plotted - get it, else return None. - - :param plt: :class:`.PlotWidget` instance on which to operate - - :return: return value of plt.getActiveCurve(), or plt.getAllCurves()[0], - or None - """ - curve = plt.getActiveCurve() - if curve is not None: - return curve - - curves = plt.getAllCurves() - if len(curves) == 0: - return None - - if len(curves) == 1 and len(plt._getItems(kind='histogram')) == 0: - return curves[0] - - return None - - -def _getUniqueHistogram(plt): - """Return the histogram if there is a single histogram and no curve in - the plot. In all other cases, return None. - - :param plt: :class:`.PlotWidget` instance on which to operate - :return: histogram or None - """ - histograms = plt._getItems(kind='histogram') - if len(histograms) != 1: - return None - if plt.getAllCurves(just_legend=True): - return None - return histograms[0] - - -class FitAction(PlotToolAction): - """QAction to open a :class:`FitWidget` and set its data to the - active curve if any, or to the first curve. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - def __init__(self, plot, parent=None): - super(FitAction, self).__init__( - plot, icon='math-fit', text='Fit curve', - tooltip='Open a fit dialog', - parent=parent) - self.fit_widget = None - - def _createToolWindow(self): - window = qt.QMainWindow(parent=self.plot) - # import done here rather than at module level to avoid circular import - # FitWidget -> BackgroundWidget -> PlotWindow -> actions -> fit -> FitWidget - from ...fit.FitWidget import FitWidget - fit_widget = FitWidget(parent=window) - window.setCentralWidget(fit_widget) - fit_widget.guibuttons.DismissButton.clicked.connect(window.close) - fit_widget.sigFitWidgetSignal.connect(self.handle_signal) - self.fit_widget = fit_widget - return window - - def _connectPlot(self, window): - # Wait for the next iteration, else the plot is not yet initialized - # No curve available - qt.QTimer.singleShot(10, lambda: self._initFit(window)) - - def _initFit(self, window): - plot = self.plot - self.xlabel = plot.getXAxis().getLabel() - self.ylabel = plot.getYAxis().getLabel() - self.xmin, self.xmax = plot.getXAxis().getLimits() - - histo = _getUniqueHistogram(self.plot) - curve = _getUniqueCurve(self.plot) - - if histo is None and curve is None: - # ambiguous case, we need to ask which plot item to fit - isd = ItemsSelectionDialog(parent=plot, plot=self.plot) - isd.setWindowTitle("Select item to be fitted") - isd.setItemsSelectionMode(qt.QTableWidget.SingleSelection) - isd.setAvailableKinds(["curve", "histogram"]) - isd.selectAllKinds() - - result = isd.exec_() - if result and len(isd.getSelectedItems()) == 1: - item = isd.getSelectedItems()[0] - else: - return - elif histo is not None: - # presence of a unique histo and no curve - item = histo - elif curve is not None: - # presence of a unique or active curve - item = curve - - self.legend = item.getLegend() - - if isinstance(item, Histogram): - bin_edges = item.getBinEdgesData(copy=False) - # take the middle coordinate between adjacent bin edges - self.x = (bin_edges[1:] + bin_edges[:-1]) / 2 - self.y = item.getValueData(copy=False) - # else take the active curve, or else the unique curve - elif isinstance(item, Curve): - self.x = item.getXData(copy=False) - self.y = item.getYData(copy=False) - - self.fit_widget.setData(self.x, self.y, - xmin=self.xmin, xmax=self.xmax) - window.setWindowTitle( - "Fitting " + self.legend + - " on x range %f-%f" % (self.xmin, self.xmax)) - - def handle_signal(self, ddict): - x_fit = self.x[self.xmin <= self.x] - x_fit = x_fit[x_fit <= self.xmax] - fit_legend = "Fit <%s>" % self.legend - fit_curve = self.plot.getCurve(fit_legend) - - if ddict["event"] == "FitFinished": - y_fit = self.fit_widget.fitmanager.gendata() - if fit_curve is None: - self.plot.addCurve(x_fit, y_fit, - fit_legend, - xlabel=self.xlabel, ylabel=self.ylabel, - resetzoom=False) - else: - fit_curve.setData(x_fit, y_fit) - fit_curve.setVisible(True) - - if ddict["event"] in ["FitStarted", "FitFailed"]: - if fit_curve is not None: - fit_curve.setVisible(False) diff --git a/silx/gui/plot/actions/histogram.py b/silx/gui/plot/actions/histogram.py deleted file mode 100644 index 9181f53..0000000 --- a/silx/gui/plot/actions/histogram.py +++ /dev/null @@ -1,146 +0,0 @@ -# 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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.histogram` provides actions relative to histograms -for :class:`.PlotWidget`. - -The following QAction are available: - -- :class:`PixelIntensitiesHistoAction` -""" - -from __future__ import division - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__date__ = "10/10/2018" -__license__ = "MIT" - -from .PlotToolAction import PlotToolAction -from silx.math.histogram import Histogramnd -from silx.math.combo import min_max -import numpy -import logging -from silx.gui import qt - -_logger = logging.getLogger(__name__) - - -class PixelIntensitiesHistoAction(PlotToolAction): - """QAction to plot the pixels intensities diagram - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - PlotToolAction.__init__(self, - plot, - icon='pixel-intensities', - text='pixels intensity', - tooltip='Compute image intensity distribution', - parent=parent) - self._connectedToActiveImage = False - self._histo = None - - def _connectPlot(self, window): - if not self._connectedToActiveImage: - self.plot.sigActiveImageChanged.connect( - self._activeImageChanged) - self._connectedToActiveImage = True - self.computeIntensityDistribution() - PlotToolAction._connectPlot(self, window) - - def _disconnectPlot(self, window): - if self._connectedToActiveImage: - self.plot.sigActiveImageChanged.disconnect( - self._activeImageChanged) - self._connectedToActiveImage = False - PlotToolAction._disconnectPlot(self, window) - - def _activeImageChanged(self, previous, legend): - """Handle active image change: toggle enabled toolbar, update curve""" - if self._isWindowInUse(): - self.computeIntensityDistribution() - - def computeIntensityDistribution(self): - """Get the active image and compute the image intensity distribution - """ - activeImage = self.plot.getActiveImage() - - if activeImage is not None: - image = activeImage.getData(copy=False) - if image.ndim == 3: # RGB(A) images - _logger.info('Converting current image from RGB(A) to grayscale\ - in order to compute the intensity distribution') - image = (image[:, :, 0] * 0.299 + - image[:, :, 1] * 0.587 + - image[:, :, 2] * 0.114) - - xmin, xmax = min_max(image, min_positive=False, finite=True) - nbins = min(1024, int(numpy.sqrt(image.size))) - data_range = xmin, xmax - - # bad hack: get 256 bins in the case we have a B&W - if numpy.issubdtype(image.dtype, numpy.integer): - if nbins > xmax - xmin: - nbins = xmax - xmin - - nbins = max(2, nbins) - - data = image.ravel().astype(numpy.float32) - histogram = Histogramnd(data, n_bins=nbins, histo_range=data_range) - assert len(histogram.edges) == 1 - self._histo = histogram.histo - edges = histogram.edges[0] - plot = self.getHistogramPlotWidget() - plot.addHistogram(histogram=self._histo, - edges=edges, - legend='pixel intensity', - fill=True, - color='#66aad7') - plot.resetZoom() - - def getHistogramPlotWidget(self): - """Create the plot histogram if needed, otherwise create it - - :return: the PlotWidget showing the histogram of the pixel intensities - """ - return self._getToolWindow() - - def _createToolWindow(self): - from silx.gui.plot.PlotWindow import Plot1D - window = Plot1D(parent=self.plot) - window.setWindowFlags(qt.Qt.Window) - window.setWindowTitle('Image Intensity Histogram') - window.getXAxis().setLabel("Value") - window.getYAxis().setLabel("Count") - return window - - def getHistogram(self): - """Return the last computed histogram - - :return: the histogram displayed in the HistogramPlotWiget - """ - return self._histo diff --git a/silx/gui/plot/actions/io.py b/silx/gui/plot/actions/io.py deleted file mode 100644 index 97de527..0000000 --- a/silx/gui/plot/actions/io.py +++ /dev/null @@ -1,743 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.io` provides a set of QAction relative of inputs -and outputs for a :class:`.PlotWidget`. - -The following QAction are available: - -- :class:`CopyAction` -- :class:`PrintAction` -- :class:`SaveAction` -""" - -from __future__ import division - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" -__date__ = "12/07/2018" - -from . import PlotAction -from silx.io.utils import save1D, savespec, NEXUS_HDF5_EXT -from silx.io.nxdata import save_NXdata -import logging -import sys -import os.path -from collections import OrderedDict -import traceback -import numpy -from silx.utils.deprecation import deprecated -from silx.gui import qt, printer -from silx.gui.dialog.GroupDialog import GroupDialog -from silx.third_party.EdfFile import EdfFile -from silx.third_party.TiffIO import TiffIO -from ...utils.image import convertArrayToQImage -if sys.version_info[0] == 3: - from io import BytesIO -else: - import cStringIO as _StringIO - BytesIO = _StringIO.StringIO - -_logger = logging.getLogger(__name__) - -_NEXUS_HDF5_EXT_STR = ' '.join(['*' + ext for ext in NEXUS_HDF5_EXT]) - - -def selectOutputGroup(h5filename): - """Open a dialog to prompt the user to select a group in - which to output data. - - :param str h5filename: name of an existing HDF5 file - :rtype: str - :return: Name of output group, or None if the dialog was cancelled - """ - dialog = GroupDialog() - dialog.addFile(h5filename) - dialog.setWindowTitle("Select an output group") - if not dialog.exec_(): - return None - return dialog.getSelectedDataUrl().data_path() - - -class SaveAction(PlotAction): - """QAction for saving Plot content. - - It opens a Save as... dialog. - - :param plot: :class:`.PlotWidget` instance on which to operate. - :param parent: See :class:`QAction`. - """ - - SNAPSHOT_FILTER_SVG = 'Plot Snapshot as SVG (*.svg)' - SNAPSHOT_FILTER_PNG = 'Plot Snapshot as PNG (*.png)' - - DEFAULT_ALL_FILTERS = (SNAPSHOT_FILTER_PNG, SNAPSHOT_FILTER_SVG) - - # Dict of curve filters with CSV-like format - # Using ordered dict to guarantee filters order - # Note: '%.18e' is numpy.savetxt default format - CURVE_FILTERS_TXT = OrderedDict(( - ('Curve as Raw ASCII (*.txt)', - {'fmt': '%.18e', 'delimiter': ' ', 'header': False}), - ('Curve as ";"-separated CSV (*.csv)', - {'fmt': '%.18e', 'delimiter': ';', 'header': True}), - ('Curve as ","-separated CSV (*.csv)', - {'fmt': '%.18e', 'delimiter': ',', 'header': True}), - ('Curve as tab-separated CSV (*.csv)', - {'fmt': '%.18e', 'delimiter': '\t', 'header': True}), - ('Curve as OMNIC CSV (*.csv)', - {'fmt': '%.7E', 'delimiter': ',', 'header': False}), - ('Curve as SpecFile (*.dat)', - {'fmt': '%.10g', 'delimiter': '', 'header': False}) - )) - - CURVE_FILTER_NPY = 'Curve as NumPy binary file (*.npy)' - - CURVE_FILTER_NXDATA = 'Curve as NXdata (%s)' % _NEXUS_HDF5_EXT_STR - - DEFAULT_CURVE_FILTERS = list(CURVE_FILTERS_TXT.keys()) + [ - CURVE_FILTER_NPY, CURVE_FILTER_NXDATA] - - DEFAULT_ALL_CURVES_FILTERS = ("All curves as SpecFile (*.dat)",) - - IMAGE_FILTER_EDF = 'Image data as EDF (*.edf)' - IMAGE_FILTER_TIFF = 'Image data as TIFF (*.tif)' - IMAGE_FILTER_NUMPY = 'Image data as NumPy binary file (*.npy)' - IMAGE_FILTER_ASCII = 'Image data as ASCII (*.dat)' - IMAGE_FILTER_CSV_COMMA = 'Image data as ,-separated CSV (*.csv)' - IMAGE_FILTER_CSV_SEMICOLON = 'Image data as ;-separated CSV (*.csv)' - IMAGE_FILTER_CSV_TAB = 'Image data as tab-separated CSV (*.csv)' - IMAGE_FILTER_RGB_PNG = 'Image as PNG (*.png)' - IMAGE_FILTER_NXDATA = 'Image as NXdata (%s)' % _NEXUS_HDF5_EXT_STR - DEFAULT_IMAGE_FILTERS = (IMAGE_FILTER_EDF, - IMAGE_FILTER_TIFF, - IMAGE_FILTER_NUMPY, - IMAGE_FILTER_ASCII, - IMAGE_FILTER_CSV_COMMA, - IMAGE_FILTER_CSV_SEMICOLON, - IMAGE_FILTER_CSV_TAB, - IMAGE_FILTER_RGB_PNG, - IMAGE_FILTER_NXDATA) - - SCATTER_FILTER_NXDATA = 'Scatter as NXdata (%s)' % _NEXUS_HDF5_EXT_STR - DEFAULT_SCATTER_FILTERS = (SCATTER_FILTER_NXDATA,) - - # filters for which we don't want an "overwrite existing file" warning - DEFAULT_APPEND_FILTERS = (CURVE_FILTER_NXDATA, IMAGE_FILTER_NXDATA, - SCATTER_FILTER_NXDATA) - - def __init__(self, plot, parent=None): - self._filters = { - 'all': OrderedDict(), - 'curve': OrderedDict(), - 'curves': OrderedDict(), - 'image': OrderedDict(), - 'scatter': OrderedDict()} - - # Initialize filters - for nameFilter in self.DEFAULT_ALL_FILTERS: - self.setFileFilter( - dataKind='all', nameFilter=nameFilter, func=self._saveSnapshot) - - for nameFilter in self.DEFAULT_CURVE_FILTERS: - self.setFileFilter( - dataKind='curve', nameFilter=nameFilter, func=self._saveCurve) - - for nameFilter in self.DEFAULT_ALL_CURVES_FILTERS: - self.setFileFilter( - dataKind='curves', nameFilter=nameFilter, func=self._saveCurves) - - for nameFilter in self.DEFAULT_IMAGE_FILTERS: - self.setFileFilter( - dataKind='image', nameFilter=nameFilter, func=self._saveImage) - - for nameFilter in self.DEFAULT_SCATTER_FILTERS: - self.setFileFilter( - dataKind='scatter', nameFilter=nameFilter, func=self._saveScatter) - - super(SaveAction, self).__init__( - plot, icon='document-save', text='Save as...', - tooltip='Save curve/image/plot snapshot dialog', - triggered=self._actionTriggered, - checkable=False, parent=parent) - self.setShortcut(qt.QKeySequence.Save) - self.setShortcutContext(qt.Qt.WidgetShortcut) - - def _errorMessage(self, informativeText=''): - """Display an error message.""" - # TODO issue with QMessageBox size fixed and too small - msg = qt.QMessageBox(self.plot) - msg.setIcon(qt.QMessageBox.Critical) - msg.setInformativeText(informativeText + ' ' + str(sys.exc_info()[1])) - msg.setDetailedText(traceback.format_exc()) - msg.exec_() - - def _saveSnapshot(self, plot, filename, nameFilter): - """Save a snapshot of the :class:`PlotWindow` widget. - - :param str filename: The name of the file to write - :param str nameFilter: The selected name filter - :return: False if format is not supported or save failed, - True otherwise. - """ - if nameFilter == self.SNAPSHOT_FILTER_PNG: - fileFormat = 'png' - elif nameFilter == self.SNAPSHOT_FILTER_SVG: - fileFormat = 'svg' - else: # Format not supported - _logger.error( - 'Saving plot snapshot failed: format not supported') - return False - - plot.saveGraph(filename, fileFormat=fileFormat) - return True - - def _getAxesLabels(self, item): - # If curve has no associated label, get the default from the plot - xlabel = item.getXLabel() or self.plot.getXAxis().getLabel() - ylabel = item.getYLabel() or self.plot.getYAxis().getLabel() - return xlabel, ylabel - - def _selectWriteableOutputGroup(self, filename): - if os.path.exists(filename) and os.path.isfile(filename) \ - and os.access(filename, os.W_OK): - entryPath = selectOutputGroup(filename) - if entryPath is None: - _logger.info("Save operation cancelled") - return None - return entryPath - elif not os.path.exists(filename): - # create new entry in new file - return "/entry" - else: - self._errorMessage('Save failed (file access issue)\n') - return None - - def _saveCurveAsNXdata(self, curve, filename): - entryPath = self._selectWriteableOutputGroup(filename) - if entryPath is None: - return False - - xlabel, ylabel = self._getAxesLabels(curve) - - return save_NXdata( - filename, - nxentry_name=entryPath, - signal=curve.getYData(copy=False), - axes=[curve.getXData(copy=False)], - signal_name="y", - axes_names=["x"], - signal_long_name=ylabel, - axes_long_names=[xlabel], - signal_errors=curve.getYErrorData(copy=False), - axes_errors=[curve.getXErrorData(copy=True)], - title=self.plot.getGraphTitle()) - - def _saveCurve(self, plot, filename, nameFilter): - """Save a curve from the plot. - - :param str filename: The name of the file to write - :param str nameFilter: The selected name filter - :return: False if format is not supported or save failed, - True otherwise. - """ - if nameFilter not in self.DEFAULT_CURVE_FILTERS: - return False - - # Check if a curve is to be saved - curve = plot.getActiveCurve() - # before calling _saveCurve, if there is no selected curve, we - # make sure there is only one curve on the graph - if curve is None: - curves = plot.getAllCurves() - if not curves: - self._errorMessage("No curve to be saved") - return False - curve = curves[0] - - if nameFilter in self.CURVE_FILTERS_TXT: - filter_ = self.CURVE_FILTERS_TXT[nameFilter] - fmt = filter_['fmt'] - csvdelim = filter_['delimiter'] - autoheader = filter_['header'] - else: - # .npy or nxdata - fmt, csvdelim, autoheader = ("", "", False) - - xlabel, ylabel = self._getAxesLabels(curve) - - if nameFilter == self.CURVE_FILTER_NXDATA: - return self._saveCurveAsNXdata(curve, filename) - - try: - save1D(filename, - curve.getXData(copy=False), - curve.getYData(copy=False), - xlabel, [ylabel], - fmt=fmt, csvdelim=csvdelim, - autoheader=autoheader) - except IOError: - self._errorMessage('Save failed\n') - return False - - return True - - def _saveCurves(self, plot, filename, nameFilter): - """Save all curves from the plot. - - :param str filename: The name of the file to write - :param str nameFilter: The selected name filter - :return: False if format is not supported or save failed, - True otherwise. - """ - if nameFilter not in self.DEFAULT_ALL_CURVES_FILTERS: - return False - - curves = plot.getAllCurves() - if not curves: - self._errorMessage("No curves to be saved") - return False - - curve = curves[0] - scanno = 1 - try: - xlabel = curve.getXLabel() or plot.getGraphXLabel() - ylabel = curve.getYLabel() or plot.getGraphYLabel(curve.getYAxis()) - specfile = savespec(filename, - curve.getXData(copy=False), - curve.getYData(copy=False), - xlabel, - ylabel, - fmt="%.7g", scan_number=1, mode="w", - write_file_header=True, - close_file=False) - except IOError: - self._errorMessage('Save failed\n') - return False - - for curve in curves[1:]: - try: - scanno += 1 - xlabel = curve.getXLabel() or plot.getGraphXLabel() - ylabel = curve.getYLabel() or plot.getGraphYLabel(curve.getYAxis()) - specfile = savespec(specfile, - curve.getXData(copy=False), - curve.getYData(copy=False), - xlabel, - ylabel, - fmt="%.7g", scan_number=scanno, - write_file_header=False, - close_file=False) - except IOError: - self._errorMessage('Save failed\n') - return False - specfile.close() - - return True - - def _saveImage(self, plot, filename, nameFilter): - """Save an image from the plot. - - :param str filename: The name of the file to write - :param str nameFilter: The selected name filter - :return: False if format is not supported or save failed, - True otherwise. - """ - if nameFilter not in self.DEFAULT_IMAGE_FILTERS: - return False - - image = plot.getActiveImage() - if image is None: - qt.QMessageBox.warning( - plot, "No Data", "No image to be saved") - return False - - data = image.getData(copy=False) - - # TODO Use silx.io for writing files - if nameFilter == self.IMAGE_FILTER_EDF: - edfFile = EdfFile(filename, access="w+") - edfFile.WriteImage({}, data, Append=0) - return True - - elif nameFilter == self.IMAGE_FILTER_TIFF: - tiffFile = TiffIO(filename, mode='w') - tiffFile.writeImage(data, software='silx') - return True - - elif nameFilter == self.IMAGE_FILTER_NUMPY: - try: - numpy.save(filename, data) - except IOError: - self._errorMessage('Save failed\n') - return False - return True - - elif nameFilter == self.IMAGE_FILTER_NXDATA: - entryPath = self._selectWriteableOutputGroup(filename) - if entryPath is None: - return False - xorigin, yorigin = image.getOrigin() - xscale, yscale = image.getScale() - xaxis = xorigin + xscale * numpy.arange(data.shape[1]) - yaxis = yorigin + yscale * numpy.arange(data.shape[0]) - xlabel, ylabel = self._getAxesLabels(image) - interpretation = "image" if len(data.shape) == 2 else "rgba-image" - - return save_NXdata(filename, - nxentry_name=entryPath, - signal=data, - axes=[yaxis, xaxis], - signal_name="image", - axes_names=["y", "x"], - axes_long_names=[ylabel, xlabel], - title=plot.getGraphTitle(), - interpretation=interpretation) - - elif nameFilter in (self.IMAGE_FILTER_ASCII, - self.IMAGE_FILTER_CSV_COMMA, - self.IMAGE_FILTER_CSV_SEMICOLON, - self.IMAGE_FILTER_CSV_TAB): - csvdelim, filetype = { - self.IMAGE_FILTER_ASCII: (' ', 'txt'), - self.IMAGE_FILTER_CSV_COMMA: (',', 'csv'), - self.IMAGE_FILTER_CSV_SEMICOLON: (';', 'csv'), - self.IMAGE_FILTER_CSV_TAB: ('\t', 'csv'), - }[nameFilter] - - height, width = data.shape - rows, cols = numpy.mgrid[0:height, 0:width] - try: - save1D(filename, rows.ravel(), (cols.ravel(), data.ravel()), - filetype=filetype, - xlabel='row', - ylabels=['column', 'value'], - csvdelim=csvdelim, - autoheader=True) - - except IOError: - self._errorMessage('Save failed\n') - return False - return True - - elif nameFilter == self.IMAGE_FILTER_RGB_PNG: - # Get displayed image - rgbaImage = image.getRgbaImageData(copy=False) - # Convert RGB QImage - qimage = convertArrayToQImage(rgbaImage[:, :, :3]) - - if qimage.save(filename, 'PNG'): - return True - else: - _logger.error('Failed to save image as %s', filename) - qt.QMessageBox.critical( - self.parent(), - 'Save image as', - 'Failed to save image') - - return False - - def _saveScatter(self, plot, filename, nameFilter): - """Save an image from the plot. - - :param str filename: The name of the file to write - :param str nameFilter: The selected name filter - :return: False if format is not supported or save failed, - True otherwise. - """ - if nameFilter not in self.DEFAULT_SCATTER_FILTERS: - return False - - if nameFilter == self.SCATTER_FILTER_NXDATA: - entryPath = self._selectWriteableOutputGroup(filename) - if entryPath is None: - return False - scatter = plot.getScatter() - - x = scatter.getXData(copy=False) - y = scatter.getYData(copy=False) - z = scatter.getValueData(copy=False) - - xerror = scatter.getXErrorData(copy=False) - if isinstance(xerror, float): - xerror = xerror * numpy.ones(x.shape, dtype=numpy.float32) - - yerror = scatter.getYErrorData(copy=False) - if isinstance(yerror, float): - yerror = yerror * numpy.ones(x.shape, dtype=numpy.float32) - - xlabel = plot.getGraphXLabel() - ylabel = plot.getGraphYLabel() - - return save_NXdata( - filename, - nxentry_name=entryPath, - signal=z, - axes=[x, y], - signal_name="values", - axes_names=["x", "y"], - axes_long_names=[xlabel, ylabel], - axes_errors=[xerror, yerror], - title=plot.getGraphTitle()) - - def setFileFilter(self, dataKind, nameFilter, func): - """Set a name filter to add/replace a file format support - - :param str dataKind: - The kind of data for which the provided filter is valid. - One of: 'all', 'curve', 'curves', 'image', 'scatter' - :param str nameFilter: The name filter in the QFileDialog. - See :meth:`QFileDialog.setNameFilters`. - :param callable func: The function to call to perform saving. - Expected signature is: - bool func(PlotWidget plot, str filename, str nameFilter) - """ - assert dataKind in ('all', 'curve', 'curves', 'image', 'scatter') - - self._filters[dataKind][nameFilter] = func - - def getFileFilters(self, dataKind): - """Returns the nameFilter and associated function for a kind of data. - - :param str dataKind: - The kind of data for which the provided filter is valid. - On of: 'all', 'curve', 'curves', 'image', 'scatter' - :return: {nameFilter: function} associations. - :rtype: collections.OrderedDict - """ - assert dataKind in ('all', 'curve', 'curves', 'image', 'scatter') - - return self._filters[dataKind].copy() - - def _actionTriggered(self, checked=False): - """Handle save action.""" - # Set-up filters - filters = OrderedDict() - - # Add image filters if there is an active image - if self.plot.getActiveImage() is not None: - filters.update(self._filters['image'].items()) - - # Add curve filters if there is a curve to save - if (self.plot.getActiveCurve() is not None or - len(self.plot.getAllCurves()) == 1): - filters.update(self._filters['curve'].items()) - if len(self.plot.getAllCurves()) >= 1: - filters.update(self._filters['curves'].items()) - - # Add scatter filters if there is a scatter - # todo: CSV - if self.plot.getScatter() is not None: - filters.update(self._filters['scatter'].items()) - - filters.update(self._filters['all'].items()) - - # Create and run File dialog - dialog = qt.QFileDialog(self.plot) - dialog.setOption(dialog.DontUseNativeDialog) - dialog.setWindowTitle("Output File Selection") - dialog.setModal(1) - dialog.setNameFilters(list(filters.keys())) - - dialog.setFileMode(dialog.AnyFile) - dialog.setAcceptMode(dialog.AcceptSave) - - def onFilterSelection(filt_): - # disable overwrite confirmation for NXdata types, - # because we append the data to existing files - if filt_ in self.DEFAULT_APPEND_FILTERS: - dialog.setOption(dialog.DontConfirmOverwrite) - else: - dialog.setOption(dialog.DontConfirmOverwrite, False) - - dialog.filterSelected.connect(onFilterSelection) - - if not dialog.exec_(): - return False - - nameFilter = dialog.selectedNameFilter() - filename = dialog.selectedFiles()[0] - dialog.close() - - if '(' in nameFilter and ')' == nameFilter.strip()[-1]: - # Check for correct file extension - # Extract file extensions as .something - extensions = [ext[ext.find('.'):] for ext in - nameFilter[nameFilter.find('(')+1:-1].split()] - for ext in extensions: - if (len(filename) > len(ext) and - filename[-len(ext):].lower() == ext.lower()): - break - else: # filename has no extension supported in nameFilter, add one - if len(extensions) >= 1: - filename += extensions[0] - - # Handle save - func = filters.get(nameFilter, None) - if func is not None: - return func(self.plot, filename, nameFilter) - else: - _logger.error('Unsupported file filter: %s', nameFilter) - return False - - -def _plotAsPNG(plot): - """Save a :class:`Plot` as PNG and return the payload. - - :param plot: The :class:`Plot` to save - """ - pngFile = BytesIO() - plot.saveGraph(pngFile, fileFormat='png') - pngFile.flush() - pngFile.seek(0) - data = pngFile.read() - pngFile.close() - return data - - -class PrintAction(PlotAction): - """QAction for printing the plot. - - It opens a Print dialog. - - Current implementation print a bitmap of the plot area and not vector - graphics, so printing quality is not great. - - :param plot: :class:`.PlotWidget` instance on which to operate. - :param parent: See :class:`QAction`. - """ - - def __init__(self, plot, parent=None): - super(PrintAction, self).__init__( - plot, icon='document-print', text='Print...', - tooltip='Open print dialog', - triggered=self.printPlot, - checkable=False, parent=parent) - self.setShortcut(qt.QKeySequence.Print) - self.setShortcutContext(qt.Qt.WidgetShortcut) - - def getPrinter(self): - """The QPrinter instance used by the PrintAction. - - :rtype: QPrinter - """ - return printer.getDefaultPrinter() - - @property - @deprecated(replacement="getPrinter()", since_version="0.8.0") - def printer(self): - return self.getPrinter() - - def printPlotAsWidget(self): - """Open the print dialog and print the plot. - - Use :meth:`QWidget.render` to print the plot - - :return: True if successful - """ - dialog = qt.QPrintDialog(self.getPrinter(), self.plot) - dialog.setWindowTitle('Print Plot') - if not dialog.exec_(): - return False - - # Print a snapshot of the plot widget at the top of the page - widget = self.plot.centralWidget() - - painter = qt.QPainter() - if not painter.begin(self.getPrinter()): - return False - - pageRect = self.getPrinter().pageRect() - xScale = pageRect.width() / widget.width() - yScale = pageRect.height() / widget.height() - scale = min(xScale, yScale) - - painter.translate(pageRect.width() / 2., 0.) - painter.scale(scale, scale) - painter.translate(-widget.width() / 2., 0.) - widget.render(painter) - painter.end() - - return True - - def printPlot(self): - """Open the print dialog and print the plot. - - Use :meth:`Plot.saveGraph` to print the plot. - - :return: True if successful - """ - # Init printer and start printer dialog - dialog = qt.QPrintDialog(self.getPrinter(), self.plot) - dialog.setWindowTitle('Print Plot') - if not dialog.exec_(): - return False - - # Save Plot as PNG and make a pixmap from it with default dpi - pngData = _plotAsPNG(self.plot) - - pixmap = qt.QPixmap() - pixmap.loadFromData(pngData, 'png') - - xScale = self.getPrinter().pageRect().width() / pixmap.width() - yScale = self.getPrinter().pageRect().height() / pixmap.height() - scale = min(xScale, yScale) - - # Draw pixmap with painter - painter = qt.QPainter() - if not painter.begin(self.getPrinter()): - return False - - painter.drawPixmap(0, 0, - pixmap.width() * scale, - pixmap.height() * scale, - pixmap) - painter.end() - - return True - - -class CopyAction(PlotAction): - """QAction to copy :class:`.PlotWidget` content to clipboard. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(CopyAction, self).__init__( - plot, icon='edit-copy', text='Copy plot', - tooltip='Copy a snapshot of the plot into the clipboard', - triggered=self.copyPlot, - checkable=False, parent=parent) - self.setShortcut(qt.QKeySequence.Copy) - self.setShortcutContext(qt.Qt.WidgetShortcut) - - def copyPlot(self): - """Copy plot content to the clipboard as a bitmap.""" - # Save Plot as PNG and make a QImage from it with default dpi - pngData = _plotAsPNG(self.plot) - image = qt.QImage.fromData(pngData, 'png') - qt.QApplication.clipboard().setImage(image) diff --git a/silx/gui/plot/actions/medfilt.py b/silx/gui/plot/actions/medfilt.py deleted file mode 100644 index 276f970..0000000 --- a/silx/gui/plot/actions/medfilt.py +++ /dev/null @@ -1,147 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.medfilt` provides a set of QAction to apply filter -on data contained in a :class:`.PlotWidget`. - -The following QAction are available: - -- :class:`MedianFilterAction` -- :class:`MedianFilter1DAction` -- :class:`MedianFilter2DAction` - -""" - -from __future__ import division - -__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__license__ = "MIT" - -__date__ = "10/10/2018" - -from .PlotToolAction import PlotToolAction -from silx.gui.widgets.MedianFilterDialog import MedianFilterDialog -from silx.math.medianfilter import medfilt2d -import logging - -_logger = logging.getLogger(__name__) - - -class MedianFilterAction(PlotToolAction): - """QAction to plot the pixels intensities diagram - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - PlotToolAction.__init__(self, - plot, - icon='median-filter', - text='median filter', - tooltip='Apply a median filter on the image', - parent=parent) - self._originalImage = None - self._legend = None - self._filteredImage = None - - def _createToolWindow(self): - popup = MedianFilterDialog(parent=self.plot) - popup.sigFilterOptChanged.connect(self._updateFilter) - return popup - - def _connectPlot(self, window): - PlotToolAction._connectPlot(self, window) - self.plot.sigActiveImageChanged.connect(self._updateActiveImage) - self._updateActiveImage() - - def _disconnectPlot(self, window): - PlotToolAction._disconnectPlot(self, window) - self.plot.sigActiveImageChanged.disconnect(self._updateActiveImage) - - def _updateActiveImage(self): - """Set _activeImageLegend and _originalImage from the active image""" - self._activeImageLegend = self.plot.getActiveImage(just_legend=True) - if self._activeImageLegend is None: - self._originalImage = None - self._legend = None - else: - self._originalImage = self.plot.getImage(self._activeImageLegend).getData(copy=False) - self._legend = self.plot.getImage(self._activeImageLegend).getLegend() - - def _updateFilter(self, kernelWidth, conditional=False): - if self._originalImage is None: - return - - self.plot.sigActiveImageChanged.disconnect(self._updateActiveImage) - filteredImage = self._computeFilteredImage(kernelWidth, conditional) - self.plot.addImage(data=filteredImage, - legend=self._legend, - replace=True) - self.plot.sigActiveImageChanged.connect(self._updateActiveImage) - - def _computeFilteredImage(self, kernelWidth, conditional): - raise NotImplementedError('MedianFilterAction is a an abstract class') - - def getFilteredImage(self): - """ - :return: the image with the median filter apply on""" - return self._filteredImage - - -class MedianFilter1DAction(MedianFilterAction): - """Define the MedianFilterAction for 1D - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - def __init__(self, plot, parent=None): - MedianFilterAction.__init__(self, - plot, - parent=parent) - - def _computeFilteredImage(self, kernelWidth, conditional): - assert(self.plot is not None) - return medfilt2d(self._originalImage, - (kernelWidth, 1), - conditional) - - -class MedianFilter2DAction(MedianFilterAction): - """Define the MedianFilterAction for 2D - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - def __init__(self, plot, parent=None): - MedianFilterAction.__init__(self, - plot, - parent=parent) - - def _computeFilteredImage(self, kernelWidth, conditional): - assert(self.plot is not None) - return medfilt2d(self._originalImage, - (kernelWidth, kernelWidth), - conditional) diff --git a/silx/gui/plot/actions/mode.py b/silx/gui/plot/actions/mode.py deleted file mode 100644 index ee05256..0000000 --- a/silx/gui/plot/actions/mode.py +++ /dev/null @@ -1,104 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-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. -# -# ###########################################################################*/ -""" -:mod:`silx.gui.plot.actions.mode` provides a set of QAction relative to mouse -mode of a :class:`.PlotWidget`. - -The following QAction are available: - -- :class:`ZoomModeAction` -- :class:`PanModeAction` -""" - -from __future__ import division - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "16/08/2017" - -from . import PlotAction -import logging - -_logger = logging.getLogger(__name__) - - -class ZoomModeAction(PlotAction): - """QAction controlling the zoom mode of a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(ZoomModeAction, self).__init__( - plot, icon='zoom', text='Zoom mode', - tooltip='Zoom in or out', - triggered=self._actionTriggered, - checkable=True, parent=parent) - # Listen to mode change - self.plot.sigInteractiveModeChanged.connect(self._modeChanged) - # Init the state - self._modeChanged(None) - - def _modeChanged(self, source): - modeDict = self.plot.getInteractiveMode() - old = self.blockSignals(True) - self.setChecked(modeDict["mode"] == "zoom") - self.blockSignals(old) - - def _actionTriggered(self, checked=False): - plot = self.plot - if plot is not None: - plot.setInteractiveMode('zoom', source=self) - - -class PanModeAction(PlotAction): - """QAction controlling the pan mode of a :class:`.PlotWidget`. - - :param plot: :class:`.PlotWidget` instance on which to operate - :param parent: See :class:`QAction` - """ - - def __init__(self, plot, parent=None): - super(PanModeAction, self).__init__( - plot, icon='pan', text='Pan mode', - tooltip='Pan the view', - triggered=self._actionTriggered, - checkable=True, parent=parent) - # Listen to mode change - self.plot.sigInteractiveModeChanged.connect(self._modeChanged) - # Init the state - self._modeChanged(None) - - def _modeChanged(self, source): - modeDict = self.plot.getInteractiveMode() - old = self.blockSignals(True) - self.setChecked(modeDict["mode"] == "pan") - self.blockSignals(old) - - def _actionTriggered(self, checked=False): - plot = self.plot - if plot is not None: - plot.setInteractiveMode('pan', source=self) |