diff options
Diffstat (limited to 'PyMca5/PyMcaGui/plotting')
-rw-r--r-- | PyMca5/PyMcaGui/plotting/ColormapDialog.py | 75 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/ImageView.py | 898 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/MaskImageTools.py | 6 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/MaskImageWidget.py | 291 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/MaskScatterWidget.py | 584 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/MaskToolBar.py | 331 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/McaROIWidget.py | 26 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/PlotWidget.py | 15 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/PlotWindow.py | 5 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/ProfileScanWidget.py | 4 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/PyMca_Icons.py | 13 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py | 11 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py | 178 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py | 4 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py | 32 | ||||
-rw-r--r-- | PyMca5/PyMcaGui/plotting/_ImageProfile.py | 34 |
16 files changed, 964 insertions, 1543 deletions
diff --git a/PyMca5/PyMcaGui/plotting/ColormapDialog.py b/PyMca5/PyMcaGui/plotting/ColormapDialog.py index b42bac2..e0851ce 100644 --- a/PyMca5/PyMcaGui/plotting/ColormapDialog.py +++ b/PyMca5/PyMcaGui/plotting/ColormapDialog.py @@ -28,12 +28,14 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt -from . import PlotWidget +from silx.gui.plot import PlotWidget QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class MyQLineEdit(qt.QLineEdit): def __init__(self,parent=None,name=""): @@ -47,8 +49,7 @@ class MyQLineEdit(qt.QLineEdit): self.returnPressed[()].emit() def setPaletteBackgroundColor(self, color): - if DEBUG: - print("setPalettebackgroundColor not implemented yet") + _logger.debug("setPalettebackgroundColor not implemented yet") pass """ @@ -213,9 +214,9 @@ class ColormapDialog(qt.QDialog): # Graph widget for color curve... - self.c = PlotWidget.PlotWidget(self, backend=None) + self.c = PlotWidget(self, backend=None) self.c.setGraphXLabel("Data Values") - self.c.setZoomModeEnabled(False) + self.c.setInteractiveMode('select') self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0 self.minmd = self.dataMin - self.marge @@ -244,11 +245,11 @@ class ColormapDialog(qt.QDialog): color = "black" #TODO symbol legend = "%d" % i - self.c.insertXMarker(x[i], - legend=legend, - text=labelList[i], - draggable=draggable, - color=color) + self.c.addXMarker(x[i], + legend=legend, + text=labelList[i], + draggable=draggable, + color=color) self.markers.append((legend, "")) self.c.setMinimumSize(qt.QSize(250,200)) @@ -267,16 +268,13 @@ class ColormapDialog(qt.QDialog): bins, counts = self.histogramData self.c.addCurve(bins, counts, "Histogram", - color='pink', # TODO: Change fill color - symbol='s', - linestyle='-', # Line style - #fill=True, - yaxis='right') - # TODO: Do not use info! + color='darkYellow', + histogram='center', + yaxis='right', + fill=True) def _update(self): - if DEBUG: - print("colormap _update called") + _logger.debug("colormap _update called") self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0 self.minmd = self.dataMin - self.marge self.maxpd = self.dataMax + self.marge @@ -300,17 +298,15 @@ class ColormapDialog(qt.QDialog): color = "black" key = self.markers[i][0] label = self.markers[i][1] - self.c.insertXMarker(self.__x[i], - legend=key, - text=label, - draggable=draggable, - color=color) - self.c.replot() + self.c.addXMarker(self.__x[i], + legend=key, + text=label, + draggable=draggable, + color=color) self.sendColormap() def buttonGroupChange(self, val): - if DEBUG: - print("buttonGroup asking to update colormap") + _logger.debug("buttonGroup asking to update colormap") self.setColormapType(val, update=True) self._update() @@ -327,8 +323,7 @@ class ColormapDialog(qt.QDialog): self._update() def chval(self, ddict): - if DEBUG: - print("Received ", ddict) + _logger.debug("Received %s", ddict) if ddict['event'] == 'markerMoving': diam = int(ddict['label']) x = ddict['x'] @@ -368,8 +363,7 @@ class ColormapDialog(qt.QDialog): self.sendColormap() def setAutoscale(self, val): - if DEBUG: - print("setAutoscale called", val) + _logger.debug("setAutoscale called %s", val) if val: self.autoScaleButton.setChecked(True) self.autoScale90Button.setChecked(False) @@ -378,14 +372,14 @@ class ColormapDialog(qt.QDialog): self.setMaxValue(self.dataMax) self.maxText.setEnabled(0) self.minText.setEnabled(0) - self.c.setEnabled(0) + self.c.setEnabled(False) #self.c.disablemarkermode() else: self.autoScaleButton.setChecked(False) self.autoScale90Button.setChecked(False) self.minText.setEnabled(1) self.maxText.setEnabled(1) - self.c.setEnabled(1) + self.c.setEnabled(True) #self.c.enablemarkermode() """ @@ -403,12 +397,12 @@ class ColormapDialog(qt.QDialog): self.setMaxValue(self.dataMax - abs(self.dataMax/10)) self.minText.setEnabled(0) self.maxText.setEnabled(0) - self.c.setEnabled(0) + self.c.setEnabled(False) else: self.autoScale90Button.setChecked(False) self.minText.setEnabled(1) self.maxText.setEnabled(1) - self.c.setEnabled(1) + self.c.setEnabled(True) self.c.setFocus() @@ -424,7 +418,7 @@ class ColormapDialog(qt.QDialog): self.__x[1] = v key = self.markers[1][0] label = self.markers[1][1] - self.c.insertXMarker(v, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(v, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", @@ -455,7 +449,7 @@ class ColormapDialog(qt.QDialog): self.__x[1] = val key = self.markers[1][0] label = self.markers[1][1] - self.c.insertXMarker(val, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(val, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", color='black', @@ -472,7 +466,7 @@ class ColormapDialog(qt.QDialog): self.__x[2] = v key = self.markers[2][0] label = self.markers[2][1] - self.c.insertXMarker(v, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(v, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", color='black', @@ -501,7 +495,7 @@ class ColormapDialog(qt.QDialog): self.__x[2] = val key = self.markers[2][0] label = self.markers[2][1] - self.c.insertXMarker(val, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(val, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", color='black', @@ -542,8 +536,7 @@ class ColormapDialog(qt.QDialog): send 'ColormapChanged' signal """ def sendColormap(self): - if DEBUG: - print("sending colormap") + _logger.debug("sending colormap") try: cmap = self.getColormap() self.sigColormapChanged.emit(cmap) diff --git a/PyMca5/PyMcaGui/plotting/ImageView.py b/PyMca5/PyMcaGui/plotting/ImageView.py index 2f8bd11..3371533 100644 --- a/PyMca5/PyMcaGui/plotting/ImageView.py +++ b/PyMca5/PyMcaGui/plotting/ImageView.py @@ -31,25 +31,11 @@ __contact__ = "thomas.vincent@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" __doc__ = """ -QWidget displaying a 2D image with histograms on its sides. +The classes in this module are deprecated. Use +:class:`silx.gui.plot.ImageView` and +:class:`silx.gui.plot.ImageViewMainWindow` instead. -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. - -The :class:`ImageView` uses :class:`PlotWindow` and also -exposes :class:`PyMca5.PyMcaGraph.Plot` API for further control -(plot title, axes labels, adding other images, ...). - -For an example of use, see the implementation of :class:`ImageViewMainWindow`. - -The ImageView module can also be used to open an EDF or TIFF file +This module can be used to open an EDF or TIFF file from the shell command line. To view an image file: ``python -m PyMca5.PyMcaGui.plotting.ImageView <file to open>`` @@ -59,872 +45,50 @@ To get help: # import ###################################################################### - -import numpy as np - +import logging +import traceback try: from .. import PyMcaQt as qt except ImportError: from PyMca5.PyMcaGui import PyMcaQt as qt -from .PlotWindow import PlotWindow -from .Toolbars import ProfileToolBar, LimitsToolBar - -from PyMca5.PyMcaGraph import Plot - - -# utils ####################################################################### - -_COLORMAP_CURSOR_COLORS = { - 'gray': 'pink', - 'reversed gray': 'pink', - 'temperature': 'black', - 'red': 'gray', - 'green': 'gray', - 'blue': 'gray'} - - -def _cursorColorForColormap(colormapName): - """Get a color suitable for overlay over a colormap. - - :param str colormapName: The name of the colormap. - :return: Name of the color. - :rtype: str - """ - return _COLORMAP_CURSOR_COLORS.get(colormapName, 'black') - - -# RadarView ################################################################### - -class RadarView(qt.QGraphicsView): - """Widget presenting a synthetic view of a 2D area and - the current visible area. - - Coordinates are as in QGraphicsView: - x goes from left to right and y goes from top to bottom. - This widget preserves the aspect ratio of the areas. - - The 2D area and the visible area can be set with :meth:`setDataRect` - and :meth:`setVisibleRect`. - When the visible area has been dragged by the user, its new position - is signaled by the *visibleRectDragged* signal. - - It is possible to invert the direction of the axes by using the - :meth:`scale` method of QGraphicsView. - """ - - visibleRectDragged = qt.pyqtSignal(float, float, float, float) - """Signals that the visible rectangle has been dragged. - - It provides: left, top, width, height in data coordinates. - """ - - _DATA_PEN = qt.QPen(qt.QColor('white')) - _DATA_BRUSH = qt.QBrush(qt.QColor('light gray')) - _VISIBLE_PEN = qt.QPen(qt.QColor('red')) - _VISIBLE_BRUSH = qt.QBrush(qt.QColor(0, 0, 0, 0)) - _TOOLTIP = 'Radar View:\nVisible area (in red)\nof the image (in gray).' - - _PIXMAP_SIZE = 256 - - class _DraggableRectItem(qt.QGraphicsRectItem): - """RectItem which signals its change through visibleRectDragged.""" - def __init__(self, *args, **kwargs): - super(RadarView._DraggableRectItem, self).__init__(*args, **kwargs) - self.setFlag(qt.QGraphicsItem.ItemIsMovable) - self.setFlag(qt.QGraphicsItem.ItemSendsGeometryChanges) - self._ignoreChange = False - self._constraint = 0, 0, 0, 0 - - def setConstraintRect(self, left, top, width, height): - """Set the constraint rectangle for dragging. - - The coordinates are in the _DraggableRectItem coordinate system. - - This constraint only applies to modification through interaction - (i.e., this constraint is not applied to change through API). - - If the _DraggableRectItem is smaller than the constraint rectangle, - the _DraggableRectItem remains within the constraint rectangle. - If the _DraggableRectItem is wider than the constraint rectangle, - the constraint rectangle remains within the _DraggableRectItem. - """ - self._constraint = left, left + width, top, top + height - - def setPos(self, *args, **kwargs): - """Overridden to ignore changes from API in itemChange.""" - self._ignoreChange = True - super(RadarView._DraggableRectItem, self).setPos(*args, **kwargs) - self._ignoreChange = False - - def moveBy(self, *args, **kwargs): - """Overridden to ignore changes from API in itemChange.""" - self._ignoreChange = True - super(RadarView._DraggableRectItem, self).moveBy(*args, **kwargs) - self._ignoreChange = False - - def itemChange(self, change, value): - """Callback called before applying changes to the item.""" - if (change == qt.QGraphicsItem.ItemPositionChange and - not self._ignoreChange): - # Makes sure that the visible area is in the data - # or that data is in the visible area if area is too wide - x, y = value.x(), value.y() - xMin, xMax, yMin, yMax = self._constraint - - if self.rect().width() <= (xMax - xMin): - if x < xMin: - value.setX(xMin) - elif x > xMax - self.rect().width(): - value.setX(xMax - self.rect().width()) - else: - if x > xMin: - value.setX(xMin) - elif x < xMax - self.rect().width(): - value.setX(xMax - self.rect().width()) - - if self.rect().height() <= (yMax - yMin): - if y < yMin: - value.setY(yMin) - elif y > yMax - self.rect().height(): - value.setY(yMax - self.rect().height()) - else: - if y > yMin: - value.setY(yMin) - elif y < yMax - self.rect().height(): - value.setY(yMax - self.rect().height()) - - if self.pos() != value: - # Notify change through signal - views = self.scene().views() - assert len(views) == 1 - views[0].visibleRectDragged.emit( - value.x() + self.rect().left(), - value.y() + self.rect().top(), - self.rect().width(), - self.rect().height()) - - return value - - return super(RadarView._DraggableRectItem, self).itemChange( - change, value) - - def __init__(self, parent=None): - self._scene = qt.QGraphicsScene() - self._dataRect = self._scene.addRect(0, 0, 1, 1, - self._DATA_PEN, - self._DATA_BRUSH) - self._visibleRect = self._DraggableRectItem(0, 0, 1, 1) - self._visibleRect.setPen(self._VISIBLE_PEN) - self._visibleRect.setBrush(self._VISIBLE_BRUSH) - self._scene.addItem(self._visibleRect) - - super(RadarView, self).__init__(self._scene, parent) - self.setHorizontalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff) - self.setVerticalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff) - self.setFocusPolicy(qt.Qt.NoFocus) - self.setStyleSheet('border: 0px') - self.setToolTip(self._TOOLTIP) - - def sizeHint(self): - # """Overridden to avoid sizeHint to depend on content size.""" - return self.minimumSizeHint() - - def wheelEvent(self, event): - # """Overridden to disable vertical scrolling with wheel.""" - event.ignore() - - def resizeEvent(self, event): - # """Overridden to fit current content to new size.""" - self.fitInView(self._scene.itemsBoundingRect(), qt.Qt.KeepAspectRatio) - super(RadarView, self).resizeEvent(event) - - def setDataRect(self, left, top, width, height): - """Set the bounds of the data rectangular area. - - This sets the coordinate system. - """ - self._dataRect.setRect(left, top, width, height) - self._visibleRect.setConstraintRect(left, top, width, height) - self.fitInView(self._scene.itemsBoundingRect(), qt.Qt.KeepAspectRatio) - - def setVisibleRect(self, left, top, width, height): - """Set the visible rectangular area. - - The coordinates are relative to the data rect. - """ - self._visibleRect.setRect(0, 0, width, height) - self._visibleRect.setPos(left, top) - self.fitInView(self._scene.itemsBoundingRect(), qt.Qt.KeepAspectRatio) - - -# ImageView ################################################################### - -class ImageView(qt.QWidget): - """Display a single image with horizontal and vertical histograms. - - Use :meth:`setImage` to control the displayed image. - This class also provides the :class:`PyMca5.PyMcaGraph.Plot` API. - """ - - 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.pyqtSignal(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. - """ - - def __init__(self, parent=None, windowFlags=qt.Qt.Widget, backend=None): - self._imageLegend = '__ImageView__image' + str(id(self)) - self._cache = None # Store currently visible data information - self._updatingLimits = False - - super(ImageView, self).__init__(parent, windowFlags) - self.setStyleSheet('background-color: white;') - self._initWidgets(backend) - - # Sync PlotBackend and ImageView - self._updateYAxisInverted() - - # Set-up focus proxy to handle arrow key event - self.setFocusProxy(self._imagePlot) - - def _initWidgets(self, backend): - """Set-up layout and plots.""" - # Monkey-patch for histogram size - # alternative: create a layout that does not use widget size hints - def sizeHint(): - return qt.QSize(self.HISTOGRAMS_HEIGHT, self.HISTOGRAMS_HEIGHT) - - self._histoHPlot = Plot.Plot(backend=backend) - self._histoHPlot.setZoomModeEnabled(True) - self._histoHPlot.setCallback(self._histoHPlotCB) - self._histoHPlot.getWidgetHandle().sizeHint = sizeHint - self._histoHPlot.getWidgetHandle().minimumSizeHint = sizeHint - - self._imagePlot = PlotWindow(backend=backend, plugins=False, - colormap=True, flip=True, - grid=False, togglePoints=False, - logx=False, logy=False, - aspect=True) - self._imagePlot.usePlotBackendColormap = True - self._imagePlot.setPanWithArrowKeys(True) - - self._imagePlot.setZoomModeEnabled(True) # Color is set in setColormap - self._imagePlot.sigPlotSignal.connect(self._imagePlotCB) - self._imagePlot.hFlipToolButton.clicked.connect( - self._updateYAxisInverted) - self._imagePlot.sigColormapChangedSignal.connect(self.setColormap) - - self._histoVPlot = Plot.Plot(backend=backend) - self._histoVPlot.setZoomModeEnabled(True) - self._histoVPlot.setCallback(self._histoVPlotCB) - self._histoVPlot.getWidgetHandle().sizeHint = sizeHint - self._histoVPlot.getWidgetHandle().minimumSizeHint = sizeHint - - self._radarView = RadarView() - self._radarView.visibleRectDragged.connect(self._radarViewCB) - - self._layout = qt.QGridLayout() - self._layout.addWidget(self._imagePlot, 0, 0) - self._layout.addWidget(self._histoVPlot.getWidgetHandle(), 0, 1) - self._layout.addWidget(self._histoHPlot.getWidgetHandle(), 1, 0) - self._layout.addWidget(self._radarView, 1, 1) - - self._layout.setColumnMinimumWidth(0, self.IMAGE_MIN_SIZE) - self._layout.setColumnStretch(0, 1) - self._layout.setColumnMinimumWidth(1, self.HISTOGRAMS_HEIGHT) - self._layout.setColumnStretch(1, 0) - - self._layout.setRowMinimumHeight(0, self.IMAGE_MIN_SIZE) - self._layout.setRowStretch(0, 1) - self._layout.setRowMinimumHeight(1, self.HISTOGRAMS_HEIGHT) - self._layout.setRowStretch(1, 0) - - self._layout.setSpacing(0) - self._layout.setContentsMargins(0, 0, 0, 0) - - self.setLayout(self._layout) - - def _dirtyCache(self): - self._cache = None - - def _updateHistograms(self): - """Update histograms content using current active image.""" - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - wasUpdatingLimits = self._updatingLimits - self._updatingLimits = True - - data, legend, info, pixmap = activeImage - xScale, yScale = info['plot_xScale'], info['plot_yScale'] - height, width = data.shape - - xMin, xMax = self._imagePlot.getGraphXLimits() - yMin, yMax = self._imagePlot.getGraphYLimits() - - # Convert plot area limits to image coordinates - # and work in image coordinates (i.e., in pixels) - xMin = int((xMin - xScale[0]) / xScale[1]) - xMax = int((xMax - xScale[0]) / xScale[1]) - yMin = int((yMin - yScale[0]) / yScale[1]) - yMax = int((yMax - yScale[0]) / yScale[1]) - - if (xMin < width and xMax >= 0 and - yMin < height and yMax >= 0): - # 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 (self._cache is None or - subsetXMin != self._cache['dataXMin'] or - subsetXMax != self._cache['dataXMax'] or - subsetYMin != self._cache['dataYMin'] or - subsetYMax != self._cache['dataYMax']): - # The visible area of data has changed, update histograms - - # Rebuild histograms for visible area - visibleData = data[subsetYMin:subsetYMax, - subsetXMin:subsetXMax] - histoHVisibleData = np.sum(visibleData, axis=0) - histoVVisibleData = np.sum(visibleData, axis=1) - - self._cache = { - 'dataXMin': subsetXMin, - 'dataXMax': subsetXMax, - 'dataYMin': subsetYMin, - 'dataYMax': subsetYMax, - - 'histoH': histoHVisibleData, - 'histoHMin': np.min(histoHVisibleData), - 'histoHMax': np.max(histoHVisibleData), - - 'histoV': histoVVisibleData, - 'histoVMin': np.min(histoVVisibleData), - 'histoVMax': np.max(histoVVisibleData) - } - - # Convert to histogram curve and update plots - # Taking into account origin and scale - coords = np.arange(2 * histoHVisibleData.size) - xCoords = (coords + 1) // 2 + subsetXMin - xCoords = xScale[0] + xScale[1] * xCoords - xData = np.take(histoHVisibleData, coords // 2) - self._histoHPlot.addCurve(xCoords, xData, - xlabel='', ylabel='', - replace=False, replot=False, - color=self.HISTOGRAMS_COLOR, - linestyle='-', - selectable=False) - vMin = self._cache['histoHMin'] - vMax = self._cache['histoHMax'] - vOffset = 0.1 * (vMax - vMin) - if vOffset == 0.: - vOffset = 1. - self._histoHPlot.setGraphYLimits(vMin - vOffset, - vMax + vOffset) - - coords = np.arange(2 * histoVVisibleData.size) - yCoords = (coords + 1) // 2 + subsetYMin - yCoords = yScale[0] + yScale[1] * yCoords - yData = np.take(histoVVisibleData, coords // 2) - self._histoVPlot.addCurve(yData, yCoords, - xlabel='', ylabel='', - replace=False, replot=False, - color=self.HISTOGRAMS_COLOR, - linestyle='-', - selectable=False) - vMin = self._cache['histoVMin'] - vMax = self._cache['histoVMax'] - vOffset = 0.1 * (vMax - vMin) - if vOffset == 0.: - vOffset = 1. - self._histoVPlot.setGraphXLimits(vMin - vOffset, - vMax + vOffset) - else: - self._dirtyCache() - self._histoHPlot.clearCurves() - self._histoVPlot.clearCurves() +from silx.gui.plot.ImageView import ImageView as SilxImageView +from silx.gui.plot.ImageView import ImageViewMainWindow as SilxImageViewMainWindow - self._updatingLimits = wasUpdatingLimits - def _updateRadarView(self): - """Update radar view visible area. +_logger = logging.getLogger(__name__) +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.ImageView instead", + __name__) +for line in traceback.format_stack(limit=3): + _logger.warning(line.rstrip()) - Takes care of y coordinate conversion. - """ - xMin, xMax = self._imagePlot.getGraphXLimits() - yMin, yMax = self._imagePlot.getGraphYLimits() - self._radarView.setVisibleRect(xMin, yMin, xMax - xMin, yMax - yMin) - - # Plots event listeners - - def _imagePlotCB(self, eventDict): - """Callback for imageView plot events.""" - if eventDict['event'] == 'mouseMoved': - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - data = activeImage[0] - height, width = data.shape - x, y = int(eventDict['x']), int(eventDict['y']) - 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': - # Do not handle histograms limitsChanged while - # updating their limits from here. - self._updatingLimits = True - - # Refresh histograms - self._updateHistograms() - - # could use eventDict['xdata'], eventDict['ydata'] instead - xMin, xMax = self._imagePlot.getGraphXLimits() - yMin, yMax = self._imagePlot.getGraphYLimits() - - # Set horizontal histo limits - self._histoHPlot.setGraphXLimits(xMin, xMax) - self._histoHPlot.replot() - - # Set vertical histo limits - self._histoVPlot.setGraphYLimits(yMin, yMax) - self._histoVPlot.replot() - - self._updateRadarView() - - self._updatingLimits = False - - # Replot in case limitsChanged due to set*Limits - # called from console. - # This results in an extra replot call in other cases. - self._imagePlot.replot() - - def _histoHPlotCB(self, eventDict): - """Callback for horizontal histogram plot events.""" - if eventDict['event'] == 'mouseMoved': - if self._cache is not None: - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - xOrigin, xScaleFactor = activeImage[2]['plot_xScale'] - - minValue = xOrigin + xScaleFactor * self._cache['dataXMin'] - data = self._cache['histoH'] - width = data.shape[0] - x = int(eventDict['x']) - if x >= minValue and x < minValue + width: - self.valueChanged.emit(float('nan'), float(x), - data[x - minValue]) - elif eventDict['event'] == 'limitsChanged': - if (not self._updatingLimits and - eventDict['xdata'] != self._imagePlot.getGraphXLimits()): - xMin, xMax = eventDict['xdata'] - self._imagePlot.setGraphXLimits(xMin, xMax) - self._imagePlot.replot() - - def _histoVPlotCB(self, eventDict): - """Callback for vertical histogram plot events.""" - if eventDict['event'] == 'mouseMoved': - if self._cache is not None: - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - yOrigin, yScaleFactor = activeImage[2]['plot_yScale'] - - minValue = yOrigin + yScaleFactor * self._cache['dataYMin'] - data = self._cache['histoV'] - height = data.shape[0] - y = int(eventDict['y']) - if y >= minValue and y < minValue + height: - self.valueChanged.emit(float(y), float('nan'), - data[y - minValue]) - elif eventDict['event'] == 'limitsChanged': - if (not self._updatingLimits and - eventDict['ydata'] != self._imagePlot.getGraphYLimits()): - yMin, yMax = eventDict['ydata'] - self._imagePlot.setGraphYLimits(yMin, yMax) - self._imagePlot.replot() - - def _radarViewCB(self, left, top, width, height): - """Slot for radar view visible rectangle changes.""" - if not self._updatingLimits: - # Takes care of Y axis conversion - self._imagePlot.setLimits(left, left + width, top, top + height) - self._imagePlot.replot() - - def _updateYAxisInverted(self): - """Sync image, vertical histogram and radar view axis orientation.""" - inverted = self._imagePlot.isYAxisInverted() - - self._imagePlot.invertYAxis(inverted) - self._histoVPlot.invertYAxis(inverted) - - # Use scale to invert radarView - # RadarView default Y direction is from top to bottom - # As opposed to Plot. So invert RadarView when Plot is NOT inverted. - self._radarView.resetTransform() - if not inverted: - self._radarView.scale(1., -1.) - self._updateRadarView() - - self._imagePlot.replot() - self._histoVPlot.replot() - self._radarView.update() - - 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 +class ImageView(SilxImageView): + def __init__(self, parent=None, windowFlags=None, backend=None): """ - assert axis in ('x', 'y') - if self._cache is None: - return None - else: - if axis == 'x': - return dict( - data=np.array(self._cache['histoH'], copy=True), - extent=(self._cache['dataXMin'], self._cache['dataXMax'])) - else: - return dict( - data=np.array(self._cache['histoV'], copy=True), - extent=(self._cache['dataYMin'], self._cache['dataYMax'])) - 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.visibleRectDragged.disconnect(self._radarViewCB) - self._radarView = radarView - self._radarView.visibleRectDragged.connect(self._radarViewCB) - self._layout.addWidget(self._radarView, 1, 1) - - self._updateYAxisInverted() - - # PlotWindow toolbar - - def toolBar(self): - """Returns the tool bar associated with the image plot. - - This is the toolBar provided by :class:`PlotWindow`. - - :return: The toolBar associated to the image plot. - :rtype: QToolBar - """ - return self._imagePlot.toolBar - - # 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 + :param parent: + :param windowFlags: windowFlags (e.g. qt.Qt.Widget, qt.Qt.Window...) + If None, the silx default behavior is used: behave as a widget if + parent is not None, else behave as a Window. + :param backend: """ - return self._imagePlot.getDefaultColormap() - - def setColormap(self, colormap=None, normalization=None, - autoscale=None, vmin=None, vmax=None, colors=256): - """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. - - :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. - """ - cmapDict = self._imagePlot.getDefaultColormap() - - if 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 == 256 - for key, value in colormap.items(): - cmapDict[key] = value - - else: - if colormap is not None: - cmapDict['name'] = colormap - if normalization is not None: - cmapDict['normalization'] = normalization - if autoscale is not None: - cmapDict['autoscale'] = autoscale - if vmin is not None: - cmapDict['vmin'] = vmin - if vmax is not None: - cmapDict['vmax'] = vmax - - if 'colors' not in cmapDict: - cmapDict['colors'] = 256 - - cursorColor = _cursorColorForColormap(cmapDict['name']) - self._imagePlot.setZoomModeEnabled(True, color=cursorColor) + super(ImageView, self).__init__(parent=parent, backend=backend) - self._imagePlot.setDefaultColormap(cmapDict) + # SilxImageView does not have a windowFlags parameter. + # A silx PlotWidget behaves as a Widget if parent is not None, + # else it behaves as a QMainWindow. + if windowFlags is not None: + self.setWindowFlags(windowFlags) - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: # Refresh image with new colormap - data, legend, info, pixmap = activeImage - - self._imagePlot.addImage(data, legend=legend, info=info, - colormap=self.getColormap(), - replace=False, replot=False) - self._imagePlot.replot() - - 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._imagePlot.removeImage(self._imageLegend, replot=False) - return - - data = np.array(image, order='C', copy=copy) - assert data.size != 0 - assert len(data.shape) == 2 - height, width = data.shape - - self._imagePlot.addImage(data, - legend=self._imageLegend, - xScale=(origin[0], scale[0]), - yScale=(origin[1], scale[1]), - colormap=self.getColormap(), - replace=False, - replot=False) - self._imagePlot.setActiveImage(self._imageLegend) - self._updateHistograms() - - self._radarView.setDataRect(origin[0], - origin[1], - width * scale[0], - height * scale[1]) - - if reset: - self.resetZoom() - else: - self._histoHPlot.replot() - self._histoVPlot.replot() - self._imagePlot.replot() - - #################### - # Plot API proxies # - #################### - - # Rebuild side histograms if active image gets changed through the Plot API - - def addImage(self, data, legend=None, info=None, - replace=True, replot=True, - xScale=None, yScale=None, z=0, - selectable=False, draggable=False, - colormap=None, **kw): - if legend == self._imagePlot.getActiveImage(just_legend=True): - # Updating active image, resets side histograms cache - self._dirtyCache() - - result = self._imagePlot.addImage(data, legend, info, replace, replot, - xScale, yScale, z, - selectable, draggable, - colormap, **kw) - self._updateHistograms() - - if replot: - self._histoHPlot.replot() - self._histoVPlot.replot() - - return result - - def clear(self): - self._dirtyCache() - return self._imagePlot.clear() - - def clearImages(self): - self._dirtyCache() - return self._imagePlot.clearImages() - - def removeImage(self, legend, replot=True): - if legend == self._imagePlot.getActiveImage(just_legend=True): - # Removing active image, resets side histograms cache - self._dirtyCache() - - result = self._imageView.removeImage(legend, replot) - self._updateHistograms() - - if replot: - self._histoHPlot.replot() - self._histoVPlot.replot() - - return result - - def setActiveImage(self, legend, replot=True): - # Active image changes, resets side histogram cache - self._dirtyCache() - - result = self._imagePlot.setActiveImage(legend, replot) - self._updateHistograms() - - if replot: - self._histoHPlot.replot() - self._histoVPlot.replot() - return result - - # Invert axes - - def invertYAxis(self, flag=True): - result = self._imagePlot.invertYAxis(flag) - self._updateYAxisInverted() # To sync vert. histo and radar view - return result - - # Ugly yet simple proxy for the Plot API - - def __getattr__(self, name): - """Proxy to expose image plot API.""" - return getattr(self._imagePlot, name) - - -# ImageViewMainWindow ######################################################### - -class ImageViewMainWindow(qt.QMainWindow): - """QMainWindow embedding an ImageView. - - Surrounds the ImageView with an associated toolbar and status bar. - """ +class ImageViewMainWindow(SilxImageViewMainWindow): def __init__(self, parent=None, windowFlags=qt.Qt.Widget, backend=None): - self._dataInfo = None - super(ImageViewMainWindow, self).__init__(parent, windowFlags) - - # Create the ImageView widget and add it to the QMainWindow - self.imageView = ImageView(backend=backend) - self.imageView.setGraphXLabel('X') - self.imageView.setGraphYLabel('Y') - self.imageView.setGraphTitle('Image') - self.imageView._imagePlot.sigColormapChangedSignal.connect( - self._colormapUpdated) - self.setCentralWidget(self.imageView) - - # Using PlotWindow's toolbar - self.addToolBar(self.imageView.toolBar()) - self.profileToolBar = ProfileToolBar(self.imageView._imagePlot) - self.addToolBar(self.profileToolBar) - self.addToolBar(qt.Qt.BottomToolBarArea, LimitsToolBar(self.imageView)) - - self.statusBar() - - # Connect to ImageView's signal - self.imageView.valueChanged.connect(self._statusBarSlot) - - def _colormapUpdated(self, colormap): - """Sync ROI color with current colormap""" - self.profileToolBar.overlayColor = _cursorColorForColormap( - colormap['name']) - - def _statusBarSlot(self, row, column, value): - """Update status bar with coordinates/value from plots.""" - if np.isnan(row): - msg = 'Column: %d, Sum: %g' % (int(column), value) - elif np.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) - - def setImage(self, image, *args, **kwargs): - """Set the displayed image. - - See :meth:`ImageView.setImage` for details. - """ - 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 + super(ImageViewMainWindow, self).__init__(parent, backend) + if windowFlags is not None: + self.setWindowFlags(windowFlags) - # Set the new image in ImageView widget - self.imageView.setImage(image, *args, **kwargs) - self.profileToolBar.updateProfile() - self.setStatusBar(None) # main ######################################################################## diff --git a/PyMca5/PyMcaGui/plotting/MaskImageTools.py b/PyMca5/PyMcaGui/plotting/MaskImageTools.py index 61466f7..5c6394f 100644 --- a/PyMca5/PyMcaGui/plotting/MaskImageTools.py +++ b/PyMca5/PyMcaGui/plotting/MaskImageTools.py @@ -30,8 +30,6 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" __doc__ = """ Common tools to deal with common graphics operations on images. """ -import sys -import os import numpy from PyMca5 import spslut @@ -253,9 +251,9 @@ def applyMaskToImage(pixmap, mask=None, colors=None, copy=True): if __name__ == "__main__": from PyMca5.PyMcaGui import PyMcaQt as qt - from PyMca5.PyMcaGui import PlotWidget + from silx.gui.plot import PlotWidget app = qt.QApplication([]) - w = PlotWidget.PlotWidget() + w = PlotWidget() data = numpy.arange(10000.).reshape(100, 100) mask = numpy.zeros(data.shape, dtype=numpy.uint8) mask[25:75, 25:75] = 1 diff --git a/PyMca5/PyMcaGui/plotting/MaskImageWidget.py b/PyMca5/PyMcaGui/plotting/MaskImageWidget.py index 5116c8b..5be6bae 100644 --- a/PyMca5/PyMcaGui/plotting/MaskImageWidget.py +++ b/PyMca5/PyMcaGui/plotting/MaskImageWidget.py @@ -30,6 +30,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaGraph.ctools import pnpoly from . import RGBCorrelatorGraph from . import ColormapDialog @@ -61,7 +62,8 @@ OVERLAY_DRAW = True DEFAULT_COLORMAP_INDEX = 2 DEFAULT_COLORMAP_LOG_FLAG = False -DEBUG = 0 +_logger = logging.getLogger(__name__) + USE_PICKER = False @@ -85,7 +87,6 @@ class MaskImageWidget(qt.QWidget): self.setMaximumWidth(int(screenWidth)-5) self.setMinimumWidth(min(int(0.5*screenWidth),800)) - self._y1AxisInverted = False self.__selectionMask = None self._selectionColors = None self.__imageData = None @@ -185,13 +186,12 @@ class MaskImageWidget(qt.QWidget): self.buildStandaloneSaveMenu() self.graphWidget.zoomResetToolButton.clicked.connect(self._zoomResetSignal) - self.graphWidget.graph.setDrawModeEnabled(False) - self.graphWidget.graph.setZoomModeEnabled(True) + self.graphWidget.graph.setInteractiveMode('zoom') if self.__selectionFlag: if self.__imageIconsFlag: self.setSelectionMode(False) self._toggleSelectionMode() - self.graphWidget.graph.setDrawModeEnabled(True, + self.graphWidget.graph.setInteractiveMode('draw', shape="rectangle", label="mask") else: @@ -323,9 +323,8 @@ class MaskImageWidget(qt.QWidget): self._profileSignalSlot(ddict) def _profileSignalSlot(self, ddict): - if DEBUG: - print("_profileSignalSLot, event = %s" % ddict['event']) - print("Received ddict = ", ddict) + _logger.debug("_profileSignalSLot, event = %s", ddict['event']) + _logger.debug("Received ddict = %s", ddict) if ddict['event'] in [None, "NONE"]: #Nothing to be made @@ -333,10 +332,10 @@ class MaskImageWidget(qt.QWidget): if ddict['event'] == "profileWidthChanged": if self.__lastOverlayLegend is not None: - legend = self.__lastOverlayLegend - #TODO: Find a better way to deal with this - if legend in self.graphWidget.graph._itemDict: - info = self.graphWidget.graph._itemDict[legend]['info'] + shape = self.graphWidget.graph._getItem(kind='item', + legend=self.__lastOverlayLegend) + if shape is not None: + info = shape.getInfo(copy=False) if info['mode'] == ddict['mode']: newDict = {} newDict['event'] = "updateProfile" @@ -344,7 +343,6 @@ class MaskImageWidget(qt.QWidget): newDict['ydata'] = info['ydata'] * 1 newDict['mode'] = info['mode'] * 1 newDict['pixelwidth'] = ddict['pixelwidth'] * 1 - info = None #self._updateProfileCurve(newDict) self._profileSignalSlot(newDict) return @@ -355,13 +353,13 @@ class MaskImageWidget(qt.QWidget): self._profileSelectionWindow = ProfileScanWidget.ProfileScanWidget(actions=False) else: self._profileSelectionWindow = ProfileScanWidget.ProfileScanWidget(actions=True) - self._profileSelectionWindow.sigAddClicked.connect( \ + self._profileSelectionWindow.sigAddClicked.connect( self._profileSelectionSlot) - self._profileSelectionWindow.sigRemoveClicked.connect( \ + self._profileSelectionWindow.sigRemoveClicked.connect( self._profileSelectionSlot) self._profileSelectionWindow.sigReplaceClicked.connect( self._profileSelectionSlot) - self._interpolate = SpecfitFuns.interpol + self._interpolate = SpecfitFuns.interpol #if I do not return here and the user interacts with the graph while #the profileSelectionWindow is not shown, I get crashes under Qt 4.5.3 and MacOS X #when calling _getProfileCurve @@ -376,16 +374,14 @@ class MaskImageWidget(qt.QWidget): if curve is None: return xdata, ydata, legend, info = curve - replot=True - replace=True idx = numpy.isfinite(ydata) xdata = xdata[idx] ydata = ydata[idx] self._profileSelectionWindow.addCurve(xdata, ydata, legend=legend, info=info, - replot=replot, - replace=replace) + resetzoom=True, + replace=True) def getGraphTitle(self): try: @@ -438,7 +434,7 @@ class MaskImageWidget(qt.QWidget): if ddict['event'] == 'profileModeChanged': if self.__lastOverlayLegend: - self.graphWidget.graph.removeItem(self.__lastOverlayLegend, replot=True) + self.graphWidget.graph.removeItem(self.__lastOverlayLegend) return #if I show the image here it does not crash, but it is not nice because @@ -476,13 +472,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[row, :] legend = "Row = %d" % row if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([0.0, shape[1], shape[1], 0.0], [row, row, row+1, row+1], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) else: row0 = int(int(ddict['row'][0]) - 0.5 * width) if row0 < 0: @@ -497,13 +492,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[row0:row1+1, :].sum(axis=0) legend = "Row = %d to %d" % (row0, row1) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([0.0, 0.0, shape[1], shape[1]], [row0, row1+1, row1+1, row0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) xdata = numpy.arange(shape[1]).astype(numpy.float) if self._xScale is not None: xdata = self._xScale[0] + xdata * self._xScale[1] @@ -519,13 +513,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[:, column] legend = "Column = %d" % column if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([column, column, column+1, column+1], [0.0, shape[0], shape[0], 0.0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) else: col0 = int(int(ddict['column'][0]) - 0.5 * width) if col0 < 0: @@ -540,13 +533,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[:, col0:col1+1].sum(axis=1) legend = "Col = %d to %d" % (col0, col1) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col0, col1+1, col1+1], [0, shape[0], shape[0], 0.], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) xdata = numpy.arange(shape[0]).astype(numpy.float) if self._yScale is not None: xdata = self._yScale[0] + xdata * self._yScale[1] @@ -577,8 +569,7 @@ class MaskImageWidget(qt.QWidget): if npoints == 1: #all points are the same - if DEBUG: - print("START AND END POINT ARE THE SAME!!") + _logger.debug("START AND END POINT ARE THE SAME!!") return if width < 0: # width = pixelwidth - 1 @@ -591,13 +582,12 @@ class MaskImageWidget(qt.QWidget): xdata = numpy.arange(float(npoints)) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col1], [row0, row1], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) elif deltaCol == 0: #vertical line col0 = int(int(ddict['column'][0]) - 0.5 * width) @@ -624,13 +614,12 @@ class MaskImageWidget(qt.QWidget): npoints = max(ydata.shape) xdata = numpy.arange(float(npoints)) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col0, col1+1, col1+1], [row0, row1+1, row1+1, row0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) elif deltaRow == 0: #horizontal line row0 = int(int(ddict['row'][0]) - 0.5 * width) @@ -657,13 +646,12 @@ class MaskImageWidget(qt.QWidget): npoints = max(ydata.shape) xdata = numpy.arange(float(npoints)) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col0, col1+1, col1+1], [row0, row1+1, row1+1, row0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) else: #restore original value of width width = ddict['pixelwidth'] @@ -687,9 +675,8 @@ class MaskImageWidget(qt.QWidget): newRow0 = 0.0 newRow1 = -(col1-col0) * sinalpha + (row1-row0) * cosalpha - if DEBUG: - print("new X0 Y0 = %f, %f " % (newCol0, newRow0)) - print("new X1 Y1 = %f, %f " % (newCol1, newRow1)) + _logger.debug("new X0 Y0 = %f, %f ", newCol0, newRow0) + _logger.debug("new X1 Y1 = %f, %f ", newCol1, newRow1) tmpX = numpy.linspace(newCol0, newCol1, npoints).astype(numpy.float) rotMatrix = numpy.zeros((2,2), numpy.float) @@ -697,19 +684,19 @@ class MaskImageWidget(qt.QWidget): rotMatrix[0,1] = - sinalpha rotMatrix[1,0] = sinalpha rotMatrix[1,1] = cosalpha - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: #test if I recover the original points - testX = numpy.zeros((2, 1) , numpy.float) + testX = numpy.zeros((2, 1), numpy.float) colRow = numpy.dot(rotMatrix, testX) - print("Recovered X0 = %f" % (colRow[0,0] + col0)) - print("Recovered Y0 = %f" % (colRow[1,0] + row0)) - print("It should be = %f, %f" % (col0, row0)) - testX[0,0] = newCol1 - testX[1,0] = newRow1 + _logger.debug("Recovered X0 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y0 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col0, row0) + testX[0, 0] = newCol1 + testX[1, 0] = newRow1 colRow = numpy.dot(rotMatrix, testX) - print("Recovered X1 = %f" % (colRow[0,0] + col0)) - print("Recovered Y1 = %f" % (colRow[1,0] + row0)) - print("It should be = %f, %f" % (col1, row1)) + _logger.debug("Recovered X1 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y1 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col1, row1) #find the drawing limits testX = numpy.zeros((2, 4) , numpy.float) @@ -727,21 +714,18 @@ class MaskImageWidget(qt.QWidget): for a in rowLimits0: if (a >= shape[0]) or (a < 0): - if DEBUG: - print("outside row limits",a) + _logger.debug("outside row limits %s", a) return for a in colLimits0: if (a >= shape[1]) or (a < 0): - if DEBUG: - print("outside column limits",a) + _logger.debug("outside column limits %s", a) return r0 = rowLimits0[0] r1 = rowLimits0[1] if r0 > r1: - if DEBUG: - print("r0 > r1", r0, r1) + _logger.debug("r0 > r1 %s %s", r0, r1) raise ValueError("r0 > r1") x = numpy.zeros((2, npoints) , numpy.float) @@ -809,8 +793,7 @@ class MaskImageWidget(qt.QWidget): rowLimits0, legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) if self.__lineProjectionMode == 'X': xLabel = self.getXLabel() xdata += col0 @@ -831,8 +814,7 @@ class MaskImageWidget(qt.QWidget): float(deltaRow) * deltaRow)/(npoints-1.0) xdata *= deltaDistance else: - if DEBUG: - print("Mode %s not supported yet" % ddict['mode']) + _logger.debug("Mode %s not supported yet", ddict['mode']) return self.__lastOverlayWidth = ddict['pixelwidth'] @@ -842,8 +824,7 @@ class MaskImageWidget(qt.QWidget): return xdata, ydata, legend, info def _profileSelectionSlot(self, ddict): - if DEBUG: - print(ddict) + _logger.debug("%s", ddict) # the curves as [[x0, y0, legend0, info0], ...] curveList = ddict['curves'] label = ddict['label'] @@ -851,25 +832,17 @@ class MaskImageWidget(qt.QWidget): if ddict['event'] == 'ADD': for i in range(n): x, y, legend, info = curveList[i] - info['profilelabel'] = label - if i == (n-1): - replot = True + resetzoom = (i == (n-1)) self._profileScanWindow.addCurve(x, y, legend=legend, info=info, - replot=replot, replace=False) + resetzoom=resetzoom, replace=False) elif ddict['event'] == 'REPLACE': for i in range(n): x, y, legend, info = curveList[i] info['profilelabel'] = label - if i in [0, n-1]: - replace = True - else: - replace = False - if i == (n-1): - replot = True - else: - replot = False + replace = (i in [0, n-1]) + resetzoom = (i == (n-1)) self._profileScanWindow.addCurve(x, y, legend=legend, info=info, - replot=replot, replace=replace) + resetzoom=resetzoom, replace=replace) elif ddict['event'] == 'REMOVE': curveList = self._profileScanWindow.getAllCurves() if curveList in [None, []]: @@ -885,13 +858,10 @@ class MaskImageWidget(qt.QWidget): n = len(toDelete) for i in range(n): legend = toDelete[i] - if i == (n-1): - replot = True - else: - replot = False - self._profileScanWindow.removeCurve(legend, replot=replot) + resetzoom = (i == (n-1)) + self._profileScanWindow.removeCurve(legend, resetzoom=resetzoom) - def drawOverlayItem(self, x, y, legend=None, info=None, replace=False, replot=True): + def drawOverlayItem(self, x, y, legend=None, info=None, replace=False): #same call as the plot1D addCurve command if legend is None: legend="UnnamedOverlayItem" @@ -911,31 +881,22 @@ class MaskImageWidget(qt.QWidget): for i in y: yList.append(self._yScale[0] + i * self._yScale[1]) self.graphWidget.graph.addItem(xList, yList, legend=legend, info=info, - replace=replace, replot=replot, - shape="polygon", fill=True) + replace=replace, shape="polygon", fill=True) self.__lastOverlayLegend = legend def _hFlipIconSignal(self): - self._y1AxisInverted = self.graphWidget.graph.isYAxisInverted() - if self._y1AxisInverted: - self._y1AxisInverted = False - else: - self._y1AxisInverted = True - #self.graphWidget.graph.zoomReset() - self.graphWidget.graph.invertYAxis(self._y1AxisInverted) - self._y1AxisInverted = self.graphWidget.graph.isYAxisInverted() - self.graphWidget.graph.replot() + yaxis = self.graphWidget.graph.getYAxis() + yaxis.setInverted(not yaxis.isInverted()) #inform the other widgets ddict = {} ddict['event'] = "hFlipSignal" - ddict['current'] = self._y1AxisInverted * 1 + ddict['current'] = yaxis.isInverted() * 1 ddict['id'] = id(self) self.emitMaskImageSignal(ddict) def setY1AxisInverted(self, value): - self._y1AxisInverted = value - self.graphWidget.graph.invertYAxis(self._y1AxisInverted) + self.graphWidget.graph.getYAxis().setInverted(value) def setXLabel(self, label="Column"): return self.graphWidget.setXLabel(label) @@ -988,38 +949,34 @@ class MaskImageWidget(qt.QWidget): self._replaceImageClicked) def _setEraseSelectionMode(self): - if DEBUG: - print("_setEraseSelectionMode") + _logger.debug("_setEraseSelectionMode") self.__eraseMode = True self.__brushMode = True - self.graphWidget.graph.setDrawModeEnabled(False) + self.graphWidget.graph.setInteractiveMode('select') def _setRectSelectionMode(self): - if DEBUG: - print("_setRectSelectionMode") + _logger.debug("_setRectSelectionMode") self.__eraseMode = False self.__brushMode = False - self.graphWidget.graph.setDrawModeEnabled(True, + self.graphWidget.graph.setInteractiveMode("draw", shape="rectangle", label="mask") def _setPolygonSelectionMode(self): self.__eraseMode = False self.__brushMode = False - self.graphWidget.graph.setDrawModeEnabled(True, + self.graphWidget.graph.setInteractiveMode('draw', shape="polygon", label="mask") def _setBrushSelectionMode(self): - if DEBUG: - print("_setBrushSelectionMode") + _logger.debug("_setBrushSelectionMode") self.__eraseMode = False self.__brushMode = True - self.graphWidget.graph.setDrawModeEnabled(False) + self.graphWidget.graph.setInteractiveMode('select') def _setBrush(self): - if DEBUG: - print("_setBrush") + _logger.debug("_setBrush") if self.__brushMenu is None: self.__brushMenu = qt.QMenu() self.__brushMenu.addAction(QString(" 1 Image Pixel Width"), @@ -1055,25 +1012,25 @@ class MaskImageWidget(qt.QWidget): self.__brushWidth = 20 def _toggleSelectionMode(self): - drawMode = self.graphWidget.graph.getDrawMode() - if drawMode is None: + mode = self.graphWidget.graph.getInteractiveMode() + if mode['mode'] != 'draw': # we are not drawing anything - if self.graphWidget.graph.isZoomModeEnabled(): + if self.graphWidget.graph.getInteractiveMode()['mode'] == 'zoom': # we have to pass to mask mode self.setSelectionMode(True) else: # we set zoom mode and show the line icons self.setSelectionMode(False) - elif drawMode['label'] is not None: - if drawMode['label'].startswith('mask'): - #we set the zoom mode and show the line icons + elif mode['label'] is not None: + if mode['label'].startswith('mask'): + # we set the zoom mode and show the line icons self.setSelectionMode(False) else: # we disable zoom and drawing and set mask mode self.setSelectionMode(True) - elif drawMode['label'] in [None]: + elif mode['label'] in [None]: # we are not drawing anything - if self.graphWidget.graph.isZoomModeEnabled(): + if self.graphWidget.graph.getInteractiveMode()['mode'] == 'zoom': # we have to pass to mask mode self.setSelectionMode(True) else: @@ -1085,17 +1042,17 @@ class MaskImageWidget(qt.QWidget): #if not self.__imageIconsFlag: # mode = False if mode: - self.graphWidget.graph.setDrawModeEnabled(True, - 'rectangle', + self.graphWidget.graph.setInteractiveMode('draw', + shape='rectangle', label='mask') - self.__brushMode = False + self.__brushMode = False self.graphWidget.hideProfileSelectionIcons() self.graphWidget.selectionToolButton.setChecked(True) self.graphWidget.selectionToolButton.setDown(True) self.graphWidget.showImageIcons() else: self.graphWidget.showProfileSelectionIcons() - self.graphWidget.graph.setZoomModeEnabled(True) + self.graphWidget.graph.setInteractiveMode('zoom') self.graphWidget.selectionToolButton.setChecked(False) self.graphWidget.selectionToolButton.setDown(False) self.graphWidget.hideImageIcons() @@ -1169,8 +1126,7 @@ class MaskImageWidget(qt.QWidget): self._resetSelection(True) def _resetSelection(self, owncall=True): - if DEBUG: - print("_resetSelection") + _logger.debug("_resetSelection") self.__selectionMask = None if self.__imageData is None: return @@ -1210,8 +1166,8 @@ class MaskImageWidget(qt.QWidget): if data is None: self.__imageData = data self.__selectionMask = None - self.plotImage(update = True) - self.graphWidget._zoomReset(replot=True) + self.plotImage(update=True) + self.graphWidget.graph.resetZoom() return else: self.__imageData = data @@ -1226,8 +1182,8 @@ class MaskImageWidget(qt.QWidget): self.colormapDialog.setDisplayedMaxValue(maxData) self.colormapDialog.setDataMinMax(minData, maxData, update=True) else: - self.plotImage(update = True) - self.graphWidget._zoomReset(replot=True) + self.plotImage(update=True) + self.graphWidget.graph.resetZoom() def getImageData(self): return self.__imageData @@ -1282,8 +1238,8 @@ class MaskImageWidget(qt.QWidget): self.__pixmap0 = pixmap if clearmask: self.__selectionMask = None - self.plotImage(update = True) - self.graphWidget._zoomReset(replot=True) + self.plotImage(update=True) + self.graphWidget.graph.resetZoom() def plotImage(self, update=True): if self.__imageData is None: @@ -1295,13 +1251,14 @@ class MaskImageWidget(qt.QWidget): self.__pixmap0 = self.__pixmap.copy() self.__applyMaskToImage() - # replot=False as it triggers a zoom reset in Plot.py + origin, scale = None, None + if self._xScale is not None: + origin = (self._xScale[0], self._yScale[0]) + scale = (self._xScale[1], self._yScale[1]) self.graphWidget.graph.addImage(self.__pixmap, - "image", - xScale=self._xScale, - yScale=self._yScale, - replot=False) - self.graphWidget.graph.replot() + "image", resetzoom=False, + origin=origin, + scale=scale) self.updateProfileSelectionWindow() def getPixmapFromData(self): @@ -1535,18 +1492,15 @@ class MaskImageWidget(qt.QWidget): alteration = (1 - (0.2 * (self.__selectionMask > 0))) - \ 0.1 * (self.__selectionMask == self._roiTags[self._nRoi - 1]) if self.colormap is None: - if DEBUG: - print("Colormap is None") + _logger.debug("Colormap is None") if self.__image is not None: if self.__image.format() == qt.QImage.Format_ARGB32: - if DEBUG: - print("__applyMaskToImage CASE 1") + _logger.debug("__applyMaskToImage CASE 1") for i in range(4): self.__pixmap[:,:,i] = (self.__pixmap0[:,:,i] *\ alteration).astype(numpy.uint8) else: - if DEBUG: - print("__applyMaskToImage CASE 2") + _logger.debug("__applyMaskToImage CASE 2") self.__pixmap = self.__pixmap0.copy() tmp = self.__selectionMask > 0 self.__pixmap[tmp, 0] = 0x40 @@ -1559,8 +1513,7 @@ class MaskImageWidget(qt.QWidget): self.__pixmap[roiTag, 3] = 2*0x40 else: if self.__defaultColormap > 1: - if DEBUG: - print("__applyMaskToImage CASE 3") + _logger.debug("__applyMaskToImage CASE 3") self.__pixmap = self.__pixmap0.copy() for i in range(3): self.__pixmap[:,:,i] = (self.__pixmap0[:,:,i] *\ @@ -1573,8 +1526,7 @@ class MaskImageWidget(qt.QWidget): for i in range(3): self.__pixmap[:,:,i] *= tmpMask else: - if DEBUG: - print("__applyMaskToImage CASE 4") + _logger.debug("__applyMaskToImage CASE 4") self.__pixmap = self.__pixmap0.copy() self.__pixmap[self.__selectionMask>0,0] = 0x40 self.__pixmap[self.__selectionMask>0,2] = 0x70 @@ -1594,8 +1546,7 @@ class MaskImageWidget(qt.QWidget): self.__pixmap[tmpMask,2] = 0xff self.__pixmap[tmpMask,3] = 0xff elif int(str(self.colormap[0])) > 1: #color - if DEBUG: - print("__applyMaskToImage CASE 5") + _logger.debug("__applyMaskToImage CASE 5") for i in range(3): self.__pixmap[:,:,i] = (self.__pixmap0[:,:,i] * alteration) if 0: @@ -1606,8 +1557,7 @@ class MaskImageWidget(qt.QWidget): for i in range(3): self.__pixmap[:,:,i] *= tmpMask elif self._maxNRois > 1: - if DEBUG: - print("__applyMaskToImage CASE 6") + _logger.debug("__applyMaskToImage CASE 6") tmp = 1 - (self.__selectionMask>0) tmp2 = (self.__selectionMask == self._roiTags[self._nRoi - 1]) self.__pixmap[:, :, 2] = (0x70 * (self.__selectionMask>0) + \ @@ -1616,8 +1566,7 @@ class MaskImageWidget(qt.QWidget): self.__pixmap[:,:, 3] = (0x40 * (self.__selectionMask>0) + 0x40 * tmp2) +\ tmp * self.__pixmap0[:,:,3] else: - if DEBUG: - print("__applyMaskToImage CASE 7") + _logger.debug("__applyMaskToImage CASE 7") self.__pixmap = self.__pixmap0.copy() tmp = 1 - self.__selectionMask self.__pixmap[:, :, 2] = (0x70 * self.__selectionMask) +\ @@ -1802,7 +1751,7 @@ class MaskImageWidget(qt.QWidget): elif self.__pixmap0 is not None: imageShape = self.__pixmap0.shape[0:2] else: - print("Cannot handle polygon mask") + _logger.warning("Cannot handle polygon mask") return x = self._xScale[0] + self._xScale[1] * numpy.arange(imageShape[1]) y = self._yScale[0] + self._yScale[1] * numpy.arange(imageShape[0]) @@ -1888,8 +1837,7 @@ class MaskImageWidget(qt.QWidget): if ownsignal: pass if None in [ddict['x'], ddict['y']]: - if DEBUG: - print("Signal from outside region", ddict) + _logger.debug("Signal from outside region %s", ddict) return if self.graphWidget.infoWidget.isHidden() or self.__brushMode: @@ -1948,7 +1896,7 @@ class MaskImageWidget(qt.QWidget): self.setMouseText("%g, %g, %g" % (x, y, self.__imageData[row, column])) if self.__brushMode: - if self.graphWidget.graph.isZoomModeEnabled(): + if self.graphWidget.graph.getInteractiveMode()['mode'] == 'zoom': return if ddict['button'] != "left": return @@ -1982,9 +1930,8 @@ class MaskImageWidget(qt.QWidget): self.sigMaskImageWidgetSignal.emit(ddict) def _zoomResetSignal(self): - if DEBUG: - print("_zoomResetSignal") - self.graphWidget._zoomReset(replot=False) + _logger.debug("_zoomResetSignal") + self.graphWidget.graph.resetZoom() self.plotImage(True) def getOutputFileName(self): @@ -1996,7 +1943,8 @@ class MaskImageWidget(qt.QWidget): filedialog.setFileMode(filedialog.AnyFile) filedialog.setAcceptMode(qt.QFileDialog.AcceptSave) filedialog.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict["gioconda16"]))) - formatlist = ["ASCII Files *.dat", + formatlist = ["TIFF Files *.tif", + "ASCII Files *.dat", "EDF Files *.edf", 'CSV(, separated) Files *.csv', 'CSV(; separated) Files *.csv', @@ -2008,7 +1956,7 @@ class MaskImageWidget(qt.QWidget): for f in formatlist: strlist.append(f) if self._saveFilter is None: - self._saveFilter =formatlist[0] + self._saveFilter = formatlist[0] if hasattr(filedialog, "setFilters"): filedialog.setFilters(strlist) filedialog.selectFilter(self._saveFilter) @@ -2027,12 +1975,12 @@ class MaskImageWidget(qt.QWidget): self._saveFilter = qt.safe_str(filedialog.selectedFilter()) else: self._saveFilter = qt.safe_str(filedialog.selectedNameFilter()) - filterused = "."+self._saveFilter[-3:] + filterused = "." + self._saveFilter[-3:] PyMcaDirs.outputDir = os.path.dirname(filename) if len(filename) < 4: - filename = filename+ filterused + filename = filename + filterused elif filename[-4:] != filterused : - filename = filename+ filterused + filename = filename + filterused else: filename = "" return filename @@ -2063,10 +2011,15 @@ class MaskImageWidget(qt.QWidget): return if filename is None: filename = self.getOutputFileName() - if not len(filename):return + if not len(filename): + return if filename.lower().endswith(".edf"): ArraySave.save2DArrayListAsEDF(imageList, filename, labels) + elif filename.lower().endswith(".tif"): + ArraySave.save2DArrayListAsMonochromaticTiff(imageList, + filename, + labels) elif filename.lower().endswith(".csv"): if "," in self._saveFilter: csvseparator = "," diff --git a/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py b/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py index d9ece40..09fff5c 100644 --- a/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py +++ b/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py @@ -37,70 +37,109 @@ ___doc__ = """ - Final layer containing the selected points with the selected colors. """ -import sys -import os import numpy +import logging from PyMca5.PyMcaGraph.ctools import pnpoly -DEBUG = 0 +_logger = logging.getLogger(__name__) -from . import PlotWindow from . import MaskImageWidget from . import MaskImageTools -qt = PlotWindow.qt +from PyMca5.PyMcaGui import PyMcaQt as qt +from .MaskToolBar import MaskToolBar +from . import ColormapDialog +from .PyMca_Icons import IconDict + +from silx.gui.plot import PlotWindow + if hasattr(qt, "QString"): QString = qt.QString else: QString = qt.safe_str -IconDict = PlotWindow.IconDict -class MaskScatterWidget(PlotWindow.PlotWindow): + +class MaskScatterWidget(PlotWindow): sigMaskScatterWidgetSignal = qt.pyqtSignal(object) DEFAULT_COLORMAP_INDEX = 2 DEFAULT_COLORMAP_LOG_FLAG = True - def __init__(self, parent=None, backend=None, plugins=False, newplot=False, - control=False, position=False, maxNRois=1, grid=False, - logx=False, logy=False, togglePoints=False, normal=True, - polygon=True, colormap=True, aspect=True, - imageIcons=True, bins=None, **kw): + def __init__(self, parent=None, backend=None, control=False, + position=False, maxNRois=1, grid=False, logScale=False, + curveStyle=False, resetzoom=True, + aspectRatio=True, imageIcons=True, polygon=True, bins=None): super(MaskScatterWidget, self).__init__(parent=parent, backend=backend, - plugins=plugins, - newplot=newplot, control=control, position=position, grid=grid, - logx=logx, - logy=logy, - togglePoints=togglePoints, - normal=normal, - aspect=aspect, - colormap=colormap, - imageIcons=imageIcons, - polygon=polygon, - **kw) - self._buildAdditionalSelectionMenuDict() + logScale=logScale, + curveStyle=curveStyle, + resetzoom=resetzoom, + aspectRatio=aspectRatio, + colormap=False, + mask=False, + yInverted=False, + roi=False, + copy=True, + print_=False) + if parent is None: + self.setWindowTitle("MaskScatterWidget") + self.setActiveCurveHandling(False) + + # No context menu by default, execute zoomBack on right click + plotArea = self.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) + + self.colormapIcon = qt.QIcon(qt.QPixmap(IconDict["colormap"])) + self.colormapToolButton = qt.QToolButton(self.toolBar()) + self.colormapToolButton.setIcon(self.colormapIcon) + self.colormapToolButton.setToolTip('Change Colormap') + self.colormapToolButton.clicked.connect(self._colormapIconSignal) + self.colormapAction = self.toolBar().insertWidget(self.getSaveAction(), + self.colormapToolButton) + + self.maskToolBar = None + if polygon or imageIcons: + self.maskToolBar = MaskToolBar(parent=self, + plot=self, + imageIcons=imageIcons, + polygon=polygon) + self.addToolBar(self.maskToolBar) + self._selectionCurve = None self._selectionMask = None - self._selectionColors = numpy.zeros((len(self.colorList), 4), numpy.uint8) self._alphaLevel = None - for i in range(len(self.colorList)): - self._selectionColors[i, 0] = eval("0x" + self.colorList[i][-2:]) - self._selectionColors[i, 1] = eval("0x" + self.colorList[i][3:-2]) - self._selectionColors[i, 2] = eval("0x" + self.colorList[i][1:3]) - self._selectionColors[i, 3] = 0xff + self._xScale = None + self._yScale = None + self._maxNRois = maxNRois self._nRoi = 1 self._zoomMode = True self._eraseMode = False self._brushMode = False self._brushWidth = 5 - self._brushMenu = None self._bins = bins self._densityPlotWidget = None self._pixmap = None + self._imageData = None + self.colormapDialog = None + self.colormap = None self.setPlotViewMode("scatter", bins=bins) - self.setDrawModeEnabled(False) + + def _colormapIconSignal(self): + image = self.getActiveImage() + if image is None: + return + + if hasattr(image, "getColormap"): + if self.colormapDialog is None: + self._initColormapDialog(image.getData(), + image.getColormap()._toDict()) + self.colormapDialog.show() + else: + # RGBA image + _logger.info("No colormap to be handled") + return def setPlotViewMode(self, mode="scatter", bins=None): if mode.lower() != "density": @@ -110,41 +149,24 @@ class MaskScatterWidget(PlotWindow.PlotWindow): def _activateScatterPlotView(self): self._plotViewMode = "scatter" - for key in ["colormap", "brushSelection", "brush"]: - self.setToolBarActionVisible(key, False) - if hasattr(self, "eraseSelectionToolButton"): - self.eraseSelectionToolButton.setToolTip("Set erase mode if checked") - self.eraseSelectionToolButton.setCheckable(True) - if self._eraseMode: - self.eraseSelectionToolButton.setChecked(True) - else: - self.eraseSelectionToolButton.setChecked(False) - if hasattr(self, "polygonSelectionToolButton"): - self.polygonSelectionToolButton.setCheckable(True) - if hasattr(self, "rectSelectionToolButton"): - self.rectSelectionToolButton.setCheckable(True) - if hasattr(self, "brushSelectionToolButton"): - if self.brushSelectionToolButton.isChecked(): - self.brushSelectionToolButton.setChecked(False) - self._brushMode = False - self.setZoomModeEnabled(True) + self.colormapAction.setVisible(False) + self._brushMode = False + self.setInteractiveMode("select") + + if hasattr(self, "maskToolBar"): + self.maskToolBar.activateScatterPlotView() + self.clearImages() self._updatePlot() def _activateDensityPlotView(self, bins=None): self._plotViewMode = "density" - for key in ["colormap", "brushSelection", "brush", "rectangle"]: - self.setToolBarActionVisible(key, True) - if hasattr(self, "eraseSelectionToolButton"): - self.eraseSelectionToolButton.setCheckable(True) - if hasattr(self, "brushSelectionToolButton"): - self.brushSelectionToolButton.setCheckable(True) - if hasattr(self, "polygonSelectionToolButton"): - self.polygonSelectionToolButton.setCheckable(True) - if hasattr(self, "rectSelectionToolButton"): - self.rectSelectionToolButton.setCheckable(True) - - if DEBUG: + self.colormapAction.setVisible(True) + + if hasattr(self, "maskToolBar"): + self.maskToolBar.activateDensityPlotView() + + if _logger.getEffectiveLevel() == logging.DEBUG: if self._densityPlotWidget is None: self._densityPlotWidget = MaskImageWidget.MaskImageWidget( imageicons=True, @@ -166,7 +188,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): curve = self.getCurve(self._selectionCurve) if curve is None: return - x, y, legend, info = curve[0:4] + x, y, = curve[0:2] if bins is not None: if type(bins) == type(1): bins = (bins, bins) @@ -175,7 +197,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): else: bins = bins[0:2] elif self._bins is None: - bins = [int(x.size/ 10), int(y.size/10)] + bins = [int(x.size / 10), int(y.size/10)] if bins[0] > 100: bins[0] = 100 elif bins[0] < 2: @@ -198,15 +220,14 @@ class MaskScatterWidget(PlotWindow.PlotWindow): #print("shape", image[0].shape, "image max min ", image[0].max(), image[0].min()) #print("deltaxmin and max", (self._binsX[1:] - self._binsX[:-1]).min(), # (self._binsX[1:] - self._binsX[:-1]).max()) - deltaX = (self._binsX[1:]- self._binsX[:-1]).mean() - deltaY = (self._binsY[1:]- self._binsY[:-1]).mean() + deltaX = (self._binsX[1:] - self._binsX[:-1]).mean() + deltaY = (self._binsY[1:] - self._binsY[:-1]).mean() self._xScale = (x0, deltaX) self._yScale = (y0, deltaY) return image[0] def _updateDensityPlot(self, bins=None): - if DEBUG: - print("_updateDensityPlot called") + _logger.debug("_updateDensityPlot called") if self._densityPlotWidget is None: return curve = self.getCurve(self._selectionCurve) @@ -234,8 +255,8 @@ class MaskScatterWidget(PlotWindow.PlotWindow): bins = self._bins x0 = x.min() y0 = y.min() - deltaX = (x.max() - x0)/float(bins[0] - 1) - deltaY = (y.max() - y0)/float(bins[1] - 1) + deltaX = (x.max() - x0) / float(bins[0] - 1) + deltaY = (y.max() - y0) / float(bins[1] - 1) self.xScale = (x0, deltaX) self.yScale = (y0, deltaY) binsX = numpy.arange(bins[0]) * deltaX @@ -244,27 +265,27 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._binsX = image[2] self._binsY = image[1] self._bins = bins - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: # this does not work properly # update mask levels if self._selectionMask is not None: weights = self._selectionMask[:] weights.shape = x.shape if self._maxNRois > 1: - print("BAD PATH") + _logger.debug("BAD PATH") # this does not work properly yet weightsSum = weights.sum(dtype=numpy.float64) volume = (binsY[1] - binsY[0]) * (binsX[1] - binsX[0]) - mask = numpy.round(numpy.histogram2d(y, x, - bins=(binsY, binsX), - weights=weights, - normed=True)[0] * weightsSum * volume).astype(numpy.uint8) + mask = numpy.round(numpy.histogram2d(y, x, + bins=(binsY, binsX), + weights=weights, + normed=True)[0] * weightsSum * volume).astype(numpy.uint8) else: #print("GOOD PATH") - mask = numpy.histogram2d(y, x, - bins=(binsY, binsX), - weights=weights, - normed=False)[0] + mask = numpy.histogram2d(y, x, + bins=(binsY, binsX), + weights=weights, + normed=False)[0] mask[mask > 0] = 1 #print(mask.min(), mask.max()) self._densityPlotWidget.setSelectionMask(mask, plot=False) @@ -275,7 +296,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): xScale=self.xScale, yScale=self.yScale) - # do not ovelay plot (yet) + # do not overlay plot (yet) pixmap = self._densityPlotWidget.getPixmap() * 1 #pixmap[:, :, 3] = 128 #self.addImage(pixmap, @@ -285,10 +306,84 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._imageData = image[0] #raise NotImplemented("Density plot view not implemented yet") + def _initColormapDialog(self, imageData, colormap=None): + """Set-up the colormap dialog default values. + + :param numpy.ndarray imageData: data used to init dialog. + :param dict colormap: Description of the colormap as a dict. + See :class:`PlotBackend` for details. + If None, use default values. + """ + goodData = imageData[numpy.isfinite(imageData)] + if goodData.size > 0: + maxData = goodData.max() + minData = goodData.min() + else: + qt.QMessageBox.critical(self, "No Data", + "Image data does not contain any real value") + return + + self.colormapDialog = ColormapDialog.ColormapDialog(self) + + if colormap is None: + colormapIndex = self.DEFAULT_COLORMAP_INDEX + if colormapIndex == 6: + colormapIndex = 1 + self.colormapDialog.setColormap(colormapIndex) + self.colormapDialog.setDataMinMax(minData, maxData) + self.colormapDialog.setAutoscale(1) + self.colormapDialog.setColormap(self.colormapDialog.colormapIndex) + # linear or logarithmic + self.colormapDialog.setColormapType(self.DEFAULT_COLORMAP_LOG_FLAG, + update=False) + else: + # Set-up colormap dialog from provided colormap dict + cmapList = ColormapDialog.colormapDictToList(colormap) + index, autoscale, vMin, vMax, dataMin, dataMax, cmapType = cmapList + self.colormapDialog.setColormap(index) + self.colormapDialog.setAutoscale(autoscale) + self.colormapDialog.setMinValue(vMin) + self.colormapDialog.setMaxValue(vMax) + self.colormapDialog.setDataMinMax(minData, maxData) + self.colormapDialog.setColormapType(cmapType, update=False) + + self.colormap = self.colormapDialog.getColormap() # Is it used? + self.colormapDialog.setWindowTitle("Colormap Dialog") + self.colormapDialog.sigColormapChanged.connect( + self.updateActiveImageColormap) + self.colormapDialog._update() + + def updateActiveImageColormap(self, colormap): + if len(colormap) == 1: + colormap = colormap[0] + # TODO: Once everything is ready to work with dict instead of + # list, we can remove this translation + plotBackendColormap = ColormapDialog.colormapListToDict(colormap) + self.setDefaultColormap(plotBackendColormap) + + image = self.getActiveImage() + if image is None: + if self.colormapDialog is not None: + self.colormapDialog.hide() + return + + if not hasattr(image, "getColormap"): + if self.colormapDialog is not None: + self.colormapDialog.hide() + return + pixmap = MaskImageTools.getPixmapFromData(image.getData(), colormap) + self.addImage(image.getData(), legend=image.getLegend(), + info=image.getInfo(), + pixmap=pixmap) + def setSelectionCurveData(self, x, y, legend=None, info=None, - replot=True, replace=True, linestyle=" ", color=None, - symbol=None, selectable=None, **kw): - self.enableActiveCurveHandling(False) + replace=True, linestyle=" ", resetzoom=True, + color=None, symbol=None, selectable=None, + **kw): + if "replot" in kw: + _logger.warning("MaskScatterWidget.setSelectionCurveData: deprecated replot parameter") + resetzoom = kw["replot"] and resetzoom + self.setActiveCurveHandling(False) if legend is None: legend = "MaskScatterWidget" if symbol is None: @@ -309,9 +404,9 @@ class MaskScatterWidget(PlotWindow.PlotWindow): # the basic curve is drawn self.addCurve(x=x, y=y, legend=legend, info=info, - replace=replace, replot=False, linestyle=linestyle, - color=color, symbol=symbol, selectable=selectable,z=0, - **kw) + replace=replace, resetzoom=False, linestyle=linestyle, + color=color, symbol=symbol, selectable=selectable, + z=0, **kw) self._selectionCurve = legend # if view mode, draw the image @@ -322,14 +417,18 @@ class MaskScatterWidget(PlotWindow.PlotWindow): if self.colormapDialog is None: self._initColormapDialog(imageData) cmap = self.colormapDialog.getColormap() - pixmap=MaskImageTools.getPixmapFromData(imageData, - colormap=cmap) + pixmap = MaskImageTools.getPixmapFromData(imageData, + colormap=cmap) + origin, scale = (0., 0.), (1., 1.) + if self._xScale is not None and self._yScale is not None: + origin = self._xScale[0], self._yScale[0] + scale = self._xScale[1], self._yScale[1] + self.addImage(imageData, legend=legend + "density", - xScale=self._xScale, - yScale=self._yScale, + origin=origin, scale=scale, z=0, pixmap=pixmap, - replot=False) + resetzoom=False) self._imageData = imageData self._pixmap = pixmap @@ -339,26 +438,26 @@ class MaskScatterWidget(PlotWindow.PlotWindow): if self._selectionMask.max(): hasMaskedData = True - if hasMaskedData or (replace==False): - self._updatePlot(replot=False) + if hasMaskedData or not replace: + self._updatePlot(resetzoom=False) - # update the plot if it was requested - if replot: - self.replot() + # update the limits if it was requested + if resetzoom: + self.resetZoom() if 0 :#or self._plotViewMode == "density": # get the binned data imageData = self.getDensityData() # get the associated pixmap - pixmap=MaskImageTools.getPixmapFromData(imageData) + pixmap = MaskImageTools.getPixmapFromData(imageData) if 0: self.addImage(imageData, legend=legend + "density", - xScale=self._xScale, - yScale=self._yScale, - z=0, - pixmap=pixmap, - replot=True) - if DEBUG: + xScale=self._xScale, + yScale=self._yScale, + z=0, + pixmap=pixmap, + resetzoom=True) + if _logger.getEffectiveLevel() == logging.DEBUG: if self._densityPlotWidget is None: self._densityPlotWidget = MaskImageWidget.MaskImageWidget( imageicons=True, @@ -367,8 +466,8 @@ class MaskScatterWidget(PlotWindow.PlotWindow): aspect=True, polygon=True) self._updateDensityPlot() - print("CLOSE = ", numpy.allclose(imageData, self._imageData)) - print("CLOSE PIXMAP = ", numpy.allclose(pixmap, self._pixmap)) + _logger.debug("CLOSE = %s", numpy.allclose(imageData, self._imageData)) + _logger.debug("CLOSE PIXMAP = %s", numpy.allclose(pixmap, self._pixmap)) self._imageData = imageData self._pixmap = pixmap #self._updatePlot() @@ -403,14 +502,14 @@ class MaskScatterWidget(PlotWindow.PlotWindow): def getSelectionMask(self): if self._selectionMask is None: if self._selectionCurve is not None: - x, y, legend, info = self.getCurve(self._selectionCurve) + x, y = self.getCurve(self._selectionCurve)[0:2] self._selectionMask = numpy.zeros(x.shape, numpy.uint8) return self._selectionMask - def _updatePlot(self, replot=True, replace=True): + def _updatePlot(self, resetzoom=False, replace=True): if self._selectionCurve is None: return - x0, y0, legend, info = self.getCurve(self._selectionCurve) + x0, y0, legend, info = self.getCurve(self._selectionCurve)[0:4] # make sure we work with views x = x0[:] y = y0[:] @@ -423,16 +522,16 @@ class MaskScatterWidget(PlotWindow.PlotWindow): tmpMask = self._selectionMask[:] tmpMask.shape = -1 for i in range(0, self._maxNRois + 1): - colors[tmpMask == i, :] = self._selectionColors[i] + colors[tmpMask == i, :] = self.maskToolBar._selectionColors[i] self.setSelectionCurveData(x, y, legend=legend, info=info, #color=colors, color="k", linestyle=" ", - replot=replot, replace=replace) + resetzoom=resetzoom, replace=replace) else: if self._selectionMask is None: for i in range(1, self._maxNRois + 1): - self.removeCurve(legend=legend + " %02d" % i, replot=False) + self.removeCurve(legend=legend + " %02d" % i) else: tmpMask = self._selectionMask[:] tmpMask.shape = -1 @@ -446,77 +545,38 @@ class MaskScatterWidget(PlotWindow.PlotWindow): xMask = x[tmpMask == i] yMask = y[tmpMask == i] if xMask.size < 1: - self.removeCurve(legend=legend + " %02d" % i, - replot=False) + self.removeCurve(legend=legend + " %02d" % i) continue - color = self._selectionColors[i].copy() + color = self.maskToolBar._selectionColors[i].copy() if useAlpha: if len(color) == 4: if type(color[3]) in [numpy.uint8, numpy.int]: color[3] = self._alphaLevel # a copy of the input info is needed in order not # to set the main curve to that color + self.addCurve(xMask, yMask, legend=legend + " %02d" % i, - info=info.copy(), color=color, linestyle=" ", + info=info.copy(), color=color, + ylabel=legend + " %02d" % i, + linestyle=" ", symbol="o", selectable=False, z=1, - replot=False, replace=False) - if replot: - self.replot() - #self.resetZoom() + resetzoom=False, replace=False) + if resetzoom: + self.resetZoom() def setActiveRoiNumber(self, intValue): if (intValue < 0) or (intValue > self._maxNRois): raise ValueError("Value %d outside the interval [0, %d]" % (intValue, self._maxNRois)) self._nRoi = intValue - - def _eraseSelectionIconSignal(self): - if self.eraseSelectionToolButton.isChecked(): - self._eraseMode = True - else: - self._eraseMode = False - - def _polygonIconSignal(self): - if self.polygonSelectionToolButton.isChecked(): - self.setPolygonSelectionMode() - else: - self.setZoomModeEnabled(True) - - def _rectSelectionIconSignal(self): - if DEBUG: - print("_rectSelectionIconSignal") - if self.rectSelectionToolButton.isChecked(): - self.setRectangularSelectionMode() - else: - self.setZoomModeEnabled(True) - - def setZoomModeEnabled(self, flag, color=None): - if color is None: - if hasattr(self, "colormapDialog"): - if self.colormapDialog is None: - color = "#00FFFF" - else: - cmap = self.colormapDialog.getColormap() - if cmap[0] < 2: - color = "#00FFFF" - else: - color = "black" - super(MaskScatterWidget, self).setZoomModeEnabled(flag, color=color) - if flag: - if hasattr(self,"polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(False) - if hasattr(self,"brushSelectionToolButton"): - self.brushSelectionToolButton.setChecked(False) - def _handlePolygonMask(self, points): - if DEBUG: - print("_handlePolygonMask called") + _logger.debug("_handlePolygonMask called") if self._eraseMode: value = 0 else: value = self._nRoi - x, y, legend, info = self.getCurve(self._selectionCurve) + x, y = self.getCurve(self._selectionCurve)[0:2] x.shape = -1 y.shape = -1 currentMask = self.getSelectionMask() @@ -533,12 +593,21 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self.setSelectionMask(currentMask, plot=True) self._emitMaskChangedSignal() + def setMouseText(self, text=""): + try: + if text: + qt.QToolTip.showText(self.cursor().pos(), + text, self, qt.QRect()) + else: + qt.QToolTip.hideText() + except: + _logger.warning("Error trying to show mouse text <%s>" % text) + def graphCallback(self, ddict): - if DEBUG: - print("MaskScatterWidget graphCallback", ddict) + _logger.debug("MaskScatterWidget graphCallback %s", ddict) if ddict["event"] == "drawingFinished": if ddict["parameters"]["shape"].lower() == "rectangle": - points = numpy.zeros((5,2), dtype=ddict["points"].dtype) + points = numpy.zeros((5, 2), dtype=ddict["points"].dtype) points[0] = ddict["points"][0] points[1, 0] = ddict["points"][0, 0] points[1, 1] = ddict["points"][1, 1] @@ -553,7 +622,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): if (self._plotViewMode == "density") and \ (self._imageData is not None): shape = self._imageData.shape - row, column = MaskImageTools.convertToRowAndColumn( \ + row, column = MaskImageTools.convertToRowAndColumn( ddict['x'], ddict['y'], shape, @@ -601,7 +670,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): #self.setMouseText("%g, %g, %g" % (row, column, self.__imageData[rowMin, columnMin])) #To show mouse coordinates: #self.setMouseText("%g, %g, %g" % (ddict['x'], ddict['y'], self.__imageData[rowMin, columnMin])) - if self._xScale is not None: + if self._xScale is not None and self._yScale is not None: x = self._xScale[0] + column * self._xScale[1] y = self._yScale[0] + row * self._yScale[1] else: @@ -610,7 +679,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self.setMouseText("%g, %g, %g" % (x, y, self._imageData[row, column])) if self._brushMode: - if self.isZoomModeEnabled(): + if self.getInteractiveMode()['mode'] == 'zoom': return if ddict['button'] != "left": return @@ -632,103 +701,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): # the base implementation handles ROIs, mouse position and activeCurve super(MaskScatterWidget, self).graphCallback(ddict) - def _brushIconSignal(self): - if DEBUG: - print("brushIconSignal") - if self._brushMenu is None: - self._brushMenu = qt.QMenu() - self._brushMenu.addAction(QString(" 1 Image Pixel Width"), - self._setBrush1) - self._brushMenu.addAction(QString(" 2 Image Pixel Width"), - self._setBrush2) - self._brushMenu.addAction(QString(" 3 Image Pixel Width"), - self._setBrush3) - self._brushMenu.addAction(QString(" 5 Image Pixel Width"), - self._setBrush4) - self._brushMenu.addAction(QString("10 Image Pixel Width"), - self._setBrush5) - self._brushMenu.addAction(QString("20 Image Pixel Width"), - self._setBrush6) - self._brushMenu.exec_(self.cursor().pos()) - - def _brushSelectionIconSignal(self): - if DEBUG: - print("_setBrushSelectionMode") - if hasattr(self, "polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(False) - self.setDrawModeEnabled(False) - if self.brushSelectionToolButton.isChecked(): - self._brushMode = True - self.setZoomModeEnabled(False) - else: - self._brushMode = False - self.setZoomModeEnabled(True) - - def _setBrush1(self): - self._brushWidth = 1 - - def _setBrush2(self): - self._brushWidth = 2 - - def _setBrush3(self): - self._brushWidth = 3 - - def _setBrush4(self): - self._brushWidth = 5 - - def _setBrush5(self): - self._brushWidth = 10 - - def _setBrush6(self): - self._brushWidth = 20 - - def setRectangularSelectionMode(self): - """ - Resets zoom mode and enters selection mode with the current active ROI index - """ - self._zoomMode = False - self._brushMode = False - color = self._selectionColors[self._nRoi] - # make sure the selection is made with a non transparent color - if len(color) == 4: - if type(color[-1]) in [numpy.uint8, numpy.int8]: - color = color.copy() - color[-1] = 255 - self.setDrawModeEnabled(True, - shape="rectangle", - label="mask", - color=color) - self.setZoomModeEnabled(False) - if hasattr(self, "brushSelectionToolButton"): - self.brushSelectionToolButton.setChecked(False) - if hasattr(self,"polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(False) - if hasattr(self,"rectSelectionToolButton"): - self.rectSelectionToolButton.setChecked(True) - - def setPolygonSelectionMode(self): - """ - Resets zoom mode and enters selection mode with the current active ROI index - """ - self._zoomMode = False - self._brushMode = False - color = self._selectionColors[self._nRoi] - # make sure the selection is made with a non transparent color - if len(color) == 4: - if type(color[-1]) in [numpy.uint8, numpy.int8]: - color = color.copy() - color[-1] = 255 - self.setDrawModeEnabled(True, shape="polygon", label="mask", - color=color) - self.setZoomModeEnabled(False) - if hasattr(self, "brushSelectionToolButton"): - self.brushSelectionToolButton.setChecked(False) - if hasattr(self,"rectSelectionToolButton"): - self.rectSelectionToolButton.setChecked(False) - if hasattr(self,"polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(True) - - def setEraseSelectionMode(self, erase=True): + def setEraseSelectionMode(self, erase=True): # TODO: unused? if erase: self._eraseMode = True else: @@ -751,54 +724,11 @@ class MaskScatterWidget(PlotWindow.PlotWindow): def emitMaskScatterWidgetSignal(self, ddict): self.sigMaskScatterWidgetSignal.emit(ddict) - def _imageIconSignal(self): - self.__resetSelection() - - def _buildAdditionalSelectionMenuDict(self): - self._additionalSelectionMenu = {} - #scatter view menu - menu = qt.QMenu() - menu.addAction(QString("Density plot view"), self.__setDensityPlotView) - menu.addAction(QString("Reset Selection"), self.__resetSelection) - menu.addAction(QString("Invert Selection"), self._invertSelection) - self._additionalSelectionMenu["scatter"] = menu - - # density view menu - menu = qt.QMenu() - menu.addAction(QString("Scatter plot view"), self.__setScatterPlotView) - menu.addAction(QString("Reset Selection"), self.__resetSelection) - menu.addAction(QString("Invert Selection"), self._invertSelection) - menu.addAction(QString("I >= Colormap Max"), self._selectMax) - menu.addAction(QString("Colormap Min < I < Colormap Max"), - self._selectMiddle) - menu.addAction(QString("I <= Colormap Min"), self._selectMin) - menu.addAction(QString("Increase mask alpha"), self._increaseMaskAlpha) - menu.addAction(QString("Decrease mask alpha"), self._decreaseMaskAlpha) - self._additionalSelectionMenu["density"] = menu - - def __setScatterPlotView(self): - self.setPlotViewMode(mode="scatter") - - def __setDensityPlotView(self): - self.setPlotViewMode(mode="density") - - def _additionalIconSignal(self): - if self._plotViewMode == "density": # and imageData is not none ... - self._additionalSelectionMenu["density"].exec_(self.cursor().pos()) - else: - self._additionalSelectionMenu["scatter"].exec_(self.cursor().pos()) - - def __resetSelection(self): - # Needed because receiving directly in _resetSelection it was passing - # False as argument - self._resetSelection(True) - def _resetSelection(self, owncall=True): - if DEBUG: - print("_resetSelection") + _logger.debug("_resetSelection") if self._selectionMask is None: - print("Selection mask is None, doing nothing") + _logger.info("Selection mask is None, doing nothing") return else: self._selectionMask[:] = 0 @@ -870,19 +800,18 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._emitMaskChangedSignal() def _setSelectionMaskFromDensityMask(self, densityPlotMask, update=None): - if DEBUG: - print("_setSelectionMaskFromDensityMask called") + _logger.debug("_setSelectionMaskFromDensityMask called") curve = self.getCurve(self._selectionCurve) if curve is None: return - x, y, legend, info = curve[0:4] + x, y = curve[0:2] bins = self._bins x0 = x.min() y0 = y.min() deltaX = (x.max() - x0)/float(bins[0]) deltaY = (y.max() - y0)/float(bins[1]) columns = numpy.digitize(x, self._binsX, right=True) - columns[columns>=densityPlotMask.shape[1]] = \ + columns[columns >= densityPlotMask.shape[1]] = \ densityPlotMask.shape[1] - 1 rows = numpy.digitize(y, self._binsY, right=True) rows[rows>=densityPlotMask.shape[0]] = densityPlotMask.shape[0] - 1 @@ -907,8 +836,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self.setSelectionMask(view, plot=True) def _densityPlotSlot(self, ddict): - if DEBUG: - print("_densityPlotSlot called") + _logger.debug("_densityPlotSlot called") if ddict["event"] == "resetSelection": self.__resetSelection() return @@ -918,13 +846,13 @@ class MaskScatterWidget(PlotWindow.PlotWindow): curve = self.getCurve(self._selectionCurve) if curve is None: return - x, y, legend, info = curve[0:4] + x, y = curve[0:2] bins = self._bins x0 = x.min() y0 = y.min() deltaX = (x.max() - x0)/float(bins[0]) deltaY = (y.max() - y0)/float(bins[1]) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: if self._selectionMask is None: view = numpy.zeros(x.size, dtype=numpy.uint8) else: @@ -960,7 +888,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): view2[:] = values[:] if self._selectionMask is not None: view2.shape = self._selectionMask.shape - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: if not numpy.allclose(view, view2): a = view[:] b = view2[:] @@ -969,15 +897,15 @@ class MaskScatterWidget(PlotWindow.PlotWindow): c = 0 for i in range(a.size): if a[i] != b[i]: - print(i, "a = ", a[i], "b = ", b[i], "(x, y) = ", x[i], y[i]) + _logger.debug("%d a = %s, b = %s, (x, y) = (%s, %s)", + i, a[i], b[i], x[i], y[i]) c += 1 if c > 10: break else: - print("OK!!!") + _logger.debug("OK!!!") self.setSelectionMask(view2) - def _initializeAlpha(self): self._alphaLevel = 128 @@ -998,6 +926,15 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._alphaLevel = 2 self._updatePlot() + def setPolygonSelectionMode(self): + """ + Resets zoom mode and enters selection mode with the current active ROI index + """ + self.maskToolBar.setPolygonSelectionMode() + + def _zoomBack(self, pos): + self.getLimitsHistory().pop() + if __name__ == "__main__": backend = "matplotlib" #backend = "opengl" @@ -1006,7 +943,8 @@ if __name__ == "__main__": print("Received: ", ddict) x = numpy.arange(100.) y = x * 1 - w = MaskScatterWidget(maxNRois=10, bins=(100,100), backend=backend) + w = MaskScatterWidget(maxNRois=10, bins=(100, 100), backend=backend, + control=True) w.setSelectionCurveData(x, y, color="k", selectable=False) import numpy.random w.setSelectionMask(numpy.random.permutation(100) % 10) diff --git a/PyMca5/PyMcaGui/plotting/MaskToolBar.py b/PyMca5/PyMcaGui/plotting/MaskToolBar.py new file mode 100644 index 0000000..b53e81a --- /dev/null +++ b/PyMca5/PyMcaGui/plotting/MaskToolBar.py @@ -0,0 +1,331 @@ +#/*########################################################################## +# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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 module implements a plot toolbar with buttons to draw and erase masks. +""" + +__author__ = "P. Knobel" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + +import numpy + +from PyMca5.PyMcaGui import PyMcaQt as qt +from .PyMca_Icons import IconDict + +from silx.gui import colors + +if hasattr(qt, "QString"): + QString = qt.QString +else: + QString = qt.safe_str + + +_COLORDICT = colors.COLORDICT +# these are color RGBA strings '#0000ff' +_COLORLIST = [_COLORDICT['black'], + _COLORDICT['blue'], + _COLORDICT['red'], + _COLORDICT['green'], + _COLORDICT['pink'], + _COLORDICT['yellow'], + _COLORDICT['brown'], + _COLORDICT['cyan'], + _COLORDICT['magenta'], + _COLORDICT['orange'], + _COLORDICT['violet'], + #_COLORDICT['bluegreen'], + _COLORDICT['grey'], + _COLORDICT['darkBlue'], + _COLORDICT['darkRed'], + _COLORDICT['darkGreen'], + _COLORDICT['darkCyan'], + _COLORDICT['darkMagenta'], + _COLORDICT['darkYellow'], + _COLORDICT['darkBrown']] + + +class MaskToolBar(qt.QToolBar): + """Toolbar with buttons controlling the mask drawing and erasing + interactions on a :class:`MaskScatterWidget`, to select or deselect + data.""" + # sigIconSignal = qt.pyqtSignal(object) + colorList = _COLORLIST + + def __init__(self, parent=None, plot=None, title="Mask tools", + imageIcons=True, polygon=True): + super(MaskToolBar, self).__init__(title, parent) + assert plot is not None + assert imageIcons or polygon,\ + "It makes no sense to build an empty mask toolbar" + self.plot = plot + self._brushMenu = None + + self.polygonIcon = qt.QIcon(qt.QPixmap(IconDict["polygon"])) + self.imageIcon = qt.QIcon(qt.QPixmap(IconDict["image"])) + self.eraseSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["eraseselect"])) + self.rectSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["boxselect"])) + self.brushSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["brushselect"])) + self.brushIcon = qt.QIcon(qt.QPixmap(IconDict["brush"])) + self.additionalIcon = qt.QIcon(qt.QPixmap(IconDict["additionalselect"])) + + self.polygonSelectionToolButton = qt.QToolButton(self) + self.imageToolButton = qt.QToolButton(self) + self.eraseSelectionToolButton = qt.QToolButton(self) + self.rectSelectionToolButton = qt.QToolButton(self) + self.brushSelectionToolButton = qt.QToolButton(self) + self.brushToolButton = qt.QToolButton(self) + self.additionalSelectionToolButton = qt.QToolButton(self) + + self.polygonSelectionToolButton.setIcon(self.polygonIcon) + self.imageToolButton.setIcon(self.imageIcon) + self.eraseSelectionToolButton.setIcon(self.eraseSelectionIcon) + self.rectSelectionToolButton.setIcon(self.rectSelectionIcon) + self.brushSelectionToolButton.setIcon(self.brushSelectionIcon) + self.brushToolButton.setIcon(self.brushIcon) + self.additionalSelectionToolButton.setIcon(self.additionalIcon) + + self.polygonSelectionToolButton.setToolTip('Polygon selection\n' + 'Click first point to finish') + self.imageToolButton.setToolTip('Reset') + self.eraseSelectionToolButton.setToolTip('Erase Selection') + self.rectSelectionToolButton.setToolTip('Rectangular Selection') + self.brushSelectionToolButton.setToolTip('Brush Selection') + self.brushToolButton.setToolTip('Brush Size') + self.additionalSelectionToolButton.setToolTip('Additional Selections Menu') + + self.eraseSelectionToolButton.setCheckable(True) + self.polygonSelectionToolButton.setCheckable(True) + self.rectSelectionToolButton.setCheckable(True) + self.brushSelectionToolButton.setCheckable(True) + + self.imageAction = self.addWidget(self.imageToolButton) + self.eraseSelectionAction = self.addWidget(self.eraseSelectionToolButton) + self.rectSelectionAction = self.addWidget(self.rectSelectionToolButton) + self.brushSelectionAction = self.addWidget(self.brushSelectionToolButton) + self.brushAction = self.addWidget(self.brushToolButton) + self.polygonSelectionAction = self.addWidget(self.polygonSelectionToolButton) + self.additionalSelectionAction = self.addWidget(self.additionalSelectionToolButton) + + self.imageToolButton.clicked.connect(self._imageIconSignal) + self.eraseSelectionToolButton.clicked.connect(self._eraseSelectionIconSignal) + self.rectSelectionToolButton.clicked.connect(self._rectSelectionIconSignal) + self.brushSelectionToolButton.clicked.connect(self._brushSelectionIconSignal) + self.brushToolButton.clicked.connect(self._brushIconSignal) + self.polygonSelectionToolButton.clicked.connect(self._polygonIconSignal) + self.additionalSelectionToolButton.clicked.connect(self._additionalIconSignal) + + if not imageIcons: + self.imageAction.setVisible(False) + self.eraseSelectionAction.setVisible(False) + self.rectSelectionAction.setVisible(False) + self.brushSelectionAction.setVisible(False) + self.brushAction.setVisible(False) + self.polygonSelectionAction.setVisible(False) + self.additionalSelectionAction.setVisible(False) + + if not polygon: + self.polygonSelectionAction.setVisible(False) + + self._buildAdditionalSelectionMenuDict() + + # selection colors as a RBGA (uint8) array + self._selectionColors = numpy.zeros((len(self.colorList), 4), numpy.uint8) + for i in range(len(self.colorList)): + self._selectionColors[i, 0] = eval("0x" + self.colorList[i][-2:]) + self._selectionColors[i, 1] = eval("0x" + self.colorList[i][3:-2]) + self._selectionColors[i, 2] = eval("0x" + self.colorList[i][1:3]) + self._selectionColors[i, 3] = 0xff + + self.plot.sigInteractiveModeChanged.connect(self._interactiveModeChanged) + + def activateScatterPlotView(self): + self.brushSelectionAction.setVisible(False) + self.brushAction.setVisible(False) + self.eraseSelectionAction.setToolTip("Set erase mode if checked") + + self.eraseSelectionToolButton.setChecked(self.plot._eraseMode) + self.brushSelectionToolButton.setChecked(False) + + def activateDensityPlotView(self): + self.brushSelectionAction.setVisible(True) + self.brushAction.setVisible(True) + self.rectSelectionAction.setVisible(True) + + def _imageIconSignal(self, checked=False): + self.plot._resetSelection(owncall=True) + + def _eraseSelectionIconSignal(self, checked=False): + self.plot._eraseMode = checked + + def _getSelectionColor(self): + """Return a selection color as hex "#RRGGBBAA" string""" + rgba_color_array = self._selectionColors[self.plot._nRoi] + # make sure the selection is made with a non transparent color + if len(rgba_color_array) == 4: + rgba_color_array = rgba_color_array.copy() + rgba_color_array[-1] = 255 + + # convert to string + s = "#" + for channel_uint8_value in rgba_color_array: + s += "{:02x}".format(channel_uint8_value) + return s + + def _polygonIconSignal(self, checked=False): + if checked: + self.plot.setInteractiveMode("draw", shape="polygon", + label="mask", + color=self._getSelectionColor()) + self.plot._zoomMode = False + self.plot._brushMode = False + + self.brushSelectionToolButton.setChecked(False) + self.rectSelectionToolButton.setChecked(False) + self.polygonSelectionToolButton.setChecked(True) + else: + self.plot.setInteractiveMode("select") + self._uncheckAllSelectionButtons() + + def setPolygonSelectionMode(self): + """ + Resets zoom mode and enters selection mode with the current active ROI index + """ + self.polygonSelectionToolButton.setChecked(True) + self.polygonSelectionAction.trigger() # calls _polygonIconSignal + + def _rectSelectionIconSignal(self, checked=False): + if checked: + self.plot._zoomMode = False + self.plot._brushMode = False + self.brushSelectionToolButton.setChecked(False) + self.polygonSelectionToolButton.setChecked(False) + self.rectSelectionToolButton.setChecked(True) + + self.plot.setInteractiveMode("draw", + shape="rectangle", + label="mask", + color=self._getSelectionColor()) + else: + self.plot.setInteractiveMode("select") + self._uncheckAllSelectionButtons() + + def _brushSelectionIconSignal(self, checked=False): + self.polygonSelectionToolButton.setChecked(False) + self.rectSelectionToolButton.setChecked(False) + if checked: + self.plot._brushMode = True + self.plot.setInteractiveMode('select') + else: + self._brushMode = False + + def _brushIconSignal(self, checked=False): + if self._brushMenu is None: + self._brushMenu = qt.QMenu() + self._brushMenu.addAction(QString(" 1 Image Pixel Width"), + self._setBrush1) + self._brushMenu.addAction(QString(" 2 Image Pixel Width"), + self._setBrush2) + self._brushMenu.addAction(QString(" 3 Image Pixel Width"), + self._setBrush3) + self._brushMenu.addAction(QString(" 5 Image Pixel Width"), + self._setBrush4) + self._brushMenu.addAction(QString("10 Image Pixel Width"), + self._setBrush5) + self._brushMenu.addAction(QString("20 Image Pixel Width"), + self._setBrush6) + self._brushMenu.exec_(self.cursor().pos()) + + def _setBrush1(self): + self.plot._brushWidth = 1 + + def _setBrush2(self): + self.plot._brushWidth = 2 + + def _setBrush3(self): + self.plot._brushWidth = 3 + + def _setBrush4(self): + self.plot._brushWidth = 5 + + def _setBrush5(self): + self.plot._brushWidth = 10 + + def _setBrush6(self): + self.plot._brushWidth = 20 + + def _buildAdditionalSelectionMenuDict(self): + self._additionalSelectionMenu = {} + #scatter view menu + menu = qt.QMenu() + menu.addAction(QString("Density plot view"), self.__setDensityPlotView) + menu.addAction(QString("Reset Selection"), self.__resetSelection) + menu.addAction(QString("Invert Selection"), self.plot._invertSelection) + self._additionalSelectionMenu["scatter"] = menu + + # density view menu + menu = qt.QMenu() + menu.addAction(QString("Scatter plot view"), self.__setScatterPlotView) + menu.addAction(QString("Reset Selection"), self.__resetSelection) + menu.addAction(QString("Invert Selection"), self.plot._invertSelection) + menu.addAction(QString("I >= Colormap Max"), self.plot._selectMax) + menu.addAction(QString("Colormap Min < I < Colormap Max"), + self.plot._selectMiddle) + menu.addAction(QString("I <= Colormap Min"), self.plot._selectMin) + menu.addAction(QString("Increase mask alpha"), self.plot._increaseMaskAlpha) + menu.addAction(QString("Decrease mask alpha"), self.plot._decreaseMaskAlpha) + + self._additionalSelectionMenu["density"] = menu + + def __setScatterPlotView(self): + self.plot.setPlotViewMode(mode="scatter") + + def __setDensityPlotView(self): + self.plot.setPlotViewMode(mode="density") + + def __resetSelection(self): + self.plot._resetSelection(owncall=True) + + def _additionalIconSignal(self, checked=False): + if self.plot._plotViewMode == "density": # and imageData is not none ... + self._additionalSelectionMenu["density"].exec_(self.cursor().pos()) + else: + self._additionalSelectionMenu["scatter"].exec_(self.cursor().pos()) + + def _uncheckAllSelectionButtons(self): + self.brushSelectionToolButton.setChecked(False) + self.polygonSelectionToolButton.setChecked(False) + self.brushSelectionToolButton.setChecked(False) + + def _interactiveModeChanged(self, source): + if self.plot.getInteractiveMode()['mode'] != "draw": + self._uncheckAllSelectionButtons() + + # def emitIconSignal(self, key, event="iconClicked"): + # ddict = {"key": key, + # "event": event} + # self.sigIconSignal.emit(ddict) + + diff --git a/PyMca5/PyMcaGui/plotting/McaROIWidget.py b/PyMca5/PyMcaGui/plotting/McaROIWidget.py index e49948e..5a2c663 100644 --- a/PyMca5/PyMcaGui/plotting/McaROIWidget.py +++ b/PyMca5/PyMcaGui/plotting/McaROIWidget.py @@ -29,6 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, "QString"): @@ -41,7 +42,9 @@ QTVERSION = qt.qVersion() from PyMca5.PyMcaCore import PyMcaDirs from PyMca5.PyMcaIO import ConfigDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + + class McaROIWidget(qt.QWidget): sigMcaROIWidgetSignal = qt.pyqtSignal(object) @@ -109,8 +112,7 @@ class McaROIWidget(qt.QWidget): self.mcaROITable.sigMcaROITableSignal.connect(self._forward) def _add(self): - if DEBUG: - print("McaROIWidget._add") + _logger.debug("McaROIWidget._add") ddict={} ddict['event'] = "AddROI" roilist, roidict = self.mcaROITable.getROIListAndDict() @@ -409,8 +411,7 @@ class McaROITable(qt.QTableWidget): else: if currentroi in self.roidict.keys(): self.selectRow(self.roidict[currentroi]['line']) - if DEBUG: - print("Qt4 ensureCellVisible to be implemented") + _logger.debug("Qt4 ensureCellVisible to be implemented") self.building = False def addROI(self, roi, key=None): @@ -472,8 +473,7 @@ class McaROITable(qt.QTableWidget): ddict['row' ] = row ddict['col' ] = col if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return row = 0 item = self.item(row, 0) @@ -492,8 +492,7 @@ class McaROITable(qt.QTableWidget): self._emitSelectionChangedSignal(row, 0) def _cellChangedSlot(self, row, col): - if DEBUG: - print("_cellChangedSlot(%d, %d)" % (row, col)) + _logger.debug("_cellChangedSlot(%d, %d)", row, col) if self.building: return if col == 0: @@ -513,8 +512,7 @@ class McaROITable(qt.QTableWidget): except: return if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return if QTVERSION < '4.0.0': text = str(self.text(row, 0)) @@ -535,8 +533,7 @@ class McaROITable(qt.QTableWidget): def nameSlot(self, row, col): if col != 0: return if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return item = self.item(row, col) if item is None: @@ -572,8 +569,7 @@ class McaROITable(qt.QTableWidget): col = var[1] if col == 0: if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return row = 0 item = self.item(row, col) diff --git a/PyMca5/PyMcaGui/plotting/PlotWidget.py b/PyMca5/PyMcaGui/plotting/PlotWidget.py index d6282a8..4619feb 100644 --- a/PyMca5/PyMcaGui/plotting/PlotWidget.py +++ b/PyMca5/PyMcaGui/plotting/PlotWidget.py @@ -28,7 +28,9 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys -import os +import logging +import traceback + from PyMca5.PyMcaGraph import Plot SVG = True @@ -82,10 +84,21 @@ else: if not hasattr(QtCore, "Signal"): QtCore.Signal = QtCore.pyqtSignal + +_logger = logging.getLogger(__name__) + DEBUG = 0 if DEBUG: + _logger.setLevel(logging.DEBUG) Plot.DEBUG = DEBUG +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.PlotWidget instead", + __name__) +for line in traceback.format_stack(limit=3): + _logger.warning(line.rstrip()) + + class PlotWidget(QtGui.QMainWindow, Plot.Plot): sigPlotSignal = QtCore.Signal(object) diff --git a/PyMca5/PyMcaGui/plotting/PlotWindow.py b/PyMca5/PyMcaGui/plotting/PlotWindow.py index 8dc46f7..b100f3e 100644 --- a/PyMca5/PyMcaGui/plotting/PlotWindow.py +++ b/PyMca5/PyMcaGui/plotting/PlotWindow.py @@ -1474,7 +1474,10 @@ class PlotWindow(PlotWidget.PlotWidget): os.remove(filename) if filterused[0].upper() == "WIDGET": fformat = filename[-3:].upper() - pixmap = qt.QPixmap.grabWidget(self) + if hasattr(qt.QPixmap,"grabWidget"): + pixmap = qt.QPixmap.grabWidget(self) + else: + pixmap = self.grab() if not pixmap.save(filename, fformat): msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) diff --git a/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py b/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py index 93cc866..6b3d95a 100644 --- a/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py +++ b/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py @@ -34,7 +34,7 @@ if 1: # if not, we miss profile fitting ... from PyMca5.PyMcaGui.pymca.ScanWindow import ScanWindow as Window else: - from .PlotWindow import PlotWindow as Window + from silx.gui.plot import PlotWindow as Window DEBUG = 0 class ProfileScanWidget(Window): @@ -118,7 +118,7 @@ class ProfileScanWidget(Window): elif action == 'REMOVE': self.sigRemoveClicked.emit(ddict) else: - self.replaceAddClicked.emit(ddict) + self.sigReplaceClicked.emit(ddict) def test(): app = qt.QApplication([]) diff --git a/PyMca5/PyMcaGui/plotting/PyMca_Icons.py b/PyMca5/PyMcaGui/plotting/PyMca_Icons.py index 56fdc97..ebb3f37 100644 --- a/PyMca5/PyMcaGui/plotting/PyMca_Icons.py +++ b/PyMca5/PyMcaGui/plotting/PyMca_Icons.py @@ -4177,9 +4177,22 @@ class _PatchedIconDict(MutableMapping): # we also need to remove the key from internal translation table del self._translation_table[key] + IconDict = _PatchedIconDict(IconDict0) +def change_icons(plot): + """Replace some of the silx icons with PyMca icons. + + :param plot: Silx plot window, or ScanWindow, or McaWindow + :return: + """ + from PyMca5.PyMcaGui import PyMcaQt as qt + plot.getRoiAction().setIcon(qt.QIcon(qt.QPixmap(IconDict["roi"]))) + if hasattr(plot, "printPreview"): + plot.printPreview.setIcon(qt.QIcon(qt.QPixmap(IconDict["fileprint"]))) + + def showIcons(): w = qt.QWidget() g = qt.QGridLayout(w) diff --git a/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py b/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py index 1a4608b..3734a52 100644 --- a/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py +++ b/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py @@ -28,7 +28,8 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys -import os +import logging +import traceback from PyMca5.PyMcaGui import PyMcaQt as qt DEBUG = 0 __revision__="$Revision: 1.7 $" @@ -39,6 +40,14 @@ __revision__="$Revision: 1.7 $" QTVERSION = qt.qVersion() +_logger = logging.getLogger(__name__) +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.widgets.PrintPreview instead", + __name__) + +for line in traceback.format_stack(limit=3): + _logger.warning(line.rstrip()) + ################################################################################ ################## PyMcaPrintPreview ################### diff --git a/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py b/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py index bcaf7ae..24d874c 100644 --- a/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py +++ b/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py @@ -30,14 +30,24 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy -from . import PlotWidget +import logging from PyMca5.PyMcaGui import PyMcaQt as qt +from silx.gui.plot import PlotWidget +from silx.gui.plot.PrintPreviewToolButton import SingletonPrintPreviewToolButton from .PyMca_Icons import IconDict -from . import PyMcaPrintPreview from PyMca5.PyMcaCore import PyMcaDirs +from silx.gui import icons as silx_icons + +if sys.version_info[0] == 3: + from io import BytesIO +else: + import cStringIO as _StringIO + BytesIO = _StringIO.StringIO QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + + def convertToRowAndColumn(x, y, shape, xScale=None, yScale=None, safe=True): if xScale is None: @@ -59,6 +69,7 @@ def convertToRowAndColumn(x, y, shape, xScale=None, yScale=None, safe=True): r = int(r) return r, c + class RGBCorrelatorGraph(qt.QWidget): sigProfileSignal = qt.pyqtSignal(object) @@ -71,17 +82,22 @@ class RGBCorrelatorGraph(qt.QWidget): self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self._keepDataAspectRatioFlag = False + self.graph = PlotWidget(parent=self, backend=backend) + self.graph.setGraphXLabel("Column") + self.graph.setGraphYLabel("Row") + self.graph.setYAxisAutoScale(True) + self.graph.setXAxisAutoScale(True) + plotArea = self.graph.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) + self._buildToolBar(selection, colormap, imageicons, standalonesave, standalonezoom=standalonezoom, profileselection=profileselection, aspect=aspect, polygon=polygon) - self.graph = PlotWidget.PlotWidget(self, backend=backend, aspect=aspect) - self.graph.setGraphXLabel("Column") - self.graph.setGraphYLabel("Row") - self.graph.setYAxisAutoScale(True) - self.graph.setXAxisAutoScale(True) + if profileselection: if len(self._pickerSelectionButtons): self.graph.sigPlotSignal.connect(\ @@ -91,9 +107,6 @@ class RGBCorrelatorGraph(qt.QWidget): self.saveDirectory = os.getcwd() self.mainLayout.addWidget(self.graph) - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) - if DEBUG: - print("printPreview id = %d" % id(self.printPreview)) def sizeHint(self): return qt.QSize(1.5 * qt.QWidget.sizeHint(self).width(), @@ -123,6 +136,7 @@ class RGBCorrelatorGraph(qt.QWidget): self.hLineIcon = qt.QIcon(qt.QPixmap(IconDict["horizontal"])) self.vLineIcon = qt.QIcon(qt.QPixmap(IconDict["vertical"])) self.lineIcon = qt.QIcon(qt.QPixmap(IconDict["diagonal"])) + self.copyIcon = silx_icons.getQIcon("edit-copy") self.toolBar = qt.QWidget(self) self.toolBarLayout = qt.QHBoxLayout(self.toolBar) @@ -160,16 +174,16 @@ class RGBCorrelatorGraph(qt.QWidget): #Aspect ratio if aspect: self.aspectButton = self._addToolButton(self.solidCircleIcon, - self._aspectButtonSignal, - 'Keep data aspect ratio', - toggle = False) + self._aspectButtonSignal, + 'Keep data aspect ratio', + toggle=False) self.aspectButton.setChecked(False) #colormap if colormap: tb = self._addToolButton(self.colormapIcon, None, - 'Change Colormap') + 'Change Colormap') self.colormapToolButton = tb #flip @@ -190,6 +204,10 @@ class RGBCorrelatorGraph(qt.QWidget): 'Save') self.saveToolButton = tb + self.copyToolButton = self._addToolButton(self.copyIcon, + self._copyIconSignal, + "Copy graph to clipboard") + #Selection if selection: tb = self._addToolButton(self.selectionIcon, @@ -221,7 +239,6 @@ class RGBCorrelatorGraph(qt.QWidget): 'Brush Selection') self.brushSelectionToolButton = tb - tb = self._addToolButton(self.brushIcon, None, 'Select Brush') @@ -292,7 +309,7 @@ class RGBCorrelatorGraph(qt.QWidget): #self.lineWidthProfileButton = tb #self._pickerSelectionButtons.append(tb) if self._polygonSelection: - print("Polygon selection not implemented yet") + _logger.info("Polygon selection not implemented yet") #hide profile selection buttons if imageicons: for button in self._pickerSelectionButtons: @@ -311,13 +328,13 @@ class RGBCorrelatorGraph(qt.QWidget): self.toolBarLayout.addWidget(qt.HorizontalSpacer(self.toolBar)) # ---print - tb = self._addToolButton(self.printIcon, - self.printGraph, - 'Prints the Graph') + self.printPreview = SingletonPrintPreviewToolButton(parent=self, + plot=self.graph) + self.printPreview.setIcon(self.printIcon) + self.toolBarLayout.addWidget(self.printPreview) def _aspectButtonSignal(self): - if DEBUG: - print("_aspectButtonSignal") + _logger.debug("_aspectButtonSignal") if self._keepDataAspectRatioFlag: self.keepDataAspectRatio(False) else: @@ -332,7 +349,7 @@ class RGBCorrelatorGraph(qt.QWidget): self._keepDataAspectRatioFlag = False self.aspectButton.setIcon(self.solidCircleIcon) self.aspectButton.setToolTip("Keep data aspect ratio") - self.graph.keepDataAspectRatio(self._keepDataAspectRatioFlag) + self.graph.setKeepDataAspectRatio(self._keepDataAspectRatioFlag) def showInfo(self): self.infoWidget.show() @@ -351,7 +368,7 @@ class RGBCorrelatorGraph(qt.QWidget): else: qt.QToolTip.hideText() except: - print("Error trying to show mouse text <%s>" % text) + _logger.warning("Error trying to show mouse text <%s>" % text) def focusOutEvent(self, ev): qt.QToolTip.hideText() @@ -447,8 +464,8 @@ class RGBCorrelatorGraph(qt.QWidget): button.hide() self._pickerSelectionWidthLabel.hide() self._pickerSelectionWidthValue.hide() - #self.graph.setPickerSelectionModeOff() - self.graph.setDrawModeEnabled(False) + if self.graph.getInteractiveMode()['mode'] == 'draw': + self.graph.setInteractiveMode('select') def showProfileSelectionIcons(self): if not len(self._pickerSelectionButtons): @@ -471,8 +488,7 @@ class RGBCorrelatorGraph(qt.QWidget): def _setPickerSelectionMode(self, mode=None): if mode is None: - self.graph.setDrawModeEnabled(False) - self.graph.setZoomModeEnabled(True) + self.graph.setInteractiveMode('zoom') else: if mode == "HORIZONTAL": shape = "hline" @@ -480,8 +496,7 @@ class RGBCorrelatorGraph(qt.QWidget): shape = "vline" else: shape = "line" - self.graph.setZoomModeEnabled(False) - self.graph.setDrawModeEnabled(True, + self.graph.setInteractiveMode('draw', shape=shape, label=mode) ddict = {} @@ -492,15 +507,14 @@ class RGBCorrelatorGraph(qt.QWidget): self.sigProfileSignal.emit(ddict) def _graphPolygonSignalReceived(self, ddict): - if DEBUG: - print("PolygonSignal Received") - for key in ddict.keys(): - print(key, ddict[key]) + _logger.debug("PolygonSignal Received") + for key in ddict.keys(): + _logger.debug("%s: %s", key, ddict[key]) if ddict['event'] not in ['drawingProgress', 'drawingFinished']: return label = ddict['parameters']['label'] - if label not in ['HORIZONTAL', 'VERTICAL', 'LINE']: + if label not in ['HORIZONTAL', 'VERTICAL', 'LINE']: return ddict['mode'] = label ddict['pixelwidth'] = self._pickerSelectionWidthValue.value() @@ -531,32 +545,33 @@ class RGBCorrelatorGraph(qt.QWidget): self._zoomReset() def _zoomReset(self, replot=None): - if DEBUG: - print("_zoomReset") - if replot is None: - replot = True + _logger.debug("_zoomReset") if self.graph is not None: self.graph.resetZoom() - if replot: - self.graph.replot() def _yAutoScaleToggle(self): if self.graph is not None: - if self.graph.isYAxisAutoScale(): - self.graph.setYAxisAutoScale(False) - self.yAutoScaleToolButton.setDown(False) - else: - self.graph.setYAxisAutoScale(True) - self.yAutoScaleToolButton.setDown(True) + self.yAutoScaleToolButton.setDown( + not self.graph.isYAxisAutoScale()) + self.graph.setYAxisAutoScale( + not self.graph.isYAxisAutoScale()) def _xAutoScaleToggle(self): if self.graph is not None: - if self.graph.isXAxisAutoScale(): - self.graph.setXAxisAutoScale(False) - self.xAutoScaleToolButton.setDown(False) - else: - self.graph.setXAxisAutoScale(True) - self.xAutoScaleToolButton.setDown(True) + self.xAutoScaleToolButton.setDown( + not self.graph.isXAxisAutoScale()) + self.graph.setXAxisAutoScale( + not self.graph.isXAxisAutoScale()) + + def _copyIconSignal(self): + pngFile = BytesIO() + self.graph.saveGraph(pngFile, fileFormat='png') + pngFile.flush() + pngFile.seek(0) + pngData = pngFile.read() + pngFile.close() + image = qt.QImage.fromData(pngData, 'png') + qt.QApplication.clipboard().setImage(image) def _saveIconSignal(self): self.saveDirectory = PyMcaDirs.outputDir @@ -620,25 +635,26 @@ class RGBCorrelatorGraph(qt.QWidget): return if filetype.upper() == "IMAGE": - self.saveGraphImage(outputFile, original = True) + self.saveGraphImage(outputFile, original=True) elif filetype.upper() == "ZOOMEDIMAGE": - self.saveGraphImage(outputFile, original = False) + self.saveGraphImage(outputFile, original=False) else: self.saveGraphWidget(outputFile) - def saveGraphImage(self, filename, original = False): + def saveGraphImage(self, filename, original=False): format_ = filename[-3:].upper() - #This is the whole image, not the zoomed one ... - rgbData, legend, info, pixmap = self.graph.getActiveImage() + activeImage = self.graph.getActiveImage() + rgbdata = activeImage.getRgbaImageData() + # silx to pymca scale convention (a + b x) + xScale = activeImage.getOrigin()[0], activeImage.getScale()[0] + yScale = activeImage.getOrigin()[1], activeImage.getScale()[1] if original: # save whole image - bgrData = numpy.array(rgbData, copy=True) - bgrData[:,:,0] = rgbData[:, :, 2] - bgrData[:,:,2] = rgbData[:, :, 0] + bgradata = numpy.array(rgbdata, copy=True) + bgradata[:, :, 0] = rgbdata[:, :, 2] + bgradata[:, :, 2] = rgbdata[:, :, 0] else: - xScale = info.get("plot_xScale", None) - yScale = info.get("plot_yScale", None) - shape = rgbData.shape[:2] + shape = rgbdata.shape[:2] xmin, xmax = self.graph.getGraphXLimits() ymin, ymax = self.graph.getGraphYLimits() # save zoomed image, for that we have to get the limits @@ -652,16 +668,16 @@ class RGBCorrelatorGraph(qt.QWidget): row1 += 1 if col1 < shape[1]: col1 += 1 - tmpArray = rgbData[row0:row1, col0:col1, :] - bgrData = numpy.array(tmpArray, copy=True, dtype=rgbData.dtype) - bgrData[:,:,0] = tmpArray[:, :, 2] - bgrData[:,:,2] = tmpArray[:, :, 0] + tmpArray = rgbdata[row0:row1, col0:col1, :] + bgradata = numpy.array(tmpArray, copy=True, dtype=rgbdata.dtype) + bgradata[:, :, 0] = tmpArray[:, :, 2] + bgradata[:, :, 2] = tmpArray[:, :, 0] if self.graph.isYAxisInverted(): - qImage = qt.QImage(bgrData, bgrData.shape[1], bgrData.shape[0], - qt.QImage.Format_RGB32) + qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0], + qt.QImage.Format_ARGB32) else: - qImage = qt.QImage(bgrData, bgrData.shape[1], bgrData.shape[0], - qt.QImage.Format_RGB32).mirrored(False, True) + qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0], + qt.QImage.Format_ARGB32).mirrored(False, True) pixmap = qt.QPixmap.fromImage(qImage) if pixmap.save(filename, format_): return @@ -669,10 +685,10 @@ class RGBCorrelatorGraph(qt.QWidget): qt.QMessageBox.critical(self, "Save Error", "%s" % sys.exc_info()[1]) return - + def saveGraphWidget(self, filename): format_ = filename[-3:].upper() - if hasattr(qt.QPixmap, "graphWidget"): + if hasattr(qt.QPixmap, "grabWidget"): # Qt4 pixmap = qt.QPixmap.grabWidget(self.graph.getWidgetHandle()) else: @@ -691,20 +707,12 @@ class RGBCorrelatorGraph(qt.QWidget): else: return False - def printGraph(self): - if hasattr(qt.QPixmap, "grabWidget"): - pixmap = qt.QPixmap.grabWidget(self.graph.getWidgetHandle()) - else: - pixmap = self.graph.getWidgetHandle().grab() - self.printPreview.addPixmap(pixmap) - if self.printPreview.isReady(): - if self.printPreview.isHidden(): - self.printPreview.show() - self.printPreview.raise_() - def selectColormap(self): qt.QMessageBox.information(self, "Open", "Not implemented (yet)") + def _zoomBack(self, pos): + self.graph.getLimitsHistory().pop() + class MyQLabel(qt.QLabel): def __init__(self,parent=None,name=None,fl=0,bold=True, color= qt.Qt.red): qt.QLabel.__init__(self,parent) diff --git a/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py b/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py index e55aff9..8d95a0d 100644 --- a/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py +++ b/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py @@ -141,12 +141,12 @@ class ScatterPlotCorrelatorWidget(MaskScatterWidget.MaskScatterWidget): self.setSelectionCurveData(x, y, legend=None, color="k", symbol=".", - replot=False, + resetzoom=False, replace=True, xlabel=xLabel, ylabel=yLabel, selectable=False) - self._updatePlot(replot=False, replace=True) + self._updatePlot(resetzoom=False, replace=True) #matplotlib needs a zoom reset to update the scales # that problem does not seem to be present with OpenGL self.resetZoom() diff --git a/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py b/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py index c8345ea..6df787a 100644 --- a/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py +++ b/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py @@ -1,5 +1,5 @@ # /*######################################################################### -# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -112,7 +112,8 @@ def convertToRowAndColumn(x, y, shape, class MyMaskToolsWidget(MaskToolsWidget): """Backport of the setSelectionMask behavior implemented in silx 0.6.0, - to synchronize mask parameters with the active image.""" + to synchronize mask parameters with the active image. + This widget must not be used with silx >= 0.6""" def setSelectionMask(self, mask, copy=True): """Set the mask to a new array. :param numpy.ndarray mask: The array to use for the mask. @@ -157,7 +158,7 @@ class MyMaskToolsDockWidget(MaskToolsDockWidget): """ def __init__(self, parent=None, plot=None, name='Mask'): super(MyMaskToolsDockWidget, self).__init__(parent, plot, name) - if silx.version < "0.6": + if silx.version_info < (0, 6): self.setWidget(MyMaskToolsWidget(plot=plot)) self.widget().sigMaskChanged.connect(self._emitSigMaskChanged) @@ -206,6 +207,10 @@ class SaveImageListAction(qt.QAction): if filename.lower().endswith(".edf"): ArraySave.save2DArrayListAsEDF(imageList, filename, labels) + elif filename.lower().endswith(".tif"): + ArraySave.save2DArrayListAsMonochromaticTiff(imageList, + filename, + labels) elif filename.lower().endswith(".csv"): assert csvseparator is not None ArraySave.save2DArrayListAsASCII(imageList, filename, labels, @@ -267,7 +272,8 @@ class SaveImageListAction(qt.QAction): filedialog.setFileMode(filedialog.AnyFile) filedialog.setAcceptMode(qt.QFileDialog.AcceptSave) filedialog.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict["gioconda16"]))) - formatlist = ["ASCII Files *.dat", + formatlist = ["TIFF Files *.tif", + "ASCII Files *.dat", "EDF Files *.edf", 'CSV(, separated) Files *.csv', 'CSV(; separated) Files *.csv', @@ -307,8 +313,7 @@ class SaveImageListAction(qt.QAction): class SaveMatplotlib(qt.QAction): - """Save current image and mask (if any) in a :class:`MaskImageWidget` - to EDF or CSV""" + """Save current image ho high quality graphics using matplotlib""" def __init__(self, title, maskImageWidget): super(SaveMatplotlib, self).__init__(QString(title), maskImageWidget) @@ -756,15 +761,16 @@ class SilxMaskImageWidget(qt.QMainWindow): The mask can be cropped or padded to fit active image, the returned shape is that of the active image. """ - if mask is None: - mask = numpy.zeros_like(self._getMaskToolsDockWidget().getSelectionMask()) - if not len(mask): - return # disconnect temporarily to avoid infinite loop self._getMaskToolsDockWidget().sigMaskChanged.disconnect( self._emitMaskImageWidgetSignal) - ret = self._getMaskToolsDockWidget().setSelectionMask(mask, - copy=copy) + if mask is None and silx.version_info <= (0, 7, 0): + self._getMaskToolsDockWidget().resetSelectionMask() + ret = None + else: + # from silx 0.8 onwards, setSelectionMask(None) is supported + ret = self._getMaskToolsDockWidget().setSelectionMask(mask, + copy=copy) self._getMaskToolsDockWidget().sigMaskChanged.connect( self._emitMaskImageWidgetSignal) return ret @@ -775,7 +781,7 @@ class SilxMaskImageWidget(qt.QMainWindow): :param bool copy: True (default) to get a copy of the mask. If False, the returned array MUST not be modified. :return: The array of the mask with dimension of the 'active' image. - If there is no active image, an empty array is returned. + If there is no active image, None is returned. :rtype: 2D numpy.ndarray of uint8 """ return self._getMaskToolsDockWidget().getSelectionMask(copy=copy) diff --git a/PyMca5/PyMcaGui/plotting/_ImageProfile.py b/PyMca5/PyMcaGui/plotting/_ImageProfile.py index e57986c..0943317 100644 --- a/PyMca5/PyMcaGui/plotting/_ImageProfile.py +++ b/PyMca5/PyMcaGui/plotting/_ImageProfile.py @@ -37,9 +37,10 @@ Functions to extract a profile curve of a region of interest in an image. # import ###################################################################### import numpy +import logging from PyMca5.PyMcaMath.fitting import SpecfitFuns -DEBUG = 0 +_logger = logging.getLogger(__name__) # utils ####################################################################### @@ -209,8 +210,7 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, coordsRange = row0, row1 if nPoints == 1: # all points are the same - if DEBUG: - print("START AND END POINT ARE THE SAME!!") + _logger.debug("START AND END POINT ARE THE SAME!!") return None # the coordinates of the reference points @@ -249,9 +249,8 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, newRow0 = 0.0 newRow1 = - (col1 - col0) * sinalpha + (row1 - row0) * cosalpha - if DEBUG: - print("new X0 Y0 = %f, %f " % (newCol0, newRow0)) - print("new X1 Y1 = %f, %f " % (newCol1, newRow1)) + _logger.debug("new X0 Y0 = %f, %f ", newCol0, newRow0) + _logger.debug("new X1 Y1 = %f, %f ", newCol1, newRow1) tmpX = numpy.linspace(newCol0, newCol1, nPoints).astype(numpy.float) @@ -260,19 +259,19 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, rotMatrix[0, 1] = - sinalpha rotMatrix[1, 0] = sinalpha rotMatrix[1, 1] = cosalpha - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: # test if I recover the original points testX = numpy.zeros((2, 1), numpy.float) colRow = numpy.dot(rotMatrix, testX) - print("Recovered X0 = %f" % (colRow[0, 0] + col0)) - print("Recovered Y0 = %f" % (colRow[1, 0] + row0)) - print("It should be = %f, %f" % (col0, row0)) + _logger.debug("Recovered X0 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y0 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col0, row0) testX[0, 0] = newCol1 testX[1, 0] = newRow1 colRow = numpy.dot(rotMatrix, testX) - print("Recovered X1 = %f" % (colRow[0, 0] + col0)) - print("Recovered Y1 = %f" % (colRow[1, 0] + row0)) - print("It should be = %f, %f" % (col1, row1)) + _logger.debug("Recovered X1 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y1 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col1, row1) # find the drawing limits testX = numpy.zeros((2, 4), numpy.float) @@ -290,21 +289,18 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, for a in rowLimits0: if (a >= image.shape[0]) or (a < 0): - if DEBUG: - print("outside row limits", a) + _logger.debug("outside row limits %s", a) return None for a in colLimits0: if (a >= image.shape[1]) or (a < 0): - if DEBUG: - print("outside column limits", a) + _logger.debug("outside column limits %s", a) return None r0 = rowLimits0[0] r1 = rowLimits0[1] if r0 > r1: - if DEBUG: - print("r0 > r1", r0, r1) + _logger.debug("r0 > r1 %s %s", r0, r1) raise ValueError("r0 > r1") x = numpy.zeros((2, nPoints), numpy.float) |