diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2017-11-25 16:55:20 +0100 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2017-11-25 16:55:20 +0100 |
commit | e19c96eff0c310c06c4f268c8b80cb33bd08996f (patch) | |
tree | f2b4a365ed899be04766f3937bcc2d58d22be065 /silx/gui/plot | |
parent | bfa4dba15485b4192f8bbe13345e9658c97ecf76 (diff) |
New upstream version 0.6.1+dfsg
Diffstat (limited to 'silx/gui/plot')
-rw-r--r-- | silx/gui/plot/ImageView.py | 17 | ||||
-rw-r--r-- | silx/gui/plot/LegendSelector.py | 7 | ||||
-rw-r--r-- | silx/gui/plot/PlotTools.py | 11 | ||||
-rw-r--r-- | silx/gui/plot/PlotWidget.py | 130 | ||||
-rw-r--r-- | silx/gui/plot/Profile.py | 9 | ||||
-rw-r--r-- | silx/gui/plot/backends/BackendMatplotlib.py | 134 | ||||
-rw-r--r-- | silx/gui/plot/items/axis.py | 2 | ||||
-rw-r--r-- | silx/gui/plot/items/core.py | 7 | ||||
-rw-r--r-- | silx/gui/plot/items/curve.py | 3 | ||||
-rw-r--r-- | silx/gui/plot/test/__init__.py | 4 | ||||
-rw-r--r-- | silx/gui/plot/test/testImageView.py | 136 | ||||
-rw-r--r-- | silx/gui/plot/test/testPlotWidget.py | 21 |
12 files changed, 365 insertions, 116 deletions
diff --git a/silx/gui/plot/ImageView.py b/silx/gui/plot/ImageView.py index 803a2fc..46e56e6 100644 --- a/silx/gui/plot/ImageView.py +++ b/silx/gui/plot/ImageView.py @@ -311,16 +311,13 @@ class ImageView(PlotWindow): 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 = PlotWidget(backend=backend) + self._histoHPlot.getWidgetHandle().setMinimumHeight( + self.HISTOGRAMS_HEIGHT) + self._histoHPlot.getWidgetHandle().setMaximumHeight( + self.HISTOGRAMS_HEIGHT) self._histoHPlot.setInteractiveMode('zoom') self._histoHPlot.sigPlotSignal.connect(self._histoHPlotCB) - self._histoHPlot.getWidgetHandle().sizeHint = sizeHint - self._histoHPlot.getWidgetHandle().minimumSizeHint = sizeHint self.setPanWithArrowKeys(True) @@ -330,10 +327,12 @@ class ImageView(PlotWindow): self.sigActiveImageChanged.connect(self._activeImageChangedSlot) self._histoVPlot = PlotWidget(backend=backend) + self._histoVPlot.getWidgetHandle().setMinimumWidth( + self.HISTOGRAMS_HEIGHT) + self._histoVPlot.getWidgetHandle().setMaximumWidth( + self.HISTOGRAMS_HEIGHT) self._histoVPlot.setInteractiveMode('zoom') self._histoVPlot.sigPlotSignal.connect(self._histoVPlotCB) - self._histoVPlot.getWidgetHandle().sizeHint = sizeHint - self._histoVPlot.getWidgetHandle().minimumSizeHint = sizeHint self._radarView = RadarView() self._radarView.visibleRectDragged.connect(self._radarViewCB) diff --git a/silx/gui/plot/LegendSelector.py b/silx/gui/plot/LegendSelector.py index 31bc3db..e9cfd1d 100644 --- a/silx/gui/plot/LegendSelector.py +++ b/silx/gui/plot/LegendSelector.py @@ -29,7 +29,7 @@ This widget is meant to work with :class:`PlotWindow`. __authors__ = ["V.A. Sole", "T. Rueter", "T. Vincent"] __license__ = "MIT" -__data__ = "08/08/2016" +__data__ = "16/10/2017" import logging @@ -971,9 +971,10 @@ class LegendsDockWidget(qt.QDockWidget): def renameCurve(self, oldLegend, newLegend): """Change the name of a curve using remove and addCurve - :param str oldLegend: The legend of the curve to be change + :param str oldLegend: The legend of the curve to be changed :param str newLegend: The new legend of the curve """ + is_active = self.plot.getActiveCurve(just_legend=True) == oldLegend curve = self.plot.getCurve(oldLegend) self.plot.remove(oldLegend, kind='curve') self.plot.addCurve(curve.getXData(copy=False), @@ -992,6 +993,8 @@ class LegendsDockWidget(qt.QDockWidget): selectable=curve.isSelectable(), fill=curve.isFill(), resetzoom=False) + if is_active: + self.plot.setActiveCurve(newLegend) def _legendSignalHandler(self, ddict): """Handles events from the LegendListView signal""" diff --git a/silx/gui/plot/PlotTools.py b/silx/gui/plot/PlotTools.py index 85dcc31..ed62d48 100644 --- a/silx/gui/plot/PlotTools.py +++ b/silx/gui/plot/PlotTools.py @@ -29,7 +29,7 @@ from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" -__date__ = "02/10/2017" +__date__ = "16/10/2017" import logging @@ -151,13 +151,16 @@ class PositionInfo(qt.QWidget): """ if event['event'] == 'mouseMoved': x, y = event['x'], event['y'] - self._updateStatusBar(x, y) + xPixel, yPixel = event['xpixel'], event['ypixel'] + self._updateStatusBar(x, y, xPixel, yPixel) - def _updateStatusBar(self, x, y): + def _updateStatusBar(self, x, y, xPixel, yPixel): """Update information from the status bar using the definitions. :param float x: Position-x in data :param float y: Position-y in data + :param float xPixel: Position-x in pixels + :param float yPixel: Position-y in pixels """ styleSheet = "color: rgb(0, 0, 0);" # Default style @@ -180,8 +183,6 @@ class PositionInfo(qt.QWidget): closestInPixels = self.plot.dataToPixel( xClosest, yClosest, axis=activeCurve.getYAxis()) if closestInPixels is not None: - xPixel, yPixel = event['xpixel'], event['ypixel'] - if (abs(closestInPixels[0] - xPixel) < 5 and abs(closestInPixels[1] - yPixel) < 5): # Update label style sheet diff --git a/silx/gui/plot/PlotWidget.py b/silx/gui/plot/PlotWidget.py index 8fd5a5e..5bf2b59 100644 --- a/silx/gui/plot/PlotWidget.py +++ b/silx/gui/plot/PlotWidget.py @@ -175,7 +175,7 @@ from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent"] __license__ = "MIT" -__date__ = "30/08/2017" +__date__ = "18/10/2017" from collections import OrderedDict, namedtuple @@ -612,8 +612,7 @@ class PlotWidget(qt.QMainWindow): if (kind == 'curve' and not self.getAllCurves(just_legend=True, withhidden=True)): - self._colorIndex = 0 - self._styleIndex = 0 + self._resetColorAndStyle() self.notify('contentChanged', action='remove', kind=kind, legend=legend) @@ -780,6 +779,9 @@ class PlotWidget(qt.QMainWindow): # Check if curve was previously active wasActive = self.getActiveCurve(just_legend=True) == legend + if replace: + self._resetColorAndStyle() + # Create/Update curve object curve = self.getCurve(legend) mustBeAdded = curve is None @@ -2319,7 +2321,7 @@ class PlotWidget(qt.QMainWindow): flag = bool(flag) self._backend.setKeepDataAspectRatio(flag=flag) self._setDirtyPlot() - self.resetZoom() + self._forceResetZoom() self.notify('setKeepDataAspectRatio', state=flag) def getGraphGrid(self): @@ -2431,6 +2433,10 @@ class PlotWidget(qt.QMainWindow): """ return Colormap.getSupportedColormaps() + def _resetColorAndStyle(self): + self._colorIndex = 0 + self._styleIndex = 0 + def _getColorAndStyle(self): color = self.colorList[self._colorIndex] style = self._styleList[self._styleIndex] @@ -2614,6 +2620,66 @@ class PlotWidget(qt.QMainWindow): self._backend.replot() self._dirty = False # reset dirty flag + def _forceResetZoom(self, dataMargins=None): + """Reset the plot limits to the bounds of the data and redraw the plot. + + This method forces a reset zoom and does not check axis autoscale. + + Extra margins can be added around the data inside the plot area + (see :meth:`setDataMargins`). + Margins are given as one ratio of the data range per limit of the + data (xMin, xMax, yMin and yMax limits). + For log scale, extra margins are applied in log10 of the data. + + :param dataMargins: Ratios of margins to add around the data inside + the plot area for each side (default: no margins). + :type dataMargins: A 4-tuple of float as (xMin, xMax, yMin, yMax). + """ + if dataMargins is None: + dataMargins = self._defaultDataMargins + + # Get data range + ranges = self.getDataRange() + xmin, xmax = (1., 100.) if ranges.x is None else ranges.x + ymin, ymax = (1., 100.) if ranges.y is None else ranges.y + if ranges.yright is None: + ymin2, ymax2 = None, None + else: + ymin2, ymax2 = ranges.yright + + # Add margins around data inside the plot area + newLimits = list(_utils.addMarginsToLimits( + dataMargins, + self._xAxis._isLogarithmic(), + self._yAxis._isLogarithmic(), + xmin, xmax, ymin, ymax, ymin2, ymax2)) + + if self.isKeepDataAspectRatio(): + # Use limits with margins to keep ratio + xmin, xmax, ymin, ymax = newLimits[:4] + + # Compute bbox wth figure aspect ratio + plotWidth, plotHeight = self.getPlotBoundsInPixels()[2:] + plotRatio = plotHeight / plotWidth + + if plotRatio > 0.: + dataRatio = (ymax - ymin) / (xmax - xmin) + if dataRatio < plotRatio: + # Increase y range + ycenter = 0.5 * (ymax + ymin) + yrange = (xmax - xmin) * plotRatio + newLimits[2] = ycenter - 0.5 * yrange + newLimits[3] = ycenter + 0.5 * yrange + + elif dataRatio > plotRatio: + # Increase x range + xcenter = 0.5 * (xmax + xmin) + xrange_ = (ymax - ymin) / plotRatio + newLimits[0] = xcenter - 0.5 * xrange_ + newLimits[1] = xcenter + 0.5 * xrange_ + + self.setLimits(*newLimits) + def resetZoom(self, dataMargins=None): """Reset the plot limits to the bounds of the data and redraw the plot. @@ -2631,9 +2697,6 @@ class PlotWidget(qt.QMainWindow): the plot area for each side (default: no margins). :type dataMargins: A 4-tuple of float as (xMin, xMax, yMin, yMax). """ - if dataMargins is None: - dataMargins = self._defaultDataMargins - xLimits = self._xAxis.getLimits() yLimits = self._yAxis.getLimits() y2Limits = self._yRightAxis.getLimits() @@ -2641,52 +2704,19 @@ class PlotWidget(qt.QMainWindow): xAuto = self._xAxis.isAutoScale() yAuto = self._yAxis.isAutoScale() + # With log axes, autoscale if limits are <= 0 + # This avoids issues with toggling log scale with matplotlib 2.1.0 + if self._xAxis.getScale() == self._xAxis.LOGARITHMIC and xLimits[0] <= 0: + xAuto = True + if self._yAxis.getScale() == self._yAxis.LOGARITHMIC and (yLimits[0] <= 0 or y2Limits[0] <= 0): + yAuto = True + if not xAuto and not yAuto: _logger.debug("Nothing to autoscale") else: # Some axes to autoscale + self._forceResetZoom(dataMargins=dataMargins) - # Get data range - ranges = self.getDataRange() - xmin, xmax = (1., 100.) if ranges.x is None else ranges.x - ymin, ymax = (1., 100.) if ranges.y is None else ranges.y - if ranges.yright is None: - ymin2, ymax2 = None, None - else: - ymin2, ymax2 = ranges.yright - - # Add margins around data inside the plot area - newLimits = list(_utils.addMarginsToLimits( - dataMargins, - self._xAxis._isLogarithmic(), - self._yAxis._isLogarithmic(), - xmin, xmax, ymin, ymax, ymin2, ymax2)) - - if self.isKeepDataAspectRatio(): - # Use limits with margins to keep ratio - xmin, xmax, ymin, ymax = newLimits[:4] - - # Compute bbox wth figure aspect ratio - plotWidth, plotHeight = self.getPlotBoundsInPixels()[2:] - plotRatio = plotHeight / plotWidth - - if plotRatio > 0.: - dataRatio = (ymax - ymin) / (xmax - xmin) - if dataRatio < plotRatio: - # Increase y range - ycenter = 0.5 * (ymax + ymin) - yrange = (xmax - xmin) * plotRatio - newLimits[2] = ycenter - 0.5 * yrange - newLimits[3] = ycenter + 0.5 * yrange - - elif dataRatio > plotRatio: - # Increase x range - xcenter = 0.5 * (xmax + xmin) - xrange_ = (ymax - ymin) / plotRatio - newLimits[0] = xcenter - 0.5 * xrange_ - newLimits[1] = xcenter + 0.5 * xrange_ - - self.setLimits(*newLimits) - + # Restore limits for axis not in autoscale if not xAuto and yAuto: self.setGraphXLimits(*xLimits) elif xAuto and not yAuto: @@ -2696,8 +2726,6 @@ class PlotWidget(qt.QMainWindow): if yLimits is not None: self.setGraphYLimits(yLimits[0], yLimits[1], axis='left') - self._setDirtyPlot() - if (xLimits != self._xAxis.getLimits() or yLimits != self._yAxis.getLimits() or y2Limits != self._yRightAxis.getLimits()): diff --git a/silx/gui/plot/Profile.py b/silx/gui/plot/Profile.py index ff85695..4a74fa7 100644 --- a/silx/gui/plot/Profile.py +++ b/silx/gui/plot/Profile.py @@ -31,6 +31,8 @@ __license__ = "MIT" __date__ = "17/08/2017" +import weakref + import numpy from silx.image.bilinear import BilinearImage @@ -348,7 +350,7 @@ class ProfileToolBar(qt.QToolBar): title='Profile Selection'): super(ProfileToolBar, self).__init__(title, parent) assert plot is not None - self.plot = plot + self._plotRef = weakref.ref(plot) self._overlayColor = None self._defaultOverlayColor = 'red' # update when active image change @@ -443,6 +445,11 @@ class ProfileToolBar(qt.QToolBar): self.getProfileMainWindow().sigClose.connect(self.clearProfile) @property + def plot(self): + """The :class:`.PlotWidget` associated to the toolbar.""" + return self._plotRef() + + @property @deprecated(since_version="0.6.0") def browseAction(self): return self._browseAction diff --git a/silx/gui/plot/backends/BackendMatplotlib.py b/silx/gui/plot/backends/BackendMatplotlib.py index 59e753e..b41f20e 100644 --- a/silx/gui/plot/backends/BackendMatplotlib.py +++ b/silx/gui/plot/backends/BackendMatplotlib.py @@ -28,7 +28,7 @@ from __future__ import division __authors__ = ["V.A. Sole", "T. Vincent, H. Payno"] __license__ = "MIT" -__date__ = "16/08/2017" +__date__ = "18/10/2017" import logging @@ -306,6 +306,14 @@ class BackendMatplotlib(BackendBase.BackendBase): ystep = 1 if scale[1] >= 0. else -1 data = data[::ystep, ::xstep] + if matplotlib.__version__ < "2.1": + # matplotlib 1.4.2 do not support float128 + dtype = data.dtype + if dtype.kind == "f" and dtype.itemsize >= 16: + _logger.warning("Your matplotlib version do not support " + "float128. Data converted to floa64.") + data = data.astype(numpy.float64) + image.set_data(data) self.ax.add_artist(image) @@ -602,16 +610,32 @@ class BackendMatplotlib(BackendBase.BackendBase): # Graph axes def setXAxisLogarithmic(self, flag): - if matplotlib.__version__ >= "2.0.0": - self.ax.cla() - self.ax2.cla() + # Workaround for matplotlib 2.1.0 when one tries to set an axis + # to log scale with both limits <= 0 + # In this case a draw with positive limits is needed first + if flag and matplotlib.__version__ >= '2.1.0': + xlim = self.ax.get_xlim() + if xlim[0] <= 0 and xlim[1] <= 0: + self.ax.set_xlim(1, 10) + self.draw() + self.ax2.set_xscale('log' if flag else 'linear') self.ax.set_xscale('log' if flag else 'linear') def setYAxisLogarithmic(self, flag): - if matplotlib.__version__ >= "2.0.0": - self.ax.cla() - self.ax2.cla() + # Workaround for matplotlib 2.1.0 when one tries to set an axis + # to log scale with both limits <= 0 + # In this case a draw with positive limits is needed first + if flag and matplotlib.__version__ >= '2.1.0': + redraw = False + for axis in (self.ax, self.ax2): + ylim = axis.get_ylim() + if ylim[0] <= 0 and ylim[1] <= 0: + axis.set_ylim(1, 10) + redraw = True + if redraw: + self.draw() + self.ax2.set_yscale('log' if flag else 'linear') self.ax.set_yscale('log' if flag else 'linear') @@ -700,12 +724,12 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): """Signal handling automatic asynchronous replot""" def __init__(self, plot, parent=None): - self._insideResizeEventMethod = False - BackendMatplotlib.__init__(self, plot, parent) FigureCanvasQTAgg.__init__(self, self.fig) self.setParent(parent) + self._limitsBeforeResize = None + FigureCanvasQTAgg.setSizePolicy( self, qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding) FigureCanvasQTAgg.updateGeometry(self) @@ -806,55 +830,83 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): # replot control def resizeEvent(self, event): - self._insideResizeEventMethod = True - # Need to dirty the whole plot on resize. - self._plot._setDirtyPlot() + # Store current limits + self._limitsBeforeResize = ( + self.ax.get_xbound(), self.ax.get_ybound(), self.ax2.get_ybound()) + FigureCanvasQTAgg.resizeEvent(self, event) - self._insideResizeEventMethod = False + if self.isKeepDataAspectRatio() or self._overlays or self._graphCursor: + # This is needed with matplotlib 1.5.x and 2.0.x + self._plot._setDirtyPlot() + + def _drawOverlays(self): + """Draw overlays if any.""" + if self._overlays or self._graphCursor: + # There is some overlays or crosshair + + # This assume that items are only on left/bottom Axes + for item in self._overlays: + self.ax.draw_artist(item) + + for item in self._graphCursor: + self.ax.draw_artist(item) def draw(self): - """Override canvas draw method to support faster draw of overlays.""" - if self._plot._getDirtyPlot(): # Need a full redraw - # Store previous limits - xLimits = self.ax.get_xbound() - yLimits = self.ax.get_ybound() - yRightLimits = self.ax2.get_ybound() + """Overload draw + It performs a full redraw (including overlays) of the plot. + It also resets background and emit limits changed signal. + + This is directly called by matplotlib for widget resize. + """ + # Starting with mpl 2.1.0, toggling autoscale raises a ValueError + # in some situations. See #1081, #1136, #1163, + if matplotlib.__version__ >= "2.0.0": + try: + FigureCanvasQTAgg.draw(self) + except ValueError as err: + _logger.debug( + "ValueError caught while calling FigureCanvasQTAgg.draw: " + "'%s'", err) + else: FigureCanvasQTAgg.draw(self) - self._background = None # Any saved background is dirty - # Check if limits changed due to a resize of the widget + if self._overlays or self._graphCursor: + # Save background + self._background = self.copy_from_bbox(self.fig.bbox) + else: + self._background = None # Reset background + + # Check if limits changed due to a resize of the widget + if self._limitsBeforeResize is not None: + xLimits, yLimits, yRightLimits = self._limitsBeforeResize + self._limitsBeforeResize = None + if xLimits != self.ax.get_xbound(): self._plot.getXAxis()._emitLimitsChanged() if yLimits != self.ax.get_ybound(): self._plot.getYAxis(axis='left')._emitLimitsChanged() if yRightLimits != self.ax2.get_ybound(): - self._plot.getYAxis(axis='left')._emitLimitsChanged() - - if (self._overlays or self._graphCursor or - self._plot._getDirtyPlot() == 'overlay'): - # There are overlays or crosshair, or they is just no more overlays + self._plot.getYAxis(axis='right')._emitLimitsChanged() - # Specific case: called from resizeEvent: - # avoid store/restore background, just draw the overlay - if not self._insideResizeEventMethod: - if self._background is None: # First store the background - self._background = self.copy_from_bbox(self.fig.bbox) + self._drawOverlays() - self.restore_region(self._background) - - # This assume that items are only on left/bottom Axes - for item in self._overlays: - self.ax.draw_artist(item) + def replot(self): + BackendMatplotlib.replot(self) - for item in self._graphCursor: - self.ax.draw_artist(item) + dirtyFlag = self._plot._getDirtyPlot() + if dirtyFlag == 'overlay': + # Only redraw overlays using fast rendering path + if self._background is None: + self._background = self.copy_from_bbox(self.fig.bbox) + self.restore_region(self._background) + self._drawOverlays() self.blit(self.fig.bbox) - def replot(self): - BackendMatplotlib.replot(self) - self.draw() + elif dirtyFlag: # Need full redraw + self.draw() + # cursor diff --git a/silx/gui/plot/items/axis.py b/silx/gui/plot/items/axis.py index 56fd762..ff36512 100644 --- a/silx/gui/plot/items/axis.py +++ b/silx/gui/plot/items/axis.py @@ -220,7 +220,7 @@ class Axis(qt.QObject): for item in self._plot._getItems(withhidden=True): item._updated() self._plot._invalidateDataRange() - self._plot.resetZoom() + self._plot._forceResetZoom() self.sigScaleChanged.emit(self._scale) if emitLog: diff --git a/silx/gui/plot/items/core.py b/silx/gui/plot/items/core.py index 0f4ffb9..34ac700 100644 --- a/silx/gui/plot/items/core.py +++ b/silx/gui/plot/items/core.py @@ -543,7 +543,7 @@ class ColorMixIn(object): def getColor(self): """Returns the RGBA color of the item - :rtype: 4-tuple of float in [0, 1] + :rtype: 4-tuple of float in [0, 1] or array of colors """ return self._color @@ -566,9 +566,8 @@ class ColorMixIn(object): else: # Array of colors assert color.ndim == 2 - if self._color != color: - self._color = color - self._updated(ItemChangedType.COLOR) + self._color = color + self._updated(ItemChangedType.COLOR) class YAxisMixIn(object): diff --git a/silx/gui/plot/items/curve.py b/silx/gui/plot/items/curve.py index ce7f03e..0ba475d 100644 --- a/silx/gui/plot/items/curve.py +++ b/silx/gui/plot/items/curve.py @@ -31,6 +31,7 @@ __date__ = "06/03/2017" import logging +import numpy from .. import Colors from .core import (Points, LabelsMixIn, ColorMixIn, YAxisMixIn, @@ -75,7 +76,7 @@ class Curve(Points, ColorMixIn, YAxisMixIn, FillMixIn, LabelsMixIn, LineMixIn): xFiltered, yFiltered, xerror, yerror = self.getData( copy=False, displayed=True) - if len(xFiltered) == 0: + if len(xFiltered) == 0 or not numpy.any(numpy.isfinite(xFiltered)): return None # No data to display, do not add renderer to backend return backend.addCurve(xFiltered, yFiltered, self.getLegend(), diff --git a/silx/gui/plot/test/__init__.py b/silx/gui/plot/test/__init__.py index 2c2943e..07338b6 100644 --- a/silx/gui/plot/test/__init__.py +++ b/silx/gui/plot/test/__init__.py @@ -51,6 +51,7 @@ from . import testItem from . import testUtilsAxis from . import testLimitConstraints from . import testComplexImageView +from . import testImageView def suite(): @@ -77,5 +78,6 @@ def suite(): testItem.suite(), testUtilsAxis.suite(), testLimitConstraints.suite(), - testComplexImageView.suite()]) + testComplexImageView.suite(), + testImageView.suite()]) return test_suite diff --git a/silx/gui/plot/test/testImageView.py b/silx/gui/plot/test/testImageView.py new file mode 100644 index 0000000..641d438 --- /dev/null +++ b/silx/gui/plot/test/testImageView.py @@ -0,0 +1,136 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2017 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Basic tests for PlotWindow""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "22/09/2017" + + +import unittest +import numpy + +from silx.gui import qt +from silx.gui.test.utils import TestCaseQt + +from silx.gui.plot import ImageView +from silx.gui.plot.Colormap import Colormap + + +class TestImageView(TestCaseQt): + """Tests of ImageView widget.""" + + def setUp(self): + super(TestImageView, self).setUp() + self.plot = ImageView() + self.plot.show() + self.qWaitForWindowExposed(self.plot) + + def tearDown(self): + self.qapp.processEvents() + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.close() + del self.plot + self.qapp.processEvents() + super(TestImageView, self).tearDown() + + def testSetImage(self): + """Test setImage""" + image = numpy.arange(100).reshape(10, 10) + + self.plot.setImage(image, reset=True) + self.qWait(100) + self.assertEqual(self.plot.getXAxis().getLimits(), (0, 10)) + self.assertEqual(self.plot.getYAxis().getLimits(), (0, 10)) + + # With reset=False + self.plot.setImage(image[::2, ::2], reset=False) + self.qWait(100) + self.assertEqual(self.plot.getXAxis().getLimits(), (0, 10)) + self.assertEqual(self.plot.getYAxis().getLimits(), (0, 10)) + + self.plot.setImage(image, origin=(10, 20), scale=(2, 4), reset=False) + self.qWait(100) + self.assertEqual(self.plot.getXAxis().getLimits(), (0, 10)) + self.assertEqual(self.plot.getYAxis().getLimits(), (0, 10)) + + # With reset=True + self.plot.setImage(image, origin=(1, 2), scale=(1, 0.5), reset=True) + self.qWait(100) + self.assertEqual(self.plot.getXAxis().getLimits(), (1, 11)) + self.assertEqual(self.plot.getYAxis().getLimits(), (2, 7)) + + self.plot.setImage(image[::2, ::2], reset=True) + self.qWait(100) + self.assertEqual(self.plot.getXAxis().getLimits(), (0, 5)) + self.assertEqual(self.plot.getYAxis().getLimits(), (0, 5)) + + def testColormap(self): + """Test get|setColormap""" + image = numpy.arange(100).reshape(10, 10) + self.plot.setImage(image) + + # Colormap as dict + self.plot.setColormap({'name': 'viridis', + 'normalization': 'log', + 'autoscale': False, + 'vmin': 0, + 'vmax': 1}) + colormap = self.plot.getColormap() + self.assertEqual(colormap.getName(), 'viridis') + self.assertEqual(colormap.getNormalization(), 'log') + self.assertEqual(colormap.getVMin(), 0) + self.assertEqual(colormap.getVMax(), 1) + + # Colormap as keyword arguments + self.plot.setColormap(colormap='magma', + normalization='linear', + autoscale=True, + vmin=1, + vmax=2) + self.assertEqual(colormap.getName(), 'magma') + self.assertEqual(colormap.getNormalization(), 'linear') + self.assertEqual(colormap.getVMin(), None) + self.assertEqual(colormap.getVMax(), None) + + # Update colormap with keyword argument + self.plot.setColormap(normalization='log') + self.assertEqual(colormap.getNormalization(), 'log') + + # Colormap as Colormap object + cmap = Colormap() + self.plot.setColormap(cmap) + self.assertIs(self.plot.getColormap(), cmap) + + +def suite(): + test_suite = unittest.TestSuite() + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(TestImageView)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testPlotWidget.py b/silx/gui/plot/test/testPlotWidget.py index deeb198..ccee428 100644 --- a/silx/gui/plot/test/testPlotWidget.py +++ b/silx/gui/plot/test/testPlotWidget.py @@ -371,6 +371,27 @@ class TestPlotCurve(PlotWidgetTestCase): color=color, linestyle="-", symbol='o') self.plot.resetZoom() + # Test updating color array + + # From array to array + newColors = numpy.ones((len(self.xData), 3), dtype=numpy.float32) + self.plot.addCurve(self.xData, self.yData, + legend="curve 2", + replace=False, resetzoom=False, + color=newColors, symbol='o') + + # Array to single color + self.plot.addCurve(self.xData, self.yData, + legend="curve 2", + replace=False, resetzoom=False, + color='green', symbol='o') + + # single color to array + self.plot.addCurve(self.xData, self.yData, + legend="curve 2", + replace=False, resetzoom=False, + color=color, symbol='o') + class TestPlotMarker(PlotWidgetTestCase): """Basic tests for add*Marker""" |