summaryrefslogtreecommitdiff
path: root/silx/gui/utils
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/utils')
-rwxr-xr-x[-rw-r--r--]silx/gui/utils/__init__.py32
-rwxr-xr-xsilx/gui/utils/qtutils.py170
-rwxr-xr-x[-rw-r--r--]silx/gui/utils/test/__init__.py12
-rw-r--r--silx/gui/utils/test/test.py76
-rwxr-xr-xsilx/gui/utils/test/test_qtutils.py75
-rw-r--r--silx/gui/utils/test/test_testutils.py55
-rw-r--r--silx/gui/utils/testutils.py3
7 files changed, 418 insertions, 5 deletions
diff --git a/silx/gui/utils/__init__.py b/silx/gui/utils/__init__.py
index 51c4fac..a4e442f 100644..100755
--- a/silx/gui/utils/__init__.py
+++ b/silx/gui/utils/__init__.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
+# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -27,3 +27,33 @@
__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "09/03/2018"
+
+
+import contextlib as _contextlib
+
+
+@_contextlib.contextmanager
+def blockSignals(*objs):
+ """Context manager blocking signals of QObjects.
+
+ It restores previous state when leaving.
+
+ :param qt.QObject objs: QObjects for which to block signals
+ """
+ blocked = [(obj, obj.blockSignals(True)) for obj in objs]
+ try:
+ yield
+ finally:
+ for obj, previous in blocked:
+ obj.blockSignals(previous)
+
+
+def getQEventName(eventType):
+ """
+ Returns the name of a QEvent.
+
+ :param Union[int,qt.QEvent] eventType: A QEvent or a QEvent type.
+ :returns: str
+ """
+ from . import qtutils
+ return qtutils.getQEventName(eventType)
diff --git a/silx/gui/utils/qtutils.py b/silx/gui/utils/qtutils.py
new file mode 100755
index 0000000..eb823a8
--- /dev/null
+++ b/silx/gui/utils/qtutils.py
@@ -0,0 +1,170 @@
+from silx.gui import qt
+
+
+QT_EVENT_NAMES = {
+ 0: "None",
+ 114: "ActionAdded",
+ 113: "ActionChanged",
+ 115: "ActionRemoved",
+ 99: "ActivationChange",
+ 121: "ApplicationActivate",
+ # ApplicationActivate: "ApplicationActivated",
+ 122: "ApplicationDeactivate",
+ 36: "ApplicationFontChange",
+ 37: "ApplicationLayoutDirectionChange",
+ 38: "ApplicationPaletteChange",
+ 214: "ApplicationStateChange",
+ 35: "ApplicationWindowIconChange",
+ 68: "ChildAdded",
+ 69: "ChildPolished",
+ 71: "ChildRemoved",
+ 40: "Clipboard",
+ 19: "Close",
+ 200: "CloseSoftwareInputPanel",
+ 178: "ContentsRectChange",
+ 82: "ContextMenu",
+ 183: "CursorChange",
+ 52: "DeferredDelete",
+ 60: "DragEnter",
+ 62: "DragLeave",
+ 61: "DragMove",
+ 63: "Drop",
+ 170: "DynamicPropertyChange",
+ 98: "EnabledChange",
+ 10: "Enter",
+ 150: "EnterEditFocus",
+ 124: "EnterWhatsThisMode",
+ 206: "Expose",
+ 116: "FileOpen",
+ 8: "FocusIn",
+ 9: "FocusOut",
+ 23: "FocusAboutToChange",
+ 97: "FontChange",
+ 198: "Gesture",
+ 202: "GestureOverride",
+ 188: "GrabKeyboard",
+ 186: "GrabMouse",
+ 159: "GraphicsSceneContextMenu",
+ 164: "GraphicsSceneDragEnter",
+ 166: "GraphicsSceneDragLeave",
+ 165: "GraphicsSceneDragMove",
+ 167: "GraphicsSceneDrop",
+ 163: "GraphicsSceneHelp",
+ 160: "GraphicsSceneHoverEnter",
+ 162: "GraphicsSceneHoverLeave",
+ 161: "GraphicsSceneHoverMove",
+ 158: "GraphicsSceneMouseDoubleClick",
+ 155: "GraphicsSceneMouseMove",
+ 156: "GraphicsSceneMousePress",
+ 157: "GraphicsSceneMouseRelease",
+ 182: "GraphicsSceneMove",
+ 181: "GraphicsSceneResize",
+ 168: "GraphicsSceneWheel",
+ 18: "Hide",
+ 27: "HideToParent",
+ 127: "HoverEnter",
+ 128: "HoverLeave",
+ 129: "HoverMove",
+ 96: "IconDrag",
+ 101: "IconTextChange",
+ 83: "InputMethod",
+ 207: "InputMethodQuery",
+ 169: "KeyboardLayoutChange",
+ 6: "KeyPress",
+ 7: "KeyRelease",
+ 89: "LanguageChange",
+ 90: "LayoutDirectionChange",
+ 76: "LayoutRequest",
+ 11: "Leave",
+ 151: "LeaveEditFocus",
+ 125: "LeaveWhatsThisMode",
+ 88: "LocaleChange",
+ 176: "NonClientAreaMouseButtonDblClick",
+ 174: "NonClientAreaMouseButtonPress",
+ 175: "NonClientAreaMouseButtonRelease",
+ 173: "NonClientAreaMouseMove",
+ 177: "MacSizeChange",
+ 43: "MetaCall",
+ 102: "ModifiedChange",
+ 4: "MouseButtonDblClick",
+ 2: "MouseButtonPress",
+ 3: "MouseButtonRelease",
+ 5: "MouseMove",
+ 109: "MouseTrackingChange",
+ 13: "Move",
+ 197: "NativeGesture",
+ 208: "OrientationChange",
+ 12: "Paint",
+ 39: "PaletteChange",
+ 131: "ParentAboutToChange",
+ 21: "ParentChange",
+ 212: "PlatformPanel",
+ 217: "PlatformSurface",
+ 75: "Polish",
+ 74: "PolishRequest",
+ 123: "QueryWhatsThis",
+ 106: "ReadOnlyChange",
+ 199: "RequestSoftwareInputPanel",
+ 14: "Resize",
+ 204: "ScrollPrepare",
+ 205: "Scroll",
+ 117: "Shortcut",
+ 51: "ShortcutOverride",
+ 17: "Show",
+ 26: "ShowToParent",
+ 50: "SockAct",
+ 192: "StateMachineSignal",
+ 193: "StateMachineWrapped",
+ 112: "StatusTip",
+ 100: "StyleChange",
+ 87: "TabletMove",
+ 92: "TabletPress",
+ 93: "TabletRelease",
+ 171: "TabletEnterProximity",
+ 172: "TabletLeaveProximity",
+ 219: "TabletTrackingChange",
+ 22: "ThreadChange",
+ 1: "Timer",
+ 120: "ToolBarChange",
+ 110: "ToolTip",
+ 184: "ToolTipChange",
+ 194: "TouchBegin",
+ 209: "TouchCancel",
+ 196: "TouchEnd",
+ 195: "TouchUpdate",
+ 189: "UngrabKeyboard",
+ 187: "UngrabMouse",
+ 78: "UpdateLater",
+ 77: "UpdateRequest",
+ 111: "WhatsThis",
+ 118: "WhatsThisClicked",
+ 31: "Wheel",
+ 132: "WinEventAct",
+ 24: "WindowActivate",
+ 103: "WindowBlocked",
+ 25: "WindowDeactivate",
+ 34: "WindowIconChange",
+ 105: "WindowStateChange",
+ 33: "WindowTitleChange",
+ 104: "WindowUnblocked",
+ 203: "WinIdChange",
+ 126: "ZOrderChange",
+ 65535: "MaxUser",
+}
+
+
+def getQEventName(eventType):
+ """
+ Returns the name of a QEvent.
+
+ :param Union[int,qt.QEvent] eventType: A QEvent or a QEvent type.
+ :returns: str
+ """
+ if isinstance(eventType, qt.QEvent):
+ eventType = eventType.type()
+ if 1000 <= eventType <= 65535:
+ return "User_%d" % eventType
+ name = QT_EVENT_NAMES.get(eventType, None)
+ if name is not None:
+ return name
+ return "Unknown_%d" % eventType
diff --git a/silx/gui/utils/test/__init__.py b/silx/gui/utils/test/__init__.py
index 9e50170..d500c05 100644..100755
--- a/silx/gui/utils/test/__init__.py
+++ b/silx/gui/utils/test/__init__.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
+# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -34,15 +34,21 @@ import unittest
from . import test_async
from . import test_image
+from . import test_qtutils
+from . import test_testutils
+from . import test
def suite():
"""Test suite for module silx.image.test"""
test_suite = unittest.TestSuite()
+ test_suite.addTest(test.suite())
test_suite.addTest(test_async.suite())
test_suite.addTest(test_image.suite())
+ test_suite.addTest(test_qtutils.suite())
+ test_suite.addTest(test_testutils.suite())
return test_suite
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
+if __name__ == "__main__":
+ unittest.main(defaultTest="suite")
diff --git a/silx/gui/utils/test/test.py b/silx/gui/utils/test/test.py
new file mode 100644
index 0000000..8bba852
--- /dev/null
+++ b/silx/gui/utils/test/test.py
@@ -0,0 +1,76 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2019 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Test of functions available in silx.gui.utils module."""
+
+from __future__ import absolute_import
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "01/08/2019"
+
+
+import unittest
+from silx.gui import qt
+from silx.gui.utils.testutils import TestCaseQt, SignalListener
+
+from silx.gui.utils import blockSignals
+
+
+class TestBlockSignals(TestCaseQt):
+ """Test blockSignals context manager"""
+
+ def _test(self, *objs):
+ """Test for provided objects"""
+ listener = SignalListener()
+ for obj in objs:
+ obj.objectNameChanged.connect(listener)
+ obj.setObjectName("received")
+
+ with blockSignals(*objs):
+ for obj in objs:
+ obj.setObjectName("silent")
+
+ self.assertEqual(listener.arguments(), [("received",)] * len(objs))
+
+ @unittest.skipUnless(qt.BINDING in ('PyQt5', 'PySide2'), 'Qt5 only test')
+ def testManyObjects(self):
+ """Test blockSignals with 2 QObjects"""
+ self._test(qt.QObject(), qt.QObject())
+
+ @unittest.skipUnless(qt.BINDING in ('PyQt5', 'PySide2'), 'Qt5 only test')
+ def testOneObject(self):
+ """Test blockSignals context manager with a single QObject"""
+ self._test(qt.QObject())
+
+
+def suite():
+ test_suite = unittest.TestSuite()
+ test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(
+ TestBlockSignals))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/silx/gui/utils/test/test_qtutils.py b/silx/gui/utils/test/test_qtutils.py
new file mode 100755
index 0000000..043a0a6
--- /dev/null
+++ b/silx/gui/utils/test/test_qtutils.py
@@ -0,0 +1,75 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2019 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Test of functions available in silx.gui.utils module."""
+
+from __future__ import absolute_import
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "01/08/2019"
+
+
+import unittest
+from silx.gui import qt
+from silx.gui import utils
+from silx.gui.utils.testutils import TestCaseQt
+
+
+class TestQEventName(TestCaseQt):
+ """Test QEvent names"""
+
+ def testNoneType(self):
+ result = utils.getQEventName(0)
+ self.assertEqual(result, "None")
+
+ def testNoneEvent(self):
+ event = qt.QEvent(qt.QEvent.Type(0))
+ result = utils.getQEventName(event)
+ self.assertEqual(result, "None")
+
+ def testUserType(self):
+ result = utils.getQEventName(1050)
+ self.assertIn("User", result)
+ self.assertIn("1050", result)
+
+ def testQtUndefinedType(self):
+ result = utils.getQEventName(900)
+ self.assertIn("Unknown", result)
+ self.assertIn("900", result)
+
+ def testUndefinedType(self):
+ result = utils.getQEventName(70000)
+ self.assertIn("Unknown", result)
+ self.assertIn("70000", result)
+
+
+def suite():
+ test_suite = unittest.TestSuite()
+ test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestQEventName))
+ return test_suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="suite")
diff --git a/silx/gui/utils/test/test_testutils.py b/silx/gui/utils/test/test_testutils.py
new file mode 100644
index 0000000..8a58e6e
--- /dev/null
+++ b/silx/gui/utils/test/test_testutils.py
@@ -0,0 +1,55 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2017-2019 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Test of testutils module."""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "16/01/2017"
+
+import unittest
+import sys
+
+from silx.gui import qt
+from ..testutils import TestCaseQt
+
+
+class TestOutcome(unittest.TestCase):
+ """Tests conversion of QImage to/from numpy array."""
+
+ @unittest.skipIf(sys.version_info.major <= 2, 'Python3 only')
+ def testNoneOutcome(self):
+ test = TestCaseQt()
+ test._currentTestSucceeded()
+
+
+def suite():
+ test_suite = unittest.TestSuite()
+ loader = unittest.defaultTestLoader.loadTestsFromTestCase
+ test_suite.addTest(loader(TestOutcome))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/silx/gui/utils/testutils.py b/silx/gui/utils/testutils.py
index d7f2f41..14dcc3f 100644
--- a/silx/gui/utils/testutils.py
+++ b/silx/gui/utils/testutils.py
@@ -155,7 +155,8 @@ class TestCaseQt(unittest.TestCase):
if hasattr(self, '_outcome'):
# For Python >= 3.4
result = self.defaultTestResult() # these 2 methods have no side effects
- self._feedErrorsToResult(result, self._outcome.errors)
+ if hasattr(self._outcome, 'errors'):
+ self._feedErrorsToResult(result, self._outcome.errors)
else:
# For Python < 3.4
result = getattr(self, '_outcomeForDoCleanups', self._resultForDoCleanups)