diff options
Diffstat (limited to 'silx/gui/plot/actions')
-rw-r--r-- | silx/gui/plot/actions/PlotToolAction.py | 150 | ||||
-rw-r--r-- | silx/gui/plot/actions/control.py | 1 | ||||
-rw-r--r-- | silx/gui/plot/actions/fit.py | 63 | ||||
-rw-r--r-- | silx/gui/plot/actions/histogram.py | 88 | ||||
-rw-r--r-- | silx/gui/plot/actions/io.py | 12 | ||||
-rw-r--r-- | silx/gui/plot/actions/medfilt.py | 38 |
6 files changed, 238 insertions, 114 deletions
diff --git a/silx/gui/plot/actions/PlotToolAction.py b/silx/gui/plot/actions/PlotToolAction.py new file mode 100644 index 0000000..77e8be2 --- /dev/null +++ b/silx/gui/plot/actions/PlotToolAction.py @@ -0,0 +1,150 @@ +# 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/control.py b/silx/gui/plot/actions/control.py index 6e08f21..10df130 100644 --- a/silx/gui/plot/actions/control.py +++ b/silx/gui/plot/actions/control.py @@ -601,3 +601,4 @@ class ShowAxisAction(PlotAction): 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 index 5ca649c..cb70733 100644 --- a/silx/gui/plot/actions/fit.py +++ b/silx/gui/plot/actions/fit.py @@ -36,9 +36,9 @@ from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" -__date__ = "03/01/2018" +__date__ = "10/10/2018" -from . import PlotAction +from .PlotToolAction import PlotToolAction import logging from silx.gui import qt from silx.gui.plot.ItemsSelectionDialog import ItemsSelectionDialog @@ -86,7 +86,7 @@ def _getUniqueHistogram(plt): return histograms[0] -class FitAction(PlotAction): +class FitAction(PlotToolAction): """QAction to open a :class:`FitWidget` and set its data to the active curve if any, or to the first curve. @@ -97,21 +97,38 @@ class FitAction(PlotAction): super(FitAction, self).__init__( plot, icon='math-fit', text='Fit curve', tooltip='Open a fit dialog', - triggered=self._getFitWindow, - checkable=False, parent=parent) - self.fit_window = None - - def _getFitWindow(self): - self.xlabel = self.plot.getXAxis().getLabel() - self.ylabel = self.plot.getYAxis().getLabel() - self.xmin, self.xmax = self.plot.getXAxis().getLimits() + 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=self.plot, plot=self.plot) + isd = ItemsSelectionDialog(parent=plot, plot=self.plot) isd.setWindowTitle("Select item to be fitted") isd.setItemsSelectionMode(qt.QTableWidget.SingleSelection) isd.setAvailableKinds(["curve", "histogram"]) @@ -141,29 +158,9 @@ class FitAction(PlotAction): self.x = item.getXData(copy=False) self.y = item.getYData(copy=False) - # open a window with a FitWidget - if self.fit_window is None: - self.fit_window = qt.QMainWindow() - # import done here rather than at module level to avoid circular import - # FitWidget -> BackgroundWidget -> PlotWindow -> actions -> fit -> FitWidget - from ...fit.FitWidget import FitWidget - self.fit_widget = FitWidget(parent=self.fit_window) - self.fit_window.setCentralWidget( - self.fit_widget) - self.fit_widget.guibuttons.DismissButton.clicked.connect( - self.fit_window.close) - self.fit_widget.sigFitWidgetSignal.connect( - self.handle_signal) - self.fit_window.show() - else: - if self.fit_window.isHidden(): - self.fit_window.show() - self.fit_widget.show() - self.fit_window.raise_() - self.fit_widget.setData(self.x, self.y, xmin=self.xmin, xmax=self.xmax) - self.fit_window.setWindowTitle( + window.setWindowTitle( "Fitting " + self.legend + " on x range %f-%f" % (self.xmin, self.xmax)) diff --git a/silx/gui/plot/actions/histogram.py b/silx/gui/plot/actions/histogram.py index d6e3269..9181f53 100644 --- a/silx/gui/plot/actions/histogram.py +++ b/silx/gui/plot/actions/histogram.py @@ -34,10 +34,10 @@ The following QAction are available: from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] -__date__ = "30/04/2018" +__date__ = "10/10/2018" __license__ = "MIT" -from . import PlotAction +from .PlotToolAction import PlotToolAction from silx.math.histogram import Histogramnd from silx.math.combo import min_max import numpy @@ -47,7 +47,7 @@ from silx.gui import qt _logger = logging.getLogger(__name__) -class PixelIntensitiesHistoAction(PlotAction): +class PixelIntensitiesHistoAction(PlotToolAction): """QAction to plot the pixels intensities diagram :param plot: :class:`.PlotWidget` instance on which to operate @@ -55,43 +55,33 @@ class PixelIntensitiesHistoAction(PlotAction): """ def __init__(self, plot, parent=None): - PlotAction.__init__(self, - plot, - icon='pixel-intensities', - text='pixels intensity', - tooltip='Compute image intensity distribution', - triggered=self._triggered, - parent=parent, - checkable=True) - self._plotHistogram = 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 _triggered(self, checked): - """Update the plot of the histogram visibility status - - :param bool checked: status of the action button - """ - if checked: - if not self._connectedToActiveImage: - self.plot.sigActiveImageChanged.connect( - self._activeImageChanged) - self._connectedToActiveImage = True - self.computeIntensityDistribution() - - self.getHistogramPlotWidget().show() - - else: - if self._connectedToActiveImage: - self.plot.sigActiveImageChanged.disconnect( - self._activeImageChanged) - self._connectedToActiveImage = False + def _connectPlot(self, window): + if not self._connectedToActiveImage: + self.plot.sigActiveImageChanged.connect( + self._activeImageChanged) + self._connectedToActiveImage = True + self.computeIntensityDistribution() + PlotToolAction._connectPlot(self, window) - self.getHistogramPlotWidget().hide() + 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.isChecked(): + if self._isWindowInUse(): self.computeIntensityDistribution() def computeIntensityDistribution(self): @@ -132,35 +122,21 @@ class PixelIntensitiesHistoAction(PlotAction): color='#66aad7') plot.resetZoom() - 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._plotHistogram is not None: - self._plotHistogram.hide() - self.setChecked(False) - - return PlotAction.eventFilter(self, qobject, event) - 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 - if self._plotHistogram is None: - self._plotHistogram = Plot1D(parent=self.plot) - self._plotHistogram.setWindowFlags(qt.Qt.Window) - self._plotHistogram.setWindowTitle('Image Intensity Histogram') - self._plotHistogram.installEventFilter(self) - self._plotHistogram.getXAxis().setLabel("Value") - self._plotHistogram.getYAxis().setLabel("Count") - - return self._plotHistogram + 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 diff --git a/silx/gui/plot/actions/io.py b/silx/gui/plot/actions/io.py index ac06942..97de527 100644 --- a/silx/gui/plot/actions/io.py +++ b/silx/gui/plot/actions/io.py @@ -37,10 +37,10 @@ from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" -__date__ = "02/02/2018" +__date__ = "12/07/2018" from . import PlotAction -from silx.io.utils import save1D, savespec +from silx.io.utils import save1D, savespec, NEXUS_HDF5_EXT from silx.io.nxdata import save_NXdata import logging import sys @@ -53,7 +53,7 @@ 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 +from ...utils.image import convertArrayToQImage if sys.version_info[0] == 3: from io import BytesIO else: @@ -62,9 +62,7 @@ else: _logger = logging.getLogger(__name__) - -_NEXUS_HDF5_EXT = [".h5", ".nx5", ".nxs", ".hdf", ".hdf5", ".cxi"] -_NEXUS_HDF5_EXT_STR = ' '.join(['*' + ext for ext in _NEXUS_HDF5_EXT]) +_NEXUS_HDF5_EXT_STR = ' '.join(['*' + ext for ext in NEXUS_HDF5_EXT]) def selectOutputGroup(h5filename): @@ -546,7 +544,7 @@ class SaveAction(PlotAction): 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: + if len(self.plot.getAllCurves()) >= 1: filters.update(self._filters['curves'].items()) # Add scatter filters if there is a scatter diff --git a/silx/gui/plot/actions/medfilt.py b/silx/gui/plot/actions/medfilt.py index 4284a8b..276f970 100644 --- a/silx/gui/plot/actions/medfilt.py +++ b/silx/gui/plot/actions/medfilt.py @@ -39,9 +39,9 @@ from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"] __license__ = "MIT" -__date__ = "03/01/2018" +__date__ = "10/10/2018" -from . import PlotAction +from .PlotToolAction import PlotToolAction from silx.gui.widgets.MedianFilterDialog import MedianFilterDialog from silx.math.medianfilter import medfilt2d import logging @@ -49,7 +49,7 @@ import logging _logger = logging.getLogger(__name__) -class MedianFilterAction(PlotAction): +class MedianFilterAction(PlotToolAction): """QAction to plot the pixels intensities diagram :param plot: :class:`.PlotWidget` instance on which to operate @@ -57,27 +57,29 @@ class MedianFilterAction(PlotAction): """ def __init__(self, plot, parent=None): - PlotAction.__init__(self, - plot, - icon='median-filter', - text='median filter', - tooltip='Apply a median filter on the image', - triggered=self._triggered, - parent=parent) + 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 - self._popup = MedianFilterDialog(parent=plot) - self._popup.sigFilterOptChanged.connect(self._updateFilter) + + 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 _triggered(self, checked): - """Update the plot of the histogram visibility status - - :param bool checked: status of the action button - """ - self._popup.show() + 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""" |