Source code for silx.gui.plot.Profile

# /*##########################################################################
#
# Copyright (c) 2004-2023 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.
#
# ###########################################################################*/
"""Utility functions, toolbars and actions  to create profile on images
and stacks of images"""


__authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel", "H. Payno"]
__license__ = "MIT"
__date__ = "12/04/2019"


import weakref

from .. import qt
from . import actions
from .tools.profile import manager
from .tools.profile import rois
from silx.gui.widgets.MultiModeAction import MultiModeAction

from .tools import roi as roi_mdl
from silx.gui.plot import items


class _CustomProfileManager(manager.ProfileManager):
    """This custom profile manager uses a single predefined profile window
    if it is specified. Else the behavior is the same as the default
    ProfileManager"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__profileWindow = None
        self.__specializedProfileWindows = {}

    def setSpecializedProfileWindow(self, roiClass, profileWindow):
        """Set a profile window for a given class or ROI.

        Setting profileWindow to None removes the roiClass from the list.

        :param roiClass:
        :param profileWindow:
        """
        if profileWindow is None:
            self.__specializedProfileWindows.pop(roiClass, None)
        else:
            self.__specializedProfileWindows[roiClass] = profileWindow

    def setProfileWindow(self, profileWindow):
        self.__profileWindow = profileWindow

    def createProfileWindow(self, plot, roi):
        for (
            roiClass,
            specializedProfileWindow,
        ) in self.__specializedProfileWindows.items():
            if isinstance(roi, roiClass):
                return specializedProfileWindow

        if self.__profileWindow is not None:
            return self.__profileWindow
        else:
            return super(_CustomProfileManager, self).createProfileWindow(plot, roi)

    def clearProfileWindow(self, profileWindow):
        for specializedProfileWindow in self.__specializedProfileWindows.values():
            if profileWindow is specializedProfileWindow:
                profileWindow.setProfile(None)
                return

        if self.__profileWindow is not None:
            self.__profileWindow.setProfile(None)
        else:
            return super(_CustomProfileManager, self).clearProfileWindow(profileWindow)


[docs] class ProfileToolBar(qt.QToolBar): """QToolBar providing profile tools operating on a :class:`PlotWindow`. Attributes: - plot: Associated :class:`PlotWindow` on which the profile line is drawn. - actionGroup: :class:`QActionGroup` of available actions. To run the following sample code, a QApplication must be initialized. First, create a PlotWindow and add a :class:`ProfileToolBar`. >>> from silx.gui.plot import PlotWindow >>> from silx.gui.plot.Profile import ProfileToolBar >>> plot = PlotWindow() # Create a PlotWindow >>> toolBar = ProfileToolBar(plot=plot) # Create a profile toolbar >>> plot.addToolBar(toolBar) # Add it to plot >>> plot.show() # To display the PlotWindow with the profile toolbar :param plot: :class:`PlotWindow` instance on which to operate. :param profileWindow: Plot widget instance where to display the profile curve or None to create one. :param parent: See :class:`QToolBar`. """ def __init__(self, parent=None, plot=None, profileWindow=None): super(ProfileToolBar, self).__init__(parent) assert plot is not None self._plotRef = weakref.ref(plot) # If a profileWindow is defined, # It will be used to display all the profiles self._manager = self.createProfileManager(self, plot) self._manager.setProfileWindow(profileWindow) self._manager.setDefaultColorFromCursorColor(True) self._manager.setItemType(image=True) self._manager.setActiveItemTracking(True) # Actions self._browseAction = actions.mode.ZoomModeAction(plot, parent=self) self._browseAction.setVisible(False) self.freeLineAction = None self._createProfileActions() self._editor = self._manager.createEditorAction(self) # ActionGroup self.actionGroup = qt.QActionGroup(self) self.actionGroup.addAction(self._browseAction) self.actionGroup.addAction(self.hLineAction) self.actionGroup.addAction(self.vLineAction) self.actionGroup.addAction(self.lineAction) self.actionGroup.addAction(self._editor) modes = MultiModeAction(self) modes.addAction(self.hLineAction) modes.addAction(self.vLineAction) modes.addAction(self.lineAction) if self.freeLineAction is not None: modes.addAction(self.freeLineAction) modes.addAction(self.crossAction) self.__multiAction = modes # Add actions to ToolBar self.addAction(self._browseAction) self.addAction(modes) self.addAction(self._editor) self.addAction(self.clearAction) plot.sigActiveImageChanged.connect(self._activeImageChanged) self._activeImageChanged() def createProfileManager(self, parent, plot): return _CustomProfileManager(parent, plot) def _createProfileActions(self): self.hLineAction = self._manager.createProfileAction( rois.ProfileImageHorizontalLineROI, self ) self.vLineAction = self._manager.createProfileAction( rois.ProfileImageVerticalLineROI, self ) self.lineAction = self._manager.createProfileAction( rois.ProfileImageLineROI, self ) self.freeLineAction = self._manager.createProfileAction( rois.ProfileImageDirectedLineROI, self ) self.crossAction = self._manager.createProfileAction( rois.ProfileImageCrossROI, self ) self.clearAction = self._manager.createClearAction(self)
[docs] def getPlotWidget(self): """The :class:`.PlotWidget` associated to the toolbar.""" return self._plotRef()
def _setRoiActionEnabled(self, itemKind, enabled): for action in self.__multiAction.getMenu().actions(): if not isinstance(action, roi_mdl.CreateRoiModeAction): continue roiClass = action.getRoiClass() if issubclass(itemKind, roiClass.ITEM_KIND): action.setEnabled(enabled) def _activeImageChanged(self, previous=None, legend=None): """Handle active image change to toggle actions""" if legend is None: self._setRoiActionEnabled(items.ImageStack, False) self._setRoiActionEnabled(items.ImageBase, False) else: plot = self.getPlotWidget() image = plot.getActiveImage() # Disable for empty image enabled = image.getData(copy=False).size > 0 self._setRoiActionEnabled(type(image), enabled)
[docs] def getProfileManager(self): """Return the manager of the profiles. :rtype: ProfileManager """ return self._manager
[docs] def clearProfile(self): """Remove profile curve and profile area.""" self._manager.clearProfile()
[docs] class Profile3DToolBar(ProfileToolBar): def __init__(self, parent=None, stackview=None): """QToolBar providing profile tools for an image or a stack of images. :param parent: the parent QWidget :param stackview: :class:`StackView` instance on which to operate. :param parent: See :class:`QToolBar`. """ # TODO: add param profileWindow (specify the plot used for profiles) super(Profile3DToolBar, self).__init__( parent=parent, plot=stackview.getPlotWidget() ) self.stackView = stackview """:class:`StackView` instance""" def _createProfileActions(self): self.hLineAction = self._manager.createProfileAction( rois.ProfileImageStackHorizontalLineROI, self ) self.vLineAction = self._manager.createProfileAction( rois.ProfileImageStackVerticalLineROI, self ) self.lineAction = self._manager.createProfileAction( rois.ProfileImageStackLineROI, self ) self.crossAction = self._manager.createProfileAction( rois.ProfileImageStackCrossROI, self ) self.clearAction = self._manager.createClearAction(self)