diff options
Diffstat (limited to 'silx/gui/plot/ImageView.py')
-rw-r--r-- | silx/gui/plot/ImageView.py | 854 |
1 files changed, 0 insertions, 854 deletions
diff --git a/silx/gui/plot/ImageView.py b/silx/gui/plot/ImageView.py deleted file mode 100644 index 1befe58..0000000 --- a/silx/gui/plot/ImageView.py +++ /dev/null @@ -1,854 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2015-2021 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. -# -# ###########################################################################*/ -"""QWidget displaying a 2D image with histograms on its sides. - -The :class:`ImageView` implements this widget, and -:class:`ImageViewMainWindow` provides a main window with additional toolbar -and status bar. - -Basic usage of :class:`ImageView` is through the following methods: - -- :meth:`ImageView.getColormap`, :meth:`ImageView.setColormap` to update the - default colormap to use and update the currently displayed image. -- :meth:`ImageView.setImage` to update the displayed image. - -For an example of use, see `imageview.py` in :ref:`sample-code`. -""" - -from __future__ import division - - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "26/04/2018" - - -import logging -import numpy -import collections -from typing import Union -import weakref - -import silx -from .. import qt -from .. import colors - -from . import items, PlotWindow, PlotWidget, actions -from ..colors import Colormap -from ..colors import cursorColorForColormap -from .tools import LimitsToolBar -from .Profile import ProfileToolBar -from ...utils.proxy import docstring -from ...utils.enum import Enum -from .tools.RadarView import RadarView -from .utils.axis import SyncAxes -from ..utils import blockSignals -from . import _utils -from .tools.profile import manager -from .tools.profile import rois - -_logger = logging.getLogger(__name__) - - -ProfileSumResult = collections.namedtuple("ProfileResult", - ["dataXRange", "dataYRange", - 'histoH', 'histoHRange', - 'histoV', 'histoVRange', - "xCoords", "xData", - "yCoords", "yData"]) - - -def computeProfileSumOnRange(imageItem, xRange, yRange, cache=None): - """ - Compute a full vertical and horizontal profile on an image item using a - a range in the plot referential. - - Optionally takes a previous computed result to be able to skip the - computation. - - :rtype: ProfileSumResult - """ - data = imageItem.getValueData(copy=False) - origin = imageItem.getOrigin() - scale = imageItem.getScale() - height, width = data.shape - - xMin, xMax = xRange - yMin, yMax = yRange - - # Convert plot area limits to image coordinates - # and work in image coordinates (i.e., in pixels) - xMin = int((xMin - origin[0]) / scale[0]) - xMax = int((xMax - origin[0]) / scale[0]) - yMin = int((yMin - origin[1]) / scale[1]) - yMax = int((yMax - origin[1]) / scale[1]) - - if (xMin >= width or xMax < 0 or - yMin >= height or yMax < 0): - return None - - # The image is at least partly in the plot area - # Get the visible bounds in image coords (i.e., in pixels) - subsetXMin = 0 if xMin < 0 else xMin - subsetXMax = (width if xMax >= width else xMax) + 1 - subsetYMin = 0 if yMin < 0 else yMin - subsetYMax = (height if yMax >= height else yMax) + 1 - - if cache is not None: - if ((subsetXMin, subsetXMax) == cache.dataXRange and - (subsetYMin, subsetYMax) == cache.dataYRange): - # The visible area of data is the same - return cache - - # Rebuild histograms for visible area - visibleData = data[subsetYMin:subsetYMax, - subsetXMin:subsetXMax] - histoHVisibleData = numpy.nansum(visibleData, axis=0) - histoVVisibleData = numpy.nansum(visibleData, axis=1) - histoHMin = numpy.nanmin(histoHVisibleData) - histoHMax = numpy.nanmax(histoHVisibleData) - histoVMin = numpy.nanmin(histoVVisibleData) - histoVMax = numpy.nanmax(histoVVisibleData) - - # Convert to histogram curve and update plots - # Taking into account origin and scale - coords = numpy.arange(2 * histoHVisibleData.size) - xCoords = (coords + 1) // 2 + subsetXMin - xCoords = origin[0] + scale[0] * xCoords - xData = numpy.take(histoHVisibleData, coords // 2) - coords = numpy.arange(2 * histoVVisibleData.size) - yCoords = (coords + 1) // 2 + subsetYMin - yCoords = origin[1] + scale[1] * yCoords - yData = numpy.take(histoVVisibleData, coords // 2) - - result = ProfileSumResult( - dataXRange=(subsetXMin, subsetXMax), - dataYRange=(subsetYMin, subsetYMax), - histoH=histoHVisibleData, - histoHRange=(histoHMin, histoHMax), - histoV=histoVVisibleData, - histoVRange=(histoVMin, histoVMax), - xCoords=xCoords, - xData=xData, - yCoords=yCoords, - yData=yData) - - return result - - -class _SideHistogram(PlotWidget): - """ - Widget displaying one of the side profile of the ImageView. - - Implement ProfileWindow - """ - - sigClose = qt.Signal() - - sigMouseMoved = qt.Signal(float, float) - - def __init__(self, parent=None, backend=None, direction=qt.Qt.Horizontal): - super(_SideHistogram, self).__init__(parent=parent, backend=backend) - self._direction = direction - self.sigPlotSignal.connect(self._plotEvents) - self._color = "blue" - self.__profile = None - self.__profileSum = None - - def _plotEvents(self, eventDict): - """Callback for horizontal histogram plot events.""" - if eventDict['event'] == 'mouseMoved': - self.sigMouseMoved.emit(eventDict['x'], eventDict['y']) - - def setProfileColor(self, color): - self._color = color - - def setProfileSum(self, result): - self.__profileSum = result - if self.__profile is None: - self.__drawProfileSum() - - def prepareWidget(self, roi): - """Implements `ProfileWindow`""" - pass - - def setRoiProfile(self, roi): - """Implements `ProfileWindow`""" - if roi is None: - return - self._roiColor = colors.rgba(roi.getColor()) - - def getProfile(self): - """Implements `ProfileWindow`""" - return self.__profile - - def setProfile(self, data): - """Implements `ProfileWindow`""" - self.__profile = data - if data is None: - self.__drawProfileSum() - else: - self.__drawProfile() - - def __drawProfileSum(self): - """Only draw the profile sum on the plot. - - Other elements are removed - """ - profileSum = self.__profileSum - - try: - self.removeCurve('profile') - except Exception: - pass - - if profileSum is None: - try: - self.removeCurve('profilesum') - except Exception: - pass - return - - if self._direction == qt.Qt.Horizontal: - xx, yy = profileSum.xCoords, profileSum.xData - elif self._direction == qt.Qt.Vertical: - xx, yy = profileSum.yData, profileSum.yCoords - else: - assert False - - self.addCurve(xx, yy, - xlabel='', ylabel='', - legend="profilesum", - color=self._color, - linestyle='-', - selectable=False, - resetzoom=False) - - self.__updateLimits() - - def __drawProfile(self): - """Only draw the profile on the plot. - - Other elements are removed - """ - profile = self.__profile - - try: - self.removeCurve('profilesum') - except Exception: - pass - - if profile is None: - try: - self.removeCurve('profile') - except Exception: - pass - self.setProfileSum(self.__profileSum) - return - - if self._direction == qt.Qt.Horizontal: - xx, yy = profile.coords, profile.profile - elif self._direction == qt.Qt.Vertical: - xx, yy = profile.profile, profile.coords - else: - assert False - - self.addCurve(xx, - yy, - legend="profile", - color=self._roiColor, - resetzoom=False) - - self.__updateLimits() - - def __updateLimits(self): - if self.__profile: - data = self.__profile.profile - vMin = numpy.nanmin(data) - vMax = numpy.nanmax(data) - elif self.__profileSum is not None: - if self._direction == qt.Qt.Horizontal: - vMin, vMax = self.__profileSum.histoHRange - elif self._direction == qt.Qt.Vertical: - vMin, vMax = self.__profileSum.histoVRange - else: - assert False - else: - vMin, vMax = 0, 0 - - # Tune the result using the data margins - margins = self.getDataMargins() - if self._direction == qt.Qt.Horizontal: - _, _, vMin, vMax = _utils.addMarginsToLimits(margins, False, False, 0, 0, vMin, vMax) - elif self._direction == qt.Qt.Vertical: - vMin, vMax, _, _ = _utils.addMarginsToLimits(margins, False, False, vMin, vMax, 0, 0) - else: - assert False - - if self._direction == qt.Qt.Horizontal: - dataAxis = self.getYAxis() - elif self._direction == qt.Qt.Vertical: - dataAxis = self.getXAxis() - else: - assert False - - with blockSignals(dataAxis): - dataAxis.setLimits(vMin, vMax) - - -class ImageView(PlotWindow): - """Display a single image with horizontal and vertical histograms. - - Use :meth:`setImage` to control the displayed image. - This class also provides the :class:`silx.gui.plot.Plot` API. - - The :class:`ImageView` inherits from :class:`.PlotWindow` (which provides - the toolbars) and also exposes :class:`.PlotWidget` API for further - plot control (plot title, axes labels, aspect ratio, ...). - - :param parent: The parent of this widget or None. - :param backend: The backend to use for the plot (default: matplotlib). - See :class:`.PlotWidget` for the list of supported backend. - :type backend: str or :class:`BackendBase.BackendBase` - """ - - HISTOGRAMS_COLOR = 'blue' - """Color to use for the side histograms.""" - - HISTOGRAMS_HEIGHT = 200 - """Height in pixels of the side histograms.""" - - IMAGE_MIN_SIZE = 200 - """Minimum size in pixels of the image area.""" - - # Qt signals - valueChanged = qt.Signal(float, float, float) - """Signals that the data value under the cursor has changed. - - It provides: row, column, data value. - - When the cursor is over an histogram, either row or column is Nan - and the provided data value is the histogram value - (i.e., the sum along the corresponding row/column). - Row and columns are either Nan or integer values. - """ - - class ProfileWindowBehavior(Enum): - """ImageView's profile window behavior options""" - - POPUP = 'popup' - """All profiles are displayed in pop-up windows""" - - EMBEDDED = 'embedded' - """Horizontal, vertical and cross profiles are displayed in - sides widgets, others are displayed in pop-up windows. - """ - - def __init__(self, parent=None, backend=None): - self._imageLegend = '__ImageView__image' + str(id(self)) - self._cache = None # Store currently visible data information - - super(ImageView, self).__init__(parent=parent, backend=backend, - resetzoom=True, autoScale=False, - logScale=False, grid=False, - curveStyle=False, colormap=True, - aspectRatio=True, yInverted=True, - copy=True, save=True, print_=True, - control=False, position=False, - roi=False, mask=True) - - # Enable mask synchronisation to use it in profiles - maskToolsWidget = self.getMaskToolsDockWidget().widget() - maskToolsWidget.setItemMaskUpdated(True) - - if parent is None: - self.setWindowTitle('ImageView') - - if silx.config.DEFAULT_PLOT_IMAGE_Y_AXIS_ORIENTATION == 'downward': - self.getYAxis().setInverted(True) - - self._initWidgets(backend) - - self.__profileWindowBehavior = self.ProfileWindowBehavior.POPUP - self.__profile = ProfileToolBar(plot=self) - self.addToolBar(self.__profile) - - def _initWidgets(self, backend): - """Set-up layout and plots.""" - self._histoHPlot = _SideHistogram(backend=backend, parent=self, direction=qt.Qt.Horizontal) - widgetHandle = self._histoHPlot.getWidgetHandle() - widgetHandle.setMinimumHeight(self.HISTOGRAMS_HEIGHT) - widgetHandle.setMaximumHeight(self.HISTOGRAMS_HEIGHT) - self._histoHPlot.setInteractiveMode('zoom') - self._histoHPlot.setDataMargins(0., 0., 0.1, 0.1) - self._histoHPlot.sigMouseMoved.connect(self._mouseMovedOnHistoH) - self._histoHPlot.setProfileColor(self.HISTOGRAMS_COLOR) - - self._histoVPlot = _SideHistogram(backend=backend, parent=self, direction=qt.Qt.Vertical) - widgetHandle = self._histoVPlot.getWidgetHandle() - widgetHandle.setMinimumWidth(self.HISTOGRAMS_HEIGHT) - widgetHandle.setMaximumWidth(self.HISTOGRAMS_HEIGHT) - self._histoVPlot.setInteractiveMode('zoom') - self._histoVPlot.setDataMargins(0.1, 0.1, 0., 0.) - self._histoVPlot.sigMouseMoved.connect(self._mouseMovedOnHistoV) - self._histoVPlot.setProfileColor(self.HISTOGRAMS_COLOR) - - self.setPanWithArrowKeys(True) - self.setInteractiveMode('zoom') # Color set in setColormap - self.sigPlotSignal.connect(self._imagePlotCB) - self.sigActiveImageChanged.connect(self._activeImageChangedSlot) - - self._radarView = RadarView(parent=self) - self._radarView.setPlotWidget(self) - - self.__syncXAxis = SyncAxes([self.getXAxis(), self._histoHPlot.getXAxis()]) - self.__syncYAxis = SyncAxes([self.getYAxis(), self._histoVPlot.getYAxis()]) - - self.__setCentralWidget() - - def __setCentralWidget(self): - """Set central widget with all its content""" - layout = qt.QGridLayout() - layout.addWidget(self.getWidgetHandle(), 0, 0) - layout.addWidget(self._histoVPlot.getWidgetHandle(), 0, 1) - layout.addWidget(self._histoHPlot.getWidgetHandle(), 1, 0) - layout.addWidget(self._radarView, 1, 1, 1, 2) - layout.addWidget(self.getColorBarWidget(), 0, 2) - - layout.setColumnMinimumWidth(0, self.IMAGE_MIN_SIZE) - layout.setColumnStretch(0, 1) - layout.setColumnMinimumWidth(1, self.HISTOGRAMS_HEIGHT) - layout.setColumnStretch(1, 0) - - layout.setRowMinimumHeight(0, self.IMAGE_MIN_SIZE) - layout.setRowStretch(0, 1) - layout.setRowMinimumHeight(1, self.HISTOGRAMS_HEIGHT) - layout.setRowStretch(1, 0) - - layout.setSpacing(0) - layout.setContentsMargins(0, 0, 0, 0) - - centralWidget = qt.QWidget(self) - centralWidget.setLayout(layout) - self.setCentralWidget(centralWidget) - - @docstring(PlotWidget) - def setBackend(self, backend): - # Use PlotWidget here since we override PlotWindow behavior - PlotWidget.setBackend(self, backend) - self.__setCentralWidget() - - def _dirtyCache(self): - self._cache = None - - def _updateHistograms(self): - """Update histograms content using current active image.""" - activeImage = self.getActiveImage() - if activeImage is not None: - xRange = self.getXAxis().getLimits() - yRange = self.getYAxis().getLimits() - result = computeProfileSumOnRange(activeImage, xRange, yRange, self._cache) - self._cache = result - self._histoHPlot.setProfileSum(result) - self._histoVPlot.setProfileSum(result) - - # Plots event listeners - - def _imagePlotCB(self, eventDict): - """Callback for imageView plot events.""" - if eventDict['event'] == 'mouseMoved': - activeImage = self.getActiveImage() - if activeImage is not None: - data = activeImage.getData(copy=False) - height, width = data.shape - - # Get corresponding coordinate in image - origin = activeImage.getOrigin() - scale = activeImage.getScale() - if (eventDict['x'] >= origin[0] and - eventDict['y'] >= origin[1]): - x = int((eventDict['x'] - origin[0]) / scale[0]) - y = int((eventDict['y'] - origin[1]) / scale[1]) - - if x >= 0 and x < width and y >= 0 and y < height: - self.valueChanged.emit(float(x), float(y), - data[y][x]) - - elif eventDict['event'] == 'limitsChanged': - self._updateHistograms() - - def _mouseMovedOnHistoH(self, x, y): - if self._cache is None: - return - activeImage = self.getActiveImage() - if activeImage is None: - return - - xOrigin = activeImage.getOrigin()[0] - xScale = activeImage.getScale()[0] - - minValue = xOrigin + xScale * self._cache.dataXRange[0] - - if x >= minValue: - data = self._cache.histoH - column = int((x - minValue) / xScale) - if column >= 0 and column < data.shape[0]: - self.valueChanged.emit( - float('nan'), - float(column + self._cache.dataXRange[0]), - data[column]) - - def _mouseMovedOnHistoV(self, x, y): - if self._cache is None: - return - activeImage = self.getActiveImage() - if activeImage is None: - return - - yOrigin = activeImage.getOrigin()[1] - yScale = activeImage.getScale()[1] - - minValue = yOrigin + yScale * self._cache.dataYRange[0] - - if y >= minValue: - data = self._cache.histoV - row = int((y - minValue) / yScale) - if row >= 0 and row < data.shape[0]: - self.valueChanged.emit( - float(row + self._cache.dataYRange[0]), - float('nan'), - data[row]) - - def _activeImageChangedSlot(self, previous, legend): - """Handle Plot active image change. - - Resets side histograms cache - """ - self._dirtyCache() - self._updateHistograms() - - def setProfileWindowBehavior(self, behavior: Union[str, ProfileWindowBehavior]): - """Set where profile widgets are displayed. - - :param ProfileWindowBehavior behavior: - - 'popup': All profiles are displayed in pop-up windows - - 'embedded': Horizontal, vertical and cross profiles are displayed in - sides widgets, others are displayed in pop-up windows. - """ - behavior = self.ProfileWindowBehavior.from_value(behavior) - if behavior is not self.getProfileWindowBehavior(): - manager = self.__profile.getProfileManager() - manager.clearProfile() - manager.requestUpdateAllProfile() - - if behavior is self.ProfileWindowBehavior.EMBEDDED: - horizontalProfileWindow = self._histoHPlot - verticalProfileWindow = self._histoVPlot - else: - horizontalProfileWindow = None - verticalProfileWindow = None - - manager.setSpecializedProfileWindow( - rois.ProfileImageHorizontalLineROI, horizontalProfileWindow - ) - manager.setSpecializedProfileWindow( - rois.ProfileImageVerticalLineROI, verticalProfileWindow - ) - self.__profileWindowBehavior = behavior - - def getProfileWindowBehavior(self) -> ProfileWindowBehavior: - """Returns current profile display behavior. - - See :meth:`setProfileWindowBehavior` and :class:`ProfileWindowBehavior` - """ - return self.__profileWindowBehavior - - def getProfileToolBar(self): - """"Returns profile tools attached to this plot. - - :rtype: silx.gui.plot.PlotTools.ProfileToolBar - """ - return self.__profile - - @property - def profile(self): - return self.getProfileToolBar() - - def getHistogram(self, axis): - """Return the histogram and corresponding row or column extent. - - The returned value when an histogram is available is a dict with keys: - - - 'data': numpy array of the histogram values. - - 'extent': (start, end) row or column index. - end index is not included in the histogram. - - :param str axis: 'x' for horizontal, 'y' for vertical - :return: The histogram and its extent as a dict or None. - :rtype: dict - """ - assert axis in ('x', 'y') - if self._cache is None: - return None - else: - if axis == 'x': - return dict( - data=numpy.array(self._cache.histoH, copy=True), - extent=self._cache.dataXRange) - else: - return dict( - data=numpy.array(self._cache.histoV, copy=True), - extent=(self._cache.dataYRange)) - - def radarView(self): - """Get the lower right radarView widget.""" - return self._radarView - - def setRadarView(self, radarView): - """Change the lower right radarView widget. - - :param RadarView radarView: Widget subclassing RadarView to replace - the lower right corner widget. - """ - self._radarView = radarView - self._radarView.setPlotWidget(self) - self.centralWidget().layout().addWidget(self._radarView, 1, 1) - - # High-level API - - def getColormap(self): - """Get the default colormap description. - - :return: A description of the current colormap. - See :meth:`setColormap` for details. - :rtype: dict - """ - return self.getDefaultColormap() - - def setColormap(self, colormap=None, normalization=None, - autoscale=None, vmin=None, vmax=None, colors=None): - """Set the default colormap and update active image. - - Parameters that are not provided are taken from the current colormap. - - The colormap parameter can also be a dict with the following keys: - - - *name*: string. The colormap to use: - 'gray', 'reversed gray', 'temperature', 'red', 'green', 'blue'. - - *normalization*: string. The mapping to use for the colormap: - either 'linear' or 'log'. - - *autoscale*: bool. Whether to use autoscale (True) - or range provided by keys 'vmin' and 'vmax' (False). - - *vmin*: float. The minimum value of the range to use if 'autoscale' - is False. - - *vmax*: float. The maximum value of the range to use if 'autoscale' - is False. - - *colors*: optional. Nx3 or Nx4 array of float in [0, 1] or uint8. - List of RGB or RGBA colors to use (only if name is None) - - :param colormap: Name of the colormap in - 'gray', 'reversed gray', 'temperature', 'red', 'green', 'blue'. - Or the description of the colormap as a dict. - :type colormap: dict or str. - :param str normalization: Colormap mapping: 'linear' or 'log'. - :param bool autoscale: Whether to use autoscale (True) - or [vmin, vmax] range (False). - :param float vmin: The minimum value of the range to use if - 'autoscale' is False. - :param float vmax: The maximum value of the range to use if - 'autoscale' is False. - :param numpy.ndarray colors: Only used if name is None. - Custom colormap colors as Nx3 or Nx4 RGB or RGBA arrays - """ - cmap = self.getDefaultColormap() - - if isinstance(colormap, Colormap): - # Replace colormap - cmap = colormap - - self.setDefaultColormap(cmap) - - # Update active image colormap - activeImage = self.getActiveImage() - if isinstance(activeImage, items.ColormapMixIn): - activeImage.setColormap(cmap) - - elif isinstance(colormap, dict): - # Support colormap parameter as a dict - assert normalization is None - assert autoscale is None - assert vmin is None - assert vmax is None - assert colors is None - cmap._setFromDict(colormap) - - else: - if colormap is not None: - cmap.setName(colormap) - if normalization is not None: - cmap.setNormalization(normalization) - if autoscale: - cmap.setVRange(None, None) - else: - if vmin is not None: - cmap.setVMin(vmin) - if vmax is not None: - cmap.setVMax(vmax) - if colors is not None: - cmap.setColormapLUT(colors) - - cursorColor = cursorColorForColormap(cmap.getName()) - self.setInteractiveMode('zoom', color=cursorColor) - - def setImage(self, image, origin=(0, 0), scale=(1., 1.), - copy=True, reset=True): - """Set the image to display. - - :param image: A 2D array representing the image or None to empty plot. - :type image: numpy.ndarray-like with 2 dimensions or None. - :param origin: The (x, y) position of the origin of the image. - Default: (0, 0). - The origin is the lower left corner of the image when - the Y axis is not inverted. - :type origin: Tuple of 2 floats: (origin x, origin y). - :param scale: The scale factor to apply to the image on X and Y axes. - Default: (1, 1). - It is the size of a pixel in the coordinates of the axes. - Scales must be positive numbers. - :type scale: Tuple of 2 floats: (scale x, scale y). - :param bool copy: Whether to copy image data (default) or not. - :param bool reset: Whether to reset zoom and ROI (default) or not. - """ - self._dirtyCache() - - assert len(origin) == 2 - assert len(scale) == 2 - assert scale[0] > 0 - assert scale[1] > 0 - - if image is None: - self.remove(self._imageLegend, kind='image') - return - - data = numpy.array(image, order='C', copy=copy) - assert data.size != 0 - assert len(data.shape) == 2 - - self.addImage(data, - legend=self._imageLegend, - origin=origin, scale=scale, - colormap=self.getColormap(), - resetzoom=False) - self.setActiveImage(self._imageLegend) - self._updateHistograms() - if reset: - self.resetZoom() - - -# ImageViewMainWindow ######################################################### - -class ImageViewMainWindow(ImageView): - """:class:`ImageView` with additional toolbars - - Adds extra toolbar and a status bar to :class:`ImageView`. - """ - def __init__(self, parent=None, backend=None): - self._dataInfo = None - super(ImageViewMainWindow, self).__init__(parent, backend) - self.setWindowFlags(qt.Qt.Window) - - self.getXAxis().setLabel('X') - self.getYAxis().setLabel('Y') - self.setGraphTitle('Image') - - # Add toolbars and status bar - self.addToolBar(qt.Qt.BottomToolBarArea, LimitsToolBar(plot=self)) - - self.statusBar() - - menu = self.menuBar().addMenu('File') - menu.addAction(self.getOutputToolBar().getSaveAction()) - menu.addAction(self.getOutputToolBar().getPrintAction()) - menu.addSeparator() - action = menu.addAction('Quit') - action.triggered[bool].connect(qt.QApplication.instance().quit) - - menu = self.menuBar().addMenu('Edit') - menu.addAction(self.getOutputToolBar().getCopyAction()) - menu.addSeparator() - menu.addAction(self.getResetZoomAction()) - menu.addAction(self.getColormapAction()) - menu.addAction(actions.control.KeepAspectRatioAction(self, self)) - menu.addAction(actions.control.YAxisInvertedAction(self, self)) - - self.__profileMenu = self.menuBar().addMenu('Profile') - self.__updateProfileMenu() - - # Connect to ImageView's signal - self.valueChanged.connect(self._statusBarSlot) - - def __updateProfileMenu(self): - """Update actions available in 'Profile' menu""" - profile = self.getProfileToolBar() - self.__profileMenu.clear() - self.__profileMenu.addAction(profile.hLineAction) - self.__profileMenu.addAction(profile.vLineAction) - self.__profileMenu.addAction(profile.crossAction) - self.__profileMenu.addAction(profile.lineAction) - self.__profileMenu.addAction(profile.clearAction) - - def _statusBarSlot(self, row, column, value): - """Update status bar with coordinates/value from plots.""" - if numpy.isnan(row): - msg = 'Column: %d, Sum: %g' % (int(column), value) - elif numpy.isnan(column): - msg = 'Row: %d, Sum: %g' % (int(row), value) - else: - msg = 'Position: (%d, %d), Value: %g' % (int(row), int(column), - value) - if self._dataInfo is not None: - msg = self._dataInfo + ', ' + msg - - self.statusBar().showMessage(msg) - - @docstring(ImageView) - def setProfileWindowBehavior(self, behavior: str): - super().setProfileWindowBehavior(behavior) - self.__updateProfileMenu() - - @docstring(ImageView) - def setImage(self, image, *args, **kwargs): - if hasattr(image, 'dtype') and hasattr(image, 'shape'): - assert len(image.shape) == 2 - height, width = image.shape - self._dataInfo = 'Data: %dx%d (%s)' % (width, height, - str(image.dtype)) - self.statusBar().showMessage(self._dataInfo) - else: - self._dataInfo = None - - # Set the new image in ImageView widget - super(ImageViewMainWindow, self).setImage(image, *args, **kwargs) - self.setStatusBar(None) |