summaryrefslogtreecommitdiff
path: root/silx/gui/plot/test
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@debian.org>2021-01-06 14:10:12 +0100
committerPicca Frédéric-Emmanuel <picca@debian.org>2021-01-06 14:10:12 +0100
commitb3bea947efa55d2c0f198b6c6795b3177be27f45 (patch)
tree4116758aafe4483bf472c1d54b519e685737fd77 /silx/gui/plot/test
parent5ad425ff4e62f5e003178813ebd073577679a00e (diff)
New upstream version 0.14.0+dfsg
Diffstat (limited to 'silx/gui/plot/test')
-rw-r--r--silx/gui/plot/test/__init__.py2
-rw-r--r--silx/gui/plot/test/testComplexImageView.py8
-rw-r--r--silx/gui/plot/test/testCurvesROIWidget.py10
-rw-r--r--silx/gui/plot/test/testItem.py90
-rw-r--r--silx/gui/plot/test/testMaskToolsWidget.py15
-rw-r--r--silx/gui/plot/test/testPlotInteraction.py6
-rwxr-xr-xsilx/gui/plot/test/testPlotWidget.py237
-rw-r--r--silx/gui/plot/test/testPlotWindow.py21
-rw-r--r--silx/gui/plot/test/testRoiStatsWidget.py290
-rw-r--r--silx/gui/plot/test/testScatterMaskToolsWidget.py16
-rw-r--r--silx/gui/plot/test/testStackView.py15
-rw-r--r--silx/gui/plot/test/testStats.py273
12 files changed, 840 insertions, 143 deletions
diff --git a/silx/gui/plot/test/__init__.py b/silx/gui/plot/test/__init__.py
index 0477e2a..dfb7c2e 100644
--- a/silx/gui/plot/test/__init__.py
+++ b/silx/gui/plot/test/__init__.py
@@ -53,6 +53,7 @@ from . import testSaveAction
from . import testScatterView
from . import testPixelIntensityHistoAction
from . import testCompareImages
+from . import testRoiStatsWidget
def suite():
@@ -86,5 +87,6 @@ def suite():
testScatterView.suite(),
testPixelIntensityHistoAction.suite(),
testCompareImages.suite(),
+ testRoiStatsWidget.suite(),
])
return test_suite
diff --git a/silx/gui/plot/test/testComplexImageView.py b/silx/gui/plot/test/testComplexImageView.py
index 051ec4d..4ac3488 100644
--- a/silx/gui/plot/test/testComplexImageView.py
+++ b/silx/gui/plot/test/testComplexImageView.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2020 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
@@ -50,7 +50,7 @@ class TestComplexImageView(PlotWidgetTestCase, ParametricTestCase):
def testPlot2DComplex(self):
"""Test API of ComplexImageView widget"""
- data = numpy.array(((0, 1j), (1, 1 + 1j)), dtype=numpy.complex)
+ data = numpy.array(((0, 1j), (1, 1 + 1j)), dtype=numpy.complex64)
self.plot.setData(data)
self.plot.setKeepDataAspectRatio(True)
self.plot.getPlot().resetZoom()
@@ -76,11 +76,11 @@ class TestComplexImageView(PlotWidgetTestCase, ParametricTestCase):
self.qWait(100)
# Test no data
- self.plot.setData(numpy.zeros((0, 0), dtype=numpy.complex))
+ self.plot.setData(numpy.zeros((0, 0), dtype=numpy.complex64))
self.qWait(100)
# Test float data
- self.plot.setData(numpy.arange(100, dtype=numpy.float).reshape(10, 10))
+ self.plot.setData(numpy.arange(100, dtype=numpy.float64).reshape(10, 10))
self.qWait(100)
diff --git a/silx/gui/plot/test/testCurvesROIWidget.py b/silx/gui/plot/test/testCurvesROIWidget.py
index 77c53a8..6a0ab8c 100644
--- a/silx/gui/plot/test/testCurvesROIWidget.py
+++ b/silx/gui/plot/test/testCurvesROIWidget.py
@@ -375,13 +375,13 @@ class TestRoiWidgetSignals(TestCaseQt):
self.listener.clear()
roi1 = CurvesROIWidget.ROI(name='linear', fromdata=0, todata=5)
- self.curves_roi_widget.roiTable.addRoi(roi1)
+ self.curves_roi_widget.roiTable.registerROI(roi1)
self.assertEqual(self.listener.callCount(), 1)
self.assertTrue(self.listener.arguments()[0][0]['current'] == 'linear')
self.listener.clear()
roi2 = CurvesROIWidget.ROI(name='linear2', fromdata=0, todata=5)
- self.curves_roi_widget.roiTable.addRoi(roi2)
+ self.curves_roi_widget.roiTable.registerROI(roi2)
self.assertEqual(self.listener.callCount(), 1)
self.assertTrue(self.listener.arguments()[0][0]['current'] == 'linear2')
self.listener.clear()
@@ -398,7 +398,7 @@ class TestRoiWidgetSignals(TestCaseQt):
self.assertTrue(self.listener.arguments()[0][0]['current'] is None)
self.listener.clear()
- self.curves_roi_widget.roiTable.addRoi(roi1)
+ self.curves_roi_widget.roiTable.registerROI(roi1)
self.assertEqual(self.listener.callCount(), 1)
self.assertTrue(self.listener.arguments()[0][0]['current'] == 'linear')
self.assertTrue(self.curves_roi_widget.roiTable.activeRoi == roi1)
@@ -415,7 +415,7 @@ class TestRoiWidgetSignals(TestCaseQt):
"""Test SigROISignal when modifying it"""
self.curves_roi_widget.roiTable.setMiddleROIMarkerFlag(True)
roi1 = CurvesROIWidget.ROI(name='linear', fromdata=2, todata=5)
- self.curves_roi_widget.roiTable.addRoi(roi1)
+ self.curves_roi_widget.roiTable.registerROI(roi1)
self.curves_roi_widget.roiTable.setActiveRoi(roi1)
# test modify the roi2 object
@@ -450,7 +450,7 @@ class TestRoiWidgetSignals(TestCaseQt):
def testSetActiveCurve(self):
"""Test sigRoiSignal when set an active curve"""
roi1 = CurvesROIWidget.ROI(name='linear', fromdata=2, todata=5)
- self.curves_roi_widget.roiTable.addRoi(roi1)
+ self.curves_roi_widget.roiTable.registerROI(roi1)
self.curves_roi_widget.roiTable.setActiveRoi(roi1)
self.listener.clear()
self.plot.setActiveCurve('curve0')
diff --git a/silx/gui/plot/test/testItem.py b/silx/gui/plot/test/testItem.py
index ad739a2..8dacdea 100644
--- a/silx/gui/plot/test/testItem.py
+++ b/silx/gui/plot/test/testItem.py
@@ -35,6 +35,7 @@ import numpy
from silx.gui.utils.testutils import SignalListener
from silx.gui.plot.items import ItemChangedType
+from silx.gui.plot import items
from .utils import PlotWidgetTestCase
@@ -242,11 +243,96 @@ class TestSymbol(PlotWidgetTestCase):
self.assertEqual('Diamond', name)
+class TestVisibleExtent(PlotWidgetTestCase):
+ """Test item's visible extent feature"""
+
+ def testGetVisibleBounds(self):
+ """Test Item.getVisibleBounds"""
+
+ # Create test items (with a bounding box of x: [1,3], y: [0,2])
+ curve = items.Curve()
+ curve.setData((1, 2, 3), (0, 1, 2))
+
+ histogram = items.Histogram()
+ histogram.setData((0, 1, 2), (1, 5/3, 7/3, 3))
+
+ image = items.ImageData()
+ image.setOrigin((1, 0))
+ image.setData(numpy.arange(4).reshape(2, 2))
+
+ scatter = items.Scatter()
+ scatter.setData((1, 2, 3), (0, 1, 2), (1, 2, 3))
+
+ bbox = items.BoundingRect()
+ bbox.setBounds((1, 3, 0, 2))
+
+ xaxis, yaxis = self.plot.getXAxis(), self.plot.getYAxis()
+ for item in (curve, histogram, image, scatter, bbox):
+ with self.subTest(item=item):
+ xaxis.setLimits(0, 100)
+ yaxis.setLimits(0, 100)
+ self.plot.addItem(item)
+ self.assertEqual(item.getVisibleBounds(), (1., 3., 0., 2.))
+
+ xaxis.setLimits(0.5, 2.5)
+ self.assertEqual(item.getVisibleBounds(), (1, 2.5, 0., 2.))
+
+ yaxis.setLimits(0.5, 1.5)
+ self.assertEqual(item.getVisibleBounds(), (1, 2.5, 0.5, 1.5))
+
+ item.setVisible(False)
+ self.assertIsNone(item.getVisibleBounds())
+
+ self.plot.clear()
+
+ def testVisibleExtentTracking(self):
+ """Test Item's visible extent tracking"""
+ image = items.ImageData()
+ image.setData(numpy.arange(6).reshape(2, 3))
+
+ listener = SignalListener()
+ image._sigVisibleBoundsChanged.connect(listener)
+ image._setVisibleBoundsTracking(True)
+ self.assertTrue(image._isVisibleBoundsTracking())
+
+ self.plot.addItem(image)
+ self.assertEqual(listener.callCount(), 1)
+
+ self.plot.getXAxis().setLimits(0, 1)
+ self.assertEqual(listener.callCount(), 2)
+
+ self.plot.hide()
+ self.qapp.processEvents()
+ # No event here
+ self.assertEqual(listener.callCount(), 2)
+
+ self.plot.getXAxis().setLimits(1, 2)
+ # No event since PlotWidget is hidden, delayed to PlotWidget show
+ self.assertEqual(listener.callCount(), 2)
+
+ self.plot.show()
+ self.qapp.processEvents()
+ # Receives delayed event now
+ self.assertEqual(listener.callCount(), 3)
+
+ image.setOrigin((-1, -1))
+ self.assertEqual(listener.callCount(), 4)
+
+ image.setVisible(False)
+ image.setOrigin((0, 0))
+ # No event since item is not visible
+ self.assertEqual(listener.callCount(), 4)
+
+ image.setVisible(True)
+ # Receives delayed event now
+ self.assertEqual(listener.callCount(), 5)
+
+
def suite():
test_suite = unittest.TestSuite()
loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite.addTest(loadTests(TestSigItemChangedSignal))
- test_suite.addTest(loadTests(TestSymbol))
+ for klass in (TestSigItemChangedSignal, TestSymbol, TestVisibleExtent):
+ test_suite.addTest(loadTests(klass))
return test_suite
diff --git a/silx/gui/plot/test/testMaskToolsWidget.py b/silx/gui/plot/test/testMaskToolsWidget.py
index a05c1be..2e8db55 100644
--- a/silx/gui/plot/test/testMaskToolsWidget.py
+++ b/silx/gui/plot/test/testMaskToolsWidget.py
@@ -84,10 +84,15 @@ class TestMaskToolsWidget(PlotWidgetTestCase, ParametricTestCase):
self.mouseMove(plot, pos=(0, 0))
self.mouseMove(plot, pos=pos0)
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos0)
- self.mouseMove(plot, pos=(0, 0))
+ self.qapp.processEvents()
+ self.mousePress(plot, qt.Qt.LeftButton, pos=pos0)
+ self.qapp.processEvents()
+ self.mouseMove(plot, pos=(pos0[0] + offset // 2, pos0[1] + offset // 2))
self.mouseMove(plot, pos=pos1)
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos1)
+ self.qapp.processEvents()
+ self.mouseRelease(plot, qt.Qt.LeftButton, pos=pos1)
+ self.qapp.processEvents()
+ self.mouseMove(plot, pos=(0, 0))
def _drawPolygon(self):
"""Draw a star polygon in the plot"""
@@ -106,7 +111,9 @@ class TestMaskToolsWidget(PlotWidgetTestCase, ParametricTestCase):
for pos in star:
self.mouseMove(plot, pos=pos)
self.qapp.processEvents()
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos)
+ self.mousePress(plot, qt.Qt.LeftButton, pos=pos)
+ self.qapp.processEvents()
+ self.mouseRelease(plot, qt.Qt.LeftButton, pos=pos)
self.qapp.processEvents()
def _drawPencil(self):
diff --git a/silx/gui/plot/test/testPlotInteraction.py b/silx/gui/plot/test/testPlotInteraction.py
index 335b1e4..7a30434 100644
--- a/silx/gui/plot/test/testPlotInteraction.py
+++ b/silx/gui/plot/test/testPlotInteraction.py
@@ -68,7 +68,11 @@ class TestSelectPolygon(PlotWidgetTestCase):
for pos in polygon:
self.mouseMove(plot, pos=pos)
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos)
+ self.qapp.processEvents()
+ self.mousePress(plot, qt.Qt.LeftButton, pos=pos)
+ self.qapp.processEvents()
+ self.mouseRelease(plot, qt.Qt.LeftButton, pos=pos)
+ self.qapp.processEvents()
self.plot.sigPlotSignal.disconnect(dump)
return [args[0] for args in dump.received]
diff --git a/silx/gui/plot/test/testPlotWidget.py b/silx/gui/plot/test/testPlotWidget.py
index 4ef6a72..f9d2281 100755
--- a/silx/gui/plot/test/testPlotWidget.py
+++ b/silx/gui/plot/test/testPlotWidget.py
@@ -43,7 +43,7 @@ from silx.test.utils import test_options
from silx.gui import qt
from silx.gui.plot import PlotWidget
from silx.gui.plot.items.curve import CurveStyle
-from silx.gui.plot.items import BoundingRect, XAxisExtent, YAxisExtent
+from silx.gui.plot.items import BoundingRect, XAxisExtent, YAxisExtent, Axis
from silx.gui.colors import Colormap
from .utils import PlotWidgetTestCase
@@ -326,6 +326,23 @@ class TestPlotImage(PlotWidgetTestCase, ParametricTestCase):
resetzoom=False)
self.plot.resetZoom()
+ def testPlotColormapNaNColor(self):
+ self.plot.setKeepDataAspectRatio(False)
+ self.plot.setGraphTitle('Colormap with NaN color')
+
+ colormap = Colormap()
+ colormap.setNaNColor('red')
+ self.assertEqual(colormap.getNaNColor(), qt.QColor(255, 0, 0))
+ data = DATA_2D.astype(numpy.float32)
+ data[len(data)//2:] = numpy.nan
+ self.plot.addImage(data, legend="image 1", colormap=colormap,
+ resetzoom=False)
+ self.plot.resetZoom()
+
+ colormap.setNaNColor((0., 1., 0., 1.))
+ self.assertEqual(colormap.getNaNColor(), qt.QColor(0, 255, 0))
+ self.qapp.processEvents()
+
def testImageOriginScale(self):
"""Test of image with different origin and scale"""
self.plot.setGraphTitle('origin and scale')
@@ -401,7 +418,7 @@ class TestPlotImage(PlotWidgetTestCase, ParametricTestCase):
def testPlotBooleanImage(self):
"""Test that a boolean image is displayed and converted to int8."""
- data = numpy.zeros((10, 10), dtype=numpy.bool)
+ data = numpy.zeros((10, 10), dtype=bool)
data[::2, ::2] = True
self.plot.addImage(data, legend='boolean')
@@ -438,6 +455,21 @@ class TestPlotCurve(PlotWidgetTestCase):
self.plot.setActiveCurveHandling(False)
+ def testPlotCurveInfinite(self):
+ """Test plot curves with not finite data"""
+ tests = {
+ 'y all not finite': ([0, 1, 2], [numpy.inf, numpy.nan, -numpy.inf]),
+ 'x all not finite': ([numpy.inf, numpy.nan, -numpy.inf], [0, 1, 2]),
+ 'x some inf': ([0, numpy.inf, 2], [0, 1, 2]),
+ 'y some inf': ([0, 1, 2], [0, numpy.inf, 2])
+ }
+ for name, args in tests.items():
+ with self.subTest(name):
+ self.plot.addCurve(*args)
+ self.plot.resetZoom()
+ self.qapp.processEvents()
+ self.plot.clear()
+
def testPlotCurveColorFloat(self):
color = numpy.array(numpy.random.random(3 * 1000),
dtype=numpy.float32).reshape(1000, 3)
@@ -799,17 +831,25 @@ class TestPlotItem(PlotWidgetTestCase):
"""Basic tests for addItem."""
# Polygon coordinates and color
- polygons = [ # legend, x coords, y coords, color
+ POLYGONS = [ # legend, x coords, y coords, color
('triangle', numpy.array((10, 30, 50)),
numpy.array((55, 70, 55)), 'red'),
('square', numpy.array((10, 10, 50, 50)),
numpy.array((10, 50, 50, 10)), 'green'),
('star', numpy.array((60, 70, 80, 60, 80)),
numpy.array((25, 50, 25, 40, 40)), 'blue'),
+ ('2 triangles-simple',
+ numpy.array((90., 95., 100., numpy.nan, 90., 95., 100.)),
+ numpy.array((25., 5., 25., numpy.nan, 30., 50., 30.)),
+ 'pink'),
+ ('2 triangles-extra NaN',
+ numpy.array((numpy.nan, 90., 95., 100., numpy.nan, 0., 90., 95., 100., numpy.nan)),
+ numpy.array((0., 55., 70., 55., numpy.nan, numpy.nan, 75., 90., 75., numpy.nan)),
+ 'black'),
]
# Rectangle coordinantes and color
- rectangles = [ # legend, x coords, y coords, color
+ RECTANGLES = [ # legend, x coords, y coords, color
('square 1', numpy.array((1., 10.)),
numpy.array((1., 10.)), 'red'),
('square 2', numpy.array((10., 20.)),
@@ -822,6 +862,8 @@ class TestPlotItem(PlotWidgetTestCase):
numpy.array((45., 45.)), 'darkRed'),
]
+ SCALES = Axis.LINEAR, Axis.LOGARITHMIC
+
def setUp(self):
super(TestPlotItem, self).setUp()
@@ -833,40 +875,60 @@ class TestPlotItem(PlotWidgetTestCase):
self.plot.setLimits(0., 100., -100., 100.)
def testPlotItemPolygonFill(self):
- self.plot.setGraphTitle('Item Fill')
-
- for legend, xList, yList, color in self.polygons:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="polygon", fill=True, color=color)
- self.plot.resetZoom()
+ for scale in self.SCALES:
+ with self.subTest(scale=scale):
+ self.plot.clear()
+ self.plot.getXAxis().setScale(scale)
+ self.plot.getYAxis().setScale(scale)
+ self.plot.setGraphTitle('Item Fill %s' % scale)
+
+ for legend, xList, yList, color in self.POLYGONS:
+ self.plot.addShape(xList, yList, legend=legend,
+ replace=False, linestyle='--',
+ shape="polygon", fill=True, color=color)
+ self.plot.resetZoom()
def testPlotItemPolygonNoFill(self):
- self.plot.setGraphTitle('Item No Fill')
-
- for legend, xList, yList, color in self.polygons:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="polygon", fill=False, color=color)
- self.plot.resetZoom()
+ for scale in self.SCALES:
+ with self.subTest(scale=scale):
+ self.plot.clear()
+ self.plot.getXAxis().setScale(scale)
+ self.plot.getYAxis().setScale(scale)
+ self.plot.setGraphTitle('Item No Fill %s' % scale)
+
+ for legend, xList, yList, color in self.POLYGONS:
+ self.plot.addShape(xList, yList, legend=legend,
+ replace=False, linestyle='--',
+ shape="polygon", fill=False, color=color)
+ self.plot.resetZoom()
def testPlotItemRectangleFill(self):
- self.plot.setGraphTitle('Rectangle Fill')
-
- for legend, xList, yList, color in self.rectangles:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="rectangle", fill=True, color=color)
- self.plot.resetZoom()
+ for scale in self.SCALES:
+ with self.subTest(scale=scale):
+ self.plot.clear()
+ self.plot.getXAxis().setScale(scale)
+ self.plot.getYAxis().setScale(scale)
+ self.plot.setGraphTitle('Rectangle Fill %s' % scale)
+
+ for legend, xList, yList, color in self.RECTANGLES:
+ self.plot.addShape(xList, yList, legend=legend,
+ replace=False,
+ shape="rectangle", fill=True, color=color)
+ self.plot.resetZoom()
def testPlotItemRectangleNoFill(self):
- self.plot.setGraphTitle('Rectangle No Fill')
-
- for legend, xList, yList, color in self.rectangles:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="rectangle", fill=False, color=color)
- self.plot.resetZoom()
+ for scale in self.SCALES:
+ with self.subTest(scale=scale):
+ self.plot.clear()
+ self.plot.getXAxis().setScale(scale)
+ self.plot.getYAxis().setScale(scale)
+ self.plot.setGraphTitle('Rectangle No Fill %s' % scale)
+
+ for legend, xList, yList, color in self.RECTANGLES:
+ self.plot.addShape(xList, yList, legend=legend,
+ replace=False,
+ shape="rectangle", fill=False, color=color)
+ self.plot.resetZoom()
class TestPlotActiveCurveImage(PlotWidgetTestCase):
@@ -1384,6 +1446,20 @@ class TestPlotAxes(TestCaseQt, ParametricTestCase):
"""Test coverage on setAxesDisplayed(True)"""
self.plot.setAxesDisplayed(True)
+ def testAxesMargins(self):
+ """Test PlotWidget's getAxesMargins and setAxesMargins"""
+ self.plot.show()
+ self.qWaitForWindowExposed(self.plot)
+
+ margins = self.plot.getAxesMargins()
+ self.assertEqual(margins, (.15, .1, .1, .15))
+
+ for margins in ((0., 0., 0., 0.), (.15, .1, .1, .15)):
+ with self.subTest(margins=margins):
+ self.plot.setAxesMargins(*margins)
+ self.qapp.processEvents()
+ self.assertEqual(self.plot.getAxesMargins(), margins)
+
def testBoundingRectItem(self):
item = BoundingRect()
item.setBounds((-1000, 1000, -2000, 2000))
@@ -1752,80 +1828,33 @@ class TestPlotMarkerLog(PlotWidgetTestCase):
self.plot.resetZoom()
-class TestPlotItemLog(PlotWidgetTestCase):
- """Basic tests for items with log scale axes"""
+class TestPlotWidgetSwitchBackend(PlotWidgetTestCase):
+ """Test [get|set]Backend to switch backend"""
- # Polygon coordinates and color
- polygons = [ # legend, x coords, y coords, color
- ('triangle', numpy.array((10, 30, 50)),
- numpy.array((55, 70, 55)), 'red'),
- ('square', numpy.array((10, 10, 50, 50)),
- numpy.array((10, 50, 50, 10)), 'green'),
- ('star', numpy.array((60, 70, 80, 60, 80)),
- numpy.array((25, 50, 25, 40, 40)), 'blue'),
- ]
-
- # Rectangle coordinantes and color
- rectangles = [ # legend, x coords, y coords, color
- ('square 1', numpy.array((1., 10.)),
- numpy.array((1., 10.)), 'red'),
- ('square 2', numpy.array((10., 20.)),
- numpy.array((10., 20.)), 'green'),
- ('square 3', numpy.array((20., 30.)),
- numpy.array((20., 30.)), 'blue'),
- ('rect 1', numpy.array((1., 30.)),
- numpy.array((35., 40.)), 'black'),
- ('line h', numpy.array((1., 30.)),
- numpy.array((45., 45.)), 'darkRed'),
- ]
-
- def setUp(self):
- super(TestPlotItemLog, self).setUp()
+ def testSwitchBackend(self):
+ """Test switching a plot with a few items"""
+ backends = {'none': 'BackendBase', 'mpl': 'BackendMatplotlibQt'}
+ if test_options.WITH_GL_TEST:
+ backends['gl'] = 'BackendOpenGL'
- self.plot.getYAxis().setLabel('Rows')
- self.plot.getXAxis().setLabel('Columns')
- self.plot.getXAxis().setAutoScale(False)
- self.plot.getYAxis().setAutoScale(False)
- self.plot.setKeepDataAspectRatio(False)
- self.plot.setLimits(1., 100., 1., 100.)
- self.plot.getXAxis()._setLogarithmic(True)
- self.plot.getYAxis()._setLogarithmic(True)
-
- def testPlotItemPolygonLogFill(self):
- self.plot.setGraphTitle('Item Fill Log')
-
- for legend, xList, yList, color in self.polygons:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="polygon", fill=True, color=color)
- self.plot.resetZoom()
-
- def testPlotItemPolygonLogNoFill(self):
- self.plot.setGraphTitle('Item No Fill Log')
-
- for legend, xList, yList, color in self.polygons:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="polygon", fill=False, color=color)
- self.plot.resetZoom()
-
- def testPlotItemRectangleLogFill(self):
- self.plot.setGraphTitle('Rectangle Fill Log')
-
- for legend, xList, yList, color in self.rectangles:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="rectangle", fill=True, color=color)
+ self.plot.addImage(numpy.arange(100).reshape(10, 10))
+ self.plot.addCurve((-3, -2, -1), (1, 2, 3))
self.plot.resetZoom()
+ xlimits = self.plot.getXAxis().getLimits()
+ ylimits = self.plot.getYAxis().getLimits()
+ items = self.plot.getItems()
+ self.assertEqual(len(items), 2)
- def testPlotItemRectangleLogNoFill(self):
- self.plot.setGraphTitle('Rectangle No Fill Log')
+ for backend, className in backends.items():
+ with self.subTest(backend=backend):
+ self.plot.setBackend(backend)
+ self.plot.replot()
- for legend, xList, yList, color in self.rectangles:
- self.plot.addShape(xList, yList, legend=legend,
- replace=False,
- shape="rectangle", fill=False, color=color)
- self.plot.resetZoom()
+ retrievedBackend = self.plot.getBackend()
+ self.assertEqual(type(retrievedBackend).__name__, className)
+ self.assertEqual(self.plot.getXAxis().getLimits(), xlimits)
+ self.assertEqual(self.plot.getYAxis().getLimits(), ylimits)
+ self.assertEqual(self.plot.getItems(), items)
def suite():
@@ -1841,8 +1870,7 @@ def suite():
TestPlotEmptyLog,
TestPlotCurveLog,
TestPlotImageLog,
- TestPlotMarkerLog,
- TestPlotItemLog)
+ TestPlotMarkerLog)
test_suite = unittest.TestSuite()
@@ -1859,6 +1887,9 @@ def suite():
for testClass in testClasses:
test_suite.addTest(parameterize(testClass, backend='gl'))
+ test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(
+ TestPlotWidgetSwitchBackend))
+
return test_suite
diff --git a/silx/gui/plot/test/testPlotWindow.py b/silx/gui/plot/test/testPlotWindow.py
index 8e7b35c..e12b756 100644
--- a/silx/gui/plot/test/testPlotWindow.py
+++ b/silx/gui/plot/test/testPlotWindow.py
@@ -33,12 +33,12 @@ import unittest
import numpy
from silx.gui.utils.testutils import TestCaseQt, getQToolButtonFromAction
+from silx.test.utils import test_options
from silx.gui import qt
from silx.gui.plot import PlotWindow
from silx.gui.colors import Colormap
-
class TestPlotWindow(TestCaseQt):
"""Base class for tests of PlotWindow."""
@@ -155,6 +155,25 @@ class TestPlotWindow(TestCaseQt):
self.assertEqual(self._count, 1)
del self._count
+ @unittest.skipUnless(test_options.WITH_GL_TEST,
+ test_options.WITH_QT_TEST_REASON)
+ def testSwitchBackend(self):
+ """Test switching an empty plot"""
+ self.plot.resetZoom()
+ xlimits = self.plot.getXAxis().getLimits()
+ ylimits = self.plot.getYAxis().getLimits()
+ isKeepAspectRatio = self.plot.isKeepDataAspectRatio()
+
+ for backend in ('gl', 'mpl'):
+ with self.subTest():
+ self.plot.setBackend(backend)
+ self.plot.replot()
+ self.assertEqual(self.plot.getXAxis().getLimits(), xlimits)
+ self.assertEqual(self.plot.getYAxis().getLimits(), ylimits)
+ self.assertEqual(
+ self.plot.isKeepDataAspectRatio(), isKeepAspectRatio)
+
+
def suite():
test_suite = unittest.TestSuite()
test_suite.addTest(
diff --git a/silx/gui/plot/test/testRoiStatsWidget.py b/silx/gui/plot/test/testRoiStatsWidget.py
new file mode 100644
index 0000000..378d499
--- /dev/null
+++ b/silx/gui/plot/test/testRoiStatsWidget.py
@@ -0,0 +1,290 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2019 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.
+#
+# ###########################################################################*/
+"""Tests for ROIStatsWidget"""
+
+
+from silx.gui.utils.testutils import TestCaseQt
+from silx.gui import qt
+from silx.gui.plot import PlotWindow
+from silx.gui.plot.stats.stats import Stats
+from silx.gui.plot.ROIStatsWidget import ROIStatsWidget
+from silx.gui.plot.CurvesROIWidget import ROI
+from silx.gui.plot.items.roi import RectangleROI, PolygonROI
+from silx.gui.plot.StatsWidget import UpdateMode
+import unittest
+import numpy
+
+
+
+class _TestRoiStatsBase(TestCaseQt):
+ """Base class for several unittest relative to ROIStatsWidget"""
+ def setUp(self):
+ TestCaseQt.setUp(self)
+ # define plot
+ self.plot = PlotWindow()
+ self.plot.addImage(numpy.arange(10000).reshape(100, 100),
+ legend='img1')
+ self.img_item = self.plot.getImage('img1')
+ self.plot.addCurve(x=numpy.linspace(0, 10, 56), y=numpy.arange(56),
+ legend='curve1')
+ self.curve_item = self.plot.getCurve('curve1')
+ self.plot.addHistogram(edges=numpy.linspace(0, 10, 56),
+ histogram=numpy.arange(56), legend='histo1')
+ self.histogram_item = self.plot.getHistogram(legend='histo1')
+ self.plot.addScatter(x=numpy.linspace(0, 10, 56),
+ y=numpy.linspace(0, 10, 56),
+ value=numpy.arange(56),
+ legend='scatter1')
+ self.scatter_item = self.plot.getScatter(legend='scatter1')
+
+ # stats widget
+ self.statsWidget = ROIStatsWidget(plot=self.plot)
+
+ # define stats
+ stats = [
+ ('sum', numpy.sum),
+ ('mean', numpy.mean),
+ ]
+ self.statsWidget.setStats(stats=stats)
+
+ # define rois
+ self.roi1D = ROI(name='range1', fromdata=0, todata=4, type_='energy')
+ self.rectangle_roi = RectangleROI()
+ self.rectangle_roi.setGeometry(origin=(0, 0), size=(20, 20))
+ self.rectangle_roi.setName('Initial ROI')
+ self.polygon_roi = PolygonROI()
+ points = numpy.array([[0, 5], [5, 0], [10, 5], [5, 10]])
+ self.polygon_roi.setPoints(points)
+
+ def statsTable(self):
+ return self.statsWidget._statsROITable
+
+ def tearDown(self):
+ Stats._getContext.cache_clear()
+ self.statsWidget.setAttribute(qt.Qt.WA_DeleteOnClose, True)
+ self.statsWidget.close()
+ self.statsWidget = None
+ self.plot.setAttribute(qt.Qt.WA_DeleteOnClose, True)
+ self.plot.close()
+ self.plot = None
+ TestCaseQt.tearDown(self)
+
+
+class TestRoiStatsCouple(_TestRoiStatsBase):
+ """
+ Test different possible couple (roi, plotItem).
+ Check that:
+
+ * computation is correct if couple is valid
+ * raise an error if couple is invalid
+ """
+ def testROICurve(self):
+ """
+ Test that the couple (ROI, curveItem) can be used for stats
+ """
+ item = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.curve_item)
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), '253')
+ self.assertEqual(tableItems['mean'].text(), '11.0')
+
+ def testRectangleImage(self):
+ """
+ Test that the couple (RectangleROI, imageItem) can be used for stats
+ """
+ item = self.statsWidget.addItem(roi=self.rectangle_roi,
+ plotItem=self.img_item)
+ assert item is not None
+ self.plot.addImage(numpy.ones(10000).reshape(100, 100),
+ legend='img1')
+ self.qapp.processEvents()
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), str(float(21*21)))
+ self.assertEqual(tableItems['mean'].text(), '1.0')
+
+ def testPolygonImage(self):
+ """
+ Test that the couple (PolygonROI, imageItem) can be used for stats
+ """
+ item = self.statsWidget.addItem(roi=self.polygon_roi,
+ plotItem=self.img_item)
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), '22750')
+ self.assertEqual(tableItems['mean'].text(), '455.0')
+
+ def testROIImage(self):
+ """
+ Test that the couple (ROI, imageItem) is raising an error
+ """
+ with self.assertRaises(TypeError):
+ self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.img_item)
+
+ def testRectangleCurve(self):
+ """
+ Test that the couple (rectangleROI, curveItem) is raising an error
+ """
+ with self.assertRaises(TypeError):
+ self.statsWidget.addItem(roi=self.rectangle_roi,
+ plotItem=self.curve_item)
+
+ def testROIHistogram(self):
+ """
+ Test that the couple (PolygonROI, imageItem) can be used for stats
+ """
+ item = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.histogram_item)
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), '253')
+ self.assertEqual(tableItems['mean'].text(), '11.0')
+
+ def testROIScatter(self):
+ """
+ Test that the couple (PolygonROI, imageItem) can be used for stats
+ """
+ item = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.scatter_item)
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), '253')
+ self.assertEqual(tableItems['mean'].text(), '11.0')
+
+
+class TestRoiStatsAddRemoveItem(_TestRoiStatsBase):
+ """Test adding and removing (roi, plotItem) items"""
+ def testAddRemoveItems(self):
+ item1 = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.scatter_item)
+ self.assertTrue(item1 is not None)
+ self.assertEqual(self.statsTable().rowCount(), 1)
+ item2 = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.histogram_item)
+ self.assertTrue(item2 is not None)
+ self.assertEqual(self.statsTable().rowCount(), 2)
+ # try to add twice the same item
+ item3 = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.histogram_item)
+ self.assertTrue(item3 is None)
+ self.assertEqual(self.statsTable().rowCount(), 2)
+ item4 = self.statsWidget.addItem(roi=self.roi1D,
+ plotItem=self.curve_item)
+ self.assertTrue(item4 is not None)
+ self.assertEqual(self.statsTable().rowCount(), 3)
+
+ self.statsWidget.removeItem(plotItem=item4._plot_item,
+ roi=item4._roi)
+ self.assertEqual(self.statsTable().rowCount(), 2)
+ # try to remove twice the same item
+ self.statsWidget.removeItem(plotItem=item4._plot_item,
+ roi=item4._roi)
+ self.assertEqual(self.statsTable().rowCount(), 2)
+ self.statsWidget.removeItem(plotItem=item2._plot_item,
+ roi=item2._roi)
+ self.statsWidget.removeItem(plotItem=item1._plot_item,
+ roi=item1._roi)
+ self.assertEqual(self.statsTable().rowCount(), 0)
+
+
+class TestRoiStatsRoiUpdate(_TestRoiStatsBase):
+ """Test that the stats will be updated if the roi is updated"""
+ def testChangeRoi(self):
+ item = self.statsWidget.addItem(roi=self.rectangle_roi,
+ plotItem=self.img_item)
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), '445410')
+ self.assertEqual(tableItems['mean'].text(), '1010.0')
+
+ # update roi
+ self.rectangle_roi.setOrigin(position=(10, 10))
+ self.assertNotEqual(tableItems['sum'].text(), '445410')
+ self.assertNotEqual(tableItems['mean'].text(), '1010.0')
+
+ def testUpdateModeScenario(self):
+ """Test update according to a simple scenario"""
+ self.statsWidget._setUpdateMode(UpdateMode.AUTO)
+ item = self.statsWidget.addItem(roi=self.rectangle_roi,
+ plotItem=self.img_item)
+
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['sum'].text(), '445410')
+ self.assertEqual(tableItems['mean'].text(), '1010.0')
+ self.statsWidget._setUpdateMode(UpdateMode.MANUAL)
+ self.rectangle_roi.setOrigin(position=(10, 10))
+ self.qapp.processEvents()
+ self.assertNotEqual(tableItems['sum'].text(), '445410')
+ self.assertNotEqual(tableItems['mean'].text(), '1010.0')
+ self.statsWidget._updateAllStats(is_request=True)
+ self.assertNotEqual(tableItems['sum'].text(), '445410')
+ self.assertNotEqual(tableItems['mean'].text(), '1010.0')
+
+
+class TestRoiStatsPlotItemUpdate(_TestRoiStatsBase):
+ """Test that the stats will be updated if the plot item is updated"""
+ def testChangeImage(self):
+ self.statsWidget._setUpdateMode(UpdateMode.AUTO)
+ item = self.statsWidget.addItem(roi=self.rectangle_roi,
+ plotItem=self.img_item)
+
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['mean'].text(), '1010.0')
+
+ # update plot
+ self.plot.addImage(numpy.arange(100, 10100).reshape(100, 100),
+ legend='img1')
+ self.assertNotEqual(tableItems['mean'].text(), '1059.5')
+
+ def testUpdateModeScenario(self):
+ """Test update according to a simple scenario"""
+ self.statsWidget._setUpdateMode(UpdateMode.MANUAL)
+ item = self.statsWidget.addItem(roi=self.rectangle_roi,
+ plotItem=self.img_item)
+
+ assert item is not None
+ tableItems = self.statsTable()._itemToTableItems(item)
+ self.assertEqual(tableItems['mean'].text(), '1010.0')
+ self.plot.addImage(numpy.arange(100, 10100).reshape(100, 100),
+ legend='img1')
+ self.assertEqual(tableItems['mean'].text(), '1010.0')
+ self.statsWidget._updateAllStats(is_request=True)
+ self.assertEqual(tableItems['mean'].text(), '1110.0')
+
+
+def suite():
+ test_suite = unittest.TestSuite()
+ for TestClass in (TestRoiStatsCouple, TestRoiStatsRoiUpdate,
+ TestRoiStatsPlotItemUpdate):
+ test_suite.addTest(
+ unittest.defaultTestLoader.loadTestsFromTestCase(TestClass))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/silx/gui/plot/test/testScatterMaskToolsWidget.py b/silx/gui/plot/test/testScatterMaskToolsWidget.py
index 171ec42..800f30e 100644
--- a/silx/gui/plot/test/testScatterMaskToolsWidget.py
+++ b/silx/gui/plot/test/testScatterMaskToolsWidget.py
@@ -86,10 +86,16 @@ class TestScatterMaskToolsWidget(PlotWidgetTestCase, ParametricTestCase):
self.mouseMove(plot, pos=(0, 0))
self.mouseMove(plot, pos=pos0)
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos0)
- self.mouseMove(plot, pos=(0, 0))
+ self.qapp.processEvents()
+ self.mousePress(plot, qt.Qt.LeftButton, pos=pos0)
+ self.qapp.processEvents()
+
+ self.mouseMove(plot, pos=(pos0[0] + offset // 2, pos0[1] + offset // 2))
self.mouseMove(plot, pos=pos1)
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos1)
+ self.qapp.processEvents()
+ self.mouseRelease(plot, qt.Qt.LeftButton, pos=pos1)
+ self.qapp.processEvents()
+ self.mouseMove(plot, pos=(0, 0))
def _drawPolygon(self):
"""Draw a star polygon in the plot"""
@@ -108,7 +114,9 @@ class TestScatterMaskToolsWidget(PlotWidgetTestCase, ParametricTestCase):
for pos in star:
self.mouseMove(plot, pos=pos)
self.qapp.processEvents()
- self.mouseClick(plot, qt.Qt.LeftButton, pos=pos)
+ self.mousePress(plot, qt.Qt.LeftButton, pos=pos)
+ self.qapp.processEvents()
+ self.mouseRelease(plot, qt.Qt.LeftButton, pos=pos)
self.qapp.processEvents()
def _drawPencil(self):
diff --git a/silx/gui/plot/test/testStackView.py b/silx/gui/plot/test/testStackView.py
index 80c85d6..7605bbc 100644
--- a/silx/gui/plot/test/testStackView.py
+++ b/silx/gui/plot/test/testStackView.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2016-2020 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
@@ -60,6 +60,19 @@ class TestStackView(TestCaseQt):
del self.stackview
super(TestStackView, self).tearDown()
+ def testScaleColormapRangeToStack(self):
+ """Test scaleColormapRangeToStack"""
+ self.stackview.setStack(self.mystack)
+ self.stackview.setColormap("viridis")
+ colormap = self.stackview.getColormap()
+
+ # Colormap autoscale to image
+ self.assertEqual(colormap.getVRange(), (None, None))
+ self.stackview.scaleColormapRangeToStack()
+
+ # Colormap range set according to stack range
+ self.assertEqual(colormap.getVRange(), (self.mystack.min(), self.mystack.max()))
+
def testSetStack(self):
self.stackview.setStack(self.mystack)
self.stackview.setColormap("viridis", autoscale=True)
diff --git a/silx/gui/plot/test/testStats.py b/silx/gui/plot/test/testStats.py
index 8db8cc9..d5046ba 100644
--- a/silx/gui/plot/test/testStats.py
+++ b/silx/gui/plot/test/testStats.py
@@ -35,6 +35,11 @@ from silx.gui.plot import StatsWidget
from silx.gui.plot.stats import statshandler
from silx.gui.utils.testutils import TestCaseQt, SignalListener
from silx.gui.plot import Plot1D, Plot2D
+from silx.gui.plot3d.SceneWidget import SceneWidget
+from silx.gui.plot.items.roi import RectangleROI, PolygonROI
+from silx.gui.plot.tools.roi import RegionOfInterestManager
+from silx.gui.plot.stats.stats import Stats
+from silx.gui.plot.CurvesROIWidget import ROI
from silx.utils.testutils import ParametricTestCase
import unittest
import logging
@@ -43,12 +48,9 @@ import numpy
_logger = logging.getLogger(__name__)
-class TestStats(TestCaseQt):
- """
- Test :class:`BaseClass` class and inheriting classes
- """
+class TestStatsBase(object):
+ """Base class for stats TestCase"""
def setUp(self):
- TestCaseQt.setUp(self)
self.createCurveContext()
self.createImageContext()
self.createScatterContext()
@@ -63,7 +65,6 @@ class TestStats(TestCaseQt):
self.scatterPlot.setAttribute(qt.Qt.WA_DeleteOnClose)
self.scatterPlot.close()
del self.scatterPlot
- TestCaseQt.tearDown(self)
def createCurveContext(self):
self.plot1d = Plot1D()
@@ -74,12 +75,13 @@ class TestStats(TestCaseQt):
self.curveContext = stats._CurveContext(
item=self.plot1d.getCurve('curve0'),
plot=self.plot1d,
- onlimits=False)
+ onlimits=False,
+ roi=None)
def createScatterContext(self):
self.scatterPlot = Plot2D()
lgd = 'scatter plot'
- self.xScatterData = numpy.array([0, 1, 2, 20, 50, 60, 36])
+ self.xScatterData = numpy.array([0, 2, 3, 20, 50, 60, 36])
self.yScatterData = numpy.array([2, 3, 4, 26, 69, 6, 18])
self.valuesScatterData = numpy.array([5, 6, 7, 10, 90, 20, 5])
self.scatterPlot.addScatter(self.xScatterData, self.yScatterData,
@@ -87,7 +89,8 @@ class TestStats(TestCaseQt):
self.scatterContext = stats._ScatterContext(
item=self.scatterPlot.getScatter(lgd),
plot=self.scatterPlot,
- onlimits=False
+ onlimits=False,
+ roi=None
)
def createImageContext(self):
@@ -99,7 +102,8 @@ class TestStats(TestCaseQt):
self.imageContext = stats._ImageContext(
item=self.plot2d.getImage(self._imgLgd),
plot=self.plot2d,
- onlimits=False
+ onlimits=False,
+ roi=None
)
def getBasicStats(self):
@@ -113,6 +117,19 @@ class TestStats(TestCaseQt):
'com': stats.StatCOM()
}
+
+class TestStats(TestStatsBase, TestCaseQt):
+ """
+ Test :class:`BaseClass` class and inheriting classes
+ """
+ def setUp(self):
+ TestCaseQt.setUp(self)
+ TestStatsBase.setUp(self)
+
+ def tearDown(self):
+ TestStatsBase.tearDown(self)
+ TestCaseQt.tearDown(self)
+
def testBasicStatsCurve(self):
"""Test result for simple stats on a curve"""
_stats = self.getBasicStats()
@@ -155,7 +172,8 @@ class TestStats(TestCaseQt):
image2Context = stats._ImageContext(
item=self.plot2d.getImage(self._imgLgd),
plot=self.plot2d,
- onlimits=False
+ onlimits=False,
+ roi=None,
)
_stats = self.getBasicStats()
self.assertEqual(_stats['min'].calculate(image2Context), 0)
@@ -225,21 +243,24 @@ class TestStats(TestCaseQt):
curveContextOnLimits = stats._CurveContext(
item=self.plot1d.getCurve('curve0'),
plot=self.plot1d,
- onlimits=True)
+ onlimits=True,
+ roi=None)
self.assertEqual(stat.calculate(curveContextOnLimits), 2)
self.plot2d.getXAxis().setLimitsConstraints(minPos=32)
imageContextOnLimits = stats._ImageContext(
item=self.plot2d.getImage('test image'),
plot=self.plot2d,
- onlimits=True)
+ onlimits=True,
+ roi=None)
self.assertEqual(stat.calculate(imageContextOnLimits), 32)
self.scatterPlot.getXAxis().setLimitsConstraints(minPos=40)
scatterContextOnLimits = stats._ScatterContext(
item=self.scatterPlot.getScatter('scatter plot'),
plot=self.scatterPlot,
- onlimits=True)
+ onlimits=True,
+ roi=None)
self.assertEqual(stat.calculate(scatterContextOnLimits), 20)
@@ -255,7 +276,8 @@ class TestStatsFormatter(TestCaseQt):
self.curveContext = stats._CurveContext(
item=self.plot1d.getCurve('curve0'),
plot=self.plot1d,
- onlimits=False)
+ onlimits=False,
+ roi=None)
self.stat = stats.StatMin()
@@ -295,6 +317,7 @@ class TestStatsHandler(TestCaseQt):
self.stat = stats.StatMin()
def tearDown(self):
+ Stats._getContext.cache_clear()
self.plot1d.setAttribute(qt.Qt.WA_DeleteOnClose)
self.plot1d.close()
self.plot1d = None
@@ -391,6 +414,7 @@ class TestStatsWidgetWithCurves(TestCaseQt, ParametricTestCase):
self.statsTable.setStats(mystats)
def tearDown(self):
+ Stats._getContext.cache_clear()
self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
self.plot.close()
self.statsTable = None
@@ -493,7 +517,6 @@ class TestStatsWidgetWithCurves(TestCaseQt, ParametricTestCase):
self.qapp.processEvents()
tableItems = self.statsTable._itemToTableItems(self.plot.getCurve('curve0'))
curve0_min = tableItems['min'].text()
- print(curve0_min)
self.assertTrue(float(curve0_min) == -1.)
self.plot.getCurve('curve0').setData(x=range(4), y=range(1, 5))
@@ -581,6 +604,7 @@ class TestStatsWidgetWithImages(TestCaseQt):
self.widget.setStats(mystats)
def tearDown(self):
+ Stats._getContext.cache_clear()
self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
self.plot.close()
self.widget.setAttribute(qt.Qt.WA_DeleteOnClose)
@@ -641,6 +665,7 @@ class TestStatsWidgetWithScatters(TestCaseQt):
self.widget.setStats(mystats)
def tearDown(self):
+ Stats._getContext.cache_clear()
self.scatterPlot.setAttribute(qt.Qt.WA_DeleteOnClose)
self.scatterPlot.close()
self.widget.setAttribute(qt.Qt.WA_DeleteOnClose)
@@ -694,6 +719,7 @@ class TestLineWidget(TestCaseQt):
stats=mystats)
def tearDown(self):
+ Stats._getContext.cache_clear()
self.qapp.processEvents()
self.plot.setAttribute(qt.Qt.WA_DeleteOnClose)
self.plot.close()
@@ -806,12 +832,223 @@ class TestUpdateModeWidget(TestCaseQt):
self.assertEqual(manualUpdateListener.callCount(), 2)
+class TestStatsROI(TestStatsBase, TestCaseQt):
+ """
+ Test stats based on ROI
+ """
+ def setUp(self):
+ TestCaseQt.setUp(self)
+ self.createRois()
+ TestStatsBase.setUp(self)
+ self.createHistogramContext()
+
+ self.roiManager = RegionOfInterestManager(self.plot2d)
+ self.roiManager.addRoi(self._2Droi_rect)
+ self.roiManager.addRoi(self._2Droi_poly)
+
+ def tearDown(self):
+ self.roiManager.clear()
+ self.roiManager = None
+ self._1Droi = None
+ self._2Droi_rect = None
+ self._2Droi_poly = None
+ self.plotHisto.setAttribute(qt.Qt.WA_DeleteOnClose)
+ self.plotHisto.close()
+ self.plotHisto = None
+ TestStatsBase.tearDown(self)
+ TestCaseQt.tearDown(self)
+
+ def createRois(self):
+ self._1Droi = ROI(name='my1DRoi', fromdata=2.0, todata=5.0)
+ self._2Droi_rect = RectangleROI()
+ self._2Droi_rect.setGeometry(size=(10, 10), origin=(10, 0))
+ self._2Droi_poly = PolygonROI()
+ points = numpy.array(((0, 20), (0, 0), (10, 0)))
+ self._2Droi_poly.setPoints(points=points)
+
+ def createCurveContext(self):
+ TestStatsBase.createCurveContext(self)
+ self.curveContext = stats._CurveContext(
+ item=self.plot1d.getCurve('curve0'),
+ plot=self.plot1d,
+ onlimits=False,
+ roi=self._1Droi)
+
+ def createHistogramContext(self):
+ self.plotHisto = Plot1D()
+ x = range(20)
+ y = range(20)
+ self.plotHisto.addHistogram(x, y, legend='histo0')
+
+ self.histoContext = stats._HistogramContext(
+ item=self.plotHisto.getHistogram('histo0'),
+ plot=self.plotHisto,
+ onlimits=False,
+ roi=self._1Droi)
+
+ def createScatterContext(self):
+ TestStatsBase.createScatterContext(self)
+ self.scatterContext = stats._ScatterContext(
+ item=self.scatterPlot.getScatter('scatter plot'),
+ plot=self.scatterPlot,
+ onlimits=False,
+ roi=self._1Droi
+ )
+
+ def createImageContext(self):
+ TestStatsBase.createImageContext(self)
+
+ self.imageContext = stats._ImageContext(
+ item=self.plot2d.getImage(self._imgLgd),
+ plot=self.plot2d,
+ onlimits=False,
+ roi=self._2Droi_rect
+ )
+
+ self.imageContext_2 = stats._ImageContext(
+ item=self.plot2d.getImage(self._imgLgd),
+ plot=self.plot2d,
+ onlimits=False,
+ roi=self._2Droi_poly
+ )
+
+ def testErrors(self):
+ # test if onlimits is True and give also a roi
+ with self.assertRaises(ValueError):
+ stats._CurveContext(item=self.plot1d.getCurve('curve0'),
+ plot=self.plot1d,
+ onlimits=True,
+ roi=self._1Droi)
+
+ # test if is a curve context and give an invalid 2D roi
+ with self.assertRaises(TypeError):
+ stats._CurveContext(item=self.plot1d.getCurve('curve0'),
+ plot=self.plot1d,
+ onlimits=False,
+ roi=self._2Droi_rect)
+
+ def testBasicStatsCurve(self):
+ """Test result for simple stats on a curve"""
+ _stats = self.getBasicStats()
+ xData = yData = numpy.array(range(0, 10))
+ self.assertEqual(_stats['min'].calculate(self.curveContext), 2)
+ self.assertEqual(_stats['max'].calculate(self.curveContext), 5)
+ self.assertEqual(_stats['minCoords'].calculate(self.curveContext), (2,))
+ self.assertEqual(_stats['maxCoords'].calculate(self.curveContext), (5,))
+ self.assertEqual(_stats['std'].calculate(self.curveContext), numpy.std(yData[2:6]))
+ self.assertEqual(_stats['mean'].calculate(self.curveContext), numpy.mean(yData[2:6]))
+ com = numpy.sum(xData[2:6] * yData[2:6]) / numpy.sum(yData[2:6])
+ self.assertEqual(_stats['com'].calculate(self.curveContext), com)
+
+ def testBasicStatsImageRectRoi(self):
+ """Test result for simple stats on an image"""
+ self.assertEqual(self.imageContext.values.compressed().size, 121)
+ _stats = self.getBasicStats()
+ self.assertEqual(_stats['min'].calculate(self.imageContext), 10)
+ self.assertEqual(_stats['max'].calculate(self.imageContext), 1300)
+ self.assertEqual(_stats['minCoords'].calculate(self.imageContext), (10, 0))
+ self.assertEqual(_stats['maxCoords'].calculate(self.imageContext), (20.0, 10.0))
+ self.assertAlmostEqual(_stats['std'].calculate(self.imageContext),
+ numpy.std(self.imageData[0:11, 10:21]))
+ self.assertAlmostEqual(_stats['mean'].calculate(self.imageContext),
+ numpy.mean(self.imageData[0:11, 10:21]))
+
+ compressed_values = self.imageContext.values.compressed()
+ compressed_values = compressed_values.reshape(11, 11)
+ yData = numpy.sum(compressed_values.astype(numpy.float64), axis=1)
+ xData = numpy.sum(compressed_values.astype(numpy.float64), axis=0)
+
+ dataYRange = range(11)
+ dataXRange = range(10, 21)
+
+ ycom = numpy.sum(yData*dataYRange) / numpy.sum(yData)
+ xcom = numpy.sum(xData*dataXRange) / numpy.sum(xData)
+ self.assertEqual(_stats['com'].calculate(self.imageContext), (xcom, ycom))
+
+ def testBasicStatsImagePolyRoi(self):
+ """Test a simple rectangle ROI"""
+ _stats = self.getBasicStats()
+ self.assertEqual(_stats['min'].calculate(self.imageContext_2), 0)
+ self.assertEqual(_stats['max'].calculate(self.imageContext_2), 2432)
+ self.assertEqual(_stats['minCoords'].calculate(self.imageContext_2), (0.0, 0.0))
+ # not 0.0, 19.0 because not fully in. Should all pixel have a weight,
+ # on to manage them in stats. For now 0 if the center is not in, else 1
+ self.assertEqual(_stats['maxCoords'].calculate(self.imageContext_2), (0.0, 19.0))
+
+ def testBasicStatsScatter(self):
+ self.assertEqual(self.scatterContext.values.compressed().size, 2)
+ _stats = self.getBasicStats()
+ self.assertEqual(_stats['min'].calculate(self.scatterContext), 6)
+ self.assertEqual(_stats['max'].calculate(self.scatterContext), 7)
+ self.assertEqual(_stats['minCoords'].calculate(self.scatterContext), (2, 3))
+ self.assertEqual(_stats['maxCoords'].calculate(self.scatterContext), (3, 4))
+ self.assertEqual(_stats['std'].calculate(self.scatterContext), numpy.std([6, 7]))
+ self.assertEqual(_stats['mean'].calculate(self.scatterContext), numpy.mean([6, 7]))
+
+ def testBasicHistogram(self):
+ _stats = self.getBasicStats()
+ xData = yData = numpy.array(range(2, 6))
+ self.assertEqual(_stats['min'].calculate(self.histoContext), 2)
+ self.assertEqual(_stats['max'].calculate(self.histoContext), 5)
+ self.assertEqual(_stats['minCoords'].calculate(self.histoContext), (2,))
+ self.assertEqual(_stats['maxCoords'].calculate(self.histoContext), (5,))
+ self.assertEqual(_stats['std'].calculate(self.histoContext), numpy.std(yData))
+ self.assertEqual(_stats['mean'].calculate(self.histoContext), numpy.mean(yData))
+ com = numpy.sum(xData * yData) / numpy.sum(yData)
+ self.assertEqual(_stats['com'].calculate(self.histoContext), com)
+
+
+class TestAdvancedROIImageContext(TestCaseQt):
+ """Test stats result on an image context with different scale and
+ origins"""
+
+ def setUp(self):
+ TestCaseQt.setUp(self)
+ self.data_dims = (100, 100)
+ self.data = numpy.random.rand(*self.data_dims)
+ self.plot = Plot2D()
+
+ def test(self):
+ """Test stats result on an image context with different scale and
+ origins"""
+ roi_origins = [(0, 0), (2, 10), (14, 20)]
+ img_origins = [(0, 0), (14, 20), (2, 10)]
+ img_scales = [1.0, 0.5, 2.0]
+ _stats = {'sum': stats.Stat(name='sum', fct=numpy.sum), }
+ for roi_origin in roi_origins:
+ for img_origin in img_origins:
+ for img_scale in img_scales:
+ with self.subTest(roi_origin=roi_origin,
+ img_origin=img_origin,
+ img_scale=img_scale):
+ self.plot.addImage(self.data, legend='img',
+ origin=img_origin,
+ scale=img_scale)
+ roi = RectangleROI()
+ roi.setGeometry(origin=roi_origin, size=(20, 20))
+ context = stats._ImageContext(
+ item=self.plot.getImage('img'),
+ plot=self.plot,
+ onlimits=False,
+ roi=roi)
+ x_start = int((roi_origin[0] - img_origin[0]) / img_scale)
+ x_end = int(x_start + (20 / img_scale)) + 1
+ y_start = int((roi_origin[1] - img_origin[1])/ img_scale)
+ y_end = int(y_start + (20 / img_scale)) + 1
+ x_start = max(x_start, 0)
+ x_end = min(max(x_end, 0), self.data_dims[1])
+ y_start = max(y_start, 0)
+ y_end = min(max(y_end, 0), self.data_dims[0])
+ th_sum = numpy.sum(self.data[y_start:y_end, x_start:x_end])
+ self.assertAlmostEqual(_stats['sum'].calculate(context),
+ th_sum)
+
def suite():
test_suite = unittest.TestSuite()
for TestClass in (TestStats, TestStatsHandler, TestStatsWidgetWithScatters,
TestStatsWidgetWithImages, TestStatsWidgetWithCurves,
- TestStatsFormatter, TestEmptyStatsWidget,
- TestLineWidget, TestUpdateModeWidget):
+ TestStatsFormatter, TestEmptyStatsWidget, TestStatsROI,
+ TestLineWidget, TestUpdateModeWidget, ):
test_suite.addTest(
unittest.defaultTestLoader.loadTestsFromTestCase(TestClass))
return test_suite