summaryrefslogtreecommitdiff
path: root/silx/gui/plot/actions
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/actions')
-rw-r--r--silx/gui/plot/actions/PlotToolAction.py150
-rw-r--r--silx/gui/plot/actions/control.py1
-rw-r--r--silx/gui/plot/actions/fit.py63
-rw-r--r--silx/gui/plot/actions/histogram.py88
-rw-r--r--silx/gui/plot/actions/io.py12
-rw-r--r--silx/gui/plot/actions/medfilt.py38
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"""