diff options
Diffstat (limited to 'silx/gui/plot/test')
19 files changed, 2030 insertions, 420 deletions
diff --git a/silx/gui/plot/test/__init__.py b/silx/gui/plot/test/__init__.py index b4378c7..2c2943e 100644 --- a/silx/gui/plot/test/__init__.py +++ b/silx/gui/plot/test/__init__.py @@ -24,48 +24,58 @@ # ###########################################################################*/ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "18/02/2016" +__date__ = "04/08/2017" import unittest -from .._utils.test import suite as testUtilsSuite -from .testColorBar import suite as testColorBarSuite -from .testColormapDialog import suite as testColormapDialogSuite -from .testColors import suite as testColorsSuite -from .testCurvesROIWidget import suite as testCurvesROIWidgetSuite -from .testAlphaSlider import suite as testAlphaSliderSuite -from .testInteraction import suite as testInteractionSuite -from .testLegendSelector import suite as testLegendSelectorSuite -from .testMaskToolsWidget import suite as testMaskToolsWidgetSuite -from .testScatterMaskToolsWidget import suite as testScatterMaskToolsWidgetSuite -from .testPlotInteraction import suite as testPlotInteractionSuite -from .testPlotTools import suite as testPlotToolsSuite -from .testPlotWidget import suite as testPlotWidgetSuite -from .testPlotWindow import suite as testPlotWindowSuite -from .testPlot import suite as testPlotSuite -from .testProfile import suite as testProfileSuite -from .testStackView import suite as testStackViewSuite +from .._utils import test +from . import testColorBar +from . import testColormap +from . import testColormapDialog +from . import testColors +from . import testCurvesROIWidget +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 +from . import testProfile +from . import testStackView +from . import testItem +from . import testUtilsAxis +from . import testLimitConstraints +from . import testComplexImageView def suite(): test_suite = unittest.TestSuite() test_suite.addTests( - [testUtilsSuite(), - testColorBarSuite(), - testColorsSuite(), - testColormapDialogSuite(), - testCurvesROIWidgetSuite(), - testAlphaSliderSuite(), - testInteractionSuite(), - testLegendSelectorSuite(), - testMaskToolsWidgetSuite(), - testScatterMaskToolsWidgetSuite(), - testPlotInteractionSuite(), - testPlotSuite(), - testPlotToolsSuite(), - testPlotWidgetSuite(), - testPlotWindowSuite(), - testProfileSuite(), - testStackViewSuite()]) + [test.suite(), + testColorBar.suite(), + testColors.suite(), + testColormapDialog.suite(), + testCurvesROIWidget.suite(), + testAlphaSlider.suite(), + testInteraction.suite(), + testLegendSelector.suite(), + testMaskToolsWidget.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()]) return test_suite diff --git a/silx/gui/plot/test/testColorBar.py b/silx/gui/plot/test/testColorBar.py index 797ff03..80ae6a8 100644 --- a/silx/gui/plot/test/testColorBar.py +++ b/silx/gui/plot/test/testColorBar.py @@ -32,22 +32,37 @@ 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.plot import Plot2D +from silx.gui import qt import numpy -class TestColorScale(unittest.TestCase): +class TestColorScale(TestCaseQt): """Test that interaction with the colorScale is correct""" def setUp(self): + super(TestColorScale, self).setUp() self.colorScaleWidget = _ColorScale(colormap=None, parent=None) + self.colorScaleWidget.show() + self.qWaitForWindowExposed(self.colorScaleWidget) def tearDown(self): - self.colorScaleWidget.deleteLater() - self.colorScaleWidget = None + self.qapp.processEvents() + self.colorScaleWidget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.colorScaleWidget.close() + del self.colorScaleWidget + super(TestColorScale, self).tearDown() + + def testNoColormap(self): + """Test _ColorScale without a colormap""" + colormap = self.colorScaleWidget.getColormap() + self.assertIsNone(colormap) def testRelativePositionLinear(self): - self.colorMapLin1 = { 'name': 'gray', 'normalization': 'linear', - 'autoscale': False, 'vmin': 0.0, 'vmax': 1.0 } + self.colorMapLin1 = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=0.0, + vmax=1.0) self.colorScaleWidget.setColormap(self.colorMapLin1) self.assertTrue( @@ -57,8 +72,10 @@ class TestColorScale(unittest.TestCase): self.assertTrue( self.colorScaleWidget.getValueFromRelativePosition(1.0) == 1.0) - self.colorMapLin2 = { 'name': 'viridis', 'normalization': 'linear', - 'autoscale': False, 'vmin': -10, 'vmax': 0 } + self.colorMapLin2 = Colormap(name='viridis', + normalization=Colormap.LINEAR, + vmin=-10, + vmax=0) self.colorScaleWidget.setColormap(self.colorMapLin2) self.assertTrue( @@ -69,8 +86,10 @@ class TestColorScale(unittest.TestCase): self.colorScaleWidget.getValueFromRelativePosition(1.0) == 0.0) def testRelativePositionLog(self): - self.colorMapLog1 = { 'name': 'temperature', 'normalization': 'log', - 'autoscale': False, 'vmin': 1.0, 'vmax': 100.0 } + self.colorMapLog1 = Colormap(name='temperature', + normalization=Colormap.LOGARITHM, + vmin=1.0, + vmax=100.0) self.colorScaleWidget.setColormap(self.colorMapLog1) @@ -83,41 +102,38 @@ class TestColorScale(unittest.TestCase): val = self.colorScaleWidget.getValueFromRelativePosition(0.0) self.assertTrue(val == 1.0) - def testNegativeLogMin(self): - colormap = { 'name': 'gray', 'normalization': 'log', - 'autoscale': False, 'vmin': -1.0, 'vmax': 1.0 } - - with self.assertRaises(ValueError): - self.colorScaleWidget.setColormap(colormap) - - def testNegativeLogMax(self): - colormap = { 'name': 'gray', 'normalization': 'log', - 'autoscale': False, 'vmin': 1.0, 'vmax': -1.0 } - with self.assertRaises(ValueError): - self.colorScaleWidget.setColormap(colormap) - -class TestNoAutoscale(unittest.TestCase): +class TestNoAutoscale(TestCaseQt): """Test that ticks and color displayed are correct in the case of a colormap with no autoscale """ def setUp(self): + super(TestNoAutoscale, self).setUp() self.plot = Plot2D() - self.colorBar = ColorBarWidget(parent=None, plot=self.plot) + self.colorBar = self.plot.getColorBarWidget() + self.colorBar.setVisible(True) # Makes sure the colormap is visible self.tickBar = self.colorBar.getColorScaleBar().getTickBar() self.colorScale = self.colorBar.getColorScaleBar().getColorScale() + self.plot.show() + self.qWaitForWindowExposed(self.plot) + def tearDown(self): + self.qapp.processEvents() self.tickBar = None self.colorScale = None del self.colorBar + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) self.plot.close() del self.plot + super(TestNoAutoscale, self).tearDown() def testLogNormNoAutoscale(self): - colormapLog = { 'name': 'gray', 'normalization': 'log', - 'autoscale': False, 'vmin': 1.0, 'vmax': 100.0 } + colormapLog = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=1.0, + vmax=100.0) data = numpy.linspace(10, 1e10, 9).reshape(3, 3) self.plot.addImage(data=data, colormap=colormapLog, legend='toto') @@ -139,8 +155,10 @@ class TestNoAutoscale(unittest.TestCase): self.assertTrue(val == 1.0) def testLinearNormNoAutoscale(self): - colormapLog = { 'name': 'gray', 'normalization': 'linear', - 'autoscale': False, 'vmin': -4, 'vmax': 5 } + colormapLog = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=-4, + vmax=5) data = numpy.linspace(1, 9, 9).reshape(3, 3) self.plot.addImage(data=data, colormap=colormapLog, legend='toto') @@ -159,20 +177,26 @@ class TestNoAutoscale(unittest.TestCase): val = self.colorScale.getValueFromRelativePosition(0.0) self.assertTrue(val == -4.0) -class TestColorbarWidget(TestCaseQt): - """Test interaction with the ColorScaleBar""" + +class TestColorBarWidget(TestCaseQt): + """Test interaction with the ColorBarWidget""" def setUp(self): - super(TestColorbarWidget, self).setUp() + super(TestColorBarWidget, self).setUp() self.plot = Plot2D() - self.colorBar = ColorBarWidget(parent=None, plot=self.plot) + self.colorBar = self.plot.getColorBarWidget() + self.colorBar.setVisible(True) # Makes sure the colormap is visible + + self.plot.show() + self.qWaitForWindowExposed(self.plot) def tearDown(self): + self.qapp.processEvents() del self.colorBar + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) self.plot.close() del self.plot - - super(TestColorbarWidget, self).tearDown() + super(TestColorBarWidget, self).tearDown() def testEmptyColorBar(self): colorBar = ColorBarWidget(parent=None) @@ -185,38 +209,43 @@ class TestColorbarWidget(TestCaseQt): Note : colorbar is modified by the Plot directly not ColorBarWidget """ - colormapLog = { 'name': 'gray', 'normalization': 'log', - 'autoscale': True, 'vmin': -1.0, 'vmax': 1.0 } - - colormapLog2 = { 'name': 'gray', 'normalization': 'log', - 'autoscale': False, 'vmin': -1.0, 'vmax': 1.0 } + colormapLog = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=None, + vmax=None) data = numpy.array([-5, -4, 0, 2, 3, 5, 10, 20, 30]) data = data.reshape(3, 3) self.plot.addImage(data=data, colormap=colormapLog, legend='toto') self.plot.setActiveImage('toto') - # default behavior when autoscale : set to minmal positive value - data[data<1] = data.max() - self.assertTrue(self.colorBar._colormap['vmin'] == data.min()) - self.assertTrue(self.colorBar._colormap['vmax'] == data.max()) - - data = numpy.linspace(-9, -2, 100).reshape(10, 10) + # default behavior when with log and negative values: should set vmin + # to 1 and vmax to 10 + self.assertTrue(self.colorBar.getColorScaleBar().minVal == 2) + self.assertTrue(self.colorBar.getColorScaleBar().maxVal == 30) - self.plot.addImage(data=data, colormap=colormapLog2, legend='toto') + # if data is positive + data[data<1] = data.max() + self.plot.addImage(data=data, + colormap=colormapLog, + legend='toto', + replace=True) self.plot.setActiveImage('toto') - # if negative values, changing bounds for default : 1, 10 - self.assertTrue(self.colorBar._colormap['vmin'] == 1) - self.assertTrue(self.colorBar._colormap['vmax'] == 10) - def testPlotAssocation(self): - """Make sure the ColorBarWidget is proparly connected with the plot""" - colormap = { 'name': 'gray', 'normalization': 'linear', - 'autoscale': True, 'vmin': -1.0, 'vmax': 1.0 } + self.assertTrue(self.colorBar.getColorScaleBar().minVal == data.min()) + self.assertTrue(self.colorBar.getColorScaleBar().maxVal == data.max()) - # make sure that default settings are the same + def testPlotAssocation(self): + """Make sure the ColorBarWidget is properly connected with the plot""" + colormap = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=None, + vmax=None) + + # make sure that default settings are the same (but a copy of the + self.colorBar.setPlot(self.plot) self.assertTrue( - self.colorBar.getColormap() == self.plot.getDefaultColormap()) + self.colorBar.getColormap() is self.plot.getDefaultColormap()) data = numpy.linspace(0, 10, 100).reshape(10, 10) self.plot.addImage(data=data, colormap=colormap, legend='toto') @@ -224,12 +253,94 @@ class TestColorbarWidget(TestCaseQt): # make sure the modification of the colormap has been done self.assertFalse( - self.colorBar.getColormap() == self.plot.getDefaultColormap()) + self.colorBar.getColormap() is self.plot.getDefaultColormap()) + self.assertTrue( + self.colorBar.getColormap() is colormap) + + # test that colorbar is updated when default plot colormap changes + self.plot.clear() + plotColormap = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=None, + vmax=None) + self.plot.setDefaultColormap(plotColormap) + self.assertTrue(self.colorBar.getColormap() is plotColormap) + + def testColormapWithoutRange(self): + """Test with a colormap with vmin==vmax""" + colormap = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=1.0, + vmax=1.0) + self.colorBar.setColormap(colormap) + + +class TestColorBarUpdate(TestCaseQt): + """Test that the ColorBar is correctly updated when the signal 'sigChanged' + of the colormap is emitted + """ + + def setUp(self): + super(TestColorBarUpdate, self).setUp() + self.plot = Plot2D() + self.colorBar = self.plot.getColorBarWidget() + self.colorBar.setVisible(True) # Makes sure the colormap is visible + self.colorBar.setPlot(self.plot) + + self.plot.show() + self.qWaitForWindowExposed(self.plot) + self.data = numpy.random.rand(9).reshape(3, 3) + + def tearDown(self): + self.qapp.processEvents() + del self.colorBar + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.close() + del self.plot + super(TestColorBarUpdate, self).tearDown() + + def testUpdateColorMap(self): + colormap = Colormap(name='gray', + normalization='linear', + vmin=0, + vmax=1) + + # check inital state + self.plot.addImage(data=self.data, colormap=colormap, legend='toto') + self.plot.setActiveImage('toto') + + self.assertTrue(self.colorBar.getColorScaleBar().minVal == 0) + self.assertTrue(self.colorBar.getColorScaleBar().maxVal == 1) + self.assertTrue( + self.colorBar.getColorScaleBar().getTickBar()._vmin == 0) + self.assertTrue( + self.colorBar.getColorScaleBar().getTickBar()._vmax == 1) + self.assertTrue( + self.colorBar.getColorScaleBar().getTickBar()._norm == "linear") + + # update colormap + colormap.setVMin(0.5) + self.assertTrue(self.colorBar.getColorScaleBar().minVal == 0.5) + self.assertTrue( + self.colorBar.getColorScaleBar().getTickBar()._vmin == 0.5) + + colormap.setVMax(0.8) + self.assertTrue(self.colorBar.getColorScaleBar().maxVal == 0.8) + self.assertTrue( + self.colorBar.getColorScaleBar().getTickBar()._vmax == 0.8) + + colormap.setNormalization('log') + self.assertTrue( + self.colorBar.getColorScaleBar().getTickBar()._norm == 'log') + + # TODO : should also check that if the colormap is changing then values (especially in log scale) + # should be coherent if in autoscale def suite(): test_suite = unittest.TestSuite() - for ui in (TestColorScale, TestNoAutoscale, TestColorbarWidget): + for ui in (TestColorScale, TestNoAutoscale, TestColorBarWidget, + TestColorBarUpdate): test_suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(ui)) @@ -237,4 +348,4 @@ def suite(): if __name__ == '__main__': - unittest.main(defaultTest='suite')
\ No newline at end of file + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testColormap.py b/silx/gui/plot/test/testColormap.py new file mode 100644 index 0000000..aa285d3 --- /dev/null +++ b/silx/gui/plot/test/testColormap.py @@ -0,0 +1,291 @@ +# 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__ = "05/12/2016" + +import unittest +import numpy +from silx.test.utils import ParametricTestCase +from silx.gui.plot.Colormap import Colormap + + +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): + colormapObject = Colormap._fromDict(clm_dict) + + +class TestObjectAPI(ParametricTestCase): + """Test the new Object API of the colormap""" + def setUp(self): + signalHasBeenEmitting = False + + 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 suite(): + test_suite = unittest.TestSuite() + for ui in (TestDictAPI, TestObjectAPI): + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(ui)) + + 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 index 94c22f3..18f0902 100644 --- a/silx/gui/plot/test/testColors.py +++ b/silx/gui/plot/test/testColors.py @@ -35,7 +35,7 @@ import unittest from silx.test.utils import ParametricTestCase from silx.gui.plot import Colors - +from silx.gui.plot.Colormap import Colormap class TestRGBA(ParametricTestCase): """Basic tests of rgba function""" @@ -65,8 +65,8 @@ class TestApplyColormapToData(ParametricTestCase): def testApplyColormapToData(self): """Simple test of applyColormapToData function""" - colormap = dict(name='gray', normalization='linear', - autoscale=False, vmin=0, vmax=255) + colormap = Colormap(name='gray', normalization='linear', + vmin=0, vmax=255) size = 10 expected = numpy.empty((size, 4), dtype='uint8') @@ -78,7 +78,7 @@ class TestApplyColormapToData(ParametricTestCase): for dtype in ('uint8', 'int32', 'float32', 'float64'): with self.subTest(dtype=dtype): array = numpy.arange(size, dtype=dtype) - result = Colors.applyColormapToData(array, **colormap) + result = colormap.applyToData(data=array) self.assertTrue(numpy.all(numpy.equal(result, expected))) diff --git a/silx/gui/plot/test/testComplexImageView.py b/silx/gui/plot/test/testComplexImageView.py new file mode 100644 index 0000000..f8ec370 --- /dev/null +++ b/silx/gui/plot/test/testComplexImageView.py @@ -0,0 +1,95 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2017 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Test suite for :class:`ComplexImageView`""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "14/09/2017" + + +import unittest +import logging +import numpy + +from silx.test.utils import ParametricTestCase +from silx.gui.plot import ComplexImageView + +from .utils import PlotWidgetTestCase + + +logger = logging.getLogger(__name__) + + +class TestComplexImageView(PlotWidgetTestCase, ParametricTestCase): + """Test suite of ComplexImageView widget""" + + def _createPlot(self): + return ComplexImageView.ComplexImageView() + + def testPlot2DComplex(self): + """Test API of ComplexImageView widget""" + data = numpy.array(((0, 1j), (1, 1 + 1j)), dtype=numpy.complex) + self.plot.setData(data) + self.plot.setKeepDataAspectRatio(True) + self.plot.getPlot().resetZoom() + self.qWait(100) + + # Test colormap API + colormap = self.plot.getColormap().copy() + colormap.setName('magma') + self.plot.setColormap(colormap) + self.qWait(100) + + # Test all modes + modes = self.plot.getSupportedVisualizationModes() + for mode in modes: + with self.subTest(mode=mode): + self.plot.setVisualizationMode(mode) + self.qWait(100) + + # Test origin and scale API + self.plot.setScale((2, 1)) + self.qWait(100) + self.plot.setOrigin((1, 1)) + self.qWait(100) + + # Test no data + self.plot.setData(numpy.zeros((0, 0), dtype=numpy.complex)) + self.qWait(100) + + # Test float data + self.plot.setData(numpy.arange(100, dtype=numpy.float).reshape(10, 10)) + self.qWait(100) + + +def suite(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase( + TestComplexImageView)) + 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 3c6f2ba..716960a 100644 --- a/silx/gui/plot/test/testCurvesROIWidget.py +++ b/silx/gui/plot/test/testCurvesROIWidget.py @@ -26,7 +26,7 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "05/12/2016" +__date__ = "15/05/2017" import logging @@ -41,7 +41,6 @@ from silx.gui.test.utils import TestCaseQt from silx.gui.plot import PlotWindow, CurvesROIWidget -logging.basicConfig() _logger = logging.getLogger(__name__) diff --git a/silx/gui/plot/test/testItem.py b/silx/gui/plot/test/testItem.py new file mode 100644 index 0000000..8c15bb7 --- /dev/null +++ b/silx/gui/plot/test/testItem.py @@ -0,0 +1,231 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2017 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ###########################################################################*/ +"""Tests for PlotWidget items.""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "01/09/2017" + + +import unittest + +import numpy + +from silx.gui.test.utils import SignalListener +from silx.gui.plot.items import ItemChangedType +from .utils import PlotWidgetTestCase + + +class TestSigItemChangedSignal(PlotWidgetTestCase): + """Test item's sigItemChanged signal""" + + def testCurveChanged(self): + """Test sigItemChanged for curve""" + self.plot.addCurve(numpy.arange(10), numpy.arange(10), legend='test') + curve = self.plot.getCurve('test') + + listener = SignalListener() + curve.sigItemChanged.connect(listener) + + # Test for signal in Item class + curve.setVisible(False) + curve.setVisible(True) + curve.setZValue(100) + + # Test for signals in Points class + curve.setData(numpy.arange(100), numpy.arange(100)) + + # SymbolMixIn + curve.setSymbol('o') + curve.setSymbol('d') + curve.setSymbolSize(20) + + # AlphaMixIn + curve.setAlpha(0.5) + + # Test for signals in Curve class + # ColorMixIn + curve.setColor('yellow') + # YAxisMixIn + curve.setYAxis('right') + # FillMixIn + curve.setFill(True) + # LineMixIn + curve.setLineStyle(':') + curve.setLineStyle(':') # Not sending event + curve.setLineWidth(2) + + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.VISIBLE, + ItemChangedType.VISIBLE, + ItemChangedType.ZVALUE, + ItemChangedType.DATA, + ItemChangedType.SYMBOL, + ItemChangedType.SYMBOL, + ItemChangedType.SYMBOL_SIZE, + ItemChangedType.ALPHA, + ItemChangedType.COLOR, + ItemChangedType.YAXIS, + ItemChangedType.FILL, + ItemChangedType.LINE_STYLE, + ItemChangedType.LINE_WIDTH]) + + def testHistogramChanged(self): + """Test sigItemChanged for Histogram""" + self.plot.addHistogram( + numpy.arange(10), edges=numpy.arange(11), legend='test') + histogram = self.plot.getHistogram('test') + listener = SignalListener() + histogram.sigItemChanged.connect(listener) + + # Test signals in Histogram class + histogram.setData(numpy.zeros(10), numpy.arange(11)) + + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.DATA]) + + def testImageDataChanged(self): + """Test sigItemChanged for ImageData""" + self.plot.addImage(numpy.arange(100).reshape(10, 10), legend='test') + image = self.plot.getImage('test') + + listener = SignalListener() + image.sigItemChanged.connect(listener) + + # ColormapMixIn + colormap = self.plot.getDefaultColormap().copy() + image.setColormap(colormap) + image.getColormap().setName('viridis') + + # Test of signals in ImageBase class + image.setOrigin(10) + image.setScale(2) + + # Test of signals in ImageData class + image.setData(numpy.ones((10, 10))) + + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.COLORMAP, + ItemChangedType.COLORMAP, + ItemChangedType.POSITION, + ItemChangedType.SCALE, + ItemChangedType.DATA]) + + def testImageRgbaChanged(self): + """Test sigItemChanged for ImageRgba""" + self.plot.addImage(numpy.ones((10, 10, 3)), legend='rgb') + image = self.plot.getImage('rgb') + + listener = SignalListener() + image.sigItemChanged.connect(listener) + + # Test of signals in ImageRgba class + image.setData(numpy.zeros((10, 10, 3))) + + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.DATA]) + + def testMarkerChanged(self): + """Test sigItemChanged for markers""" + self.plot.addMarker(10, 20, legend='test') + marker = self.plot._getMarker('test') + + listener = SignalListener() + marker.sigItemChanged.connect(listener) + + # Test signals in _BaseMarker + marker.setPosition(10, 10) + marker.setPosition(10, 10) # Not sending event + marker.setText('toto') + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.POSITION, + ItemChangedType.TEXT]) + + # XMarker + self.plot.addXMarker(10, legend='x') + marker = self.plot._getMarker('x') + + listener = SignalListener() + marker.sigItemChanged.connect(listener) + marker.setPosition(20, 20) + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.POSITION]) + + # YMarker + self.plot.addYMarker(10, legend='x') + marker = self.plot._getMarker('x') + + listener = SignalListener() + marker.sigItemChanged.connect(listener) + marker.setPosition(20, 20) + self.assertEqual(listener.arguments(argumentIndex=0), + [ItemChangedType.POSITION]) + + def testScatterChanged(self): + """Test sigItemChanged for scatter""" + data = numpy.arange(10) + self.plot.addScatter(data, data, data, legend='test') + scatter = self.plot.getScatter('test') + + listener = SignalListener() + scatter.sigItemChanged.connect(listener) + + # ColormapMixIn + scatter.getColormap().setName('viridis') + data2 = data + 10 + + # Test of signals in Scatter class + scatter.setData(data2, data2, data2) + + self.assertEqual(listener.arguments(), + [(ItemChangedType.COLORMAP,), + (ItemChangedType.DATA,)]) + + def testShapeChanged(self): + """Test sigItemChanged for shape""" + data = numpy.array((1., 10.)) + self.plot.addItem(data, data, legend='test', shape='rectangle') + shape = self.plot._getItem(kind='item', legend='test') + + listener = SignalListener() + shape.sigItemChanged.connect(listener) + + shape.setOverlay(True) + shape.setPoints(((2., 2.), (3., 3.))) + + self.assertEqual(listener.arguments(), + [(ItemChangedType.OVERLAY,), + (ItemChangedType.DATA,)]) + + +def suite(): + test_suite = unittest.TestSuite() + loadTests = unittest.defaultTestLoader.loadTestsFromTestCase + test_suite.addTest(loadTests(TestSigItemChangedSignal)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testLegendSelector.py b/silx/gui/plot/test/testLegendSelector.py index 371197f..9d4ada7 100644 --- a/silx/gui/plot/test/testLegendSelector.py +++ b/silx/gui/plot/test/testLegendSelector.py @@ -26,7 +26,7 @@ __authors__ = ["T. Rueter", "T. Vincent"] __license__ = "MIT" -__date__ = "05/12/2016" +__date__ = "15/05/2017" import logging @@ -37,7 +37,6 @@ from silx.gui.test.utils import TestCaseQt from silx.gui.plot import LegendSelector -logging.basicConfig() _logger = logging.getLogger(__name__) diff --git a/silx/gui/plot/test/testLimitConstraints.py b/silx/gui/plot/test/testLimitConstraints.py new file mode 100644 index 0000000..94aae76 --- /dev/null +++ b/silx/gui/plot/test/testLimitConstraints.py @@ -0,0 +1,125 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016 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. +# +# ###########################################################################*/ +"""Test setLimitConstaints on the PlotWidget""" + +__authors__ = ["V. Valls"] +__license__ = "MIT" +__date__ = "30/08/2017" + + +import unittest +from silx.gui.plot import PlotWidget + + +class TestLimitConstaints(unittest.TestCase): + """Tests setLimitConstaints class""" + + def setUp(self): + self.plot = PlotWidget() + + def tearDown(self): + self.plot = None + + def testApi(self): + """Test availability of the API""" + self.plot.getXAxis().setLimitsConstraints(minPos=1, maxPos=1) + self.plot.getXAxis().setRangeConstraints(minRange=1, maxRange=1) + self.plot.getYAxis().setLimitsConstraints(minPos=1, maxPos=1) + self.plot.getYAxis().setRangeConstraints(minRange=1, maxRange=1) + + def testXMinMax(self): + """Test limit constains on x-axis""" + self.plot.getXAxis().setLimitsConstraints(minPos=0, maxPos=100) + self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101) + self.assertEqual(self.plot.getXAxis().getLimits(), (0, 100)) + self.assertEqual(self.plot.getYAxis().getLimits(), (-1, 101)) + + def testYMinMax(self): + """Test limit constains on y-axis""" + self.plot.getYAxis().setLimitsConstraints(minPos=0, maxPos=100) + self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101) + self.assertEqual(self.plot.getXAxis().getLimits(), (-1, 101)) + self.assertEqual(self.plot.getYAxis().getLimits(), (0, 100)) + + def testMinXRange(self): + """Test min range constains on x-axis""" + self.plot.getXAxis().setRangeConstraints(minRange=100) + self.plot.setLimits(xmin=1, xmax=99, ymin=1, ymax=99) + limits = self.plot.getXAxis().getLimits() + self.assertEqual(limits[1] - limits[0], 100) + limits = self.plot.getYAxis().getLimits() + self.assertNotEqual(limits[1] - limits[0], 100) + + def testMaxXRange(self): + """Test max range constains on x-axis""" + self.plot.getXAxis().setRangeConstraints(maxRange=100) + self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101) + limits = self.plot.getXAxis().getLimits() + self.assertEqual(limits[1] - limits[0], 100) + limits = self.plot.getYAxis().getLimits() + self.assertNotEqual(limits[1] - limits[0], 100) + + def testMinYRange(self): + """Test min range constains on y-axis""" + self.plot.getYAxis().setRangeConstraints(minRange=100) + self.plot.setLimits(xmin=1, xmax=99, ymin=1, ymax=99) + limits = self.plot.getXAxis().getLimits() + self.assertNotEqual(limits[1] - limits[0], 100) + limits = self.plot.getYAxis().getLimits() + self.assertEqual(limits[1] - limits[0], 100) + + def testMaxYRange(self): + """Test max range constains on y-axis""" + self.plot.getYAxis().setRangeConstraints(maxRange=100) + self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101) + limits = self.plot.getXAxis().getLimits() + self.assertNotEqual(limits[1] - limits[0], 100) + limits = self.plot.getYAxis().getLimits() + self.assertEqual(limits[1] - limits[0], 100) + + def testChangeOfConstraints(self): + """Test changing of the constraints""" + self.plot.getXAxis().setRangeConstraints(minRange=10, maxRange=10) + # There is no more constraints on the range + self.plot.getXAxis().setRangeConstraints(minRange=None, maxRange=None) + self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101) + self.assertEqual(self.plot.getXAxis().getLimits(), (-1, 101)) + + def testSettingConstraints(self): + """Test setting a constaint (setLimits first then the constaint)""" + self.plot.setLimits(xmin=-1, xmax=101, ymin=-1, ymax=101) + self.plot.getXAxis().setLimitsConstraints(minPos=0, maxPos=100) + self.assertEqual(self.plot.getXAxis().getLimits(), (0, 100)) + + +def suite(): + test_suite = unittest.TestSuite() + loadTests = unittest.defaultTestLoader.loadTestsFromTestCase + test_suite.addTest(loadTests(TestLimitConstaints)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/plot/test/testMaskToolsWidget.py b/silx/gui/plot/test/testMaskToolsWidget.py index 0c11928..191bbe0 100644 --- a/silx/gui/plot/test/testMaskToolsWidget.py +++ b/silx/gui/plot/test/testMaskToolsWidget.py @@ -26,7 +26,7 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "24/01/2017" +__date__ = "01/09/2017" import logging @@ -37,8 +37,9 @@ import numpy from silx.gui import qt from silx.test.utils import temp_dir, ParametricTestCase -from silx.gui.test.utils import TestCaseQt, getQToolButtonFromAction +from silx.gui.test.utils import getQToolButtonFromAction from silx.gui.plot import PlotWindow, MaskToolsWidget +from .utils import PlotWidgetTestCase try: import fabio @@ -46,33 +47,24 @@ except ImportError: fabio = None -logging.basicConfig() _logger = logging.getLogger(__name__) -class TestMaskToolsWidget(TestCaseQt, ParametricTestCase): +class TestMaskToolsWidget(PlotWidgetTestCase, ParametricTestCase): """Basic test for MaskToolsWidget""" + def _createPlot(self): + return PlotWindow() + def setUp(self): super(TestMaskToolsWidget, self).setUp() - self.plot = PlotWindow() - self.widget = MaskToolsWidget.MaskToolsDockWidget(plot=self.plot, name='TEST') self.plot.addDockWidget(qt.Qt.BottomDockWidgetArea, self.widget) - - self.plot.show() - self.qWaitForWindowExposed(self.plot) - self.maskWidget = self.widget.widget() def tearDown(self): del self.maskWidget del self.widget - - self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) - self.plot.close() - del self.plot - super(TestMaskToolsWidget, self).tearDown() def testEmptyPlot(self): @@ -85,7 +77,7 @@ class TestMaskToolsWidget(TestCaseQt, ParametricTestCase): def _drag(self): """Drag from plot center to offset position""" - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() xCenter, yCenter = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -99,7 +91,7 @@ class TestMaskToolsWidget(TestCaseQt, ParametricTestCase): def _drawPolygon(self): """Draw a star polygon in the plot""" - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() x, y = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -107,16 +99,17 @@ class TestMaskToolsWidget(TestCaseQt, ParametricTestCase): (x - offset, y - offset), (x + offset, y), (x - offset, y), - (x + offset, y - offset)] + (x + offset, y - offset), + (x, y + offset)] # Close polygon + self.mouseMove(plot, pos=(0, 0)) for pos in star: self.mouseMove(plot, pos=pos) - btn = qt.Qt.LeftButton if pos != star[-1] else qt.Qt.RightButton - self.mouseClick(plot, btn, pos=pos) + self.mouseClick(plot, qt.Qt.LeftButton, pos=pos) def _drawPencil(self): """Draw a star polygon in the plot""" - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() x, y = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -126,9 +119,10 @@ class TestMaskToolsWidget(TestCaseQt, ParametricTestCase): (x - offset, y), (x + offset, y - offset)] + self.mouseMove(plot, pos=(0, 0)) self.mouseMove(plot, pos=star[0]) self.mousePress(plot, qt.Qt.LeftButton, pos=star[0]) - for pos in star: + for pos in star[1:]: self.mouseMove(plot, pos=pos) self.mouseRelease( plot, qt.Qt.LeftButton, pos=star[-1]) diff --git a/silx/gui/plot/test/testPlotInteraction.py b/silx/gui/plot/test/testPlotInteraction.py index 25f57a9..335b1e4 100644 --- a/silx/gui/plot/test/testPlotInteraction.py +++ b/silx/gui/plot/test/testPlotInteraction.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016 European Synchrotron Radiation Facility +# 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 @@ -26,12 +26,12 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "13/10/2016" +__date__ = "01/09/2017" import unittest from silx.gui import qt -from silx.gui.plot.test.testPlotWidget import _PlotWidgetTest +from .utils import PlotWidgetTestCase class _SignalDump(object): @@ -49,7 +49,7 @@ class _SignalDump(object): return list(self._received) -class TestSelectPolygon(_PlotWidgetTest): +class TestSelectPolygon(PlotWidgetTestCase): """Test polygon selection interaction""" def _interactionModeChanged(self, source): @@ -59,17 +59,16 @@ class TestSelectPolygon(_PlotWidgetTest): def _draw(self, polygon): """Draw a polygon in the plot - :param polygon: List of points (x, y) of the polygon (not closed) + :param polygon: List of points (x, y) of the polygon (closed) """ - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() dump = _SignalDump() self.plot.sigPlotSignal.connect(dump) for pos in polygon: self.mouseMove(plot, pos=pos) - btn = qt.Qt.LeftButton if pos != polygon[-1] else qt.Qt.RightButton - self.mouseClick(plot, btn, pos=pos) + self.mouseClick(plot, qt.Qt.LeftButton, pos=pos) self.plot.sigPlotSignal.disconnect(dump) return [args[0] for args in dump.received] @@ -89,7 +88,7 @@ class TestSelectPolygon(_PlotWidgetTest): self.plot.sigInteractiveModeChanged.disconnect( self._interactionModeChanged) - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() xCenter, yCenter = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -98,7 +97,8 @@ class TestSelectPolygon(_PlotWidgetTest): (xCenter - offset, yCenter - offset), (xCenter + offset, yCenter), (xCenter - offset, yCenter), - (xCenter + offset, yCenter - offset)] + (xCenter + offset, yCenter - offset), + (xCenter, yCenter + offset)] # Close polygon # Draw while dumping signals events = self._draw(star) @@ -113,7 +113,8 @@ class TestSelectPolygon(_PlotWidgetTest): largeSquare = [(xCenter - offset, yCenter - offset), (xCenter + offset, yCenter - offset), (xCenter + offset, yCenter + offset), - (xCenter - offset, yCenter + offset)] + (xCenter - offset, yCenter + offset), + (xCenter - offset, yCenter - offset)] # Close polygon # Draw while dumping signals events = self._draw(largeSquare) @@ -128,7 +129,7 @@ class TestSelectPolygon(_PlotWidgetTest): thinRectX = [(xCenter, yCenter - offset), (xCenter, yCenter + offset), (xCenter + 1, yCenter + offset), - (xCenter + 1, yCenter - offset)] + (xCenter + 1, yCenter - offset)] # Close polygon # Draw while dumping signals events = self._draw(thinRectX) @@ -143,7 +144,7 @@ class TestSelectPolygon(_PlotWidgetTest): thinRectY = [(xCenter - offset, yCenter), (xCenter + offset, yCenter), (xCenter + offset, yCenter + 1), - (xCenter - offset, yCenter + 1)] + (xCenter - offset, yCenter + 1)] # Close polygon # Draw while dumping signals events = self._draw(thinRectY) diff --git a/silx/gui/plot/test/testPlotTools.py b/silx/gui/plot/test/testPlotTools.py index 1d5e148..a08a18a 100644 --- a/silx/gui/plot/test/testPlotTools.py +++ b/silx/gui/plot/test/testPlotTools.py @@ -26,7 +26,7 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "05/12/2016" +__date__ = "01/09/2017" import numpy @@ -37,6 +37,7 @@ 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 @@ -67,23 +68,19 @@ def _tearDownDocTest(docTest): # """ -class TestPositionInfo(TestCaseQt): +class TestPositionInfo(PlotWidgetTestCase): """Tests for PositionInfo widget.""" + def _createPlot(self): + return PlotWindow() + def setUp(self): super(TestPositionInfo, self).setUp() - self.plot = PlotWindow() - self.plot.show() - self.qWaitForWindowExposed(self.plot) - self.mouseMove(self.plot, pos=(1, 1)) + self.mouseMove(self.plot, pos=(0, 0)) self.qapp.processEvents() self.qWait(100) def tearDown(self): - self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) - self.plot.close() - del self.plot - super(TestPositionInfo, self).tearDown() def _test(self, positionWidget, converterNames, **kwargs): @@ -104,10 +101,10 @@ class TestPositionInfo(TestCaseQt): with TestLogging(PlotTools.__name__, **kwargs): # Move mouse to center - self.mouseMove(self.plot) + center = self.plot.size() / 2 + self.mouseMove(self.plot, pos=(center.width(), center.height())) + # Move out self.mouseMove(self.plot, pos=(1, 1)) - self.qapp.processEvents() - self.qWait(100) def testDefaultConverters(self): """Test PositionInfo with default converters""" diff --git a/silx/gui/plot/test/testPlotWidget.py b/silx/gui/plot/test/testPlotWidget.py index 2de18a8..deeb198 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 European Synchrotron Radiation Facility +# 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 @@ -26,18 +26,24 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "05/12/2016" +__date__ = "01/09/2017" import unittest - +import logging import numpy from silx.test.utils import ParametricTestCase +from silx.gui.test.utils import SignalListener from silx.gui.test.utils import TestCaseQt +from silx.test import utils +from silx.utils import deprecation from silx.gui import qt from silx.gui.plot import PlotWidget +from silx.gui.plot.Colormap import Colormap + +from .utils import PlotWidgetTestCase SIZE = 1024 @@ -47,27 +53,10 @@ DATA_2D = numpy.arange(SIZE ** 2).reshape(SIZE, SIZE) """Image data set""" -class _PlotWidgetTest(TestCaseQt): - """Base class for tests of PlotWidget, not a TestCase in itself. - - plot attribute is the PlotWidget created for the test. - """ - - def setUp(self): - super(_PlotWidgetTest, self).setUp() - self.plot = PlotWidget() - self.plot.show() - self.qWaitForWindowExposed(self.plot) - - def tearDown(self): - self.qapp.processEvents() - self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) - self.plot.close() - del self.plot - super(_PlotWidgetTest, self).tearDown() +logger = logging.getLogger(__name__) -class TestPlotWidget(_PlotWidgetTest, ParametricTestCase): +class TestPlotWidget(PlotWidgetTestCase, ParametricTestCase): """Basic tests for PlotWidget""" def testShow(self): @@ -79,77 +68,115 @@ class TestPlotWidget(_PlotWidgetTest, ParametricTestCase): title, xlabel, ylabel = 'the title', 'x label', 'y label' self.plot.setGraphTitle(title) - self.plot.setGraphXLabel(xlabel) - self.plot.setGraphYLabel(ylabel) + self.plot.getXAxis().setLabel(xlabel) + self.plot.getYAxis().setLabel(ylabel) self.qapp.processEvents() self.assertEqual(self.plot.getGraphTitle(), title) - self.assertEqual(self.plot.getGraphXLabel(), xlabel) - self.assertEqual(self.plot.getGraphYLabel(), ylabel) + self.assertEqual(self.plot.getXAxis().getLabel(), xlabel) + self.assertEqual(self.plot.getYAxis().getLabel(), ylabel) - def testChangeLimitsWithAspectRatio(self): - def checkLimits(expectedXLim=None, expectedYLim=None, - expectedRatio=None): - xlim = self.plot.getGraphXLimits() - ylim = self.plot.getGraphYLimits() - ratio = abs(xlim[1] - xlim[0]) / abs(ylim[1] - ylim[0]) + def _checkLimits(self, + expectedXLim=None, + expectedYLim=None, + expectedRatio=None): + """Assert that limits are as expected""" + xlim = self.plot.getXAxis().getLimits() + ylim = self.plot.getYAxis().getLimits() + ratio = abs(xlim[1] - xlim[0]) / abs(ylim[1] - ylim[0]) - if expectedXLim is not None: - self.assertEqual(expectedXLim, xlim) + if expectedXLim is not None: + self.assertEqual(expectedXLim, xlim) - if expectedYLim is not None: - self.assertEqual(expectedYLim, ylim) + if expectedYLim is not None: + self.assertEqual(expectedYLim, ylim) - if expectedRatio is not None: - self.assertTrue( - numpy.allclose(expectedRatio, ratio, atol=0.01)) + if expectedRatio is not None: + self.assertTrue( + numpy.allclose(expectedRatio, ratio, atol=0.01)) + def testChangeLimitsWithAspectRatio(self): self.plot.setKeepDataAspectRatio() self.qapp.processEvents() - xlim = self.plot.getGraphXLimits() - ylim = self.plot.getGraphYLimits() + xlim = self.plot.getXAxis().getLimits() + ylim = self.plot.getYAxis().getLimits() defaultRatio = abs(xlim[1] - xlim[0]) / abs(ylim[1] - ylim[0]) - self.plot.setGraphXLimits(1., 10.) - checkLimits(expectedXLim=(1., 10.), expectedRatio=defaultRatio) + self.plot.getXAxis().setLimits(1., 10.) + self._checkLimits(expectedXLim=(1., 10.), expectedRatio=defaultRatio) + self.qapp.processEvents() + self._checkLimits(expectedXLim=(1., 10.), expectedRatio=defaultRatio) + + self.plot.getYAxis().setLimits(1., 10.) + self._checkLimits(expectedYLim=(1., 10.), expectedRatio=defaultRatio) self.qapp.processEvents() - checkLimits(expectedXLim=(1., 10.), expectedRatio=defaultRatio) + self._checkLimits(expectedYLim=(1., 10.), expectedRatio=defaultRatio) - self.plot.setGraphYLimits(1., 10.) - checkLimits(expectedYLim=(1., 10.), expectedRatio=defaultRatio) + def testResizeWidget(self): + """Test resizing the widget and receiving limitsChanged events""" + self.plot.resize(200, 200) self.qapp.processEvents() - checkLimits(expectedYLim=(1., 10.), expectedRatio=defaultRatio) + xlim = self.plot.getXAxis().getLimits() + ylim = self.plot.getYAxis().getLimits() -class TestPlotImage(_PlotWidgetTest, ParametricTestCase): + listener = SignalListener() + self.plot.getXAxis().sigLimitsChanged.connect(listener.partial('x')) + self.plot.getYAxis().sigLimitsChanged.connect(listener.partial('y')) + + # Resize without aspect ratio + self.plot.resize(200, 300) + self.qapp.processEvents() + self._checkLimits(expectedXLim=xlim, expectedYLim=ylim) + self.assertEqual(listener.callCount(), 0) + + # Resize with aspect ratio + self.plot.setKeepDataAspectRatio(True) + listener.clear() # Clean-up received signal + self.qapp.processEvents() + self.assertEqual(listener.callCount(), 0) # No event when redrawing + + self.plot.resize(200, 200) + self.qapp.processEvents() + + self.assertNotEqual(listener.callCount(), 0) + + +class TestPlotImage(PlotWidgetTestCase, ParametricTestCase): """Basic tests for addImage""" def setUp(self): super(TestPlotImage, self).setUp() - self.plot.setGraphYLabel('Rows') - self.plot.setGraphXLabel('Columns') + self.plot.getYAxis().setLabel('Rows') + self.plot.getXAxis().setLabel('Columns') def testPlotColormapTemperature(self): self.plot.setGraphTitle('Temp. Linear') - colormap = {'name': 'temperature', 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0} + colormap = Colormap(name='temperature', + normalization='linear', + vmin=None, + vmax=None) self.plot.addImage(DATA_2D, legend="image 1", colormap=colormap) def testPlotColormapGray(self): self.plot.setKeepDataAspectRatio(False) self.plot.setGraphTitle('Gray Linear') - colormap = {'name': 'gray', 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0} + colormap = Colormap(name='gray', + normalization='linear', + vmin=None, + vmax=None) self.plot.addImage(DATA_2D, legend="image 1", colormap=colormap) def testPlotColormapTemperatureLog(self): self.plot.setGraphTitle('Temp. Log') - colormap = {'name': 'temperature', 'normalization': 'log', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0} + colormap = Colormap(name='temperature', + normalization=Colormap.LOGARITHM, + vmin=None, + vmax=None) self.plot.addImage(DATA_2D, legend="image 1", colormap=colormap) def testPlotRgbRgba(self): @@ -180,19 +207,23 @@ class TestPlotImage(_PlotWidgetTest, ParametricTestCase): self.plot.setKeepDataAspectRatio(False) self.plot.setGraphTitle('Custom colormap') - colormap = {'name': None, 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0, - 'colors': ((0., 0., 0.), (1., 0., 0.), - (0., 1., 0.), (0., 0., 1.))} + colormap = Colormap(name=None, + normalization=Colormap.LINEAR, + vmin=None, + vmax=None, + 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) - colormap = {'name': None, 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0, - 'colors': numpy.array( - ((0, 0, 0, 0), (0, 0, 0, 128), - (128, 128, 128, 128), (255, 255, 255, 255)), - dtype=numpy.uint8)} + colormap = Colormap(name=None, + normalization=Colormap.LINEAR, + vmin=None, + vmax=None, + colors=numpy.array( + ((0, 0, 0, 0), (0, 0, 0, 128), + (128, 128, 128, 128), (255, 255, 255, 255)), + dtype=numpy.uint8)) self.plot.addImage(DATA_2D, legend="image 2", colormap=colormap, origin=(DATA_2D.shape[0], 0), replace=False, resetzoom=False) @@ -228,8 +259,8 @@ class TestPlotImage(_PlotWidgetTest, ParametricTestCase): ybounds = oy, oy + DATA_2D.shape[0] * sy # Check limits without aspect ratio - xmin, xmax = self.plot.getGraphXLimits() - ymin, ymax = self.plot.getGraphYLimits() + xmin, xmax = self.plot.getXAxis().getLimits() + ymin, ymax = self.plot.getYAxis().getLimits() self.assertEqual(xmin, min(xbounds)) self.assertEqual(xmax, max(xbounds)) self.assertEqual(ymin, min(ybounds)) @@ -237,8 +268,8 @@ class TestPlotImage(_PlotWidgetTest, ParametricTestCase): # Check limits with aspect ratio self.plot.setKeepDataAspectRatio(True) - xmin, xmax = self.plot.getGraphXLimits() - ymin, ymax = self.plot.getGraphYLimits() + xmin, xmax = self.plot.getXAxis().getLimits() + ymin, ymax = self.plot.getYAxis().getLimits() self.assertTrue(xmin <= min(xbounds)) self.assertTrue(xmax >= max(xbounds)) self.assertTrue(ymin <= min(ybounds)) @@ -248,8 +279,42 @@ class TestPlotImage(_PlotWidgetTest, ParametricTestCase): self.plot.clear() self.plot.resetZoom() + def testPlotColormapDictAPI(self): + """Test that the addImage API using a colormap dictionary is still + working""" + self.plot.setGraphTitle('Temp. Log') + + colormap = { + 'name': 'temperature', + 'normalization': 'log', + 'vmin': None, + 'vmax': None + } + self.plot.addImage(DATA_2D, legend="image 1", colormap=colormap) + + def testPlotComplexImage(self): + """Test that a complex image is displayed as its absolute value.""" + data = numpy.linspace(1, 1j, 100).reshape(10, 10) + self.plot.addImage(data, legend='complex') + + image = self.plot.getActiveImage() + retrievedData = image.getData(copy=False) + self.assertTrue( + numpy.all(numpy.equal(retrievedData, numpy.absolute(data)))) + + def testPlotBooleanImage(self): + """Test that a boolean image is displayed and converted to int8.""" + data = numpy.zeros((10, 10), dtype=numpy.bool) + data[::2, ::2] = True + self.plot.addImage(data, legend='boolean') -class TestPlotCurve(_PlotWidgetTest): + image = self.plot.getActiveImage() + retrievedData = image.getData(copy=False) + self.assertTrue(numpy.all(numpy.equal(retrievedData, data))) + self.assertIs(retrievedData.dtype.type, numpy.int8) + + +class TestPlotCurve(PlotWidgetTestCase): """Basic tests for addCurve.""" # Test data sets @@ -261,8 +326,8 @@ class TestPlotCurve(_PlotWidgetTest): def setUp(self): super(TestPlotCurve, self).setUp() self.plot.setGraphTitle('Curve') - self.plot.setGraphYLabel('Rows') - self.plot.setGraphXLabel('Columns') + self.plot.getYAxis().setLabel('Rows') + self.plot.getXAxis().setLabel('Columns') self.plot.setActiveCurveHandling(False) @@ -307,16 +372,16 @@ class TestPlotCurve(_PlotWidgetTest): self.plot.resetZoom() -class TestPlotMarker(_PlotWidgetTest): +class TestPlotMarker(PlotWidgetTestCase): """Basic tests for add*Marker""" def setUp(self): super(TestPlotMarker, self).setUp() - self.plot.setGraphYLabel('Rows') - self.plot.setGraphXLabel('Columns') + self.plot.getYAxis().setLabel('Rows') + self.plot.getXAxis().setLabel('Columns') - self.plot.setXAxisAutoScale(False) - self.plot.setYAxisAutoScale(False) + self.plot.getXAxis().setAutoScale(False) + self.plot.getYAxis().setAutoScale(False) self.plot.setKeepDataAspectRatio(False) self.plot.setLimits(0., 100., -100., 100.) @@ -382,7 +447,7 @@ class TestPlotMarker(_PlotWidgetTest): def testPlotMarkerWithoutLegend(self): self.plot.setGraphTitle('Markers without legend') - self.plot.setYAxisInverted(True) + self.plot.getYAxis().setInverted(True) # Markers without legend self.plot.addMarker(10, 10) @@ -401,7 +466,7 @@ class TestPlotMarker(_PlotWidgetTest): # TestPlotItem ################################################################ -class TestPlotItem(_PlotWidgetTest): +class TestPlotItem(PlotWidgetTestCase): """Basic tests for addItem.""" # Polygon coordinates and color @@ -431,10 +496,10 @@ class TestPlotItem(_PlotWidgetTest): def setUp(self): super(TestPlotItem, self).setUp() - self.plot.setGraphYLabel('Rows') - self.plot.setGraphXLabel('Columns') - self.plot.setXAxisAutoScale(False) - self.plot.setYAxisAutoScale(False) + 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(0., 100., -100., 100.) @@ -475,102 +540,428 @@ class TestPlotItem(_PlotWidgetTest): self.plot.resetZoom() -class TestPlotActiveCurveImage(_PlotWidgetTest): +class TestPlotActiveCurveImage(PlotWidgetTestCase): """Basic tests for active image handling""" def testActiveCurveAndLabels(self): # Active curve handling off, no label change self.plot.setActiveCurveHandling(False) - self.plot.setGraphXLabel('XLabel') - self.plot.setGraphYLabel('YLabel') + self.plot.getXAxis().setLabel('XLabel') + self.plot.getYAxis().setLabel('YLabel') self.plot.addCurve((1, 2), (1, 2)) - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') self.plot.addCurve((1, 2), (2, 3), xlabel='x1', ylabel='y1') - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') self.plot.clear() - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') # Active curve handling on, label changes self.plot.setActiveCurveHandling(True) - self.plot.setGraphXLabel('XLabel') - self.plot.setGraphYLabel('YLabel') + self.plot.getXAxis().setLabel('XLabel') + self.plot.getYAxis().setLabel('YLabel') # labels changed as active curve self.plot.addCurve((1, 2), (1, 2), legend='1', xlabel='x1', ylabel='y1') - self.assertEqual(self.plot.getGraphXLabel(), 'x1') - self.assertEqual(self.plot.getGraphYLabel(), 'y1') + self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') + self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') # labels not changed as not active curve self.plot.addCurve((1, 2), (2, 3), legend='2') - self.assertEqual(self.plot.getGraphXLabel(), 'x1') - self.assertEqual(self.plot.getGraphYLabel(), 'y1') + self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') + self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') # labels changed self.plot.setActiveCurve('2') - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') self.plot.setActiveCurve('1') - self.assertEqual(self.plot.getGraphXLabel(), 'x1') - self.assertEqual(self.plot.getGraphYLabel(), 'y1') + self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') + self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') self.plot.clear() - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') def testActiveImageAndLabels(self): # Active image handling always on, no API for toggling it - self.plot.setGraphXLabel('XLabel') - self.plot.setGraphYLabel('YLabel') + self.plot.getXAxis().setLabel('XLabel') + self.plot.getYAxis().setLabel('YLabel') # labels changed as active curve self.plot.addImage(numpy.arange(100).reshape(10, 10), replace=False, legend='1', xlabel='x1', ylabel='y1') - self.assertEqual(self.plot.getGraphXLabel(), 'x1') - self.assertEqual(self.plot.getGraphYLabel(), '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, legend='2') - self.assertEqual(self.plot.getGraphXLabel(), 'x1') - self.assertEqual(self.plot.getGraphYLabel(), 'y1') + self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') + self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') # labels changed self.plot.setActiveImage('2') - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') self.plot.setActiveImage('1') - self.assertEqual(self.plot.getGraphXLabel(), 'x1') - self.assertEqual(self.plot.getGraphYLabel(), 'y1') + self.assertEqual(self.plot.getXAxis().getLabel(), 'x1') + self.assertEqual(self.plot.getYAxis().getLabel(), 'y1') self.plot.clear() - self.assertEqual(self.plot.getGraphXLabel(), 'XLabel') - self.assertEqual(self.plot.getGraphYLabel(), 'YLabel') + self.assertEqual(self.plot.getXAxis().getLabel(), 'XLabel') + self.assertEqual(self.plot.getYAxis().getLabel(), 'YLabel') ############################################################################## # Log ############################################################################## -class TestPlotEmptyLog(_PlotWidgetTest): +class TestPlotEmptyLog(PlotWidgetTestCase): """Basic tests for log plot""" def testEmptyPlotTitleLabelsLog(self): self.plot.setGraphTitle('Empty Log Log') - self.plot.setGraphXLabel('X') - self.plot.setGraphYLabel('Y') + self.plot.getXAxis().setLabel('X') + self.plot.getYAxis().setLabel('Y') + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) + self.plot.resetZoom() + + +class TestPlotAxes(TestCaseQt, ParametricTestCase): + + # Test data + xData = numpy.arange(1, 10) + yData = xData ** 2 + + def setUp(self): + super(TestPlotAxes, self).setUp() + self.plot = PlotWidget() + # It is not needed to display the plot + # It saves a lot of time + # self.plot.show() + # self.qWaitForWindowExposed(self.plot) + + def tearDown(self): + self.qapp.processEvents() + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.close() + del self.plot + super(TestPlotAxes, self).tearDown() + + def testDefaultAxes(self): + axis = self.plot.getXAxis() + self.assertEqual(axis.getScale(), axis.LINEAR) + axis = self.plot.getYAxis() + self.assertEqual(axis.getScale(), axis.LINEAR) + axis = self.plot.getYAxis(axis="right") + self.assertEqual(axis.getScale(), axis.LINEAR) + + def testOldPlotAxis_getterSetter(self): + """Test silx API prior to silx 0.6""" + x = self.plot.getXAxis() + y = self.plot.getYAxis() + p = self.plot + + tests = [ + # setters + (p.setGraphXLimits, (10, 20), x.getLimits, (10, 20)), + (p.setGraphYLimits, (10, 20), y.getLimits, (10, 20)), + (p.setGraphXLabel, "foox", x.getLabel, "foox"), + (p.setGraphYLabel, "fooy", y.getLabel, "fooy"), + (p.setYAxisInverted, True, y.isInverted, True), + (p.setXAxisLogarithmic, True, x.getScale, x.LOGARITHMIC), + (p.setYAxisLogarithmic, True, y.getScale, y.LOGARITHMIC), + (p.setXAxisAutoScale, False, x.isAutoScale, False), + (p.setYAxisAutoScale, False, y.isAutoScale, False), + # getters + (x.setLimits, (11, 20), p.getGraphXLimits, (11, 20)), + (y.setLimits, (11, 20), p.getGraphYLimits, (11, 20)), + (x.setLabel, "fooxx", p.getGraphXLabel, "fooxx"), + (y.setLabel, "fooyy", p.getGraphYLabel, "fooyy"), + (y.setInverted, False, p.isYAxisInverted, False), + (x.setScale, x.LINEAR, p.isXAxisLogarithmic, False), + (y.setScale, y.LINEAR, p.isYAxisLogarithmic, False), + (x.setAutoScale, True, p.isXAxisAutoScale, True), + (y.setAutoScale, True, p.isYAxisAutoScale, True), + ] + for testCase in tests: + setter, value, getter, expected = testCase + with self.subTest(): + if setter is not None: + if not isinstance(value, tuple): + value = (value, ) + setter(*value) + if getter is not None: + self.assertEqual(getter(), expected) + + @utils.test_logging(deprecation.depreclog.name, warning=2) + def testOldPlotAxis_Logarithmic(self): + """Test silx API prior to silx 0.6""" + x = self.plot.getXAxis() + y = self.plot.getYAxis() + yright = self.plot.getYAxis(axis="right") + + listener = SignalListener() + self.plot.sigSetXAxisLogarithmic.connect(listener.partial("x")) + self.plot.sigSetYAxisLogarithmic.connect(listener.partial("y")) + + self.assertEqual(x.getScale(), x.LINEAR) + self.assertEqual(y.getScale(), x.LINEAR) + self.assertEqual(yright.getScale(), x.LINEAR) + self.plot.setXAxisLogarithmic(True) + self.assertEqual(x.getScale(), x.LOGARITHMIC) + self.assertEqual(y.getScale(), x.LINEAR) + self.assertEqual(yright.getScale(), x.LINEAR) + self.assertEqual(self.plot.isXAxisLogarithmic(), True) + self.assertEqual(self.plot.isYAxisLogarithmic(), False) + self.assertEqual(listener.arguments(callIndex=-1), ("x", True)) + self.plot.setYAxisLogarithmic(True) - self.plot.resetZoom() + self.assertEqual(x.getScale(), x.LOGARITHMIC) + self.assertEqual(y.getScale(), x.LOGARITHMIC) + self.assertEqual(yright.getScale(), x.LOGARITHMIC) + self.assertEqual(self.plot.isXAxisLogarithmic(), True) + self.assertEqual(self.plot.isYAxisLogarithmic(), True) + self.assertEqual(listener.arguments(callIndex=-1), ("y", True)) + + yright.setScale(yright.LINEAR) + self.assertEqual(x.getScale(), x.LOGARITHMIC) + self.assertEqual(y.getScale(), x.LINEAR) + self.assertEqual(yright.getScale(), x.LINEAR) + self.assertEqual(self.plot.isXAxisLogarithmic(), True) + self.assertEqual(self.plot.isYAxisLogarithmic(), False) + self.assertEqual(listener.arguments(callIndex=-1), ("y", False)) + + @utils.test_logging(deprecation.depreclog.name, warning=2) + def testOldPlotAxis_AutoScale(self): + """Test silx API prior to silx 0.6""" + x = self.plot.getXAxis() + y = self.plot.getYAxis() + yright = self.plot.getYAxis(axis="right") + + listener = SignalListener() + self.plot.sigSetXAxisAutoScale.connect(listener.partial("x")) + self.plot.sigSetYAxisAutoScale.connect(listener.partial("y")) + + self.assertEqual(x.isAutoScale(), True) + self.assertEqual(y.isAutoScale(), True) + self.assertEqual(yright.isAutoScale(), True) + + self.plot.setXAxisAutoScale(False) + self.assertEqual(x.isAutoScale(), False) + self.assertEqual(y.isAutoScale(), True) + self.assertEqual(yright.isAutoScale(), True) + self.assertEqual(self.plot.isXAxisAutoScale(), False) + self.assertEqual(self.plot.isYAxisAutoScale(), True) + self.assertEqual(listener.arguments(callIndex=-1), ("x", False)) + + self.plot.setYAxisAutoScale(False) + self.assertEqual(x.isAutoScale(), False) + self.assertEqual(y.isAutoScale(), False) + self.assertEqual(yright.isAutoScale(), False) + self.assertEqual(self.plot.isXAxisAutoScale(), False) + self.assertEqual(self.plot.isYAxisAutoScale(), False) + self.assertEqual(listener.arguments(callIndex=-1), ("y", False)) + + yright.setAutoScale(True) + self.assertEqual(x.isAutoScale(), False) + self.assertEqual(y.isAutoScale(), True) + self.assertEqual(yright.isAutoScale(), True) + self.assertEqual(self.plot.isXAxisAutoScale(), False) + self.assertEqual(self.plot.isYAxisAutoScale(), True) + self.assertEqual(listener.arguments(callIndex=-1), ("y", True)) + + @utils.test_logging(deprecation.depreclog.name, warning=1) + def testOldPlotAxis_Inverted(self): + """Test silx API prior to silx 0.6""" + x = self.plot.getXAxis() + y = self.plot.getYAxis() + yright = self.plot.getYAxis(axis="right") + + listener = SignalListener() + self.plot.sigSetYAxisInverted.connect(listener.partial("y")) + + self.assertEqual(x.isInverted(), False) + self.assertEqual(y.isInverted(), False) + self.assertEqual(yright.isInverted(), False) + + self.plot.setYAxisInverted(True) + self.assertEqual(x.isInverted(), False) + self.assertEqual(y.isInverted(), True) + self.assertEqual(yright.isInverted(), True) + self.assertEqual(self.plot.isYAxisInverted(), True) + self.assertEqual(listener.arguments(callIndex=-1), ("y", True)) + + yright.setInverted(False) + self.assertEqual(x.isInverted(), False) + self.assertEqual(y.isInverted(), False) + self.assertEqual(yright.isInverted(), False) + self.assertEqual(self.plot.isYAxisInverted(), False) + self.assertEqual(listener.arguments(callIndex=-1), ("y", False)) + + def testLogXWithData(self): + self.plot.setGraphTitle('Curve X: Log Y: Linear') + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=True, + color='green', linestyle="-", symbol='o') + axis = self.plot.getXAxis() + axis.setScale(axis.LOGARITHMIC) + + self.assertEqual(axis.getScale(), axis.LOGARITHMIC) + + def testLogYWithData(self): + self.plot.setGraphTitle('Curve X: Linear Y: Log') + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=True, + color='green', linestyle="-", symbol='o') + axis = self.plot.getYAxis() + axis.setScale(axis.LOGARITHMIC) + self.assertEqual(axis.getScale(), axis.LOGARITHMIC) + axis = self.plot.getYAxis(axis="right") + self.assertEqual(axis.getScale(), axis.LOGARITHMIC) -class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): + def testLogYRightWithData(self): + self.plot.setGraphTitle('Curve X: Linear Y: Log') + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=True, + color='green', linestyle="-", symbol='o') + axis = self.plot.getYAxis(axis="right") + axis.setScale(axis.LOGARITHMIC) + + self.assertEqual(axis.getScale(), axis.LOGARITHMIC) + axis = self.plot.getYAxis() + self.assertEqual(axis.getScale(), axis.LOGARITHMIC) + + def testLimitsChanged_setLimits(self): + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=False, + color='green', linestyle="-", symbol='o') + listener = SignalListener() + self.plot.getXAxis().sigLimitsChanged.connect(listener.partial(axis="x")) + self.plot.getYAxis().sigLimitsChanged.connect(listener.partial(axis="y")) + self.plot.getYAxis(axis="right").sigLimitsChanged.connect(listener.partial(axis="y2")) + self.plot.setLimits(0, 1, 0, 1, 0, 1) + # at least one event per axis + self.assertEquals(len(set(listener.karguments(argumentName="axis"))), 3) + + def testLimitsChanged_resetZoom(self): + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=False, + color='green', linestyle="-", symbol='o') + listener = SignalListener() + self.plot.getXAxis().sigLimitsChanged.connect(listener.partial(axis="x")) + self.plot.getYAxis().sigLimitsChanged.connect(listener.partial(axis="y")) + self.plot.getYAxis(axis="right").sigLimitsChanged.connect(listener.partial(axis="y2")) + self.plot.resetZoom() + # at least one event per axis + self.assertEquals(len(set(listener.karguments(argumentName="axis"))), 3) + + def testLimitsChanged_setXLimit(self): + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=False, + color='green', linestyle="-", symbol='o') + listener = SignalListener() + axis = self.plot.getXAxis() + axis.sigLimitsChanged.connect(listener) + axis.setLimits(20, 30) + # at least one event per axis + self.assertEquals(listener.arguments(callIndex=-1), (20.0, 30.0)) + self.assertEquals(axis.getLimits(), (20.0, 30.0)) + + def testLimitsChanged_setYLimit(self): + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=False, + color='green', linestyle="-", symbol='o') + listener = SignalListener() + axis = self.plot.getYAxis() + axis.sigLimitsChanged.connect(listener) + axis.setLimits(20, 30) + # at least one event per axis + self.assertEquals(listener.arguments(callIndex=-1), (20.0, 30.0)) + self.assertEquals(axis.getLimits(), (20.0, 30.0)) + + def testLimitsChanged_setYRightLimit(self): + self.plot.addCurve(self.xData, self.yData, + legend="curve", + replace=False, resetzoom=False, + color='green', linestyle="-", symbol='o') + listener = SignalListener() + axis = self.plot.getYAxis(axis="right") + axis.sigLimitsChanged.connect(listener) + axis.setLimits(20, 30) + # at least one event per axis + self.assertEquals(listener.arguments(callIndex=-1), (20.0, 30.0)) + self.assertEquals(axis.getLimits(), (20.0, 30.0)) + + def testScaleProxy(self): + listener = SignalListener() + y = self.plot.getYAxis() + yright = self.plot.getYAxis(axis="right") + y.sigScaleChanged.connect(listener.partial("left")) + yright.sigScaleChanged.connect(listener.partial("right")) + yright.setScale(yright.LOGARITHMIC) + + self.assertEquals(y.getScale(), y.LOGARITHMIC) + events = listener.arguments() + self.assertEquals(len(events), 2) + self.assertIn(("left", y.LOGARITHMIC), events) + self.assertIn(("right", y.LOGARITHMIC), events) + + def testAutoScaleProxy(self): + listener = SignalListener() + y = self.plot.getYAxis() + yright = self.plot.getYAxis(axis="right") + y.sigAutoScaleChanged.connect(listener.partial("left")) + yright.sigAutoScaleChanged.connect(listener.partial("right")) + yright.setAutoScale(False) + + self.assertEquals(y.isAutoScale(), False) + events = listener.arguments() + self.assertEquals(len(events), 2) + self.assertIn(("left", False), events) + self.assertIn(("right", False), events) + + def testInvertedProxy(self): + listener = SignalListener() + y = self.plot.getYAxis() + yright = self.plot.getYAxis(axis="right") + y.sigInvertedChanged.connect(listener.partial("left")) + yright.sigInvertedChanged.connect(listener.partial("right")) + yright.setInverted(True) + + self.assertEquals(y.isInverted(), True) + events = listener.arguments() + self.assertEquals(len(events), 2) + self.assertIn(("left", True), events) + self.assertIn(("right", True), events) + + def testAxesDisplayedFalse(self): + """Test coverage on setAxesDisplayed(False)""" + self.plot.setAxesDisplayed(False) + + def testAxesDisplayedTrue(self): + """Test coverage on setAxesDisplayed(True)""" + self.plot.setAxesDisplayed(True) + + +class TestPlotCurveLog(PlotWidgetTestCase, ParametricTestCase): """Basic tests for addCurve with log scale axes""" # Test data @@ -578,12 +969,12 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): yData = xData ** 2 def _setLabels(self): - self.plot.setGraphXLabel('X') - self.plot.setGraphYLabel('X * X') + self.plot.getXAxis().setLabel('X') + self.plot.getYAxis().setLabel('X * X') def testPlotCurveLogX(self): self._setLabels() - self.plot.setXAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) self.plot.setGraphTitle('Curve X: Log Y: Linear') self.plot.addCurve(self.xData, self.yData, @@ -593,7 +984,7 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): def testPlotCurveLogY(self): self._setLabels() - self.plot.setYAxisLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) self.plot.setGraphTitle('Curve X: Linear Y: Log') @@ -604,8 +995,8 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): def testPlotCurveLogXY(self): self._setLabels() - self.plot.setXAxisLogarithmic(True) - self.plot.setYAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) self.plot.setGraphTitle('Curve X: Log Y: Log') @@ -615,8 +1006,8 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): color='green', linestyle="-", symbol='o') def testPlotCurveErrorLogXY(self): - self.plot.setXAxisLogarithmic(True) - self.plot.setYAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) # Every second error leads to negative number errors = numpy.ones_like(self.xData) @@ -666,17 +1057,17 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): self.qapp.processEvents() # no log axis - xLim = self.plot.getGraphXLimits() + xLim = self.plot.getXAxis().getLimits() self.assertEqual(xLim, (min(xData), max(xData))) - yLim = self.plot.getGraphYLimits() + yLim = self.plot.getYAxis().getLimits() self.assertEqual(yLim, (min(yData), max(yData))) # x axis log - self.plot.setXAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) self.qapp.processEvents() - xLim = self.plot.getGraphXLimits() - yLim = self.plot.getGraphYLimits() + xLim = self.plot.getXAxis().getLimits() + yLim = self.plot.getYAxis().getLimits() positives = xData > 0 if numpy.any(positives): self.assertTrue(numpy.allclose( @@ -688,11 +1079,11 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): self.assertEqual(yLim, (1., 100.)) # x axis and y axis log - self.plot.setYAxisLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) self.qapp.processEvents() - xLim = self.plot.getGraphXLimits() - yLim = self.plot.getGraphYLimits() + xLim = self.plot.getXAxis().getLimits() + yLim = self.plot.getYAxis().getLimits() positives = numpy.logical_and(xData > 0, yData > 0) if numpy.any(positives): self.assertTrue(numpy.allclose( @@ -704,11 +1095,11 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): self.assertEqual(yLim, (1., 100.)) # y axis log - self.plot.setXAxisLogarithmic(False) + self.plot.getXAxis()._setLogarithmic(False) self.qapp.processEvents() - xLim = self.plot.getGraphXLimits() - yLim = self.plot.getGraphYLimits() + xLim = self.plot.getXAxis().getLimits() + yLim = self.plot.getYAxis().getLimits() positives = yData > 0 if numpy.any(positives): self.assertEqual( @@ -720,12 +1111,12 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): self.assertEqual(yLim, (1., 100.)) # no log axis - self.plot.setYAxisLogarithmic(False) + self.plot.getYAxis()._setLogarithmic(False) self.qapp.processEvents() - xLim = self.plot.getGraphXLimits() + xLim = self.plot.getXAxis().getLimits() self.assertEqual(xLim, (min(xData), max(xData))) - yLim = self.plot.getGraphYLimits() + yLim = self.plot.getYAxis().getLimits() self.assertEqual(yLim, (min(yData), max(yData))) self.plot.clear() @@ -733,52 +1124,58 @@ class TestPlotCurveLog(_PlotWidgetTest, ParametricTestCase): self.qapp.processEvents() -class TestPlotImageLog(_PlotWidgetTest): +class TestPlotImageLog(PlotWidgetTestCase): """Basic tests for addImage with log scale axes.""" def setUp(self): super(TestPlotImageLog, self).setUp() - self.plot.setGraphXLabel('Columns') - self.plot.setGraphYLabel('Rows') + self.plot.getXAxis().setLabel('Columns') + self.plot.getYAxis().setLabel('Rows') def testPlotColormapGrayLogX(self): - self.plot.setXAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) self.plot.setGraphTitle('CMap X: Log Y: Linear') - colormap = {'name': 'gray', 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0} + colormap = Colormap(name='gray', + normalization='linear', + vmin=None, + vmax=None) self.plot.addImage(DATA_2D, legend="image 1", origin=(1., 1.), scale=(1., 1.), replace=False, resetzoom=False, colormap=colormap) self.plot.resetZoom() def testPlotColormapGrayLogY(self): - self.plot.setYAxisLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) self.plot.setGraphTitle('CMap X: Linear Y: Log') - colormap = {'name': 'gray', 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0} + colormap = Colormap(name='gray', + normalization='linear', + vmin=None, + vmax=None) self.plot.addImage(DATA_2D, legend="image 1", origin=(1., 1.), scale=(1., 1.), replace=False, resetzoom=False, colormap=colormap) self.plot.resetZoom() def testPlotColormapGrayLogXY(self): - self.plot.setXAxisLogarithmic(True) - self.plot.setYAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) self.plot.setGraphTitle('CMap X: Log Y: Log') - colormap = {'name': 'gray', 'normalization': 'linear', - 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0} + colormap = Colormap(name='gray', + normalization='linear', + vmin=None, + vmax=None) self.plot.addImage(DATA_2D, legend="image 1", origin=(1., 1.), scale=(1., 1.), replace=False, resetzoom=False, colormap=colormap) self.plot.resetZoom() def testPlotRgbRgbaLogXY(self): - self.plot.setXAxisLogarithmic(True) - self.plot.setYAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) self.plot.setGraphTitle('RGB + RGBA X: Log Y: Log') rgb = numpy.array( @@ -801,7 +1198,7 @@ class TestPlotImageLog(_PlotWidgetTest): self.plot.resetZoom() -class TestPlotMarkerLog(_PlotWidgetTest): +class TestPlotMarkerLog(PlotWidgetTestCase): """Basic tests for markers on log scales""" # Test marker parameters @@ -816,14 +1213,14 @@ class TestPlotMarkerLog(_PlotWidgetTest): def setUp(self): super(TestPlotMarkerLog, self).setUp() - self.plot.setGraphYLabel('Rows') - self.plot.setGraphXLabel('Columns') - self.plot.setXAxisAutoScale(False) - self.plot.setYAxisAutoScale(False) + 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., 1000.) - self.plot.setXAxisLogarithmic(True) - self.plot.setYAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) def testPlotMarkerXLog(self): self.plot.setGraphTitle('Markers X, Log axes') @@ -862,7 +1259,7 @@ class TestPlotMarkerLog(_PlotWidgetTest): self.plot.resetZoom() -class TestPlotItemLog(_PlotWidgetTest): +class TestPlotItemLog(PlotWidgetTestCase): """Basic tests for items with log scale axes""" # Polygon coordinates and color @@ -892,14 +1289,14 @@ class TestPlotItemLog(_PlotWidgetTest): def setUp(self): super(TestPlotItemLog, self).setUp() - self.plot.setGraphYLabel('Rows') - self.plot.setGraphXLabel('Columns') - self.plot.setXAxisAutoScale(False) - self.plot.setYAxisAutoScale(False) + 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.setXAxisLogarithmic(True) - self.plot.setYAxisLogarithmic(True) + self.plot.getXAxis()._setLogarithmic(True) + self.plot.getYAxis()._setLogarithmic(True) def testPlotItemPolygonLogFill(self): self.plot.setGraphTitle('Item Fill Log') @@ -940,26 +1337,18 @@ class TestPlotItemLog(_PlotWidgetTest): def suite(): test_suite = unittest.TestSuite() - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotWidget)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotImage)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotCurve)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotMarker)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotItem)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotEmptyLog)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotCurveLog)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotImageLog)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotMarkerLog)) - test_suite.addTest( - unittest.defaultTestLoader.loadTestsFromTestCase(TestPlotItemLog)) + 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)) return test_suite diff --git a/silx/gui/plot/test/testPlot.py b/silx/gui/plot/test/testPlotWidgetNoBackend.py index 25e7511..3094a20 100644 --- a/silx/gui/plot/test/testPlot.py +++ b/silx/gui/plot/test/testPlotWidgetNoBackend.py @@ -22,11 +22,11 @@ # THE SOFTWARE. # # ###########################################################################*/ -"""Basic tests for Plot""" +"""Basic tests for PlotWidget with 'none' backend""" __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "05/12/2016" +__date__ = "27/06/2017" import unittest @@ -35,7 +35,7 @@ from silx.test.utils import ParametricTestCase import numpy -from silx.gui.plot.Plot import Plot +from silx.gui.plot.PlotWidget import PlotWidget from silx.gui.plot.items.histogram import _getHistogramCurve, _computeEdges @@ -45,21 +45,21 @@ class TestPlot(unittest.TestCase): def testPlotTitleLabels(self): """Create a Plot and set the labels""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') title, xlabel, ylabel = 'the title', 'x label', 'y label' plot.setGraphTitle(title) - plot.setGraphXLabel(xlabel) - plot.setGraphYLabel(ylabel) + plot.getXAxis().setLabel(xlabel) + plot.getYAxis().setLabel(ylabel) self.assertEqual(plot.getGraphTitle(), title) - self.assertEqual(plot.getGraphXLabel(), xlabel) - self.assertEqual(plot.getGraphYLabel(), ylabel) + self.assertEqual(plot.getXAxis().getLabel(), xlabel) + self.assertEqual(plot.getYAxis().getLabel(), ylabel) def testAddNoRemove(self): """add objects to the Plot""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') plot.addCurve(x=(1, 2, 3), y=(3, 2, 1)) plot.addImage(numpy.arange(100.).reshape(10, -1)) plot.addItem( @@ -96,7 +96,7 @@ class TestPlotRanges(ParametricTestCase): def testDataRangeNoPlot(self): """empty plot data range""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') for logX, logY in ((False, False), (True, False), @@ -104,8 +104,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() self.assertIsNone(dataRange.x) self.assertIsNone(dataRange.y) @@ -114,7 +114,7 @@ class TestPlotRanges(ParametricTestCase): def testDataRangeLeft(self): """left axis range""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') xData = numpy.arange(10) - 4.9 # range : -4.9 , 4.1 yData = numpy.arange(10) - 6.9 # range : -6.9 , 2.1 @@ -130,8 +130,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRange, yRange = self._getRanges([xData, yData], [logX, logY]) @@ -142,7 +142,7 @@ class TestPlotRanges(ParametricTestCase): def testDataRangeRight(self): """right axis range""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') xData = numpy.arange(10) - 4.9 # range : -4.9 , 4.1 yData = numpy.arange(10) - 6.9 # range : -6.9 , 2.1 plot.addCurve(x=xData, @@ -156,8 +156,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRange, yRange = self._getRanges([xData, yData], [logX, logY]) @@ -172,7 +172,7 @@ class TestPlotRanges(ParametricTestCase): scale = (3., 8.) image = numpy.arange(100.).reshape(20, 5) - plot = Plot(backend='none') + plot = PlotWidget(backend='none') plot.addImage(image, origin=origin, scale=scale) @@ -190,8 +190,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRange, yRange = ranges[logX, logY] self.assertTrue(numpy.array_equal(dataRange.x, xRange), @@ -203,7 +203,7 @@ class TestPlotRanges(ParametricTestCase): def testDataRangeLeftRight(self): """right+left axis range""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') xData_l = numpy.arange(10) - 0.9 # range : -0.9 , 8.1 yData_l = numpy.arange(10) - 1.9 # range : -1.9 , 7.1 @@ -225,8 +225,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRangeL, yRangeL = self._getRanges([xData_l, yData_l], [logX, logY]) @@ -244,7 +244,7 @@ class TestPlotRanges(ParametricTestCase): # image sets x min and y max # plot_left sets y min # plot_right sets x max (and yright) - plot = Plot(backend='none') + plot = PlotWidget(backend='none') origin = (-10, 5) scale = (3., 8.) @@ -276,8 +276,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRangeL, yRangeL = self._getRanges([xData_l, yData_l], [logX, logY]) @@ -301,7 +301,7 @@ class TestPlotRanges(ParametricTestCase): scale = (-3., 8.) image = numpy.arange(100.).reshape(20, 5) - plot = Plot(backend='none') + plot = PlotWidget(backend='none') plot.addImage(image, origin=origin, scale=scale) @@ -320,8 +320,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRange, yRange = ranges[logX, logY] self.assertTrue(numpy.array_equal(dataRange.x, xRange), @@ -337,7 +337,7 @@ class TestPlotRanges(ParametricTestCase): scale = (3., -8.) image = numpy.arange(100.).reshape(20, 5) - plot = Plot(backend='none') + plot = PlotWidget(backend='none') plot.addImage(image, origin=origin, scale=scale) @@ -356,8 +356,8 @@ class TestPlotRanges(ParametricTestCase): (False, True), (False, False)): with self.subTest(logX=logX, logY=logY): - plot.setXAxisLogarithmic(logX) - plot.setYAxisLogarithmic(logY) + plot.getXAxis()._setLogarithmic(logX) + plot.getYAxis()._setLogarithmic(logY) dataRange = plot.getDataRange() xRange, yRange = ranges[logX, logY] self.assertTrue(numpy.array_equal(dataRange.x, xRange), @@ -368,7 +368,7 @@ class TestPlotRanges(ParametricTestCase): def testDataRangeHiddenCurve(self): """curves with a hidden curve""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') plot.addCurve((0, 1), (0, 1), legend='shown') plot.addCurve((0, 1, 2), (5, 5, 5), legend='hidden') range1 = plot.getDataRange() @@ -384,9 +384,9 @@ class TestPlotGetCurveImage(unittest.TestCase): """Test of plot getCurve and getImage methods""" def testGetCurve(self): - """Plot.getCurve and Plot.getActiveCurve tests""" + """PlotWidget.getCurve and Plot.getActiveCurve tests""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') # No curve curve = plot.getCurve() @@ -423,9 +423,9 @@ class TestPlotGetCurveImage(unittest.TestCase): self.assertIsNone(curve) def testGetCurveOldApi(self): - """old API Plot.getCurve and Plot.getActiveCurve tests""" + """old API PlotWidget.getCurve and Plot.getActiveCurve tests""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') # No curve curve = plot.getCurve() @@ -433,7 +433,7 @@ class TestPlotGetCurveImage(unittest.TestCase): plot.setActiveCurveHandling(True) x = numpy.arange(10.).astype(numpy.float32) - y = x * x; + y = x * x plot.addCurve(x=x, y=y, legend='curve 0', info=["whatever"]) plot.addCurve(x=x, y=2*x, legend='curve 1', info="anything") plot.setActiveCurve('curve 0') @@ -449,12 +449,12 @@ class TestPlotGetCurveImage(unittest.TestCase): self.assertEqual(legend, 'curve 1') self.assertEqual(info, 'anything') self.assertTrue(numpy.allclose(xOut, x), 'curve 1 wrong x data') - self.assertTrue(numpy.allclose(yOut, 2*x), 'curve 1 wrong y data') + self.assertTrue(numpy.allclose(yOut, 2 * x), 'curve 1 wrong y data') def testGetImage(self): - """Plot.getImage and Plot.getActiveImage tests""" + """PlotWidget.getImage and PlotWidget.getActiveImage tests""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') # No image image = plot.getImage() @@ -485,9 +485,9 @@ class TestPlotGetCurveImage(unittest.TestCase): self.assertEqual(image.getLegend(), 'image 1') def testGetImageOldApi(self): - """Plot.getImage and Plot.getActiveImage old API tests""" + """PlotWidget.getImage and PlotWidget.getActiveImage old API tests""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') # No image image = plot.getImage() @@ -505,9 +505,9 @@ class TestPlotGetCurveImage(unittest.TestCase): self.assertTrue(numpy.allclose(data, image), "image 0 data not correct") def testGetAllImages(self): - """Plot.getAllImages test""" + """PlotWidget.getAllImages test""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') # No image images = plot.getAllImages() @@ -530,7 +530,7 @@ class TestPlotAddScatter(unittest.TestCase): def testAddGetScatter(self): - plot = Plot(backend='none') + plot = PlotWidget(backend='none') # No curve scatter = plot._getItem(kind="scatter") @@ -565,9 +565,9 @@ class TestPlotAddScatter(unittest.TestCase): self.assertEqual(scatter1.getLegend(), 'scatter 1') def testGetAllScatters(self): - """Plot.getAllImages test""" + """PlotWidget.getAllImages test""" - plot = Plot(backend='none') + plot = PlotWidget(backend='none') scatters = plot._getItems(kind='scatter') self.assertEqual(len(scatters), 0) diff --git a/silx/gui/plot/test/testPlotWindow.py b/silx/gui/plot/test/testPlotWindow.py index 5afd53a..24d840b 100644 --- a/silx/gui/plot/test/testPlotWindow.py +++ b/silx/gui/plot/test/testPlotWindow.py @@ -26,7 +26,7 @@ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "05/12/2016" +__date__ = "27/06/2017" import doctest @@ -84,10 +84,10 @@ class TestPlotWindow(TestCaseQt): self.plot.setLimits(1, 100, 1, 100) checkList = [ # QAction, Plot state getter - (self.plot.xAxisAutoScaleAction, self.plot.isXAxisAutoScale), - (self.plot.yAxisAutoScaleAction, self.plot.isYAxisAutoScale), - (self.plot.xAxisLogarithmicAction, self.plot.isXAxisLogarithmic), - (self.plot.yAxisLogarithmicAction, self.plot.isYAxisLogarithmic), + (self.plot.xAxisAutoScaleAction, self.plot.getXAxis().isAutoScale), + (self.plot.yAxisAutoScaleAction, self.plot.getYAxis().isAutoScale), + (self.plot.xAxisLogarithmicAction, self.plot.getXAxis()._isLogarithmic), + (self.plot.yAxisLogarithmicAction, self.plot.getYAxis()._isLogarithmic), (self.plot.gridAction, self.plot.getGraphGrid), ] @@ -121,9 +121,9 @@ class TestPlotWindow(TestCaseQt): def testToolYAxisOrigin(self): self.plot.toolBar() self.plot.yAxisInvertedButton.setYAxisUpward() - self.assertFalse(self.plot.isYAxisInverted()) + self.assertFalse(self.plot.getYAxis().isInverted()) self.plot.yAxisInvertedButton.setYAxisDownward() - self.assertTrue(self.plot.isYAxisInverted()) + self.assertTrue(self.plot.getYAxis().isInverted()) def suite(): diff --git a/silx/gui/plot/test/testScatterMaskToolsWidget.py b/silx/gui/plot/test/testScatterMaskToolsWidget.py index 8b5f2ad..178274a 100644 --- a/silx/gui/plot/test/testScatterMaskToolsWidget.py +++ b/silx/gui/plot/test/testScatterMaskToolsWidget.py @@ -26,7 +26,7 @@ __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" -__date__ = "10/07/2017" +__date__ = "01/09/2017" import logging @@ -37,8 +37,9 @@ import numpy from silx.gui import qt from silx.test.utils import temp_dir, ParametricTestCase -from silx.gui.test.utils import TestCaseQt, getQToolButtonFromAction +from silx.gui.test.utils import getQToolButtonFromAction from silx.gui.plot import PlotWindow, ScatterMaskToolsWidget +from .utils import PlotWidgetTestCase try: import fabio @@ -46,34 +47,26 @@ except ImportError: fabio = None -logging.basicConfig() _logger = logging.getLogger(__name__) -class TestScatterMaskToolsWidget(TestCaseQt, ParametricTestCase): +class TestScatterMaskToolsWidget(PlotWidgetTestCase, ParametricTestCase): """Basic test for MaskToolsWidget""" + def _createPlot(self): + return PlotWindow() + def setUp(self): super(TestScatterMaskToolsWidget, self).setUp() - self.plot = PlotWindow() - self.widget = ScatterMaskToolsWidget.ScatterMaskToolsDockWidget( plot=self.plot, name='TEST') self.plot.addDockWidget(qt.Qt.BottomDockWidgetArea, self.widget) - self.plot.show() - self.qWaitForWindowExposed(self.plot) - self.maskWidget = self.widget.widget() def tearDown(self): del self.maskWidget del self.widget - - self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) - self.plot.close() - del self.plot - super(TestScatterMaskToolsWidget, self).tearDown() def testEmptyPlot(self): @@ -86,7 +79,7 @@ class TestScatterMaskToolsWidget(TestCaseQt, ParametricTestCase): def _drag(self): """Drag from plot center to offset position""" - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() xCenter, yCenter = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -100,7 +93,7 @@ class TestScatterMaskToolsWidget(TestCaseQt, ParametricTestCase): def _drawPolygon(self): """Draw a star polygon in the plot""" - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() x, y = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -108,16 +101,17 @@ class TestScatterMaskToolsWidget(TestCaseQt, ParametricTestCase): (x - offset, y - offset), (x + offset, y), (x - offset, y), - (x + offset, y - offset)] + (x + offset, y - offset), + (x, y + offset)] # Close polygon + self.mouseMove(plot, pos=[0, 0]) for pos in star: self.mouseMove(plot, pos=pos) - btn = qt.Qt.LeftButton if pos != star[-1] else qt.Qt.RightButton - self.mouseClick(plot, btn, pos=pos) + self.mouseClick(plot, qt.Qt.LeftButton, pos=pos) def _drawPencil(self): """Draw a star polygon in the plot""" - plot = self.plot.centralWidget() + plot = self.plot.getWidgetHandle() x, y = plot.width() // 2, plot.height() // 2 offset = min(plot.width(), plot.height()) // 10 @@ -127,9 +121,10 @@ class TestScatterMaskToolsWidget(TestCaseQt, ParametricTestCase): (x - offset, y), (x + offset, y - offset)] + self.mouseMove(plot, pos=[0, 0]) self.mouseMove(plot, pos=star[0]) self.mousePress(plot, qt.Qt.LeftButton, pos=star[0]) - for pos in star: + for pos in star[1:]: self.mouseMove(plot, pos=pos) self.mouseRelease( plot, qt.Qt.LeftButton, pos=star[-1]) diff --git a/silx/gui/plot/test/testStackView.py b/silx/gui/plot/test/testStackView.py index 69584cd..8d2a0ee 100644 --- a/silx/gui/plot/test/testStackView.py +++ b/silx/gui/plot/test/testStackView.py @@ -130,7 +130,7 @@ class TestStackView(TestCaseQt): self.assertEqual(self.stackview._perspective, 2, "Perspective not set in setStack(..., perspective=2).") - def testTitle(self): + def testDefaultTitle(self): """Test that the plot title contains the proper Z information""" self.stackview.setStack(numpy.arange(24).reshape((4, 3, 2)), calibrations=[(0, 1), (-10, 10), (3.14, 3.14)]) @@ -156,6 +156,37 @@ class TestStackView(TestCaseQt): self.assertEqual(self.stackview._plot.getGraphTitle(), "Image z=6.28") + def testCustomTitle(self): + """Test setting the plot title with a user defined callback""" + self.stackview.setStack(numpy.arange(24).reshape((4, 3, 2)), + calibrations=[(0, 1), (-10, 10), (3.14, 3.14)]) + + def title_callback(frame_idx): + return "Cubed index title %d" % (frame_idx**3) + + self.stackview.setTitleCallback(title_callback) + self.assertEqual(self.stackview._plot.getGraphTitle(), + "Cubed index title 0") + self.stackview.setFrameNumber(2) + self.assertEqual(self.stackview._plot.getGraphTitle(), + "Cubed index title 8") + + # perspective should not matter, only frame index + self.stackview._StackView__planeSelection.setPerspective(1) + self.stackview.setFrameNumber(0) + self.assertEqual(self.stackview._plot.getGraphTitle(), + "Cubed index title 0") + self.stackview.setFrameNumber(2) + self.assertEqual(self.stackview._plot.getGraphTitle(), + "Cubed index title 8") + + with self.assertRaises(TypeError): + # setTitleCallback should not accept non-callable objects like strings + self.stackview.setTitleCallback( + "Là, vous faites sirop de vingt-et-un et vous dites : " + "beau sirop, mi-sirop, siroté, gagne-sirop, sirop-grelot," + " passe-montagne, sirop au bon goût.") + class TestStackViewMainWindow(TestCaseQt): """Base class for tests of StackView.""" diff --git a/silx/gui/plot/test/testUtilsAxis.py b/silx/gui/plot/test/testUtilsAxis.py new file mode 100644 index 0000000..6702b00 --- /dev/null +++ b/silx/gui/plot/test/testUtilsAxis.py @@ -0,0 +1,148 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016 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 PlotWidget""" + +__authors__ = ["V. Valls"] +__license__ = "MIT" +__date__ = "04/08/2017" + + +import unittest +from silx.gui.plot import PlotWidget +from silx.gui.plot.utils.axis import SyncAxes + + +class TestAxisSync(unittest.TestCase): + """Tests AxisSync class""" + + def setUp(self): + self.plot1 = PlotWidget() + self.plot2 = PlotWidget() + self.plot3 = PlotWidget() + + def tearDown(self): + self.plot1 = None + self.plot2 = None + self.plot3 = None + + def testMoveFirstAxis(self): + """Test synchronization after construction""" + _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + + self.plot1.getXAxis().setLimits(10, 500) + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500)) + + def testMoveSecondAxis(self): + """Test synchronization after construction""" + _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + + self.plot2.getXAxis().setLimits(10, 500) + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500)) + + def testMoveTwoAxes(self): + """Test synchronization after construction""" + _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + + self.plot1.getXAxis().setLimits(1, 50) + self.plot2.getXAxis().setLimits(10, 500) + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500)) + + def testDestruction(self): + """Test synchronization when sync object is destroyed""" + sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + del sync + + self.plot1.getXAxis().setLimits(10, 500) + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + self.assertNotEqual(self.plot2.getXAxis().getLimits(), (10, 500)) + self.assertNotEqual(self.plot3.getXAxis().getLimits(), (10, 500)) + + def testStop(self): + """Test synchronization after calling stop""" + sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + sync.stop() + + self.plot1.getXAxis().setLimits(10, 500) + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + self.assertNotEqual(self.plot2.getXAxis().getLimits(), (10, 500)) + self.assertNotEqual(self.plot3.getXAxis().getLimits(), (10, 500)) + + def testStopMovingStart(self): + """Test synchronization after calling stop, moving an axis, then start again""" + sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + sync.stop() + self.plot1.getXAxis().setLimits(10, 500) + self.plot2.getXAxis().setLimits(1, 50) + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + sync.start() + + # The first axis is the reference + self.assertEqual(self.plot1.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot2.getXAxis().getLimits(), (10, 500)) + self.assertEqual(self.plot3.getXAxis().getLimits(), (10, 500)) + + def testDoubleStop(self): + """Test double stop""" + sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + sync.stop() + self.assertRaises(RuntimeError, sync.stop) + + def testDoubleStart(self): + """Test double stop""" + sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + self.assertRaises(RuntimeError, sync.start) + + def testScale(self): + """Test scale change""" + _sync = SyncAxes([self.plot1.getXAxis(), self.plot2.getXAxis(), self.plot3.getXAxis()]) + self.plot1.getXAxis().setScale(self.plot1.getXAxis().LOGARITHMIC) + self.assertEqual(self.plot1.getXAxis().getScale(), self.plot1.getXAxis().LOGARITHMIC) + self.assertEqual(self.plot2.getXAxis().getScale(), self.plot1.getXAxis().LOGARITHMIC) + self.assertEqual(self.plot3.getXAxis().getScale(), self.plot1.getXAxis().LOGARITHMIC) + + def testDirection(self): + """Test direction change""" + _sync = SyncAxes([self.plot1.getYAxis(), self.plot2.getYAxis(), self.plot3.getYAxis()]) + self.plot1.getYAxis().setInverted(True) + self.assertEqual(self.plot1.getYAxis().isInverted(), True) + self.assertEqual(self.plot2.getYAxis().isInverted(), True) + self.assertEqual(self.plot3.getYAxis().isInverted(), True) + + +def suite(): + test_suite = unittest.TestSuite() + loadTests = unittest.defaultTestLoader.loadTestsFromTestCase + test_suite.addTest(loadTests(TestAxisSync)) + 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 new file mode 100644 index 0000000..ef547c6 --- /dev/null +++ b/silx/gui/plot/test/utils.py @@ -0,0 +1,194 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016 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 PlotWidget""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "01/09/2017" + + +import logging +import contextlib + +from silx.gui.test.utils import TestCaseQt + +from silx.gui import qt +from silx.gui.plot import PlotWidget +from silx.gui.plot.backends.BackendMatplotlib import BackendMatplotlibQt + + +logger = logging.getLogger(__name__) + + +class PlotWidgetTestCase(TestCaseQt): + """Base class for tests of PlotWidget, not a TestCase in itself. + + plot attribute is the PlotWidget created for the test. + """ + + def __init__(self, methodName='runTest'): + TestCaseQt.__init__(self, methodName=methodName) + self.__mousePos = None + + def _createPlot(self): + return PlotWidget() + + def setUp(self): + super(PlotWidgetTestCase, self).setUp() + self.plot = self._createPlot() + self.plot.show() + self.plotAlive = True + self.qWaitForWindowExposed(self.plot) + TestCaseQt.mouseClick(self, self.plot, button=qt.Qt.LeftButton, pos=(0, 0)) + + def __onPlotDestroyed(self): + self.plotAlive = False + + def _waitForPlotClosed(self): + self.plot.setAttribute(qt.Qt.WA_DeleteOnClose) + self.plot.destroyed.connect(self.__onPlotDestroyed) + self.plot.close() + del self.plot + for _ in range(100): + if not self.plotAlive: + break + self.qWait(10) + else: + logger.error("Plot is still alive") + + def tearDown(self): + self.qapp.processEvents() + self._waitForPlotClosed() + super(PlotWidgetTestCase, self).tearDown() + + def _logMplEvents(self, event): + self.__mplEvents.append(event) + + @contextlib.contextmanager + def _waitForMplEvent(self, plot, mplEventType): + """Check if an event was received by the MPL backend. + + :param PlotWidget plot: A plot widget or a MPL plot backend + :param str mplEventType: MPL event type + :raises RuntimeError: When the event did not happen + """ + self.__mplEvents = [] + if isinstance(plot, BackendMatplotlibQt): + backend = plot + else: + backend = plot._backend + + callbackId = backend.mpl_connect(mplEventType, self._logMplEvents) + received = False + yield + for _ in range(100): + if len(self.__mplEvents) > 0: + received = True + break + self.qWait(10) + backend.mpl_disconnect(callbackId) + del self.__mplEvents + if not received: + self.logScreenShot() + raise RuntimeError("MPL event %s expected but nothing received" % mplEventType) + + def _haveMplEvent(self, widget, pos): + """Check if the widget at this position is a matplotlib widget.""" + if isinstance(pos, qt.QPoint): + pass + else: + pos = qt.QPoint(pos[0], pos[1]) + pos = widget.mapTo(widget.window(), pos) + target = widget.window().childAt(pos) + + # Check if the target is a MPL container + backend = target + if hasattr(target, "_backend"): + backend = target._backend + haveEvent = isinstance(backend, BackendMatplotlibQt) + return haveEvent + + def _patchPos(self, widget, pos): + """Return a real position relative to the widget. + + If pos is None, the returned value is the center of the widget, + as the default behaviour of functions like QTest.mouseMove. + Else the position is returned as it is. + """ + if pos is None: + pos = widget.size() / 2 + pos = pos.width(), pos.height() + return pos + + def _checkMouseMove(self, widget, pos): + """Returns true if the position differe from the current position of + the cursor""" + pos = qt.QPoint(pos[0], pos[1]) + pos = widget.mapTo(widget.window(), pos) + willMove = pos != self.__mousePos + self.__mousePos = pos + return willMove + + def mouseMove(self, widget, pos=None, delay=-1): + """Override TestCaseQt to wait while MPL did not reveive the expected + event""" + pos = self._patchPos(widget, pos) + willMove = self._checkMouseMove(widget, pos) + hadMplEvents = self._haveMplEvent(widget, self.__mousePos) + willHaveMplEvents = self._haveMplEvent(widget, pos) + if (not hadMplEvents and not willHaveMplEvents) or not willMove: + return TestCaseQt.mouseMove(self, widget, pos=pos, delay=delay) + with self._waitForMplEvent(widget, "motion_notify_event"): + TestCaseQt.mouseMove(self, widget, pos=pos, delay=delay) + + def mouseClick(self, widget, button, modifier=None, pos=None, delay=-1): + """Override TestCaseQt to wait while MPL did not reveive the expected + event""" + pos = self._patchPos(widget, pos) + self._checkMouseMove(widget, pos) + if not self._haveMplEvent(widget, pos): + return TestCaseQt.mouseClick(self, widget, button, modifier=modifier, pos=pos, delay=delay) + with self._waitForMplEvent(widget, "button_release_event"): + TestCaseQt.mouseClick(self, widget, button, modifier=modifier, pos=pos, delay=delay) + + def mousePress(self, widget, button, modifier=None, pos=None, delay=-1): + """Override TestCaseQt to wait while MPL did not reveive the expected + event""" + pos = self._patchPos(widget, pos) + self._checkMouseMove(widget, pos) + if not self._haveMplEvent(widget, pos): + return TestCaseQt.mousePress(self, widget, button, modifier=modifier, pos=pos, delay=delay) + with self._waitForMplEvent(widget, "button_press_event"): + TestCaseQt.mousePress(self, widget, button, modifier=modifier, pos=pos, delay=delay) + + def mouseRelease(self, widget, button, modifier=None, pos=None, delay=-1): + """Override TestCaseQt to wait while MPL did not reveive the expected + event""" + pos = self._patchPos(widget, pos) + self._checkMouseMove(widget, pos) + if not self._haveMplEvent(widget, pos): + return TestCaseQt.mouseRelease(self, widget, button, modifier=modifier, pos=pos, delay=delay) + with self._waitForMplEvent(widget, "button_release_event"): + TestCaseQt.mouseRelease(self, widget, button, modifier=modifier, pos=pos, delay=delay) |