diff options
author | Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr> | 2018-07-31 16:22:25 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr> | 2018-07-31 16:22:25 +0200 |
commit | 159ef14fb9e198bb0066ea14e6b980f065de63dd (patch) | |
tree | bc37c7d4ba09ee59deb708897fa0571709aec293 /silx/gui/plot/test | |
parent | 270d5ddc31c26b62379e3caa9044dd75ccc71847 (diff) |
New upstream version 0.8.0+dfsg
Diffstat (limited to 'silx/gui/plot/test')
-rw-r--r-- | silx/gui/plot/test/__init__.py | 25 | ||||
-rw-r--r-- | silx/gui/plot/test/testColorBar.py | 12 | ||||
-rw-r--r-- | silx/gui/plot/test/testColormap.py | 353 | ||||
-rw-r--r-- | silx/gui/plot/test/testColormapDialog.py | 385 | ||||
-rw-r--r-- | silx/gui/plot/test/testColors.py | 94 | ||||
-rw-r--r-- | silx/gui/plot/test/testCurvesROIWidget.py | 24 | ||||
-rw-r--r-- | silx/gui/plot/test/testImageView.py | 4 | ||||
-rw-r--r-- | silx/gui/plot/test/testLimitConstraints.py | 6 | ||||
-rw-r--r-- | silx/gui/plot/test/testPixelIntensityHistoAction.py | 104 | ||||
-rw-r--r-- | silx/gui/plot/test/testPlotTools.py | 200 | ||||
-rw-r--r-- | silx/gui/plot/test/testPlotWidget.py | 71 | ||||
-rw-r--r-- | silx/gui/plot/test/testPlotWidgetNoBackend.py | 12 | ||||
-rw-r--r-- | silx/gui/plot/test/testSaveAction.py | 40 | ||||
-rw-r--r-- | silx/gui/plot/test/testScatterView.py | 115 | ||||
-rw-r--r-- | silx/gui/plot/test/testStackView.py | 15 | ||||
-rw-r--r-- | silx/gui/plot/test/testStats.py | 561 | ||||
-rw-r--r-- | silx/gui/plot/test/utils.py | 7 |
17 files changed, 923 insertions, 1105 deletions
diff --git a/silx/gui/plot/test/__init__.py b/silx/gui/plot/test/__init__.py index 154a70a..1428bad 100644 --- a/silx/gui/plot/test/__init__.py +++ b/silx/gui/plot/test/__init__.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility +# Copyright (c) 2016-2018 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 @@ -24,24 +24,21 @@ # ###########################################################################*/ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "28/11/2017" +__date__ = "24/04/2018" import unittest from .._utils import test from . import testColorBar -from . import testColormap -from . import testColormapDialog -from . import testColors from . import testCurvesROIWidget +from . import testStats from . import testAlphaSlider from . import testInteraction from . import testLegendSelector from . import testMaskToolsWidget from . import testScatterMaskToolsWidget from . import testPlotInteraction -from . import testPlotTools from . import testPlotWidgetNoBackend from . import testPlotWidget from . import testPlotWindow @@ -53,16 +50,21 @@ from . import testLimitConstraints from . import testComplexImageView from . import testImageView from . import testSaveAction +from . import testScatterView +from . import testPixelIntensityHistoAction def suite(): + # Lazy-loading to avoid cyclic reference + from ..tools import test as testTools + test_suite = unittest.TestSuite() test_suite.addTests( [test.suite(), + testTools.suite(), testColorBar.suite(), - testColors.suite(), - testColormapDialog.suite(), testCurvesROIWidget.suite(), + testStats.suite(), testAlphaSlider.suite(), testInteraction.suite(), testLegendSelector.suite(), @@ -70,16 +72,17 @@ def suite(): testScatterMaskToolsWidget.suite(), testPlotInteraction.suite(), testPlotWidgetNoBackend.suite(), - testPlotTools.suite(), testPlotWidget.suite(), testPlotWindow.suite(), testProfile.suite(), testStackView.suite(), - testColormap.suite(), testItem.suite(), testUtilsAxis.suite(), testLimitConstraints.suite(), testComplexImageView.suite(), testImageView.suite(), - testSaveAction.suite()]) + testSaveAction.suite(), + testScatterView.suite(), + testPixelIntensityHistoAction.suite() + ]) return test_suite diff --git a/silx/gui/plot/test/testColorBar.py b/silx/gui/plot/test/testColorBar.py index 80ae6a8..0d1c952 100644 --- a/silx/gui/plot/test/testColorBar.py +++ b/silx/gui/plot/test/testColorBar.py @@ -26,13 +26,13 @@ __authors__ = ["H. Payno"] __license__ = "MIT" -__date__ = "11/04/2017" +__date__ = "24/04/2018" import unittest from silx.gui.test.utils import TestCaseQt from silx.gui.plot.ColorBar import _ColorScale from silx.gui.plot.ColorBar import ColorBarWidget -from silx.gui.plot.Colormap import Colormap +from silx.gui.colors import Colormap from silx.gui.plot import Plot2D from silx.gui import qt import numpy @@ -64,7 +64,7 @@ class TestColorScale(TestCaseQt): vmin=0.0, vmax=1.0) self.colorScaleWidget.setColormap(self.colorMapLin1) - + self.assertTrue( self.colorScaleWidget.getValueFromRelativePosition(0.25) == 0.25) self.assertTrue( @@ -77,7 +77,7 @@ class TestColorScale(TestCaseQt): vmin=-10, vmax=0) self.colorScaleWidget.setColormap(self.colorMapLin2) - + self.assertTrue( self.colorScaleWidget.getValueFromRelativePosition(0.25) == -7.5) self.assertTrue( @@ -98,7 +98,7 @@ class TestColorScale(TestCaseQt): val = self.colorScaleWidget.getValueFromRelativePosition(0.5) self.assertTrue(val == 10.0) - + val = self.colorScaleWidget.getValueFromRelativePosition(0.0) self.assertTrue(val == 1.0) @@ -225,7 +225,7 @@ class TestColorBarWidget(TestCaseQt): self.assertTrue(self.colorBar.getColorScaleBar().maxVal == 30) # if data is positive - data[data<1] = data.max() + data[data < 1] = data.max() self.plot.addImage(data=data, colormap=colormapLog, legend='toto', diff --git a/silx/gui/plot/test/testColormap.py b/silx/gui/plot/test/testColormap.py deleted file mode 100644 index 4888a7c..0000000 --- a/silx/gui/plot/test/testColormap.py +++ /dev/null @@ -1,353 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2015-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. -# -# ###########################################################################*/ -"""This module provides the Colormap object -""" - -from __future__ import absolute_import - -__authors__ = ["H.Payno"] -__license__ = "MIT" -__date__ = "17/01/2018" - -import unittest -import numpy -from silx.utils.testutils import ParametricTestCase -from silx.gui.plot.Colormap import Colormap -from silx.gui.plot.Colormap import preferredColormaps, setPreferredColormaps -from silx.utils.exceptions import NotEditableError - - -class TestDictAPI(unittest.TestCase): - """Make sure the old dictionary API is working - """ - - def setUp(self): - self.vmin = -1.0 - self.vmax = 12 - - def testGetItem(self): - """test the item getter API ([xxx])""" - colormap = Colormap(name='viridis', - normalization=Colormap.LINEAR, - vmin=self.vmin, - vmax=self.vmax) - self.assertTrue(colormap['name'] == 'viridis') - self.assertTrue(colormap['normalization'] == Colormap.LINEAR) - self.assertTrue(colormap['vmin'] == self.vmin) - self.assertTrue(colormap['vmax'] == self.vmax) - with self.assertRaises(KeyError): - colormap['toto'] - - def testGetDict(self): - """Test the getDict function API""" - clmObject = Colormap(name='viridis', - normalization=Colormap.LINEAR, - vmin=self.vmin, - vmax=self.vmax) - clmDict = clmObject._toDict() - self.assertTrue(clmDict['name'] == 'viridis') - self.assertTrue(clmDict['autoscale'] is False) - self.assertTrue(clmDict['vmin'] == self.vmin) - self.assertTrue(clmDict['vmax'] == self.vmax) - self.assertTrue(clmDict['normalization'] == Colormap.LINEAR) - - clmObject.setVRange(None, None) - self.assertTrue(clmObject._toDict()['autoscale'] is True) - - def testSetValidDict(self): - """Test that if a colormap is created from a dict then it is correctly - created and the values are copied (so if some values from the dict - is changing, this won't affect the Colormap object""" - clm_dict = { - 'name': 'temperature', - 'vmin': 1.0, - 'vmax': 2.0, - 'normalization': 'linear', - 'colors': None, - 'autoscale': False - } - - # Test that the colormap is correctly created - colormapObject = Colormap._fromDict(clm_dict) - self.assertTrue(colormapObject.getName() == clm_dict['name']) - self.assertTrue(colormapObject.getColormapLUT() == clm_dict['colors']) - self.assertTrue(colormapObject.getVMin() == clm_dict['vmin']) - self.assertTrue(colormapObject.getVMax() == clm_dict['vmax']) - self.assertTrue(colormapObject.isAutoscale() == clm_dict['autoscale']) - - # Check that the colormap has copied the values - clm_dict['vmin'] = None - clm_dict['vmax'] = None - clm_dict['colors'] = [1.0, 2.0] - clm_dict['autoscale'] = True - clm_dict['normalization'] = Colormap.LOGARITHM - clm_dict['name'] = 'viridis' - - self.assertFalse(colormapObject.getName() == clm_dict['name']) - self.assertFalse(colormapObject.getColormapLUT() == clm_dict['colors']) - self.assertFalse(colormapObject.getVMin() == clm_dict['vmin']) - self.assertFalse(colormapObject.getVMax() == clm_dict['vmax']) - self.assertFalse(colormapObject.isAutoscale() == clm_dict['autoscale']) - - def testMissingKeysFromDict(self): - """Make sure we can create a Colormap object from a dictionnary even if - there is missing keys excepts if those keys are 'colors' or 'name' - """ - colormap = Colormap._fromDict({'name': 'toto'}) - self.assertTrue(colormap.getVMin() is None) - colormap = Colormap._fromDict({'colors': numpy.zeros(10)}) - self.assertTrue(colormap.getName() is None) - - with self.assertRaises(ValueError): - Colormap._fromDict({}) - - def testUnknowNorm(self): - """Make sure an error is raised if the given normalization is not - knowed - """ - clm_dict = { - 'name': 'temperature', - 'vmin': 1.0, - 'vmax': 2.0, - 'normalization': 'toto', - 'colors': None, - 'autoscale': False - } - with self.assertRaises(ValueError): - Colormap._fromDict(clm_dict) - - -class TestObjectAPI(ParametricTestCase): - """Test the new Object API of the colormap""" - def testVMinVMax(self): - """Test getter and setter associated to vmin and vmax values""" - vmin = 1.0 - vmax = 2.0 - - colormapObject = Colormap(name='viridis', - vmin=vmin, - vmax=vmax, - normalization=Colormap.LINEAR) - - with self.assertRaises(ValueError): - colormapObject.setVMin(3) - - with self.assertRaises(ValueError): - colormapObject.setVMax(-2) - - with self.assertRaises(ValueError): - colormapObject.setVRange(3, -2) - - self.assertTrue(colormapObject.getColormapRange() == (1.0, 2.0)) - self.assertTrue(colormapObject.isAutoscale() is False) - colormapObject.setVRange(None, None) - self.assertTrue(colormapObject.getVMin() is None) - self.assertTrue(colormapObject.getVMax() is None) - self.assertTrue(colormapObject.isAutoscale() is True) - - def testCopy(self): - """Make sure the copy function is correctly processing - """ - colormapObject = Colormap(name='toto', - colors=numpy.array([12, 13, 14]), - vmin=None, - vmax=None, - normalization=Colormap.LOGARITHM) - - colormapObject2 = colormapObject.copy() - self.assertTrue(colormapObject == colormapObject2) - colormapObject.setColormapLUT(numpy.array([0, 1])) - self.assertFalse(colormapObject == colormapObject2) - - colormapObject2 = colormapObject.copy() - self.assertTrue(colormapObject == colormapObject2) - colormapObject.setNormalization(Colormap.LINEAR) - self.assertFalse(colormapObject == colormapObject2) - - def testGetColorMapRange(self): - """Make sure the getColormapRange function of colormap is correctly - applying - """ - # test linear scale - data = numpy.array([-1, 1, 2, 3, float('nan')]) - cl1 = Colormap(name='gray', - normalization=Colormap.LINEAR, - vmin=0, - vmax=2) - cl2 = Colormap(name='gray', - normalization=Colormap.LINEAR, - vmin=None, - vmax=2) - cl3 = Colormap(name='gray', - normalization=Colormap.LINEAR, - vmin=0, - vmax=None) - cl4 = Colormap(name='gray', - normalization=Colormap.LINEAR, - vmin=None, - vmax=None) - - self.assertTrue(cl1.getColormapRange(data) == (0, 2)) - self.assertTrue(cl2.getColormapRange(data) == (-1, 2)) - self.assertTrue(cl3.getColormapRange(data) == (0, 3)) - self.assertTrue(cl4.getColormapRange(data) == (-1, 3)) - - # test linear with annoying cases - self.assertEqual(cl3.getColormapRange((-1, -2)), (0, 0)) - self.assertEqual(cl4.getColormapRange(()), (0., 1.)) - self.assertEqual(cl4.getColormapRange( - (float('nan'), float('inf'), 1., -float('inf'), 2)), (1., 2.)) - self.assertEqual(cl4.getColormapRange( - (float('nan'), float('inf'))), (0., 1.)) - - # test log scale - data = numpy.array([float('nan'), -1, 1, 10, 100, 1000]) - cl1 = Colormap(name='gray', - normalization=Colormap.LOGARITHM, - vmin=1, - vmax=100) - cl2 = Colormap(name='gray', - normalization=Colormap.LOGARITHM, - vmin=None, - vmax=100) - cl3 = Colormap(name='gray', - normalization=Colormap.LOGARITHM, - vmin=1, - vmax=None) - cl4 = Colormap(name='gray', - normalization=Colormap.LOGARITHM, - vmin=None, - vmax=None) - - self.assertTrue(cl1.getColormapRange(data) == (1, 100)) - self.assertTrue(cl2.getColormapRange(data) == (1, 100)) - self.assertTrue(cl3.getColormapRange(data) == (1, 1000)) - self.assertTrue(cl4.getColormapRange(data) == (1, 1000)) - - # test log with annoying cases - self.assertEqual(cl3.getColormapRange((0.1, 0.2)), (1, 1)) - self.assertEqual(cl4.getColormapRange((-2., -1.)), (1., 1.)) - self.assertEqual(cl4.getColormapRange(()), (1., 10.)) - self.assertEqual(cl4.getColormapRange( - (float('nan'), float('inf'), 1., -float('inf'), 2)), (1., 2.)) - self.assertEqual(cl4.getColormapRange( - (float('nan'), float('inf'))), (1., 10.)) - - def testApplyToData(self): - """Test applyToData on different datasets""" - datasets = [ - numpy.zeros((0, 0)), # Empty array - numpy.array((numpy.nan, numpy.inf)), # All non-finite - numpy.array((-numpy.inf, numpy.inf, 1.0, 2.0)), # Some infinite - ] - - for normalization in ('linear', 'log'): - colormap = Colormap(name='gray', - normalization=normalization, - vmin=None, - vmax=None) - - for data in datasets: - with self.subTest(data=data): - image = colormap.applyToData(data) - self.assertEqual(image.dtype, numpy.uint8) - self.assertEqual(image.shape[-1], 4) - self.assertEqual(image.shape[:-1], data.shape) - - def testGetNColors(self): - """Test getNColors method""" - # specific LUT - colormap = Colormap(name=None, - colors=((0, 0, 0), (1, 1, 1)), - vmin=1000, - vmax=2000) - colors = colormap.getNColors() - self.assertTrue(numpy.all(numpy.equal( - colors, - ((0, 0, 0, 255), (255, 255, 255, 255))))) - - def testEditableMode(self): - """Make sure the colormap will raise NotEditableError when try to - change a colormap not editable""" - colormap = Colormap() - colormap.setEditable(False) - with self.assertRaises(NotEditableError): - colormap.setVRange(0., 1.) - with self.assertRaises(NotEditableError): - colormap.setVMin(1.) - with self.assertRaises(NotEditableError): - colormap.setVMax(1.) - with self.assertRaises(NotEditableError): - colormap.setNormalization(Colormap.LOGARITHM) - with self.assertRaises(NotEditableError): - colormap.setName('magma') - with self.assertRaises(NotEditableError): - colormap.setColormapLUT(numpy.array([0, 1])) - with self.assertRaises(NotEditableError): - colormap._setFromDict(colormap._toDict()) - state = colormap.saveState() - with self.assertRaises(NotEditableError): - colormap.restoreState(state) - - -class TestPreferredColormaps(unittest.TestCase): - """Test get|setPreferredColormaps functions""" - - def setUp(self): - # Save preferred colormaps - self._colormaps = preferredColormaps() - - def tearDown(self): - # Restore saved preferred colormaps - setPreferredColormaps(self._colormaps) - - def test(self): - colormaps = 'viridis', 'magma' - - setPreferredColormaps(colormaps) - self.assertEqual(preferredColormaps(), colormaps) - - with self.assertRaises(ValueError): - setPreferredColormaps(()) - - with self.assertRaises(ValueError): - setPreferredColormaps(('This is not a colormap',)) - - colormaps = 'red', 'green' - setPreferredColormaps(('This is not a colormap',) + colormaps) - self.assertEqual(preferredColormaps(), colormaps) - - -def suite(): - test_suite = unittest.TestSuite() - for ui in (TestDictAPI, TestObjectAPI, TestPreferredColormaps): - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(ui)) - - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testColormapDialog.py b/silx/gui/plot/test/testColormapDialog.py deleted file mode 100644 index 8087369..0000000 --- a/silx/gui/plot/test/testColormapDialog.py +++ /dev/null @@ -1,385 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-2018 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 ColormapDialog""" - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "17/01/2018" - - -import doctest -import unittest - -from silx.gui.test.utils import qWaitForWindowExposedAndActivate -from silx.gui import qt -from silx.gui.plot import ColormapDialog -from silx.gui.test.utils import TestCaseQt -from silx.gui.plot.Colormap import Colormap, preferredColormaps -from silx.utils.testutils import ParametricTestCase -from silx.gui.plot.PlotWindow import PlotWindow - -import numpy.random - - -# Makes sure a QApplication exists -_qapp = qt.QApplication.instance() or qt.QApplication([]) - - -def _tearDownQt(docTest): - """Tear down to use for test from docstring. - - Checks that dialog widget is displayed - """ - dialogWidget = docTest.globs['dialog'] - qWaitForWindowExposedAndActivate(dialogWidget) - dialogWidget.setAttribute(qt.Qt.WA_DeleteOnClose) - dialogWidget.close() - del dialogWidget - _qapp.processEvents() - - -cmapDocTestSuite = doctest.DocTestSuite(ColormapDialog, tearDown=_tearDownQt) -"""Test suite of tests from the module's docstrings.""" - - -class TestColormapDialog(TestCaseQt, ParametricTestCase): - """Test the ColormapDialog.""" - def setUp(self): - TestCaseQt.setUp(self) - ParametricTestCase.setUp(self) - self.colormap = Colormap(name='gray', vmin=10.0, vmax=20.0, - normalization='linear') - - self.colormapDiag = ColormapDialog.ColormapDialog() - self.colormapDiag.setAttribute(qt.Qt.WA_DeleteOnClose) - - def tearDown(self): - del self.colormapDiag - ParametricTestCase.tearDown(self) - TestCaseQt.tearDown(self) - - def testGUIEdition(self): - """Make sure the colormap is correctly edited and also that the - modification are correctly updated if an other colormapdialog is - editing the same colormap""" - colormapDiag2 = ColormapDialog.ColormapDialog() - colormapDiag2.setColormap(self.colormap) - self.colormapDiag.setColormap(self.colormap) - - self.colormapDiag._comboBoxColormap.setCurrentName('red') - self.colormapDiag._normButtonLog.setChecked(True) - self.assertTrue(self.colormap.getName() == 'red') - self.assertTrue(self.colormapDiag.getColormap().getName() == 'red') - self.assertTrue(self.colormap.getNormalization() == 'log') - self.assertTrue(self.colormap.getVMin() == 10) - self.assertTrue(self.colormap.getVMax() == 20) - # checked second colormap dialog - self.assertTrue(colormapDiag2._comboBoxColormap.getCurrentName() == 'red') - self.assertTrue(colormapDiag2._normButtonLog.isChecked()) - self.assertTrue(int(colormapDiag2._minValue.getValue()) == 10) - self.assertTrue(int(colormapDiag2._maxValue.getValue()) == 20) - colormapDiag2.close() - - def testGUIModalOk(self): - """Make sure the colormap is modified if gone through accept""" - assert self.colormap.isAutoscale() is False - self.colormapDiag.setModal(True) - self.colormapDiag.show() - self.colormapDiag.setColormap(self.colormap) - self.assertTrue(self.colormap.getVMin() is not None) - self.colormapDiag._minValue.setValue(None) - self.assertTrue(self.colormap.getVMin() is None) - self.colormapDiag._maxValue.setValue(None) - self.mouseClick( - widget=self.colormapDiag._buttonsModal.button(qt.QDialogButtonBox.Ok), - button=qt.Qt.LeftButton - ) - self.assertTrue(self.colormap.getVMin() is None) - self.assertTrue(self.colormap.getVMax() is None) - self.assertTrue(self.colormap.isAutoscale() is True) - - def testGUIModalCancel(self): - """Make sure the colormap is not modified if gone through reject""" - assert self.colormap.isAutoscale() is False - self.colormapDiag.setModal(True) - self.colormapDiag.show() - self.colormapDiag.setColormap(self.colormap) - self.assertTrue(self.colormap.getVMin() is not None) - self.colormapDiag._minValue.setValue(None) - self.assertTrue(self.colormap.getVMin() is None) - self.mouseClick( - widget=self.colormapDiag._buttonsModal.button(qt.QDialogButtonBox.Cancel), - button=qt.Qt.LeftButton - ) - self.assertTrue(self.colormap.getVMin() is not None) - - def testGUIModalClose(self): - assert self.colormap.isAutoscale() is False - self.colormapDiag.setModal(False) - self.colormapDiag.show() - self.colormapDiag.setColormap(self.colormap) - self.assertTrue(self.colormap.getVMin() is not None) - self.colormapDiag._minValue.setValue(None) - self.assertTrue(self.colormap.getVMin() is None) - self.mouseClick( - widget=self.colormapDiag._buttonsNonModal.button(qt.QDialogButtonBox.Close), - button=qt.Qt.LeftButton - ) - self.assertTrue(self.colormap.getVMin() is None) - - def testGUIModalReset(self): - assert self.colormap.isAutoscale() is False - self.colormapDiag.setModal(False) - self.colormapDiag.show() - self.colormapDiag.setColormap(self.colormap) - self.assertTrue(self.colormap.getVMin() is not None) - self.colormapDiag._minValue.setValue(None) - self.assertTrue(self.colormap.getVMin() is None) - self.mouseClick( - widget=self.colormapDiag._buttonsNonModal.button(qt.QDialogButtonBox.Reset), - button=qt.Qt.LeftButton - ) - self.assertTrue(self.colormap.getVMin() is not None) - self.colormapDiag.close() - - def testGUIClose(self): - """Make sure the colormap is modify if go through reject""" - assert self.colormap.isAutoscale() is False - self.colormapDiag.show() - self.colormapDiag.setColormap(self.colormap) - self.assertTrue(self.colormap.getVMin() is not None) - self.colormapDiag._minValue.setValue(None) - self.assertTrue(self.colormap.getVMin() is None) - self.colormapDiag.close() - self.assertTrue(self.colormap.getVMin() is None) - - def testSetColormapIsCorrect(self): - """Make sure the interface fir the colormap when set a new colormap""" - self.colormap.setName('red') - for norm in (Colormap.NORMALIZATIONS): - for autoscale in (True, False): - if autoscale is True: - self.colormap.setVRange(None, None) - else: - self.colormap.setVRange(11, 101) - self.colormap.setNormalization(norm) - with self.subTest(colormap=self.colormap): - self.colormapDiag.setColormap(self.colormap) - self.assertTrue( - self.colormapDiag._normButtonLinear.isChecked() == (norm is Colormap.LINEAR)) - self.assertTrue( - self.colormapDiag._comboBoxColormap.getCurrentName() == 'red') - self.assertTrue( - self.colormapDiag._minValue.isAutoChecked() == autoscale) - self.assertTrue( - self.colormapDiag._maxValue.isAutoChecked() == autoscale) - if autoscale is False: - self.assertTrue(self.colormapDiag._minValue.getValue() == 11) - self.assertTrue(self.colormapDiag._maxValue.getValue() == 101) - self.assertTrue(self.colormapDiag._minValue.isEnabled()) - self.assertTrue(self.colormapDiag._maxValue.isEnabled()) - else: - self.assertFalse(self.colormapDiag._minValue._numVal.isEnabled()) - self.assertFalse(self.colormapDiag._maxValue._numVal.isEnabled()) - - def testColormapDel(self): - """Check behavior if the colormap has been deleted outside. For now - we make sure the colormap is still running and nothing more""" - self.colormapDiag.setColormap(self.colormap) - self.colormapDiag.show() - del self.colormap - self.assertTrue(self.colormapDiag.getColormap() is None) - self.colormapDiag._comboBoxColormap.setCurrentName('blue') - - def testColormapEditedOutside(self): - """Make sure the GUI is still up to date if the colormap is modified - outside""" - self.colormapDiag.setColormap(self.colormap) - self.colormapDiag.show() - - self.colormap.setName('red') - self.assertTrue( - self.colormapDiag._comboBoxColormap.getCurrentName() == 'red') - self.colormap.setNormalization(Colormap.LOGARITHM) - self.assertFalse(self.colormapDiag._normButtonLinear.isChecked()) - self.colormap.setVRange(11, 201) - self.assertTrue(self.colormapDiag._minValue.getValue() == 11) - self.assertTrue(self.colormapDiag._maxValue.getValue() == 201) - self.assertTrue(self.colormapDiag._minValue._numVal.isEnabled()) - self.assertTrue(self.colormapDiag._maxValue._numVal.isEnabled()) - self.assertFalse(self.colormapDiag._minValue.isAutoChecked()) - self.assertFalse(self.colormapDiag._maxValue.isAutoChecked()) - self.colormap.setVRange(None, None) - self.assertFalse(self.colormapDiag._minValue._numVal.isEnabled()) - self.assertFalse(self.colormapDiag._maxValue._numVal.isEnabled()) - self.assertTrue(self.colormapDiag._minValue.isAutoChecked()) - self.assertTrue(self.colormapDiag._maxValue.isAutoChecked()) - - def testSetColormapScenario(self): - """Test of a simple scenario of a colormap dialog editing several - colormap""" - colormap1 = Colormap(name='gray', vmin=10.0, vmax=20.0, - normalization='linear') - colormap2 = Colormap(name='red', vmin=10.0, vmax=20.0, - normalization='log') - colormap3 = Colormap(name='blue', vmin=None, vmax=None, - normalization='linear') - self.colormapDiag.setColormap(self.colormap) - self.colormapDiag.setColormap(colormap1) - del colormap1 - self.colormapDiag.setColormap(colormap2) - del colormap2 - self.colormapDiag.setColormap(colormap3) - del colormap3 - - def testNotPreferredColormap(self): - """Test that the colormapEditor is able to edit a colormap which is not - part of the 'prefered colormap' - """ - def getFirstNotPreferredColormap(): - cms = Colormap.getSupportedColormaps() - preferred = preferredColormaps() - for cm in cms: - if cm not in preferred: - return cm - return None - - colormapName = getFirstNotPreferredColormap() - assert colormapName is not None - colormap = Colormap(name=colormapName) - self.colormapDiag.setColormap(colormap) - self.colormapDiag.show() - cb = self.colormapDiag._comboBoxColormap - self.assertTrue(cb.getCurrentName() == colormapName) - cb.setCurrentIndex(0) - index = cb.findColormap(colormapName) - assert index is not 0 # if 0 then the rest of the test has no sense - cb.setCurrentIndex(index) - self.assertTrue(cb.getCurrentName() == colormapName) - - def testColormapEditableMode(self): - """Test that the colormapDialog is correctly updated when changing the - colormap editable status""" - colormap = Colormap(normalization='linear', vmin=1.0, vmax=10.0) - self.colormapDiag.setColormap(colormap) - for editable in (True, False): - with self.subTest(editable=editable): - colormap.setEditable(editable) - self.assertTrue( - self.colormapDiag._comboBoxColormap.isEnabled() is editable) - self.assertTrue( - self.colormapDiag._minValue.isEnabled() is editable) - self.assertTrue( - self.colormapDiag._maxValue.isEnabled() is editable) - self.assertTrue( - self.colormapDiag._normButtonLinear.isEnabled() is editable) - self.assertTrue( - self.colormapDiag._normButtonLog.isEnabled() is editable) - - # Make sure the reset button is also set to enable when edition mode is - # False - self.colormapDiag.setModal(False) - colormap.setEditable(True) - self.colormapDiag._normButtonLog.setChecked(True) - resetButton = self.colormapDiag._buttonsNonModal.button(qt.QDialogButtonBox.Reset) - self.assertTrue(resetButton.isEnabled()) - colormap.setEditable(False) - self.assertFalse(resetButton.isEnabled()) - - -class TestColormapAction(TestCaseQt): - def setUp(self): - TestCaseQt.setUp(self) - self.plot = PlotWindow() - self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) - - self.colormap1 = Colormap(name='blue', vmin=0.0, vmax=1.0, - normalization='linear') - self.colormap2 = Colormap(name='red', vmin=10.0, vmax=100.0, - normalization='log') - self.defaultColormap = self.plot.getDefaultColormap() - - self.plot.getColormapAction()._actionTriggered(checked=True) - self.colormapDialog = self.plot.getColormapAction()._dialog - self.colormapDialog.setAttribute(qt.Qt.WA_DeleteOnClose) - - def tearDown(self): - self.colormapDialog.close() - self.plot.close() - del self.colormapDialog - del self.plot - TestCaseQt.tearDown(self) - - def testActiveColormap(self): - self.assertTrue(self.colormapDialog.getColormap() is self.defaultColormap) - - self.plot.addImage(data=numpy.random.rand(10, 10), legend='img1', - replace=False, origin=(0, 0), - colormap=self.colormap1) - self.plot.setActiveImage('img1') - self.assertTrue(self.colormapDialog.getColormap() is self.colormap1) - - self.plot.addImage(data=numpy.random.rand(10, 10), legend='img2', - replace=False, origin=(0, 0), - colormap=self.colormap2) - self.plot.addImage(data=numpy.random.rand(10, 10), legend='img3', - replace=False, origin=(0, 0)) - - self.plot.setActiveImage('img3') - self.assertTrue(self.colormapDialog.getColormap() is self.defaultColormap) - self.plot.getActiveImage().setColormap(self.colormap2) - self.assertTrue(self.colormapDialog.getColormap() is self.colormap2) - - self.plot.remove('img2') - self.plot.remove('img3') - self.plot.remove('img1') - self.assertTrue(self.colormapDialog.getColormap() is self.defaultColormap) - - def testShowHideColormapDialog(self): - self.plot.getColormapAction()._actionTriggered(checked=False) - self.assertFalse(self.plot.getColormapAction().isChecked()) - self.plot.getColormapAction()._actionTriggered(checked=True) - self.assertTrue(self.plot.getColormapAction().isChecked()) - self.plot.addImage(data=numpy.random.rand(10, 10), legend='img1', - replace=False, origin=(0, 0), - colormap=self.colormap1) - self.colormap1.setName('red') - self.plot.getColormapAction()._actionTriggered() - self.colormap1.setName('blue') - self.colormapDialog.close() - self.assertFalse(self.plot.getColormapAction().isChecked()) - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest(cmapDocTestSuite) - for testClass in (TestColormapDialog, TestColormapAction): - test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase( - testClass)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testColors.py b/silx/gui/plot/test/testColors.py deleted file mode 100644 index 4d617eb..0000000 --- a/silx/gui/plot/test/testColors.py +++ /dev/null @@ -1,94 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-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 Colors""" - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "17/01/2018" - - -import numpy - -import unittest -from silx.utils.testutils import ParametricTestCase - -from silx.gui.plot import Colors -from silx.gui.plot.Colormap import Colormap - -class TestRGBA(ParametricTestCase): - """Basic tests of rgba function""" - - def testRGBA(self): - """"Test rgba function with accepted values""" - tests = { # name: (colors, expected values) - 'blue': ('blue', (0., 0., 1., 1.)), - '#010203': ('#010203', (1. / 255., 2. / 255., 3. / 255., 1.)), - '#01020304': ('#01020304', (1. / 255., 2. / 255., 3. / 255., 4. / 255.)), - '3 x uint8': (numpy.array((1, 255, 0), dtype=numpy.uint8), - (1 / 255., 1., 0., 1.)), - '4 x uint8': (numpy.array((1, 255, 0, 1), dtype=numpy.uint8), - (1 / 255., 1., 0., 1 / 255.)), - '3 x float overflow': ((3., 0.5, 1.), (1., 0.5, 1., 1.)), - } - - for name, test in tests.items(): - color, expected = test - with self.subTest(msg=name): - result = Colors.rgba(color) - self.assertEqual(result, expected) - - -class TestApplyColormapToData(ParametricTestCase): - """Tests of applyColormapToData function""" - - def testApplyColormapToData(self): - """Simple test of applyColormapToData function""" - colormap = Colormap(name='gray', normalization='linear', - vmin=0, vmax=255) - - size = 10 - expected = numpy.empty((size, 4), dtype='uint8') - expected[:, 0] = numpy.arange(size, dtype='uint8') - expected[:, 1] = expected[:, 0] - expected[:, 2] = expected[:, 0] - expected[:, 3] = 255 - - for dtype in ('uint8', 'int32', 'float32', 'float64'): - with self.subTest(dtype=dtype): - array = numpy.arange(size, dtype=dtype) - result = colormap.applyToData(data=array) - self.assertTrue(numpy.all(numpy.equal(result, expected))) - - -def suite(): - test_suite = unittest.TestSuite() - for testClass in (TestRGBA, TestApplyColormapToData): - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(testClass)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testCurvesROIWidget.py b/silx/gui/plot/test/testCurvesROIWidget.py index 0fd2456..7a2e3d1 100644 --- a/silx/gui/plot/test/testCurvesROIWidget.py +++ b/silx/gui/plot/test/testCurvesROIWidget.py @@ -24,7 +24,7 @@ # ###########################################################################*/ """Basic tests for CurvesROIWidget""" -__authors__ = ["T. Vincent", "P. Knobel"] +__authors__ = ["T. Vincent", "P. Knobel", "H. Payno"] __license__ = "MIT" __date__ = "16/11/2017" @@ -32,9 +32,8 @@ __date__ = "16/11/2017" import logging import os.path import unittest - +from collections import OrderedDict import numpy - from silx.gui import qt from silx.test.utils import temp_dir from silx.gui.test.utils import TestCaseQt @@ -153,6 +152,25 @@ class TestCurvesROIWidget(TestCaseQt): self.assertEqual(output["negative"]["rawcounts"], y[selection].sum(), "Calculation failed on negative X coordinates") + def testDeferedInit(self): + x = numpy.arange(100.) + y = numpy.arange(100.) + self.plot.addCurve(x=x, y=y, legend="name", replace="True") + roisDefs = OrderedDict([ + ["range1", + OrderedDict([["from", 20], ["to", 200], ["type", "energy"]])], + ["range2", + OrderedDict([["from", 300], ["to", 500], ["type", "energy"]])] + ]) + + roiWidget = self.plot.getCurvesRoiDockWidget().roiWidget + self.assertFalse(roiWidget._isInit) + self.plot.getCurvesRoiDockWidget().setRois(roisDefs) + self.assertTrue(len(roiWidget.getRois()) is len(roisDefs)) + self.plot.getCurvesRoiDockWidget().setVisible(True) + self.assertTrue(len(roiWidget.getRois()) is len(roisDefs)) + + def suite(): test_suite = unittest.TestSuite() for TestClass in (TestCurvesROIWidget,): diff --git a/silx/gui/plot/test/testImageView.py b/silx/gui/plot/test/testImageView.py index 641d438..5059a0b 100644 --- a/silx/gui/plot/test/testImageView.py +++ b/silx/gui/plot/test/testImageView.py @@ -26,7 +26,7 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "22/09/2017" +__date__ = "24/04/2018" import unittest @@ -36,7 +36,7 @@ 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 +from silx.gui.colors import Colormap class TestImageView(TestCaseQt): diff --git a/silx/gui/plot/test/testLimitConstraints.py b/silx/gui/plot/test/testLimitConstraints.py index 94aae76..5e7e0b1 100644 --- a/silx/gui/plot/test/testLimitConstraints.py +++ b/silx/gui/plot/test/testLimitConstraints.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016 European Synchrotron Radiation Facility +# Copyright (c) 2016-2018 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 @@ -44,9 +44,9 @@ class TestLimitConstaints(unittest.TestCase): def testApi(self): """Test availability of the API""" - self.plot.getXAxis().setLimitsConstraints(minPos=1, maxPos=1) + self.plot.getXAxis().setLimitsConstraints(minPos=1, maxPos=10) self.plot.getXAxis().setRangeConstraints(minRange=1, maxRange=1) - self.plot.getYAxis().setLimitsConstraints(minPos=1, maxPos=1) + self.plot.getYAxis().setLimitsConstraints(minPos=1, maxPos=10) self.plot.getYAxis().setRangeConstraints(minRange=1, maxRange=1) def testXMinMax(self): diff --git a/silx/gui/plot/test/testPixelIntensityHistoAction.py b/silx/gui/plot/test/testPixelIntensityHistoAction.py new file mode 100644 index 0000000..987e5b2 --- /dev/null +++ b/silx/gui/plot/test/testPixelIntensityHistoAction.py @@ -0,0 +1,104 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-2018 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 PixelIntensitiesHistoAction""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "02/03/2018" + + +import numpy +import unittest + +from silx.utils.testutils import ParametricTestCase +from silx.gui.test.utils import TestCaseQt, getQToolButtonFromAction +from silx.gui import qt +from silx.gui.plot import Plot2D + + +class TestPixelIntensitiesHisto(TestCaseQt, ParametricTestCase): + """Tests for PixelIntensitiesHistoAction widget.""" + + def setUp(self): + super(TestPixelIntensitiesHisto, self).setUp() + self.image = numpy.random.rand(100, 100) + self.plotImage = Plot2D() + self.plotImage.getIntensityHistogramAction().setVisible(True) + + def tearDown(self): + del self.plotImage + super(TestPixelIntensitiesHisto, self).tearDown() + + def testShowAndHide(self): + """Simple test that the plot is showing and hiding when activating the + action""" + self.plotImage.addImage(self.image, origin=(0, 0), legend='sino') + self.plotImage.show() + + histoAction = self.plotImage.getIntensityHistogramAction() + + # test the pixel intensity diagram is showing + button = getQToolButtonFromAction(histoAction) + self.assertIsNot(button, None) + self.mouseMove(button) + self.mouseClick(button, qt.Qt.LeftButton) + self.qapp.processEvents() + self.assertTrue(histoAction.getHistogramPlotWidget().isVisible()) + + # test the pixel intensity diagram is hiding + self.qapp.setActiveWindow(self.plotImage) + self.qapp.processEvents() + self.mouseMove(button) + self.mouseClick(button, qt.Qt.LeftButton) + self.qapp.processEvents() + self.assertFalse(histoAction.getHistogramPlotWidget().isVisible()) + + def testImageFormatInput(self): + """Test multiple type as image input""" + typesToTest = [numpy.uint8, numpy.int8, numpy.int16, numpy.int32, + numpy.float32, numpy.float64] + self.plotImage.addImage(self.image, origin=(0, 0), legend='sino') + self.plotImage.show() + button = getQToolButtonFromAction( + self.plotImage.getIntensityHistogramAction()) + self.mouseMove(button) + self.mouseClick(button, qt.Qt.LeftButton) + self.qapp.processEvents() + for typeToTest in typesToTest: + with self.subTest(typeToTest=typeToTest): + self.plotImage.addImage(self.image.astype(typeToTest), + origin=(0, 0), legend='sino') + + +def suite(): + test_suite = unittest.TestSuite() + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase( + TestPixelIntensitiesHisto)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testPlotTools.py b/silx/gui/plot/test/testPlotTools.py deleted file mode 100644 index 3d5849f..0000000 --- a/silx/gui/plot/test/testPlotTools.py +++ /dev/null @@ -1,200 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2016-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 PlotTools""" - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "17/01/2018" - - -import numpy -import unittest - -from silx.utils.testutils import ParametricTestCase, TestLogging -from silx.gui.test.utils import ( - qWaitForWindowExposedAndActivate, TestCaseQt, getQToolButtonFromAction) -from silx.gui import qt -from silx.gui.plot import Plot2D, PlotWindow, PlotTools -from .utils import PlotWidgetTestCase - - -# Makes sure a QApplication exists -_qapp = qt.QApplication.instance() or qt.QApplication([]) - - -def _tearDownDocTest(docTest): - """Tear down to use for test from docstring. - - Checks that plot widget is displayed - """ - plot = docTest.globs['plot'] - qWaitForWindowExposedAndActivate(plot) - plot.setAttribute(qt.Qt.WA_DeleteOnClose) - plot.close() - del plot - -# Disable doctest because of -# "NameError: name 'numpy' is not defined" -# -# import doctest -# positionInfoTestSuite = doctest.DocTestSuite( -# PlotTools, tearDown=_tearDownDocTest, -# optionflags=doctest.ELLIPSIS) -# """Test suite of tests from PlotTools docstrings. -# -# Test PositionInfo and ProfileToolBar docstrings. -# """ - - -class TestPositionInfo(PlotWidgetTestCase): - """Tests for PositionInfo widget.""" - - def _createPlot(self): - return PlotWindow() - - def setUp(self): - super(TestPositionInfo, self).setUp() - self.mouseMove(self.plot, pos=(0, 0)) - self.qapp.processEvents() - self.qWait(100) - - def tearDown(self): - super(TestPositionInfo, self).tearDown() - - def _test(self, positionWidget, converterNames, **kwargs): - """General test of PositionInfo. - - - Add it to a toolbar and - - Move mouse around the center of the PlotWindow. - """ - toolBar = qt.QToolBar() - self.plot.addToolBar(qt.Qt.BottomToolBarArea, toolBar) - - toolBar.addWidget(positionWidget) - - converters = positionWidget.getConverters() - self.assertEqual(len(converters), len(converterNames)) - for index, name in enumerate(converterNames): - self.assertEqual(converters[index][0], name) - - with TestLogging(PlotTools.__name__, **kwargs): - # Move mouse to center - center = self.plot.size() / 2 - self.mouseMove(self.plot, pos=(center.width(), center.height())) - # Move out - self.mouseMove(self.plot, pos=(1, 1)) - - def testDefaultConverters(self): - """Test PositionInfo with default converters""" - positionWidget = PlotTools.PositionInfo(plot=self.plot) - self._test(positionWidget, ('X', 'Y')) - - def testCustomConverters(self): - """Test PositionInfo with custom converters""" - converters = [ - ('Coords', lambda x, y: (int(x), int(y))), - ('Radius', lambda x, y: numpy.sqrt(x * x + y * y)), - ('Angle', lambda x, y: numpy.degrees(numpy.arctan2(y, x))) - ] - positionWidget = PlotTools.PositionInfo(plot=self.plot, - converters=converters) - self._test(positionWidget, ('Coords', 'Radius', 'Angle')) - - def testFailingConverters(self): - """Test PositionInfo with failing custom converters""" - def raiseException(x, y): - raise RuntimeError() - - positionWidget = PlotTools.PositionInfo( - plot=self.plot, - converters=[('Exception', raiseException)]) - self._test(positionWidget, ['Exception'], error=2) - - -class TestPixelIntensitiesHisto(TestCaseQt, ParametricTestCase): - """Tests for ProfileToolBar widget.""" - - def setUp(self): - super(TestPixelIntensitiesHisto, self).setUp() - self.image = numpy.random.rand(100, 100) - self.plotImage = Plot2D() - self.plotImage.getIntensityHistogramAction().setVisible(True) - - def tearDown(self): - del self.plotImage - super(TestPixelIntensitiesHisto, self).tearDown() - - def testShowAndHide(self): - """Simple test that the plot is showing and hiding when activating the - action""" - self.plotImage.addImage(self.image, origin=(0, 0), legend='sino') - self.plotImage.show() - - histoAction = self.plotImage.getIntensityHistogramAction() - - # test the pixel intensity diagram is showing - button = getQToolButtonFromAction(histoAction) - self.assertIsNot(button, None) - self.mouseMove(button) - self.mouseClick(button, qt.Qt.LeftButton) - self.qapp.processEvents() - self.assertTrue(histoAction.getHistogramPlotWidget().isVisible()) - - # test the pixel intensity diagram is hiding - self.qapp.setActiveWindow(self.plotImage) - self.qapp.processEvents() - self.mouseMove(button) - self.mouseClick(button, qt.Qt.LeftButton) - self.qapp.processEvents() - self.assertFalse(histoAction.getHistogramPlotWidget().isVisible()) - - def testImageFormatInput(self): - """Test multiple type as image input""" - typesToTest = [numpy.uint8, numpy.int8, numpy.int16, numpy.int32, - numpy.float32, numpy.float64] - self.plotImage.addImage(self.image, origin=(0, 0), legend='sino') - self.plotImage.show() - button = getQToolButtonFromAction( - self.plotImage.getIntensityHistogramAction()) - self.mouseMove(button) - self.mouseClick(button, qt.Qt.LeftButton) - self.qapp.processEvents() - for typeToTest in typesToTest: - with self.subTest(typeToTest=typeToTest): - self.plotImage.addImage(self.image.astype(typeToTest), - origin=(0, 0), legend='sino') - - -def suite(): - test_suite = unittest.TestSuite() - # test_suite.addTest(positionInfoTestSuite) - for testClass in (TestPositionInfo, TestPixelIntensitiesHisto): - test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase( - testClass)) - 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 72617e5..dac6580 100644 --- a/silx/gui/plot/test/testPlotWidget.py +++ b/silx/gui/plot/test/testPlotWidget.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility +# Copyright (c) 2016-2018 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 @@ -26,22 +26,24 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "26/01/2018" +__date__ = "24/04/2018" import unittest import logging import numpy -from silx.utils.testutils import ParametricTestCase +from silx.utils.testutils import ParametricTestCase, parameterize from silx.gui.test.utils import SignalListener from silx.gui.test.utils import TestCaseQt from silx.utils import testutils from silx.utils import deprecation +from silx.test.utils import test_options + from silx.gui import qt from silx.gui.plot import PlotWidget -from silx.gui.plot.Colormap import Colormap +from silx.gui.colors import Colormap from .utils import PlotWidgetTestCase @@ -188,7 +190,7 @@ class TestPlotImage(PlotWidgetTestCase, ParametricTestCase): self.plot.addImage(rgb, legend="rgb", origin=(0, 0), scale=(10, 10), - replace=False, resetzoom=False) + resetzoom=False) rgba = numpy.array( (((0, 0, 0, .5), (.5, 0, 0, 1), (1, 0, 0, .5)), @@ -197,7 +199,7 @@ class TestPlotImage(PlotWidgetTestCase, ParametricTestCase): self.plot.addImage(rgba, legend="rgba", origin=(5, 5), scale=(10, 10), - replace=False, resetzoom=False) + resetzoom=False) self.plot.resetZoom() @@ -212,7 +214,7 @@ class TestPlotImage(PlotWidgetTestCase, ParametricTestCase): colors=((0., 0., 0.), (1., 0., 0.), (0., 1., 0.), (0., 0., 1.))) self.plot.addImage(DATA_2D, legend="image 1", colormap=colormap, - replace=False, resetzoom=False) + resetzoom=False) colormap = Colormap(name=None, normalization=Colormap.LINEAR, @@ -224,7 +226,7 @@ class TestPlotImage(PlotWidgetTestCase, ParametricTestCase): dtype=numpy.uint8)) self.plot.addImage(DATA_2D, legend="image 2", colormap=colormap, origin=(DATA_2D.shape[0], 0), - replace=False, resetzoom=False) + resetzoom=False) self.plot.resetZoom() def testImageOriginScale(self): @@ -614,13 +616,13 @@ class TestPlotActiveCurveImage(PlotWidgetTestCase): self.plot.getYAxis().setLabel('YLabel') # labels changed as active curve - self.plot.addImage(numpy.arange(100).reshape(10, 10), replace=False, + self.plot.addImage(numpy.arange(100).reshape(10, 10), legend='1', xlabel='x1', ylabel='y1') self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') # labels not changed as not active curve - self.plot.addImage(numpy.arange(100).reshape(10, 10), replace=False, + self.plot.addImage(numpy.arange(100).reshape(10, 10), legend='2') self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') @@ -660,9 +662,13 @@ class TestPlotAxes(TestCaseQt, ParametricTestCase): xData = numpy.arange(1, 10) yData = xData ** 2 + def __init__(self, methodName='runTest', backend=None): + unittest.TestCase.__init__(self, methodName) + self.__backend = backend + def setUp(self): super(TestPlotAxes, self).setUp() - self.plot = PlotWidget() + self.plot = PlotWidget(backend=self.__backend) # It is not needed to display the plot # It saves a lot of time # self.plot.show() @@ -721,7 +727,7 @@ class TestPlotAxes(TestCaseQt, ParametricTestCase): if getter is not None: self.assertEqual(getter(), expected) - @testutils.test_logging(deprecation.depreclog.name, warning=2) + @testutils.test_logging(deprecation.depreclog.name) def testOldPlotAxis_Logarithmic(self): """Test silx API prior to silx 0.6""" x = self.plot.getXAxis() @@ -760,7 +766,7 @@ class TestPlotAxes(TestCaseQt, ParametricTestCase): self.assertEqual(self.plot.isYAxisLogarithmic(), False) self.assertEqual(listener.arguments(callIndex=-1), ("y", False)) - @testutils.test_logging(deprecation.depreclog.name, warning=2) + @testutils.test_logging(deprecation.depreclog.name) def testOldPlotAxis_AutoScale(self): """Test silx API prior to silx 0.6""" x = self.plot.getXAxis() @@ -799,7 +805,7 @@ class TestPlotAxes(TestCaseQt, ParametricTestCase): self.assertEqual(self.plot.isYAxisAutoScale(), True) self.assertEqual(listener.arguments(callIndex=-1), ("y", True)) - @testutils.test_logging(deprecation.depreclog.name, warning=1) + @testutils.test_logging(deprecation.depreclog.name) def testOldPlotAxis_Inverted(self): """Test silx API prior to silx 0.6""" x = self.plot.getXAxis() @@ -1162,7 +1168,7 @@ class TestPlotImageLog(PlotWidgetTestCase): vmax=None) self.plot.addImage(DATA_2D, legend="image 1", origin=(1., 1.), scale=(1., 1.), - replace=False, resetzoom=False, colormap=colormap) + resetzoom=False, colormap=colormap) self.plot.resetZoom() def testPlotColormapGrayLogY(self): @@ -1175,7 +1181,7 @@ class TestPlotImageLog(PlotWidgetTestCase): vmax=None) self.plot.addImage(DATA_2D, legend="image 1", origin=(1., 1.), scale=(1., 1.), - replace=False, resetzoom=False, colormap=colormap) + resetzoom=False, colormap=colormap) self.plot.resetZoom() def testPlotColormapGrayLogXY(self): @@ -1189,7 +1195,7 @@ class TestPlotImageLog(PlotWidgetTestCase): vmax=None) self.plot.addImage(DATA_2D, legend="image 1", origin=(1., 1.), scale=(1., 1.), - replace=False, resetzoom=False, colormap=colormap) + resetzoom=False, colormap=colormap) self.plot.resetZoom() def testPlotRgbRgbaLogXY(self): @@ -1204,7 +1210,7 @@ class TestPlotImageLog(PlotWidgetTestCase): self.plot.addImage(rgb, legend="rgb", origin=(1, 1), scale=(10, 10), - replace=False, resetzoom=False) + resetzoom=False) rgba = numpy.array( (((0, 0, 0, .5), (.5, 0, 0, 1), (1, 0, 0, .5)), @@ -1213,7 +1219,7 @@ class TestPlotImageLog(PlotWidgetTestCase): self.plot.addImage(rgba, legend="rgba", origin=(5., 5.), scale=(10., 10.), - replace=False, resetzoom=False) + resetzoom=False) self.plot.resetZoom() @@ -1355,19 +1361,22 @@ class TestPlotItemLog(PlotWidgetTestCase): def suite(): + testClasses = (TestPlotWidget, TestPlotImage, TestPlotCurve, + TestPlotMarker, TestPlotItem, TestPlotAxes, + TestPlotEmptyLog, TestPlotCurveLog, TestPlotImageLog, + TestPlotMarkerLog, TestPlotItemLog) + test_suite = unittest.TestSuite() - loadTests = unittest.defaultTestLoader.loadTestsFromTestCase - test_suite.addTest(loadTests(TestPlotWidget)) - test_suite.addTest(loadTests(TestPlotImage)) - test_suite.addTest(loadTests(TestPlotCurve)) - test_suite.addTest(loadTests(TestPlotMarker)) - test_suite.addTest(loadTests(TestPlotItem)) - test_suite.addTest(loadTests(TestPlotAxes)) - test_suite.addTest(loadTests(TestPlotEmptyLog)) - test_suite.addTest(loadTests(TestPlotCurveLog)) - test_suite.addTest(loadTests(TestPlotImageLog)) - test_suite.addTest(loadTests(TestPlotMarkerLog)) - test_suite.addTest(loadTests(TestPlotItemLog)) + + # Tests with matplotlib + for testClass in testClasses: + test_suite.addTest(parameterize(testClass, backend=None)) + + if test_options.WITH_GL_TEST: + # Tests with OpenGL backend + for testClass in testClasses: + test_suite.addTest(parameterize(testClass, backend='gl')) + return test_suite diff --git a/silx/gui/plot/test/testPlotWidgetNoBackend.py b/silx/gui/plot/test/testPlotWidgetNoBackend.py index 0d0ddc4..cd7cbb3 100644 --- a/silx/gui/plot/test/testPlotWidgetNoBackend.py +++ b/silx/gui/plot/test/testPlotWidgetNoBackend.py @@ -460,8 +460,8 @@ class TestPlotGetCurveImage(unittest.TestCase): image = plot.getImage() self.assertIsNone(image) - plot.addImage(((0, 1), (2, 3)), legend='image 0', replace=False) - plot.addImage(((0, 1), (2, 3)), legend='image 1', replace=False) + plot.addImage(((0, 1), (2, 3)), legend='image 0') + plot.addImage(((0, 1), (2, 3)), legend='image 1') # Active image active = plot.getActiveImage() @@ -470,7 +470,7 @@ class TestPlotGetCurveImage(unittest.TestCase): self.assertEqual(image.getLegend(), 'image 0') # No active image - plot.addImage(((0, 1), (2, 3)), legend='image 2', replace=False) + plot.addImage(((0, 1), (2, 3)), legend='image 2') plot.setActiveImage(None) active = plot.getActiveImage() self.assertIsNone(active) @@ -496,7 +496,7 @@ class TestPlotGetCurveImage(unittest.TestCase): image = numpy.arange(10).astype(numpy.float32) image.shape = 5, 2 - plot.addImage(image, legend='image 0', info=["Hi!"], replace=False) + plot.addImage(image, legend='image 0', info=["Hi!"]) # Active image data, legend, info, something, params = plot.getActiveImage() @@ -515,8 +515,8 @@ class TestPlotGetCurveImage(unittest.TestCase): # 2 images data = numpy.arange(100).reshape(10, 10) - plot.addImage(data, legend='1', replace=False) - plot.addImage(data, origin=(10, 10), legend='2', replace=False) + plot.addImage(data, legend='1') + plot.addImage(data, origin=(10, 10), legend='2') images = plot.getAllImages(just_legend=True) self.assertEqual(list(images), ['1', '2']) images = plot.getAllImages(just_legend=False) diff --git a/silx/gui/plot/test/testSaveAction.py b/silx/gui/plot/test/testSaveAction.py index 4dfe373..85669bf 100644 --- a/silx/gui/plot/test/testSaveAction.py +++ b/silx/gui/plot/test/testSaveAction.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017 European Synchrotron Radiation Facility +# Copyright (c) 2017-2018 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 @@ -33,11 +33,13 @@ import unittest import tempfile import os +from silx.gui.plot.test.utils import PlotWidgetTestCase + from silx.gui.plot import PlotWidget from silx.gui.plot.actions.io import SaveAction -class TestSaveAction(unittest.TestCase): +class TestSaveActionSaveCurvesAsSpec(unittest.TestCase): def setUp(self): self.plot = PlotWidget(backend='none') @@ -63,8 +65,9 @@ class TestSaveAction(unittest.TestCase): ylabel="curve2 Y") self.plot.addCurve([3, 1], [7, 6], "curve with no labels") - self.saveAction._saveCurves(self.out_fname, - SaveAction.ALL_CURVES_FILTERS[0]) # "All curves as SpecFile (*.dat)" + self.saveAction._saveCurves(self.plot, + self.out_fname, + SaveAction.DEFAULT_ALL_CURVES_FILTERS[0]) # "All curves as SpecFile (*.dat)" with open(self.out_fname, "rb") as f: file_content = f.read() @@ -86,10 +89,35 @@ class TestSaveAction(unittest.TestCase): self.assertIn("#L graph x label graph y label", file_content) +class TestSaveActionExtension(PlotWidgetTestCase): + """Test SaveAction file filter API""" + + def _dummySaveFunction(self, plot, filename, nameFilter): + pass + + def testFileFilterAPI(self): + """Test addition/update of a file filter""" + saveAction = SaveAction(plot=self.plot, parent=self.plot) + + # Add a new file filter + nameFilter = 'Dummy file (*.dummy)' + saveAction.setFileFilter('all', nameFilter, self._dummySaveFunction) + self.assertTrue(nameFilter in saveAction.getFileFilters('all')) + self.assertEqual(saveAction.getFileFilters('all')[nameFilter], + self._dummySaveFunction) + + # Update an existing file filter + nameFilter = SaveAction.IMAGE_FILTER_EDF + saveAction.setFileFilter('image', nameFilter, self._dummySaveFunction) + self.assertEqual(saveAction.getFileFilters('image')[nameFilter], + self._dummySaveFunction) + + def suite(): test_suite = unittest.TestSuite() - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestSaveAction)) + for cls in (TestSaveActionSaveCurvesAsSpec, TestSaveActionExtension): + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(cls)) return test_suite diff --git a/silx/gui/plot/test/testScatterView.py b/silx/gui/plot/test/testScatterView.py new file mode 100644 index 0000000..40fdac6 --- /dev/null +++ b/silx/gui/plot/test/testScatterView.py @@ -0,0 +1,115 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2018 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 ScatterView""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "06/03/2018" + + +import unittest + +import numpy + +from silx.gui.plot.items import Axis, Scatter +from silx.gui.plot import ScatterView +from silx.gui.plot.test.utils import PlotWidgetTestCase + + +class TestScatterView(PlotWidgetTestCase): + """Test of ScatterView widget""" + + def _createPlot(self): + return ScatterView() + + def test(self): + """Simple tests""" + x = numpy.arange(100) + y = numpy.arange(100) + value = numpy.arange(100) + self.plot.setData(x, y, value) + self.qapp.processEvents() + + data = self.plot.getData() + self.assertEqual(len(data), 5) + self.assertTrue(numpy.all(numpy.equal(x, data[0]))) + self.assertTrue(numpy.all(numpy.equal(y, data[1]))) + self.assertTrue(numpy.all(numpy.equal(value, data[2]))) + self.assertIsNone(data[3]) # xerror + self.assertIsNone(data[4]) # yerror + + # Test access to scatter item + self.assertIsInstance(self.plot.getScatterItem(), Scatter) + + # Test toolbar actions + + action = self.plot.getScatterToolBar().getXAxisLogarithmicAction() + action.trigger() + self.qapp.processEvents() + + maskAction = self.plot.getScatterToolBar().actions()[-1] + maskAction.trigger() + self.qapp.processEvents() + + # Test proxy API + + self.plot.resetZoom() + self.qapp.processEvents() + + scale = self.plot.getXAxis().getScale() + self.assertEqual(scale, Axis.LOGARITHMIC) + + scale = self.plot.getYAxis().getScale() + self.assertEqual(scale, Axis.LINEAR) + + title = 'Test ScatterView' + self.plot.setGraphTitle(title) + self.assertEqual(self.plot.getGraphTitle(), title) + + self.qapp.processEvents() + + # Reset scatter data + + self.plot.setData(None, None, None) + self.qapp.processEvents() + + data = self.plot.getData() + self.assertEqual(len(data), 5) + self.assertEqual(len(data[0]), 0) # x + self.assertEqual(len(data[1]), 0) # y + self.assertEqual(len(data[2]), 0) # value + self.assertIsNone(data[3]) # xerror + self.assertIsNone(data[4]) # yerror + + +def suite(): + test_suite = unittest.TestSuite() + loadTests = unittest.defaultTestLoader.loadTestsFromTestCase + test_suite.addTest(loadTests(TestScatterView)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testStackView.py b/silx/gui/plot/test/testStackView.py index 8d2a0ee..3dcea36 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-2017 European Synchrotron Radiation Facility +# Copyright (c) 2016-2018 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 @@ -32,7 +32,7 @@ __date__ = "20/03/2017" import unittest import numpy -from silx.gui.test.utils import TestCaseQt +from silx.gui.test.utils import TestCaseQt, SignalListener from silx.gui import qt from silx.gui.plot import StackView @@ -187,6 +187,17 @@ class TestStackView(TestCaseQt): "beau sirop, mi-sirop, siroté, gagne-sirop, sirop-grelot," " passe-montagne, sirop au bon goût.") + def testStackFrameNumber(self): + self.stackview.setStack(self.mystack) + self.assertEqual(self.stackview.getFrameNumber(), 0) + + listener = SignalListener() + self.stackview.sigFrameChanged.connect(listener) + + self.stackview.setFrameNumber(1) + self.assertEqual(self.stackview.getFrameNumber(), 1) + self.assertEqual(listener.arguments(), [(1,)]) + class TestStackViewMainWindow(TestCaseQt): """Base class for tests of StackView.""" diff --git a/silx/gui/plot/test/testStats.py b/silx/gui/plot/test/testStats.py new file mode 100644 index 0000000..123eb89 --- /dev/null +++ b/silx/gui/plot/test/testStats.py @@ -0,0 +1,561 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-2018 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 CurvesROIWidget""" + +__authors__ = ["H. Payno"] +__license__ = "MIT" +__date__ = "07/03/2018" + + +from silx.gui import qt +from silx.gui.plot.stats import stats +from silx.gui.plot import StatsWidget +from silx.gui.plot.stats import statshandler +from silx.gui.test.utils import TestCaseQt +from silx.gui.plot import Plot1D, Plot2D +import unittest +import logging +import numpy + +_logger = logging.getLogger(__name__) + + +class TestStats(TestCaseQt): + """ + Test :class:`BaseClass` class and inheriting classes + """ + def setUp(self): + TestCaseQt.setUp(self) + self.createCurveContext() + self.createImageContext() + self.createScatterContext() + + def tearDown(self): + self.plot1d.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot1d.close() + self.plot2d.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot2d.close() + self.scatterPlot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.scatterPlot.close() + + def createCurveContext(self): + self.plot1d = Plot1D() + x = range(20) + y = range(20) + self.plot1d.addCurve(x, y, legend='curve0') + + self.curveContext = stats._CurveContext( + item=self.plot1d.getCurve('curve0'), + plot=self.plot1d, + onlimits=False) + + def createScatterContext(self): + self.scatterPlot = Plot2D() + lgd = 'scatter plot' + self.xScatterData = numpy.array([0, 1, 2, 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, + self.valuesScatterData, legend=lgd) + self.scatterContext = stats._ScatterContext( + item=self.scatterPlot.getScatter(lgd), + plot=self.scatterPlot, + onlimits=False + ) + + def createImageContext(self): + self.plot2d = Plot2D() + self._imgLgd = 'test image' + self.imageData = numpy.arange(32*128).reshape(32, 128) + self.plot2d.addImage(data=self.imageData, + legend=self._imgLgd, replace=False) + self.imageContext = stats._ImageContext( + item=self.plot2d.getImage(self._imgLgd), + plot=self.plot2d, + onlimits=False + ) + + def getBasicStats(self): + return { + 'min': stats.StatMin(), + 'minCoords': stats.StatCoordMin(), + 'max': stats.StatMax(), + 'maxCoords': stats.StatCoordMax(), + 'std': stats.Stat(name='std', fct=numpy.std), + 'mean': stats.Stat(name='mean', fct=numpy.mean), + 'com': stats.StatCOM() + } + + def testBasicStatsCurve(self): + """Test result for simple stats on a curve""" + _stats = self.getBasicStats() + xData = yData = numpy.array(range(20)) + self.assertTrue(_stats['min'].calculate(self.curveContext) == 0) + self.assertTrue(_stats['max'].calculate(self.curveContext) == 19) + self.assertTrue(_stats['minCoords'].calculate(self.curveContext) == [0]) + self.assertTrue(_stats['maxCoords'].calculate(self.curveContext) == [19]) + self.assertTrue(_stats['std'].calculate(self.curveContext) == numpy.std(yData)) + self.assertTrue(_stats['mean'].calculate(self.curveContext) == numpy.mean(yData)) + com = numpy.sum(xData * yData) / numpy.sum(yData) + self.assertTrue(_stats['com'].calculate(self.curveContext) == com) + + def testBasicStatsImage(self): + """Test result for simple stats on an image""" + _stats = self.getBasicStats() + self.assertTrue(_stats['min'].calculate(self.imageContext) == 0) + self.assertTrue(_stats['max'].calculate(self.imageContext) == 128 * 32 - 1) + self.assertTrue(_stats['minCoords'].calculate(self.imageContext) == (0, 0)) + self.assertTrue(_stats['maxCoords'].calculate(self.imageContext) == (127, 31)) + self.assertTrue(_stats['std'].calculate(self.imageContext) == numpy.std(self.imageData)) + self.assertTrue(_stats['mean'].calculate(self.imageContext) == numpy.mean(self.imageData)) + + yData = numpy.sum(self.imageData, axis=1) + xData = numpy.sum(self.imageData, axis=0) + dataXRange = range(self.imageData.shape[1]) + dataYRange = range(self.imageData.shape[0]) + + ycom = numpy.sum(yData*dataYRange) / numpy.sum(yData) + xcom = numpy.sum(xData*dataXRange) / numpy.sum(xData) + + self.assertTrue(_stats['com'].calculate(self.imageContext) == (xcom, ycom)) + + def testStatsImageAdv(self): + """Test that scale and origin are taking into account for images""" + + image2Data = numpy.arange(32 * 128).reshape(32, 128) + self.plot2d.addImage(data=image2Data, legend=self._imgLgd, + replace=True, origin=(100, 10), scale=(2, 0.5)) + image2Context = stats._ImageContext( + item=self.plot2d.getImage(self._imgLgd), + plot=self.plot2d, + onlimits=False + ) + _stats = self.getBasicStats() + self.assertTrue(_stats['min'].calculate(image2Context) == 0) + self.assertTrue( + _stats['max'].calculate(image2Context) == 128 * 32 - 1) + self.assertTrue( + _stats['minCoords'].calculate(image2Context) == (100, 10)) + self.assertTrue( + _stats['maxCoords'].calculate(image2Context) == (127*2. + 100, + 31 * 0.5 + 10) + ) + self.assertTrue( + _stats['std'].calculate(image2Context) == numpy.std( + self.imageData)) + self.assertTrue( + _stats['mean'].calculate(image2Context) == numpy.mean( + self.imageData)) + + yData = numpy.sum(self.imageData, axis=1) + xData = numpy.sum(self.imageData, axis=0) + dataXRange = range(self.imageData.shape[1]) + dataYRange = range(self.imageData.shape[0]) + + ycom = numpy.sum(yData * dataYRange) / numpy.sum(yData) + ycom = (ycom * 0.5) + 10 + xcom = numpy.sum(xData * dataXRange) / numpy.sum(xData) + xcom = (xcom * 2.) + 100 + self.assertTrue( + _stats['com'].calculate(image2Context) == (xcom, ycom)) + + def testBasicStatsScatter(self): + """Test result for simple stats on a scatter""" + _stats = self.getBasicStats() + self.assertTrue(_stats['min'].calculate(self.scatterContext) == 5) + self.assertTrue(_stats['max'].calculate(self.scatterContext) == 90) + self.assertTrue(_stats['minCoords'].calculate(self.scatterContext) == (0, 2)) + self.assertTrue(_stats['maxCoords'].calculate(self.scatterContext) == (50, 69)) + self.assertTrue(_stats['std'].calculate(self.scatterContext) == numpy.std(self.valuesScatterData)) + self.assertTrue(_stats['mean'].calculate(self.scatterContext) == numpy.mean(self.valuesScatterData)) + + comx = numpy.sum(self.xScatterData * self.valuesScatterData).astype(numpy.float32) / numpy.sum( + self.valuesScatterData).astype(numpy.float32) + comy = numpy.sum(self.yScatterData * self.valuesScatterData).astype(numpy.float32) / numpy.sum( + self.valuesScatterData).astype(numpy.float32) + self.assertTrue(numpy.all( + numpy.equal(_stats['com'].calculate(self.scatterContext), + (comx, comy))) + ) + + def testKindNotManagedByStat(self): + """Make sure an exception is raised if we try to execute calculate + of the base class""" + b = stats.StatBase(name='toto', compatibleKinds='curve') + with self.assertRaises(NotImplementedError): + b.calculate(self.imageContext) + + def testKindNotManagedByContext(self): + """ + Make sure an error is raised if we try to calculate a statistic with + a context not managed + """ + myStat = stats.Stat(name='toto', fct=numpy.std, kinds=('curve')) + myStat.calculate(self.curveContext) + with self.assertRaises(ValueError): + myStat.calculate(self.scatterContext) + with self.assertRaises(ValueError): + myStat.calculate(self.imageContext) + + def testOnLimits(self): + stat = stats.StatMin() + + self.plot1d.getXAxis().setLimitsConstraints(minPos=2, maxPos=5) + curveContextOnLimits = stats._CurveContext( + item=self.plot1d.getCurve('curve0'), + plot=self.plot1d, + onlimits=True) + self.assertTrue(stat.calculate(curveContextOnLimits) == 2) + + self.plot2d.getXAxis().setLimitsConstraints(minPos=32) + imageContextOnLimits = stats._ImageContext( + item=self.plot2d.getImage('test image'), + plot=self.plot2d, + onlimits=True) + self.assertTrue(stat.calculate(imageContextOnLimits) == 32) + + self.scatterPlot.getXAxis().setLimitsConstraints(minPos=40) + scatterContextOnLimits = stats._ScatterContext( + item=self.scatterPlot.getScatter('scatter plot'), + plot=self.scatterPlot, + onlimits=True) + self.assertTrue(stat.calculate(scatterContextOnLimits) == 20) + + +class TestStatsFormatter(TestCaseQt): + """Simple test to check usage of the :class:`StatsFormatter`""" + def setUp(self): + self.plot1d = Plot1D() + x = range(20) + y = range(20) + self.plot1d.addCurve(x, y, legend='curve0') + + self.curveContext = stats._CurveContext( + item=self.plot1d.getCurve('curve0'), + plot=self.plot1d, + onlimits=False) + + self.stat = stats.StatMin() + + def tearDown(self): + self.plot1d.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot1d.close() + + def testEmptyFormatter(self): + """Make sure a formatter with no formatter definition will return a + simple cast to str""" + emptyFormatter = statshandler.StatFormatter() + self.assertTrue( + emptyFormatter.format(self.stat.calculate(self.curveContext)) == '0.000') + + def testSettedFormatter(self): + """Make sure a formatter with no formatter definition will return a + simple cast to str""" + formatter= statshandler.StatFormatter(formatter='{0:.3f}') + self.assertTrue( + formatter.format(self.stat.calculate(self.curveContext)) == '0.000') + + +class TestStatsHandler(unittest.TestCase): + """Make sure the StatHandler is correctly making the link between + :class:`StatBase` and :class:`StatFormatter` and checking the API is valid + """ + def setUp(self): + self.plot1d = Plot1D() + x = range(20) + y = range(20) + self.plot1d.addCurve(x, y, legend='curve0') + self.curveItem = self.plot1d.getCurve('curve0') + + self.stat = stats.StatMin() + + def tearDown(self): + self.plot1d.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot1d.close() + + def testConstructor(self): + """Make sure the constructor can deal will all possible arguments: + + * tuple of :class:`StatBase` derivated classes + * tuple of tuples (:class:`StatBase`, :class:`StatFormatter`) + * tuple of tuples (str, pointer to function, kind) + """ + handler0 = statshandler.StatsHandler( + (stats.StatMin(), stats.StatMax()) + ) + + res = handler0.calculate(item=self.curveItem, plot=self.plot1d, + onlimits=False) + self.assertTrue('min' in res) + self.assertTrue(res['min'] == '0') + self.assertTrue('max' in res) + self.assertTrue(res['max'] == '19') + + handler1 = statshandler.StatsHandler( + ( + (stats.StatMin(), statshandler.StatFormatter(formatter=None)), + (stats.StatMax(), statshandler.StatFormatter()) + ) + ) + + res = handler1.calculate(item=self.curveItem, plot=self.plot1d, + onlimits=False) + self.assertTrue('min' in res) + self.assertTrue(res['min'] == '0') + self.assertTrue('max' in res) + self.assertTrue(res['max'] == '19.000') + + handler2 = statshandler.StatsHandler( + ( + (stats.StatMin(), None), + (stats.StatMax(), statshandler.StatFormatter()) + )) + + res = handler2.calculate(item=self.curveItem, plot=self.plot1d, + onlimits=False) + self.assertTrue('min' in res) + self.assertTrue(res['min'] == '0') + self.assertTrue('max' in res) + self.assertTrue(res['max'] == '19.000') + + handler3 = statshandler.StatsHandler(( + (('amin', numpy.argmin), statshandler.StatFormatter()), + ('amax', numpy.argmax) + )) + + res = handler3.calculate(item=self.curveItem, plot=self.plot1d, + onlimits=False) + self.assertTrue('amin' in res) + self.assertTrue(res['amin'] == '0.000') + self.assertTrue('amax' in res) + self.assertTrue(res['amax'] == '19') + + with self.assertRaises(ValueError): + statshandler.StatsHandler(('name')) + + +class TestStatsWidgetWithCurves(TestCaseQt): + """Basic test for StatsWidget with curves""" + def setUp(self): + TestCaseQt.setUp(self) + self.plot = Plot1D() + x = range(20) + y = range(20) + self.plot.addCurve(x, y, legend='curve0') + y = range(12, 32) + self.plot.addCurve(x, y, legend='curve1') + y = range(-2, 18) + self.plot.addCurve(x, y, legend='curve2') + self.widget = StatsWidget.StatsTable(plot=self.plot) + + mystats = statshandler.StatsHandler(( + stats.StatMin(), + (stats.StatCoordMin(), statshandler.StatFormatter(None, qt.QTableWidgetItem)), + stats.StatMax(), + (stats.StatCoordMax(), statshandler.StatFormatter(None, qt.QTableWidgetItem)), + stats.StatDelta(), + ('std', numpy.std), + ('mean', numpy.mean), + stats.StatCOM() + )) + + self.widget.setStats(mystats) + + def tearDown(self): + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.close() + self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.widget.close() + self.widget = None + self.plot = None + TestCaseQt.tearDown(self) + + def testInit(self): + """Make sure all the curves are registred on initialization""" + self.assertTrue(self.widget.rowCount() is 3) + + def testRemoveCurve(self): + """Make sure the Curves stats take into account the curve removal from + plot""" + self.plot.removeCurve('curve2') + self.assertTrue(self.widget.rowCount() is 2) + for iRow in range(2): + self.assertTrue(self.widget.item(iRow, 0).text() in ('curve0', 'curve1')) + + self.plot.removeCurve('curve0') + self.assertTrue(self.widget.rowCount() is 1) + self.plot.removeCurve('curve1') + self.assertTrue(self.widget.rowCount() is 0) + + def testAddCurve(self): + """Make sure the Curves stats take into account the add curve action""" + self.plot.addCurve(legend='curve3', x=range(10), y=range(10)) + self.assertTrue(self.widget.rowCount() is 4) + + def testUpdateCurveFrmAddCurve(self): + """Make sure the stats of the cuve will be removed after updating a + curve""" + self.plot.addCurve(legend='curve0', x=range(10), y=range(10)) + self.assertTrue(self.widget.rowCount() is 3) + itemMax = self.widget._getItem(name='max', legend='curve0', + kind='curve', indexTable=None) + self.assertTrue(itemMax.text() == '9') + + def testUpdateCurveFrmCurveObj(self): + self.plot.getCurve('curve0').setData(x=range(4), y=range(4)) + self.assertTrue(self.widget.rowCount() is 3) + itemMax = self.widget._getItem(name='max', legend='curve0', + kind='curve', indexTable=None) + self.assertTrue(itemMax.text() == '3') + + def testSetAnotherPlot(self): + plot2 = Plot1D() + plot2.addCurve(x=range(26), y=range(26), legend='new curve') + self.widget.setPlot(plot2) + self.assertTrue(self.widget.rowCount() is 1) + self.qapp.processEvents() + plot2.setAttribute(qt.Qt.WA_DeleteOnClose) + plot2.close() + plot2 = None + + +class TestStatsWidgetWithImages(TestCaseQt): + """Basic test for StatsWidget with images""" + def setUp(self): + TestCaseQt.setUp(self) + self.plot = Plot2D() + + self.plot.addImage(data=numpy.arange(128*128).reshape(128, 128), + legend='test image', replace=False) + + self.widget = StatsWidget.StatsTable(plot=self.plot) + + mystats = statshandler.StatsHandler(( + (stats.StatMin(), statshandler.StatFormatter()), + (stats.StatCoordMin(), statshandler.StatFormatter(None, qt.QTableWidgetItem)), + (stats.StatMax(), statshandler.StatFormatter()), + (stats.StatCoordMax(), statshandler.StatFormatter(None, qt.QTableWidgetItem)), + (stats.StatDelta(), statshandler.StatFormatter()), + ('std', numpy.std), + ('mean', numpy.mean), + (stats.StatCOM(), statshandler.StatFormatter(None)) + )) + + self.widget.setStats(mystats) + + def tearDown(self): + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.close() + self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.widget.close() + self.widget = None + self.plot = None + TestCaseQt.tearDown(self) + + def test(self): + columnsIndex = self.widget._columns_index + itemLegend = self.widget._lgdAndKindToItems[('test image', 'image')]['legend'] + itemMin = self.widget.item(itemLegend.row(), columnsIndex['min']) + itemMax = self.widget.item(itemLegend.row(), columnsIndex['max']) + itemDelta = self.widget.item(itemLegend.row(), columnsIndex['delta']) + itemCoordsMin = self.widget.item(itemLegend.row(), + columnsIndex['coords min']) + itemCoordsMax = self.widget.item(itemLegend.row(), + columnsIndex['coords max']) + max = (128 * 128) - 1 + self.assertTrue(itemMin.text() == '0.000') + self.assertTrue(itemMax.text() == '{0:.3f}'.format(max)) + self.assertTrue(itemDelta.text() == '{0:.3f}'.format(max)) + self.assertTrue(itemCoordsMin.text() == '0.0, 0.0') + self.assertTrue(itemCoordsMax.text() == '127.0, 127.0') + + +class TestStatsWidgetWithScatters(TestCaseQt): + def setUp(self): + TestCaseQt.setUp(self) + self.scatterPlot = Plot2D() + self.scatterPlot.addScatter([0, 1, 2, 20, 50, 60], + [2, 3, 4, 26, 69, 6], + [5, 6, 7, 10, 90, 20], + legend='scatter plot') + self.widget = StatsWidget.StatsTable(plot=self.scatterPlot) + + mystats = statshandler.StatsHandler(( + stats.StatMin(), + (stats.StatCoordMin(), statshandler.StatFormatter(None, qt.QTableWidgetItem)), + stats.StatMax(), + (stats.StatCoordMax(), statshandler.StatFormatter(None, qt.QTableWidgetItem)), + stats.StatDelta(), + ('std', numpy.std), + ('mean', numpy.mean), + stats.StatCOM() + )) + + self.widget.setStats(mystats) + + def tearDown(self): + self.scatterPlot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.scatterPlot.close() + self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.widget.close() + self.widget = None + self.scatterPlot = None + TestCaseQt.tearDown(self) + + def testStats(self): + columnsIndex = self.widget._columns_index + itemLegend = self.widget._lgdAndKindToItems[('scatter plot', 'scatter')]['legend'] + itemMin = self.widget.item(itemLegend.row(), columnsIndex['min']) + itemMax = self.widget.item(itemLegend.row(), columnsIndex['max']) + itemDelta = self.widget.item(itemLegend.row(), columnsIndex['delta']) + itemCoordsMin = self.widget.item(itemLegend.row(), + columnsIndex['coords min']) + itemCoordsMax = self.widget.item(itemLegend.row(), + columnsIndex['coords max']) + self.assertTrue(itemMin.text() == '5') + self.assertTrue(itemMax.text() == '90') + self.assertTrue(itemDelta.text() == '85') + self.assertTrue(itemCoordsMin.text() == '0, 2') + self.assertTrue(itemCoordsMax.text() == '50, 69') + + +class TestEmptyStatsWidget(TestCaseQt): + def test(self): + widget = StatsWidget.StatsWidget() + widget.show() + + +def suite(): + test_suite = unittest.TestSuite() + for TestClass in (TestStats, TestStatsHandler, TestStatsWidgetWithScatters, + TestStatsWidgetWithImages, TestStatsWidgetWithCurves, + TestStatsFormatter, TestEmptyStatsWidget): + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(TestClass)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/utils.py b/silx/gui/plot/test/utils.py index ec9bc7c..efba39c 100644 --- a/silx/gui/plot/test/utils.py +++ b/silx/gui/plot/test/utils.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016 European Synchrotron Radiation Facility +# Copyright (c) 2016-2018 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 @@ -48,11 +48,12 @@ class PlotWidgetTestCase(TestCaseQt): __screenshot_already_taken = False - def __init__(self, methodName='runTest'): + def __init__(self, methodName='runTest', backend=None): TestCaseQt.__init__(self, methodName=methodName) + self.__backend = backend def _createPlot(self): - return PlotWidget() + return PlotWidget(backend=self.__backend) def setUp(self): super(PlotWidgetTestCase, self).setUp() |