diff -Nru python-pyface-6.1.2/appveyor-clean-cache.txt python-pyface-7.4.0/appveyor-clean-cache.txt --- python-pyface-6.1.2/appveyor-clean-cache.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/appveyor-clean-cache.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -1 diff -Nru python-pyface-6.1.2/CHANGES.txt python-pyface-7.4.0/CHANGES.txt --- python-pyface-6.1.2/CHANGES.txt 2019-07-20 11:50:06.000000000 +0000 +++ python-pyface-7.4.0/CHANGES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,371 +0,0 @@ -================ -Pyface Changelog -================ - -Release 6.1.2 -============= - -This is a bugfix release which fixes a number of minor issues with the 6.1.1 release. - -Thanks to: - -Mark Dickinson, Prabhu Ramachandran, Corran Webster. - -Change summary --------------- - -Fixes - -* Don't try to open directories in PythonEditorTask (#428) -* Regression test for split editor area merge messages (#424) -* Fix some comments that should be Traits doc-comments. (#426) -* Fix some occurrences of PyFace (should be Pyface) (#420) -* Use decorator form of classmethod and staticmethod. (#417) -* Use equality rather than is when checking orientation. (#422) -* Fix timer issues. (#409, #414, #415) -* Remove use of etsdevtools in an example file. (#413) -* Fix a questionable version check. (#411) -* Fix error during tab completion on shell. (#410) - - -Release 6.1.1 -============= - -This is a bugfix release which fixes a number of minor issues with the 6.1.0 release. - -Thanks to: - -David Baddeley, Christian Brodbeck, Mark Dickinson, Rahul Poruri, Corran Webster. - -Change summary --------------- - -Fixes - -* Remove use of deprecated Traits get() method (#403) -* Fix pyqt5 webkit imports (#396) -* Remove use of cmp in fix_introspection_bug (#395) -* Update CI infrastructure (#394, #399) -* Update unittest imports (#391) -* Fix bug in argspec usage (#393) -* Fix regression in DoLaterTimer API (#389) - - -Release 6.1.0 -============= - -This release introduces a number of new features and bugfixes. The most -prominent of these is a set of Application classes designed to smooth the -transition between Tasks applications which use only Pyface, and those that -take advantage of the Envisage plug-in framework. Developers can now write -a Pyface TasksApplication and then easily transition to an Envisage -TasksApplication as the needs of their codebase grows. This feature has been -a few years in development, and thanks is due to Jonathan Rocher for helping -push this through to completion. - -This release also includes a complete re-write of the Pyface timer code to -make it easier to use and to provide a consistent and more Pythonic API -shared by the different back-ends. A backwards compatible API is still -available, but users of the Pyface timer code are encouraged to update to -the new API. - -ToolBars can now embed simple widgets, such as text fields, integer spinners, -and comboboxes, as well as simple TraitsUI views. This is provided via a -new "widget" style for Actions as well as the utility subclasses FieldAction -and TraitsUIWidgetAction. - -Although not visible to most users of Pyface, this release removes the use -of 2to3 and now uses a unified codebase supported by the six library. Thanks -to Rahul Poruri for performing this transformation. This should help -significantly with ongoing development work in the coming years. - -We also include experimental support for PySide2 (also called "Qt for Python"). -We are running CI tests using the 5.11 release of PySide2 with success on OS X -and Linux for Python 3, but are experiencing failures with the 5.12 release. - -Finally, this release includes a number of bugfixes and minor enhancements -which should improve the reliability and utility of the codebase. - -Thanks to: - -Martin Bergtholdt, Mark Dickinson, Robert Kern, Eric Larson, Gregory Lee, -Eric McDonald, Rahul Poruri, Jonathan Rocher, Hamdi Sahloul, Roger Serwy, -Brian Teague, Corran Webster, John Wiggins. - -Apologies to any contributors that have been omitted. - -Change summary --------------- - -Enhancements - -* Application classes (#334, #355, #345) -* New API for Timers (#340, #365) -* Support simple widgets in toolbars (#359, #360, #368) -* Experimental support of Pyside2 (#318, #335, #358) -* Support single codebase for Python 2 and 3 (#322, #351, #369, #370) -* Support ui dispatch for Traits even if TraitsUI not available (#363) - -Fixes - -* Fix context menu behaviour (#356) -* Remove TraitsUI dependency in GuiTestAssistant (#366) -* Fixes for ProgressDialog (#357) -* Fixes for toolbar and statusbar life cycles (#332) -* Fixes for CI (#342) -* Fix usages of event_loop() in tests (#336) -* Fix SplitEditorAreaPane tab dragging on Qt5 (#327) -* Fix resource warnings in test suite from un-closed files (#326) -* Remove uses of deprecated HasTraits.set() method (#320) -* Add Qt5 support for deprecated Qt Style options that Mayavi needs (#323) -* Various fixes around Qt API selection (#319, #344, #347, #371, #376, #381) -* Fixes for setup.py (#380) - - -Release 6.0.0 -============= - -This release introduces preliminary support for Qt5 via PyQt5, thanks to the -work of Gregor Thalhammer which got the ball rolling. Qt5 support is -not yet robustly tested in deployed applications, so there may yet be bugs to -find. As part of this effort all occurences of old-style signals and slots -have been removed; and this has greatly improved stability under Qt. - -In addition, thanks to the dedicated work of Rob McMullen, we now have Tasks -support for wxPython. - -This release also features a great deal of work at the API level to disentangle -the two-way dependencies between Pyface and TraitsUI. This has involved moving -a number of sub-packages between the two libraries, most notably the zipped -image resource support and a number of trait definitions. We have endeavored -to keep backwards compatibility via stub modules in the original locations, -but we can't guarantee that there will be no issues with third party code -caused by the change in locations. We haven't been able to remove all -dependencies, but as of this release on the dock and workbench submodules have -required dependencies on TraitsUI. - -Another long-desired feature was the ability to write toolkit backends for -Pyface and TraitsUI that are not part of the main codebase. This is now -possible by contributing new toolkit backends to the "pyface.toolkit" -pkg_resources entry point in a setup.py. This work was accompanied by an -overhaul of the toolkit discovery and loading infrastructure; in particular -Pyface and TraitsUI now share the same code for performing these searches -and loading the backends. - -Finally, the testing infrastructure has been overhauled to provide a one-stop -location to run tests in self-contained environments using Enthought's EDM -package management tool. Tests can be run from any python environment with the -"edm" command available and the "click" library installed with the "etstool.py" -script at the top level of the repository. In particular:: - - python etstool.py test_all - -will run all relevant tests for all available toolkits in all supported -python versions. The TravisCI and Appveyor continuous integration tools have -been updated to make use of these facilities as well. - -Thanks to Florian Bender, Kit Choi, Mark Dickinson, Simon Jagoe, Robert Kern, -Rob McMullen, @nprksh, Prabhu Ramachandran, Gregor Thalhammer, Senganal -Thirunavukkarasu, Ioannis Tziakos, Joris Vankerscharver, and Corran Webster. -Apologies to any contributors that have been omitted. - -Change summary --------------- - -Enhancements - -* Tasks support for wxPython (#223, #247) -* Qt5 Support (#170, #256, #264, #265, #268, #271, #279, #292) -* Disentangling Pyface and TraitsUI (#221, #250) -* EDM-based test runner (#243, #296, #298) -* Improved toolkit selection (#259, #260, #276, #304, #305) -* Move wxPython-specific code to pyface.wx (#261) -* Single choice dialog for Qt toolkit (was already in wx toolkit) (#217) -* Add 'detail' and 'informative' to window dialog helpers (#181) -* Code coverage reporting (#214) -* All Pyface widgets now have visible and enabled traits (#284) -* Allow "qt" as a synonym for "qt4" (#304) -* PythonShell widget history exposed via the API (#314) -* Experimental PySide2 support (#303) - -Fixes - -* Documentation fixes (#220, #231) -* Testing and CI fixes (#224, #226, #239, #248, #252, #253, #257, #267, #270, - #277, #278, #281, #283) -* Fix bug updating Tasks window titles (#225) -* Python 3 compatibility fixes (#228) -* Remove use of cmp in the AdvancedEditorAreaPane (#237) -* AutoPEP8 of Pyface codebase (#241) -* Minor WxPython fixes (#230, #269, #275, #294) -* Don't use sendPostedEvents in Qt GUI.do_trait_later (#273) -* Fix ordering of items when appending to an ActionManager default group (#290) -* Explicit life-cycle for Qt event filters on Widgets (#300) -* Fixes to ensure demos and examples work (#308) - -Release 5.1.0 -============= - -Change summary since 5.0.0 --------------------------- - -Enhancements - -* Added documentation on toolkit selection (#197) -* Clarify the purpose of LayoutContainer construction args (#189) -* Improve ETSConfig toolkit selection (#187) - -Fixes - -* Fix restoring show_editor_area from saved perspective(#207) -* Fix WorkbenchWindowLayout in Python 3 (#206) -* Update SplitEditorAreaPane's active editor when focus changes under Qt (#203, #204) -* Read-only CodeEditor should not allow adding newlines in View (#200) -* Fix exception when closing all docks in a workbench window (#199) -* Fix extended trait change description in qt dock pane (#194) -* Fix Qt Workbench dock widget control in a workbench window (#192) -* Fix double UnInit of AUI in ApplicationWindow under Wx (#184, #185) -* Fix IPythonWidget syntax error (#178, #186) -* Fix ProgressDialog timer labels #(183) -* Fix Qt CodeWidget when focus goes out of a widget (#176, #177) -* Fix Qt and Wx ProgressDialog for updating message (#176, #177) - - -Release 5.0.0 -============= - -This release introduces preliminary support for Python 3 with the Qt toolkit! - -This is based in large part on the work of Yves Delley and Pradyun Gedam, but -also owes a lot to Ioannis Tziakos for implementing container-based continuous -integration and Prabhu Ramachandran and Corran Webster for tracking down the -few remaining bugs. Python 3 support is probably not yet ready for production -use, but feedback and bug reports are welcome, and all future pull requests -will be expected to work with Python 3.4 and later. Python 3 support requires -Traits 4.5 or greater, and TraitsUI 5.0 or greater. - -This release also bring support for wxPython 3.0, while dropping support for -wxPython 2.6 and earlier. There are also a couple of minor bug fixes detailed -below. - -Finally, this release changes the default GUI toolkit from Wx to Qt. This -changes the behaviour of the library in the case where both WxPython and -PyQt/PySide are installed and the user or code doesn't specify the toolkit to -use explicitly. - -Change summary since 4.5.2 --------------------------- - -Features - -* Experimental support for Python 3 (#160, #161, #162, #163). -* Experimental support for wxPython 3 (#168). - -Enhancements - -* Make Qt 4 the default GUI toolkit (#172). - -Fixes - -* Fix issue with reading unicode text from Qt clipboard (#159). -* Remove usage of `logging.warn` in favour of `logger.warning` (#167). - -Release 4.5.2 -============= - -This release includes a large number of additions to the test suite that -provide at least basic smoke-test coverage of the core of the library. There -has also been work done to improve the continuous integration testing of -the library. Much more work needs to be done on this front, but the situation -is improving. - -The process of adding these tests also discovered a number of minor bugs -which have been fixed. - -Finally, this release warns that as of Pyface 5.0 the default GUI toolkit -will switch from Wx to Qt. This future update will change the behaviour of -the library in the case where both WxPython and PyQt/PySide are installed and -the user or code doesn't specify the toolkit to use explicitly. - -Change summary since 4.5.1 --------------------------- - -Enhancements - -* Added many tests, increasing test coverage by about 10% (PR#147) -* Add continuous integration support for Windows and Qt via Appveyor (PR #154) -* Use Travis CI's container infrastructure (PR #153) - -Fixes - -* Stop TaskWindow layout running after window control destroyed (PR#156). -* Fix file execution for PythonShell on Windows with Qt backend (PR#147). -* Fix Group objects setting wrong trait when naming new Actions (PR#147). -* Fix ActionItem change handler for `visible` trait (PR#147). -* Fix failure to release application window from AUIManager in Wx (PR#147). -* Fix missing import for qt4 confirmation dialog (PR#147). -* Deprecate Wx as the default toolkit choice for Pyface (PR#150). -* Improve error handling in toolkit imports; fix some import errors which - made the Qt PythonEditor unavailable; and make pygments a formal requirement - rather than implicit (PR#144). - - -Release 4.5.1 -============= - -Change summary since 4.5.0 --------------------------- - -Fixes - -* fix MANIFEST.in (PR#131). -* Fix incompatibility with Pygments 2.x, which was causing tracebacks - during syntax highlighting (PR#136). - - -Release 4.5.0 -============= - -Change summary since 4.4.0 --------------------------- - -Enhancements - -* Add testing assistants (Qt) (PR#118). -* Support menus in toolbars (Qt) (PR#104). -* keybindings update for other editor area classes (PR#103). -* Change advanced editor area tab switching shortcut to Ctrl+PgUp/Down (PR#99). -* ToolBar: Listen to Action name changes for toolbar actions (PR#90). - -Fixes - -* Fix reference to attribute (PR#112). -* Fixed wx 2.9 incompatibility bug in ProgressDialog (PR#106). -* Qt CodeEditor pygments monkeypatch conflict with ipython (PR#100). -* Documentation fix (PR#95). - -* Tooltip for first editor of SplitEditorAreaPane was broken (PR#108) - - -Release 4.4.0 -============= - -This is a bug fix release. The biggest change in this release is support for the -new adaptation mechanism in Traits 4.4.0. - - -Change summary since 4.3.0 --------------------------- - -New features - - * Support Enaml 0.8 (PR#92) - -Enhancements - - * Improvements to SplitEditorAreaPane (PR#83). - -Fixes - - * Fixed split editor area pane focus (PR#89). - * Fixed bug when a VSplitter contains an HSplitter (PR#88, issue#87) diff -Nru python-pyface-6.1.2/ci-src-requirements.txt python-pyface-7.4.0/ci-src-requirements.txt --- python-pyface-6.1.2/ci-src-requirements.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/ci-src-requirements.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -git+http://github.com/enthought/traitsui.git#egg=traitsui diff -Nru python-pyface-6.1.2/debian/changelog python-pyface-7.4.0/debian/changelog --- python-pyface-6.1.2/debian/changelog 2019-12-09 20:51:04.000000000 +0000 +++ python-pyface-7.4.0/debian/changelog 2022-02-08 14:44:54.000000000 +0000 @@ -1,3 +1,36 @@ +python-pyface (7.4.0-1) unstable; urgency=medium + + * Team upload. + + [ Debian Janitor ] + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + * Bump debhelper from old 12 to 13. + + Drop check for DEB_BUILD_OPTIONS containing "nocheck", since debhelper now + does this. + + [ Ondřej Nový ] + * d/control: Update Maintainer field with new Debian Python Team + contact address. + * d/control: Update Vcs-* fields with new Debian Python Team Salsa + layout. + + [ Andreas Tille ] + * New upstream version + Closes: #1002395 + * Standards-Version: 4.6.0 (routine-update) + * Add salsa-ci file (routine-update) + * Rules-Requires-Root: no (routine-update) + * Prevent setup.py from reading git versions + * Update name of sip module + Closes: #966044 + * Add several Build-Depends needed for build time test + * Versioned Build-Depends: python3-traits (>= 6) + * Fix Homepage field + * Fix argument type in Python3.10 + + -- Andreas Tille Tue, 08 Feb 2022 15:44:54 +0100 + python-pyface (6.1.2-2) unstable; urgency=medium * Team upload. diff -Nru python-pyface-6.1.2/debian/clean python-pyface-7.4.0/debian/clean --- python-pyface-6.1.2/debian/clean 2019-12-09 20:51:04.000000000 +0000 +++ python-pyface-7.4.0/debian/clean 2022-02-08 14:44:54.000000000 +0000 @@ -1,4 +1,3 @@ -pyface/_version.py pyface.egg-info/PKG-INFO pyface.egg-info/SOURCES.txt pyface.egg-info/requires.txt diff -Nru python-pyface-6.1.2/debian/control python-pyface-7.4.0/debian/control --- python-pyface-6.1.2/debian/control 2019-12-09 20:51:04.000000000 +0000 +++ python-pyface-7.4.0/debian/control 2022-02-08 14:44:54.000000000 +0000 @@ -1,28 +1,33 @@ Source: python-pyface -Maintainer: Debian Python Modules Team +Maintainer: Debian Python Team Uploaders: Varun Hiremath Section: python Testsuite: autopkgtest-pkg-python Priority: optional -Build-Depends: debhelper-compat (= 12), +Build-Depends: debhelper-compat (= 13), dh-python, python3-all, + python3-importlib-resources , python3-mock , python3-numpy , + python3-packaging , python3-pygments , python3-pyqt5 , + python3-pyqt5.qtmultimedia , python3-pyqt5.qtopengl , python3-pyqt5.qtsvg , python3-pyqt5.qtwebkit , python3-setuptools, python3-six , - python3-traits , + python3-traits (>= 6) , + python3-wxgtk4.0 , xauth , xvfb , -Standards-Version: 4.4.1 -Vcs-Browser: https://salsa.debian.org/python-team/modules/python-pyface -Vcs-Git: https://salsa.debian.org/python-team/modules/python-pyface.git -Homepage: https://pypi.org/project/pyface/ +Standards-Version: 4.6.0 +Vcs-Browser: https://salsa.debian.org/python-team/packages/python-pyface +Vcs-Git: https://salsa.debian.org/python-team/packages/python-pyface.git +Homepage: https://docs.enthought.com/pyface/ +Rules-Requires-Root: no Package: python3-pyface Architecture: all diff -Nru python-pyface-6.1.2/debian/patches/do_not_fiddle_with_git_version.patch python-pyface-7.4.0/debian/patches/do_not_fiddle_with_git_version.patch --- python-pyface-6.1.2/debian/patches/do_not_fiddle_with_git_version.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/patches/do_not_fiddle_with_git_version.patch 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,23 @@ +Author: Andreas Tille +Last-Update: Tue, 08 Feb 2022 11:44:07 +0100 +Description: Prevent setup.py from reading git versions + +--- a/setup.py ++++ b/setup.py +@@ -291,7 +291,6 @@ def resolve_version(): + + + if __name__ == "__main__": +- __version__, _ = resolve_version() + data = read_module("__init__") + __requires__ = data["__requires__"] + __extras_require__ = data["__extras_require__"] +@@ -300,7 +299,7 @@ if __name__ == "__main__": + + setup( + name="pyface", +- version=__version__, ++ version=os.environ['DEB_VERSION_UPSTREAM'], + url="http://docs.enthought.com/pyface", + author="David C. Morrill, et al.", + author_email="dmorrill@enthought.com", diff -Nru python-pyface-6.1.2/debian/patches/fix_TypeError_for_py3.10.patch python-pyface-7.4.0/debian/patches/fix_TypeError_for_py3.10.patch --- python-pyface-6.1.2/debian/patches/fix_TypeError_for_py3.10.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/patches/fix_TypeError_for_py3.10.patch 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,19 @@ +Author: Andreas Tille +Last-Update: Tue, 08 Feb 2022 13:51:45 +0100 +Description: Fixes: + File "/build/python-pyface-7.4.0/.pybuild/cpython3_3.10_pyface/build/pyface/ui/qt4/font.py", line 117, in font_to_toolkit_font + qt_font.setStretch(font.stretch) + TypeError: setStretch(self, int): argument 1 has unexpected type 'float' + for Python3.10 + +--- a/pyface/ui/qt4/font.py ++++ b/pyface/ui/qt4/font.py +@@ -114,7 +114,7 @@ def font_to_toolkit_font(font): + + qt_font.setPointSizeF(font.size) + qt_font.setWeight(weight_to_qt_weight[font.weight_]) +- qt_font.setStretch(font.stretch) ++ qt_font.setStretch(int(font.stretch)) + qt_font.setStyle(style_to_qt_style[font.style]) + qt_font.setUnderline('underline' in font.decorations) + qt_font.setStrikeOut('strikethrough' in font.decorations) diff -Nru python-pyface-6.1.2/debian/patches/ignore_tests_needing_gui.patch python-pyface-7.4.0/debian/patches/ignore_tests_needing_gui.patch --- python-pyface-6.1.2/debian/patches/ignore_tests_needing_gui.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/patches/ignore_tests_needing_gui.patch 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,63 @@ +Author: Andreas Tille +Last-Update: Tue, 08 Feb 2022 13:51:45 +0100 +Description: Ignore tests that are ending up with + wx._core.PyNoAppError: The wx.App object must be created first! + +--- a/pyface/ui/wx/util/tests/test_image_helpers.py ++++ b/pyface/ui/wx/util/tests/test_image_helpers.py +@@ -20,6 +20,7 @@ from ..image_helpers import ( + + class TestImageHelpers(unittest.TestCase): + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_image_to_bitmap(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) +@@ -28,6 +29,7 @@ class TestImageHelpers(unittest.TestCase + + self.assertIsInstance(wxbitmap, wx.Bitmap) + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_bitmap_to_image(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) +@@ -37,6 +39,7 @@ class TestImageHelpers(unittest.TestCase + + self.assertIsInstance(wximage, wx.Image) + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_bitmap_to_icon(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) +@@ -86,6 +89,7 @@ class TestImageHelpers(unittest.TestCase + self.assertEqual(wximage.GetWidth(), 128) + self.assertEqual(wximage.GetHeight(), 256) + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_resize_bitmap(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) +@@ -97,6 +101,7 @@ class TestImageHelpers(unittest.TestCase + self.assertEqual(wxbitmap.GetWidth(), 128) + self.assertEqual(wxbitmap.GetHeight(), 128) + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_resize_bitmap_smooth(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) +@@ -108,6 +113,7 @@ class TestImageHelpers(unittest.TestCase + self.assertEqual(wxbitmap.GetWidth(), 128) + self.assertEqual(wxbitmap.GetHeight(), 128) + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_resize_bitmap_constrain(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) +@@ -119,6 +125,7 @@ class TestImageHelpers(unittest.TestCase + self.assertEqual(wxbitmap.GetWidth(), 64) + self.assertEqual(wxbitmap.GetHeight(), 128) + ++ @unittest.skip("Debian: wx._core.PyNoAppError: The wx.App object must be created first!") + def test_resize_bitmap_expand(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) diff -Nru python-pyface-6.1.2/debian/patches/new_name_of_sip_module.patch python-pyface-7.4.0/debian/patches/new_name_of_sip_module.patch --- python-pyface-6.1.2/debian/patches/new_name_of_sip_module.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/patches/new_name_of_sip_module.patch 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,20 @@ +Author: Dmitry Shachnev +Last-Update: Wed, 22 Jul 2020 11:39:09 UTC +Bug-Debian: https://bugs.debian.org/966044 +Description: With the latest version of PyQt5, the private sip module + used by PyQt5 is called "PyQt5.sip", not just "sip". + +--- a/pyface/ui/qt4/workbench/split_tab_widget.py ++++ b/pyface/ui/qt4/workbench/split_tab_widget.py +@@ -303,7 +303,10 @@ class SplitTabWidget(QtGui.QSplitter): + # the time when the focus change signal is emitted and time when the + # slots are dispatched by the Qt event loop. This may be a bug in PyQt4. + if qt_api == "pyqt": +- import sip ++ try: ++ from PyQt5 import sip ++ except ModuleNotFoundError: ++ import sip + + if sip.isdeleted(self): + return diff -Nru python-pyface-6.1.2/debian/patches/series python-pyface-7.4.0/debian/patches/series --- python-pyface-6.1.2/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/patches/series 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,4 @@ +do_not_fiddle_with_git_version.patch +new_name_of_sip_module.patch +ignore_tests_needing_gui.patch +fix_TypeError_for_py3.10.patch diff -Nru python-pyface-6.1.2/debian/rules python-pyface-7.4.0/debian/rules --- python-pyface-6.1.2/debian/rules 2019-12-09 20:51:04.000000000 +0000 +++ python-pyface-7.4.0/debian/rules 2022-02-08 14:44:54.000000000 +0000 @@ -6,13 +6,11 @@ export PYBUILD_NAME=pyface export PYBUILD_AFTER_INSTALL=cp -v {dir}/pyface/image/library/*.zip {destdir}{install_dir}/pyface/image/library -## When trying better hardening the build fails -# export DEB_BUILD_MAINT_OPTIONS = hardening=+all +include /usr/share/dpkg/default.mk +export DEB_VERSION_UPSTREAM:=$(DEB_VERSION_UPSTREAM) %: dh $@ --with python3 --buildsystem=pybuild override_dh_auto_test: -ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" dh_auto_test -endif diff -Nru python-pyface-6.1.2/debian/salsa-ci.yml python-pyface-7.4.0/debian/salsa-ci.yml --- python-pyface-6.1.2/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/salsa-ci.yml 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru python-pyface-6.1.2/debian/upstream/metadata python-pyface-7.4.0/debian/upstream/metadata --- python-pyface-6.1.2/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/debian/upstream/metadata 2022-02-08 14:44:54.000000000 +0000 @@ -0,0 +1,4 @@ +Bug-Database: https://github.com/enthought/pyface/issues +Bug-Submit: https://github.com/enthought/pyface/issues/new +Repository: https://github.com/enthought/pyface.git +Repository-Browse: https://github.com/enthought/pyface diff -Nru python-pyface-6.1.2/dev_requirements.txt python-pyface-7.4.0/dev_requirements.txt --- python-pyface-6.1.2/dev_requirements.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/dev_requirements.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -unittest2 -https://github.com/nucleic/enaml/archive/0.8.8.tar.gz#egg=enaml --e git+http://github.com/enthought/traits.git#egg=traits --e git+http://github.com/enthought/traitsui.git#egg=traitsui --e git+http://github.com/enthought/traits_enaml.git#egg=traits_enaml diff -Nru python-pyface-6.1.2/docs/CHANGES.txt python-pyface-7.4.0/docs/CHANGES.txt --- python-pyface-6.1.2/docs/CHANGES.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/CHANGES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -TraitsGUI 3.3.1 (not yet released) -================================== - - - -TraitsGUI 3.3.0 (Feb 24, 2010) -============================== - -Enhancements ------------- - - -Fixes ------ - - - -TraitsGUI 3.1.0 (July 15, 2009) -=============================== - -Enhancements ------------- - - * Removed Theming support from DockWindows. Borders and tabs are now drawn using lines instead of stretching images. - - * Changed default font to use the system font - - * Moved pyface.wx.clipboard to Pyface - - * Moved the grid package out of pyface and into pyface.ui.wx, left deprecated warnings - - -Fixes ------ - - * Improved info shown to the user if toolkits don't work as expected diff -Nru python-pyface-6.1.2/docs/source/api/pyface.about_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.about_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.about_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.about_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.about_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.action_controller.rst python-pyface-7.4.0/docs/source/api/pyface.action.action_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.action_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.action_controller.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.action.action_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.action_event.rst python-pyface-7.4.0/docs/source/api/pyface.action.action_event.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.action_event.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.action_event.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.action.action_event - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.action_item.rst python-pyface-7.4.0/docs/source/api/pyface.action.action_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.action_item.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.action_item.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.action.action_item - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.action_manager_item.rst python-pyface-7.4.0/docs/source/api/pyface.action.action_manager_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.action_manager_item.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.action_manager_item.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.action.action_manager_item - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.action_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.action_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.action_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.action_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.action.action_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.action.rst python-pyface-7.4.0/docs/source/api/pyface.action.action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.action.action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.api.rst python-pyface-7.4.0/docs/source/api/pyface.action.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================== .. automodule:: pyface.action.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.field_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.field_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.field_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.field_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.action.field_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.group.rst python-pyface-7.4.0/docs/source/api/pyface.action.group.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.group.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.group.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.action.group - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.gui_application_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.gui_application_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.gui_application_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.gui_application_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================================= .. automodule:: pyface.action.gui_application_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.listening_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.listening_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.listening_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.listening_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.action.listening_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.menu_bar_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.menu_bar_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.menu_bar_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.menu_bar_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.action.menu_bar_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.menu_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.menu_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.menu_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.menu_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.action.menu_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.rst python-pyface-7.4.0/docs/source/api/pyface.action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,21 +2,23 @@ ===================== .. automodule:: pyface.action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - pyface.action.tests + pyface.action.schema Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.action.action pyface.action.action_controller @@ -36,4 +38,3 @@ pyface.action.tool_palette_manager pyface.action.traitsui_widget_action pyface.action.window_action - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.schema.action_manager_builder.rst python-pyface-7.4.0/docs/source/api/pyface.action.schema.action_manager_builder.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.schema.action_manager_builder.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.schema.action_manager_builder.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.action.schema.action\_manager\_builder module +==================================================== + +.. automodule:: pyface.action.schema.action_manager_builder + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.schema.api.rst python-pyface-7.4.0/docs/source/api/pyface.action.schema.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.schema.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.schema.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.action.schema.api module +=============================== + +.. automodule:: pyface.action.schema.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.schema.rst python-pyface-7.4.0/docs/source/api/pyface.action.schema.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.schema.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.schema.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,18 @@ +pyface.action.schema package +============================ + +.. automodule:: pyface.action.schema + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.action.schema.action_manager_builder + pyface.action.schema.api + pyface.action.schema.schema + pyface.action.schema.schema_addition diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.schema.schema_addition.rst python-pyface-7.4.0/docs/source/api/pyface.action.schema.schema_addition.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.schema.schema_addition.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.schema.schema_addition.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.action.schema.schema\_addition module +============================================ + +.. automodule:: pyface.action.schema.schema_addition + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.schema.schema.rst python-pyface-7.4.0/docs/source/api/pyface.action.schema.schema.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.schema.schema.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.schema.schema.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.action.schema.schema module +================================== + +.. automodule:: pyface.action.schema.schema + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.status_bar_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.status_bar_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.status_bar_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.status_bar_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.action.status_bar_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -pyface.action.tests package -=========================== - -.. automodule:: pyface.action.tests - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.action.tests.test_action - pyface.action.tests.test_action_controller - pyface.action.tests.test_action_event - pyface.action.tests.test_action_item - pyface.action.tests.test_action_manager - pyface.action.tests.test_field_action - pyface.action.tests.test_group - pyface.action.tests.test_gui_application_action - pyface.action.tests.test_listening_action - pyface.action.tests.test_traitsui_widget_action - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_controller.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_controller.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_action\_controller module -=================================================== - -.. automodule:: pyface.action.tests.test_action_controller - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_event.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_event.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_event.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_event.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_action\_event module -============================================== - -.. automodule:: pyface.action.tests.test_action_event - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_item.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_item.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_item.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_action\_item module -============================================= - -.. automodule:: pyface.action.tests.test_action_item - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action_manager.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_action\_manager module -================================================ - -.. automodule:: pyface.action.tests.test_action_manager - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_action.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_action module -======================================= - -.. automodule:: pyface.action.tests.test_action - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_field_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_field_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_field_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_field_action.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_field\_action module -============================================== - -.. automodule:: pyface.action.tests.test_field_action - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_group.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_group.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_group.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_group.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_group module -====================================== - -.. automodule:: pyface.action.tests.test_group - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_gui_application_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_gui_application_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_gui_application_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_gui_application_action.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_gui\_application\_action module -========================================================= - -.. automodule:: pyface.action.tests.test_gui_application_action - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_listening_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_listening_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_listening_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_listening_action.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_listening\_action module -================================================== - -.. automodule:: pyface.action.tests.test_listening_action - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_traitsui_widget_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_traitsui_widget_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tests.test_traitsui_widget_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tests.test_traitsui_widget_action.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.action.tests.test\_traitsui\_widget\_action module -========================================================= - -.. automodule:: pyface.action.tests.test_traitsui_widget_action - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tool_bar_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.tool_bar_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tool_bar_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tool_bar_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.action.tool_bar_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.tool_palette_manager.rst python-pyface-7.4.0/docs/source/api/pyface.action.tool_palette_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.tool_palette_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.tool_palette_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================================== .. automodule:: pyface.action.tool_palette_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.traitsui_widget_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.traitsui_widget_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.traitsui_widget_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.traitsui_widget_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================================= .. automodule:: pyface.action.traitsui_widget_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.action.window_action.rst python-pyface-7.4.0/docs/source/api/pyface.action.window_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.action.window_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.action.window_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.action.window_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.api.rst python-pyface-7.4.0/docs/source/api/pyface.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.api.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================= .. automodule:: pyface.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.application.rst python-pyface-7.4.0/docs/source/api/pyface.application.rst --- python-pyface-6.1.2/docs/source/api/pyface.application.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.application.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ========================= .. automodule:: pyface.application - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.application_window.rst python-pyface-7.4.0/docs/source/api/pyface.application_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.application_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.application_window.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.application_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.array_image.rst python-pyface-7.4.0/docs/source/api/pyface.array_image.rst --- python-pyface-6.1.2/docs/source/api/pyface.array_image.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.array_image.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.array\_image module +========================== + +.. automodule:: pyface.array_image + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.base_toolkit.rst python-pyface-7.4.0/docs/source/api/pyface.base_toolkit.rst --- python-pyface-6.1.2/docs/source/api/pyface.base_toolkit.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.base_toolkit.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.base_toolkit - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.beep.rst python-pyface-7.4.0/docs/source/api/pyface.beep.rst --- python-pyface-6.1.2/docs/source/api/pyface.beep.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.beep.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================== .. automodule:: pyface.beep - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.clipboard.rst python-pyface-7.4.0/docs/source/api/pyface.clipboard.rst --- python-pyface-6.1.2/docs/source/api/pyface.clipboard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.clipboard.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.clipboard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.color_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.color_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.color_dialog.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.color_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.color\_dialog module +=========================== + +.. automodule:: pyface.color_dialog + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.color.rst python-pyface-7.4.0/docs/source/api/pyface.color.rst --- python-pyface-6.1.2/docs/source/api/pyface.color.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.color.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.color module +=================== + +.. automodule:: pyface.color + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.confirmation_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.confirmation_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.confirmation_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.confirmation_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.confirmation_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.constant.rst python-pyface-7.4.0/docs/source/api/pyface.constant.rst --- python-pyface-6.1.2/docs/source/api/pyface.constant.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.constant.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ====================== .. automodule:: pyface.constant - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.abstract_data_exporter.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.abstract_data_exporter.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.abstract_data_exporter.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.abstract_data_exporter.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.abstract\_data\_exporter module +================================================= + +.. automodule:: pyface.data_view.abstract_data_exporter + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.abstract_data_model.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.abstract_data_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.abstract_data_model.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.abstract_data_model.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.abstract\_data\_model module +============================================== + +.. automodule:: pyface.data_view.abstract_data_model + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.abstract_value_type.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.abstract_value_type.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.abstract_value_type.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.abstract_value_type.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.abstract\_value\_type module +============================================== + +.. automodule:: pyface.data_view.abstract_value_type + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.api.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.api module +============================ + +.. automodule:: pyface.data_view.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_formats.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_formats.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_formats.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_formats.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_formats module +====================================== + +.. automodule:: pyface.data_view.data_formats + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.api.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_models.api module +========================================= + +.. automodule:: pyface.data_view.data_models.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.array_data_model.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.array_data_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.array_data_model.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.array_data_model.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_models.array\_data\_model module +======================================================== + +.. automodule:: pyface.data_view.data_models.array_data_model + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.data_accessors.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.data_accessors.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.data_accessors.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.data_accessors.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_models.data\_accessors module +===================================================== + +.. automodule:: pyface.data_view.data_models.data_accessors + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.row_table_data_model.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.row_table_data_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.row_table_data_model.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.row_table_data_model.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_models.row\_table\_data\_model module +============================================================= + +.. automodule:: pyface.data_view.data_models.row_table_data_model + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_models.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_models.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,18 @@ +pyface.data\_view.data\_models package +====================================== + +.. automodule:: pyface.data_view.data_models + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.data_view.data_models.api + pyface.data_view.data_models.array_data_model + pyface.data_view.data_models.data_accessors + pyface.data_view.data_models.row_table_data_model diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_view_errors.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_view_errors.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_view_errors.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_view_errors.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_view\_errors module +=========================================== + +.. automodule:: pyface.data_view.data_view_errors + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_view_widget.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_view_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_view_widget.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_view_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_view\_widget module +=========================================== + +.. automodule:: pyface.data_view.data_view_widget + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.data_wrapper.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.data_wrapper.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.data_wrapper.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.data_wrapper.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.data\_wrapper module +====================================== + +.. automodule:: pyface.data_view.data_wrapper + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.api.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.exporters.api module +====================================== + +.. automodule:: pyface.data_view.exporters.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.item_exporter.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.item_exporter.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.item_exporter.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.item_exporter.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.exporters.item\_exporter module +================================================= + +.. automodule:: pyface.data_view.exporters.item_exporter + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.row_exporter.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.row_exporter.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.row_exporter.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.row_exporter.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.exporters.row\_exporter module +================================================ + +.. automodule:: pyface.data_view.exporters.row_exporter + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.exporters.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.exporters.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,17 @@ +pyface.data\_view.exporters package +=================================== + +.. automodule:: pyface.data_view.exporters + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.data_view.exporters.api + pyface.data_view.exporters.item_exporter + pyface.data_view.exporters.row_exporter diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.i_data_view_widget.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.i_data_view_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.i_data_view_widget.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.i_data_view_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.i\_data\_view\_widget module +============================================== + +.. automodule:: pyface.data_view.i_data_view_widget + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.i_data_wrapper.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.i_data_wrapper.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.i_data_wrapper.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.i_data_wrapper.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.i\_data\_wrapper module +========================================= + +.. automodule:: pyface.data_view.i_data_wrapper + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.index_manager.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.index_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.index_manager.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.index_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.index\_manager module +======================================= + +.. automodule:: pyface.data_view.index_manager + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,35 @@ +pyface.data\_view package +========================= + +.. automodule:: pyface.data_view + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + pyface.data_view.data_models + pyface.data_view.exporters + pyface.data_view.value_types + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.data_view.abstract_data_exporter + pyface.data_view.abstract_data_model + pyface.data_view.abstract_value_type + pyface.data_view.api + pyface.data_view.data_formats + pyface.data_view.data_view_errors + pyface.data_view.data_view_widget + pyface.data_view.data_wrapper + pyface.data_view.i_data_view_widget + pyface.data_view.i_data_wrapper + pyface.data_view.index_manager diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.api.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.api module +========================================= + +.. automodule:: pyface.data_view.value_types.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.bool_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.bool_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.bool_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.bool_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.bool\_value module +================================================= + +.. automodule:: pyface.data_view.value_types.bool_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.color_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.color_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.color_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.color_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.color\_value module +================================================== + +.. automodule:: pyface.data_view.value_types.color_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.constant_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.constant_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.constant_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.constant_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.constant\_value module +===================================================== + +.. automodule:: pyface.data_view.value_types.constant_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.editable_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.editable_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.editable_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.editable_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.editable\_value module +===================================================== + +.. automodule:: pyface.data_view.value_types.editable_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.enum_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.enum_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.enum_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.enum_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.enum\_value module +================================================= + +.. automodule:: pyface.data_view.value_types.enum_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.no_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.no_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.no_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.no_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.no\_value module +=============================================== + +.. automodule:: pyface.data_view.value_types.no_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.numeric_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.numeric_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.numeric_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.numeric_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.numeric\_value module +==================================================== + +.. automodule:: pyface.data_view.value_types.numeric_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,23 @@ +pyface.data\_view.value\_types package +====================================== + +.. automodule:: pyface.data_view.value_types + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.data_view.value_types.api + pyface.data_view.value_types.bool_value + pyface.data_view.value_types.color_value + pyface.data_view.value_types.constant_value + pyface.data_view.value_types.editable_value + pyface.data_view.value_types.enum_value + pyface.data_view.value_types.no_value + pyface.data_view.value_types.numeric_value + pyface.data_view.value_types.text_value diff -Nru python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.text_value.rst python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.text_value.rst --- python-pyface-6.1.2/docs/source/api/pyface.data_view.value_types.text_value.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.data_view.value_types.text_value.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.data\_view.value\_types.text\_value module +================================================= + +.. automodule:: pyface.data_view.value_types.text_value + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.dialog.rst python-pyface-7.4.0/docs/source/api/pyface.dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ==================== .. automodule:: pyface.dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.directory_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.directory_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.directory_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.directory_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.directory_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.drop_handler.rst python-pyface-7.4.0/docs/source/api/pyface.drop_handler.rst --- python-pyface-6.1.2/docs/source/api/pyface.drop_handler.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.drop_handler.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.drop_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.expandable_header.rst python-pyface-7.4.0/docs/source/api/pyface.expandable_header.rst --- python-pyface-6.1.2/docs/source/api/pyface.expandable_header.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.expandable_header.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.expandable\_header module -================================ - -.. automodule:: pyface.expandable_header - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.expandable_panel.rst python-pyface-7.4.0/docs/source/api/pyface.expandable_panel.rst --- python-pyface-6.1.2/docs/source/api/pyface.expandable_panel.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.expandable_panel.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.expandable_panel - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.api.rst python-pyface-7.4.0/docs/source/api/pyface.fields.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================== .. automodule:: pyface.fields.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.combo_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.combo_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.combo_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.combo_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.fields.combo_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.i_combo_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.i_combo_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.i_combo_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.i_combo_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.fields.i_combo_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.i_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.i_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.i_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.i_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.fields.i_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.i_spin_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.i_spin_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.i_spin_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.i_spin_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.fields.i_spin_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.i_text_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.i_text_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.i_text_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.i_text_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.fields.i_text_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.i_time_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.i_time_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.i_time_field.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.i_time_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.fields.i\_time\_field module +=================================== + +.. automodule:: pyface.fields.i_time_field + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.i_toggle_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.i_toggle_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.i_toggle_field.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.i_toggle_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.fields.i\_toggle\_field module +===================================== + +.. automodule:: pyface.fields.i_toggle_field + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.rst python-pyface-7.4.0/docs/source/api/pyface.fields.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,21 +2,15 @@ ===================== .. automodule:: pyface.fields - :members: - :undoc-members: - :show-inheritance: - -Subpackages ------------ - -.. toctree:: - - pyface.fields.tests + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.fields.api pyface.fields.combo_field @@ -24,6 +18,9 @@ pyface.fields.i_field pyface.fields.i_spin_field pyface.fields.i_text_field + pyface.fields.i_time_field + pyface.fields.i_toggle_field pyface.fields.spin_field pyface.fields.text_field - + pyface.fields.time_field + pyface.fields.toggle_field diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.spin_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.spin_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.spin_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.spin_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.fields.spin_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.tests.field_mixin.rst python-pyface-7.4.0/docs/source/api/pyface.fields.tests.field_mixin.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.tests.field_mixin.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.tests.field_mixin.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.fields.tests.field\_mixin module -======================================= - -.. automodule:: pyface.fields.tests.field_mixin - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.tests.rst python-pyface-7.4.0/docs/source/api/pyface.fields.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.tests.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -pyface.fields.tests package -=========================== - -.. automodule:: pyface.fields.tests - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.fields.tests.field_mixin - pyface.fields.tests.test_combo_field - pyface.fields.tests.test_spin_field - pyface.fields.tests.test_text_field - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.tests.test_combo_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.tests.test_combo_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.tests.test_combo_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.tests.test_combo_field.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.fields.tests.test\_combo\_field module -============================================= - -.. automodule:: pyface.fields.tests.test_combo_field - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.tests.test_spin_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.tests.test_spin_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.tests.test_spin_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.tests.test_spin_field.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.fields.tests.test\_spin\_field module -============================================ - -.. automodule:: pyface.fields.tests.test_spin_field - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.tests.test_text_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.tests.test_text_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.tests.test_text_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.tests.test_text_field.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.fields.tests.test\_text\_field module -============================================ - -.. automodule:: pyface.fields.tests.test_text_field - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.text_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.text_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.text_field.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.text_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.fields.text_field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.time_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.time_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.time_field.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.time_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.fields.time\_field module +================================ + +.. automodule:: pyface.fields.time_field + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.fields.toggle_field.rst python-pyface-7.4.0/docs/source/api/pyface.fields.toggle_field.rst --- python-pyface-6.1.2/docs/source/api/pyface.fields.toggle_field.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.fields.toggle_field.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.fields.toggle\_field module +================================== + +.. automodule:: pyface.fields.toggle_field + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.file_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.file_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.file_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.file_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.file_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.filter.rst python-pyface-7.4.0/docs/source/api/pyface.filter.rst --- python-pyface-6.1.2/docs/source/api/pyface.filter.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.filter.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ==================== .. automodule:: pyface.filter - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.font_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.font_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.font_dialog.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.font_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.font\_dialog module +========================== + +.. automodule:: pyface.font_dialog + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.font.rst python-pyface-7.4.0/docs/source/api/pyface.font.rst --- python-pyface-6.1.2/docs/source/api/pyface.font.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.font.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.font module +================== + +.. automodule:: pyface.font + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.gui_application.rst python-pyface-7.4.0/docs/source/api/pyface.gui_application.rst --- python-pyface-6.1.2/docs/source/api/pyface.gui_application.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.gui_application.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.gui_application - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.gui.rst python-pyface-7.4.0/docs/source/api/pyface.gui.rst --- python-pyface-6.1.2/docs/source/api/pyface.gui.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.gui.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================= .. automodule:: pyface.gui - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.heading_text.rst python-pyface-7.4.0/docs/source/api/pyface.heading_text.rst --- python-pyface-6.1.2/docs/source/api/pyface.heading_text.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.heading_text.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.heading_text - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_about_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_about_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_about_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_about_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.i_about_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_application_window.rst python-pyface-7.4.0/docs/source/api/pyface.i_application_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_application_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_application_window.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.i_application_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_clipboard.rst python-pyface-7.4.0/docs/source/api/pyface.i_clipboard.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_clipboard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_clipboard.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.i_clipboard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_color_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_color_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_color_dialog.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_color_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_color\_dialog module +============================== + +.. automodule:: pyface.i_color_dialog + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_confirmation_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_confirmation_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_confirmation_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_confirmation_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.i_confirmation_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.i_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_directory_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_directory_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_directory_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_directory_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.i_directory_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_drop_handler.rst python-pyface-7.4.0/docs/source/api/pyface.i_drop_handler.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_drop_handler.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_drop_handler.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.i_drop_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_file_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_file_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_file_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_file_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.i_file_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_font_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_font_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_font_dialog.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_font_dialog.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_font\_dialog module +============================= + +.. automodule:: pyface.i_font_dialog + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_gui.rst python-pyface-7.4.0/docs/source/api/pyface.i_gui.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_gui.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_gui.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ==================== .. automodule:: pyface.i_gui - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_heading_text.rst python-pyface-7.4.0/docs/source/api/pyface.i_heading_text.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_heading_text.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_heading_text.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.i_heading_text - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_image_cache.rst python-pyface-7.4.0/docs/source/api/pyface.i_image_cache.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_image_cache.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_image_cache.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.i_image_cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_image_resource.rst python-pyface-7.4.0/docs/source/api/pyface.i_image_resource.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_image_resource.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_image_resource.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.i_image_resource - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_image.rst python-pyface-7.4.0/docs/source/api/pyface.i_image.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_image.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_image.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_image module +====================== + +.. automodule:: pyface.i_image + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_layered_panel.rst python-pyface-7.4.0/docs/source/api/pyface.i_layered_panel.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_layered_panel.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_layered_panel.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_layered\_panel module +=============================== + +.. automodule:: pyface.i_layered_panel + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_layout_item.rst python-pyface-7.4.0/docs/source/api/pyface.i_layout_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_layout_item.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_layout_item.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_layout\_item module +============================= + +.. automodule:: pyface.i_layout_item + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_layout_widget.rst python-pyface-7.4.0/docs/source/api/pyface.i_layout_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_layout_widget.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_layout_widget.rst 2022-02-02 10:01:04.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_layout\_widget module +=============================== + +.. automodule:: pyface.i_layout_widget + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image_button.rst python-pyface-7.4.0/docs/source/api/pyface.image_button.rst --- python-pyface-6.1.2/docs/source/api/pyface.image_button.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image_button.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.image\_button module -=========================== - -.. automodule:: pyface.image_button - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image_cache.rst python-pyface-7.4.0/docs/source/api/pyface.image_cache.rst --- python-pyface-6.1.2/docs/source/api/pyface.image_cache.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image_cache.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.image_cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image.image.rst python-pyface-7.4.0/docs/source/api/pyface.image.image.rst --- python-pyface-6.1.2/docs/source/api/pyface.image.image.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image.image.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================= .. automodule:: pyface.image.image - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image_list.rst python-pyface-7.4.0/docs/source/api/pyface.image_list.rst --- python-pyface-6.1.2/docs/source/api/pyface.image_list.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image_list.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.image\_list module -========================= - -.. automodule:: pyface.image_list - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image_resource.rst python-pyface-7.4.0/docs/source/api/pyface.image_resource.rst --- python-pyface-6.1.2/docs/source/api/pyface.image_resource.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image_resource.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.image_resource - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image.rst python-pyface-7.4.0/docs/source/api/pyface.image.rst --- python-pyface-6.1.2/docs/source/api/pyface.image.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,14 +2,14 @@ ==================== .. automodule:: pyface.image - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.image.image - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.image_widget.rst python-pyface-7.4.0/docs/source/api/pyface.image_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.image_widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.image_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.image_widget - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_message_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_message_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_message_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_message_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.i_message_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_pil_image.rst python-pyface-7.4.0/docs/source/api/pyface.i_pil_image.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_pil_image.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_pil_image.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.i\_pil\_image module +=========================== + +.. automodule:: pyface.i_pil_image + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_progress_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_progress_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_progress_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_progress_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.i_progress_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_python_editor.rst python-pyface-7.4.0/docs/source/api/pyface.i_python_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_python_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_python_editor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.i_python_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_python_shell.rst python-pyface-7.4.0/docs/source/api/pyface.i_python_shell.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_python_shell.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_python_shell.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.i_python_shell - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.ipython_widget.rst python-pyface-7.4.0/docs/source/api/pyface.ipython_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.ipython_widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.ipython_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.ipython_widget - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_single_choice_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.i_single_choice_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_single_choice_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_single_choice_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.i_single_choice_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_splash_screen.rst python-pyface-7.4.0/docs/source/api/pyface.i_splash_screen.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_splash_screen.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_splash_screen.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.i_splash_screen - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_split_widget.rst python-pyface-7.4.0/docs/source/api/pyface.i_split_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_split_widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_split_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.i_split_widget - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_system_metrics.rst python-pyface-7.4.0/docs/source/api/pyface.i_system_metrics.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_system_metrics.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_system_metrics.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.i_system_metrics - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_widget.rst python-pyface-7.4.0/docs/source/api/pyface.i_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.i_widget - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.i_window.rst python-pyface-7.4.0/docs/source/api/pyface.i_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.i_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.i_window.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.i_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.key_pressed_event.rst python-pyface-7.4.0/docs/source/api/pyface.key_pressed_event.rst --- python-pyface-6.1.2/docs/source/api/pyface.key_pressed_event.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.key_pressed_event.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.key_pressed_event - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.layered_panel.rst python-pyface-7.4.0/docs/source/api/pyface.layered_panel.rst --- python-pyface-6.1.2/docs/source/api/pyface.layered_panel.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.layered_panel.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================ .. automodule:: pyface.layered_panel - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.layout_widget.rst python-pyface-7.4.0/docs/source/api/pyface.layout_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.layout_widget.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.layout_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.layout\_widget module +============================ + +.. automodule:: pyface.layout_widget + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.list_box_model.rst python-pyface-7.4.0/docs/source/api/pyface.list_box_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.list_box_model.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.list_box_model.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.list_box_model - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.list_box.rst python-pyface-7.4.0/docs/source/api/pyface.list_box.rst --- python-pyface-6.1.2/docs/source/api/pyface.list_box.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.list_box.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.list_box - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.mdi_application_window.rst python-pyface-7.4.0/docs/source/api/pyface.mdi_application_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.mdi_application_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.mdi_application_window.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.mdi_application_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.mdi_window_menu.rst python-pyface-7.4.0/docs/source/api/pyface.mdi_window_menu.rst --- python-pyface-6.1.2/docs/source/api/pyface.mdi_window_menu.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.mdi_window_menu.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.mdi_window_menu - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.message_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.message_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.message_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.message_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.message_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.mimedata.rst python-pyface-7.4.0/docs/source/api/pyface.mimedata.rst --- python-pyface-6.1.2/docs/source/api/pyface.mimedata.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.mimedata.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================== .. automodule:: pyface.mimedata - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.multi_toolbar_window.rst python-pyface-7.4.0/docs/source/api/pyface.multi_toolbar_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.multi_toolbar_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.multi_toolbar_window.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.multi_toolbar_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.pil_image.rst python-pyface-7.4.0/docs/source/api/pyface.pil_image.rst --- python-pyface-6.1.2/docs/source/api/pyface.pil_image.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.pil_image.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.pil\_image module +======================== + +.. automodule:: pyface.pil_image + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.preference.api.rst python-pyface-7.4.0/docs/source/api/pyface.preference.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.preference.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.preference.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================ .. automodule:: pyface.preference.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.preference.preference_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.preference.preference_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.preference.preference_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.preference.preference_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================================== .. automodule:: pyface.preference.preference_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.preference.preference_node.rst python-pyface-7.4.0/docs/source/api/pyface.preference.preference_node.rst --- python-pyface-6.1.2/docs/source/api/pyface.preference.preference_node.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.preference.preference_node.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.preference.preference_node - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.preference.preference_page.rst python-pyface-7.4.0/docs/source/api/pyface.preference.preference_page.rst --- python-pyface-6.1.2/docs/source/api/pyface.preference.preference_page.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.preference.preference_page.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.preference.preference_page - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.preference.rst python-pyface-7.4.0/docs/source/api/pyface.preference.rst --- python-pyface-6.1.2/docs/source/api/pyface.preference.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.preference.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,17 +2,17 @@ ========================= .. automodule:: pyface.preference - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.preference.api pyface.preference.preference_dialog pyface.preference.preference_node pyface.preference.preference_page - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.progress_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.progress_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.progress_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.progress_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.progress_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.python_editor.rst python-pyface-7.4.0/docs/source/api/pyface.python_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.python_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.python_editor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================ .. automodule:: pyface.python_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.python_shell.rst python-pyface-7.4.0/docs/source/api/pyface.python_shell.rst --- python-pyface-6.1.2/docs/source/api/pyface.python_shell.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.python_shell.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.python_shell - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource.api.rst python-pyface-7.4.0/docs/source/api/pyface.resource.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.resource.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource_manager.rst python-pyface-7.4.0/docs/source/api/pyface.resource_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.resource_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource.resource_factory.rst python-pyface-7.4.0/docs/source/api/pyface.resource.resource_factory.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource.resource_factory.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource.resource_factory.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================== .. automodule:: pyface.resource.resource_factory - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource.resource_manager.rst python-pyface-7.4.0/docs/source/api/pyface.resource.resource_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource.resource_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource.resource_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================== .. automodule:: pyface.resource.resource_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource.resource_path.rst python-pyface-7.4.0/docs/source/api/pyface.resource.resource_path.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource.resource_path.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource.resource_path.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.resource.resource_path - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource.resource_reference.rst python-pyface-7.4.0/docs/source/api/pyface.resource.resource_reference.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource.resource_reference.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource.resource_reference.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.resource.resource_reference - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.resource.rst python-pyface-7.4.0/docs/source/api/pyface.resource.rst --- python-pyface-6.1.2/docs/source/api/pyface.resource.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.resource.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,18 +2,18 @@ ======================= .. automodule:: pyface.resource - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.resource.api pyface.resource.resource_factory pyface.resource.resource_manager pyface.resource.resource_path pyface.resource.resource_reference - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.rst python-pyface-7.4.0/docs/source/api/pyface.rst --- python-pyface-6.1.2/docs/source/api/pyface.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.rst 2022-02-02 10:01:04.000000000 +0000 @@ -2,67 +2,82 @@ ============== .. automodule:: pyface - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - pyface.action - pyface.fields - pyface.image - pyface.preference - pyface.resource - pyface.sizers - pyface.tasks - pyface.tests - pyface.timer - pyface.tree - pyface.util - pyface.viewer - pyface.wizard - pyface.workbench + pyface.action + pyface.data_view + pyface.fields + pyface.image + pyface.preference + pyface.resource + pyface.sizers + pyface.tasks + pyface.testing + pyface.timer + pyface.tree + pyface.undo + pyface.util + pyface.viewer + pyface.wizard + pyface.workbench Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.about_dialog pyface.api pyface.application pyface.application_window + pyface.array_image pyface.base_toolkit pyface.beep pyface.clipboard + pyface.color + pyface.color_dialog pyface.confirmation_dialog pyface.constant pyface.dialog pyface.directory_dialog pyface.drop_handler - pyface.expandable_header pyface.expandable_panel pyface.file_dialog pyface.filter + pyface.font + pyface.font_dialog pyface.gui pyface.gui_application pyface.heading_text pyface.i_about_dialog pyface.i_application_window pyface.i_clipboard + pyface.i_color_dialog pyface.i_confirmation_dialog pyface.i_dialog pyface.i_directory_dialog pyface.i_drop_handler pyface.i_file_dialog + pyface.i_font_dialog pyface.i_gui pyface.i_heading_text + pyface.i_image pyface.i_image_cache pyface.i_image_resource + pyface.i_layered_panel + pyface.i_layout_item + pyface.i_layout_widget pyface.i_message_dialog + pyface.i_pil_image pyface.i_progress_dialog pyface.i_python_editor pyface.i_python_shell @@ -72,14 +87,13 @@ pyface.i_system_metrics pyface.i_widget pyface.i_window - pyface.image_button pyface.image_cache - pyface.image_list pyface.image_resource pyface.image_widget pyface.ipython_widget pyface.key_pressed_event pyface.layered_panel + pyface.layout_widget pyface.list_box pyface.list_box_model pyface.mdi_application_window @@ -87,6 +101,7 @@ pyface.message_dialog pyface.mimedata pyface.multi_toolbar_window + pyface.pil_image pyface.progress_dialog pyface.python_editor pyface.python_shell @@ -104,5 +119,3 @@ pyface.ui_traits pyface.widget pyface.window - pyface.xrc_dialog - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.single_choice_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.single_choice_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.single_choice_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.single_choice_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.single_choice_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.sizers.flow.rst python-pyface-7.4.0/docs/source/api/pyface.sizers.flow.rst --- python-pyface-6.1.2/docs/source/api/pyface.sizers.flow.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.sizers.flow.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================= .. automodule:: pyface.sizers.flow - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.sizers.rst python-pyface-7.4.0/docs/source/api/pyface.sizers.rst --- python-pyface-6.1.2/docs/source/api/pyface.sizers.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.sizers.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,14 +2,14 @@ ===================== .. automodule:: pyface.sizers - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.sizers.flow - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.sorter.rst python-pyface-7.4.0/docs/source/api/pyface.sorter.rst --- python-pyface-6.1.2/docs/source/api/pyface.sorter.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.sorter.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================== .. automodule:: pyface.sorter - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.splash_screen_log_handler.rst python-pyface-7.4.0/docs/source/api/pyface.splash_screen_log_handler.rst --- python-pyface-6.1.2/docs/source/api/pyface.splash_screen_log_handler.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.splash_screen_log_handler.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.splash_screen_log_handler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.splash_screen.rst python-pyface-7.4.0/docs/source/api/pyface.splash_screen.rst --- python-pyface-6.1.2/docs/source/api/pyface.splash_screen.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.splash_screen.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================ .. automodule:: pyface.splash_screen - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.split_application_window.rst python-pyface-7.4.0/docs/source/api/pyface.split_application_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.split_application_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.split_application_window.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================== .. automodule:: pyface.split_application_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.split_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.split_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.split_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.split_dialog.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.split_dialog - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.split_panel.rst python-pyface-7.4.0/docs/source/api/pyface.split_panel.rst --- python-pyface-6.1.2/docs/source/api/pyface.split_panel.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.split_panel.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.split_panel - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.split_widget.rst python-pyface-7.4.0/docs/source/api/pyface.split_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.split_widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.split_widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.split_widget - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.system_metrics.rst python-pyface-7.4.0/docs/source/api/pyface.system_metrics.rst --- python-pyface-6.1.2/docs/source/api/pyface.system_metrics.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.system_metrics.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.system_metrics - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.api.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.tasks.action.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.dock_pane_toggle_group.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.dock_pane_toggle_group.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.dock_pane_toggle_group.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.dock_pane_toggle_group.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================================================== .. automodule:: pyface.tasks.action.dock_pane_toggle_group - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.listening_action.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.listening_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.listening_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.listening_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================================ .. automodule:: pyface.tasks.action.listening_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,14 +2,15 @@ =========================== .. automodule:: pyface.tasks.action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.tasks.action.api pyface.tasks.action.dock_pane_toggle_group @@ -22,4 +23,3 @@ pyface.tasks.action.task_toggle_group pyface.tasks.action.task_window_toggle_group pyface.tasks.action.tasks_application_action - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.schema_addition.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.schema_addition.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.schema_addition.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.schema_addition.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =========================================== .. automodule:: pyface.tasks.action.schema_addition - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.schema.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.schema.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.schema.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.schema.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.tasks.action.schema - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_action_controller.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_action_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_action_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_action_controller.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =================================================== .. automodule:: pyface.tasks.action.task_action_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_action_manager_builder.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_action_manager_builder.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_action_manager_builder.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_action_manager_builder.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================================= .. automodule:: pyface.tasks.action.task_action_manager_builder - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_action.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.tasks.action.task_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.tasks_application_action.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.tasks_application_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.tasks_application_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.tasks_application_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================================================== .. automodule:: pyface.tasks.action.tasks_application_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_toggle_group.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_toggle_group.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_toggle_group.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_toggle_group.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================================== .. automodule:: pyface.tasks.action.task_toggle_group - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_window_toggle_group.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_window_toggle_group.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.action.task_window_toggle_group.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.action.task_window_toggle_group.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================================== .. automodule:: pyface.tasks.action.task_window_toggle_group - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.advanced_editor_area_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.advanced_editor_area_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.advanced_editor_area_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.advanced_editor_area_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================================ .. automodule:: pyface.tasks.advanced_editor_area_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.api.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.tasks.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.contrib.python_shell.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.contrib.python_shell.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.contrib.python_shell.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.contrib.python_shell.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.tasks.contrib.python_shell - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.contrib.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.contrib.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.contrib.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.contrib.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,14 +2,14 @@ ============================ .. automodule:: pyface.tasks.contrib - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.tasks.contrib.python_shell - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.dock_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.dock_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.dock_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.dock_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.tasks.dock_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.editor_area_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.editor_area_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.editor_area_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.editor_area_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.tasks.editor_area_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.editor.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.editor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.tasks.editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_dock_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_dock_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_dock_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_dock_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.tasks.enaml_dock_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_editor.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_editor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.tasks.enaml_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.tasks.enaml_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_task_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_task_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.enaml_task_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.enaml_task_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.tasks.enaml_task_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.i_advanced_editor_area_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.i_advanced_editor_area_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.i_advanced_editor_area_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.i_advanced_editor_area_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ =================================================== .. automodule:: pyface.tasks.i_advanced_editor_area_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.i_dock_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.i_dock_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.i_dock_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.i_dock_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.tasks.i_dock_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.i_editor_area_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.i_editor_area_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.i_editor_area_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.i_editor_area_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.tasks.i_editor_area_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.i_editor.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.i_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.i_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.i_editor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.tasks.i_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.i_task_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.i_task_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.i_task_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.i_task_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.tasks.i_task_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.i_task_window_backend.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.i_task_window_backend.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.i_task_window_backend.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.i_task_window_backend.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================================ .. automodule:: pyface.tasks.i_task_window_backend - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,23 +2,24 @@ ==================== .. automodule:: pyface.tasks - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - pyface.tasks.action - pyface.tasks.contrib - pyface.tasks.tests + pyface.tasks.action + pyface.tasks.contrib Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.tasks.advanced_editor_area_pane pyface.tasks.api @@ -47,4 +48,3 @@ pyface.tasks.traits_dock_pane pyface.tasks.traits_editor pyface.tasks.traits_task_pane - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.split_editor_area_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.split_editor_area_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.split_editor_area_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.split_editor_area_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================================= .. automodule:: pyface.tasks.split_editor_area_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.task_layout.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.task_layout.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.task_layout.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.task_layout.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.tasks.task_layout - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.task_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.task_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.task_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.task_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.tasks.task_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.task.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.task.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.task.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.task.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================== .. automodule:: pyface.tasks.task - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tasks_application.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tasks_application.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tasks_application.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tasks_application.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.tasks.tasks_application - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.task_window_backend.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.task_window_backend.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.task_window_backend.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.task_window_backend.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.tasks.task_window_backend - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.task_window_layout.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.task_window_layout.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.task_window_layout.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.task_window_layout.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================================== .. automodule:: pyface.tasks.task_window_layout - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.task_window.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.task_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.task_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.task_window.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.tasks.task_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -pyface.tasks.tests package -========================== - -.. automodule:: pyface.tasks.tests - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.tasks.tests.test_action_manager_builder - pyface.tasks.tests.test_dock_pane_toggle_group - pyface.tasks.tests.test_editor_area_pane - pyface.tasks.tests.test_enaml_dock_pane - pyface.tasks.tests.test_enaml_editor - pyface.tasks.tests.test_enaml_task_pane - pyface.tasks.tests.test_task_layout - pyface.tasks.tests.test_task_window - pyface.tasks.tests.test_tasks_application - pyface.tasks.tests.test_topological_sort - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_action_manager_builder.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_action_manager_builder.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_action_manager_builder.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_action_manager_builder.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_action\_manager\_builder module -======================================================== - -.. automodule:: pyface.tasks.tests.test_action_manager_builder - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_dock_pane_toggle_group.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_dock_pane_toggle_group.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_dock_pane_toggle_group.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_dock_pane_toggle_group.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_dock\_pane\_toggle\_group module -========================================================= - -.. automodule:: pyface.tasks.tests.test_dock_pane_toggle_group - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_editor_area_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_editor_area_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_editor_area_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_editor_area_pane.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_editor\_area\_pane module -================================================== - -.. automodule:: pyface.tasks.tests.test_editor_area_pane - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_enaml_dock_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_enaml_dock_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_enaml_dock_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_enaml_dock_pane.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_enaml\_dock\_pane module -================================================= - -.. automodule:: pyface.tasks.tests.test_enaml_dock_pane - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_enaml_editor.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_enaml_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_enaml_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_enaml_editor.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_enaml\_editor module -============================================= - -.. automodule:: pyface.tasks.tests.test_enaml_editor - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_enaml_task_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_enaml_task_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_enaml_task_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_enaml_task_pane.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_enaml\_task\_pane module -================================================= - -.. automodule:: pyface.tasks.tests.test_enaml_task_pane - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_task_layout.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_task_layout.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_task_layout.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_task_layout.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_task\_layout module -============================================ - -.. automodule:: pyface.tasks.tests.test_task_layout - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_tasks_application.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_tasks_application.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_tasks_application.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_tasks_application.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_tasks\_application module -================================================== - -.. automodule:: pyface.tasks.tests.test_tasks_application - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_task_window.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_task_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_task_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_task_window.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_task\_window module -============================================ - -.. automodule:: pyface.tasks.tests.test_task_window - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_topological_sort.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_topological_sort.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.tests.test_topological_sort.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.tests.test_topological_sort.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tasks.tests.test\_topological\_sort module -================================================= - -.. automodule:: pyface.tasks.tests.test_topological_sort - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.topological_sort.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.topological_sort.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.topological_sort.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.topological_sort.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.tasks.topological_sort - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.traits_dock_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.traits_dock_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.traits_dock_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.traits_dock_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.tasks.traits_dock_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.traits_editor.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.traits_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.traits_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.traits_editor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.tasks.traits_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tasks.traits_task_pane.rst python-pyface-7.4.0/docs/source/api/pyface.tasks.traits_task_pane.rst --- python-pyface-6.1.2/docs/source/api/pyface.tasks.traits_task_pane.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tasks.traits_task_pane.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.tasks.traits_task_pane - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.testing.layout_widget_mixin.rst python-pyface-7.4.0/docs/source/api/pyface.testing.layout_widget_mixin.rst --- python-pyface-6.1.2/docs/source/api/pyface.testing.layout_widget_mixin.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.testing.layout_widget_mixin.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.testing.layout\_widget\_mixin module +=========================================== + +.. automodule:: pyface.testing.layout_widget_mixin + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.testing.rst python-pyface-7.4.0/docs/source/api/pyface.testing.rst --- python-pyface-6.1.2/docs/source/api/pyface.testing.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.testing.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,16 @@ +pyface.testing package +====================== + +.. automodule:: pyface.testing + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.testing.layout_widget_mixin + pyface.testing.widget_mixin diff -Nru python-pyface-6.1.2/docs/source/api/pyface.testing.widget_mixin.rst python-pyface-7.4.0/docs/source/api/pyface.testing.widget_mixin.rst --- python-pyface-6.1.2/docs/source/api/pyface.testing.widget_mixin.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.testing.widget_mixin.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.testing.widget\_mixin module +=================================== + +.. automodule:: pyface.testing.widget_mixin + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.python_shell_script.rst python-pyface-7.4.0/docs/source/api/pyface.tests.python_shell_script.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.python_shell_script.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.python_shell_script.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.python\_shell\_script module -========================================= - -.. automodule:: pyface.tests.python_shell_script - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.rst python-pyface-7.4.0/docs/source/api/pyface.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -pyface.tests package -==================== - -.. automodule:: pyface.tests - :members: - :undoc-members: - :show-inheritance: - -Subpackages ------------ - -.. toctree:: - - pyface.tests.test_new_toolkit - -Submodules ----------- - -.. toctree:: - - pyface.tests.python_shell_script - pyface.tests.test_about_dialog - pyface.tests.test_application - pyface.tests.test_application_window - pyface.tests.test_base_toolkit - pyface.tests.test_beep - pyface.tests.test_clipboard - pyface.tests.test_confirmation_dialog - pyface.tests.test_dialog - pyface.tests.test_directory_dialog - pyface.tests.test_file_dialog - pyface.tests.test_gui_application - pyface.tests.test_heading_text - pyface.tests.test_image_cache - pyface.tests.test_image_resource - pyface.tests.test_message_dialog - pyface.tests.test_progress_dialog - pyface.tests.test_python_editor - pyface.tests.test_python_shell - pyface.tests.test_resource_manager - pyface.tests.test_single_choice_dialog - pyface.tests.test_splash_screen - pyface.tests.test_splash_screen_log_handler - pyface.tests.test_split_application_window - pyface.tests.test_split_dialog - pyface.tests.test_split_panel - pyface.tests.test_system_metrics - pyface.tests.test_toolkit - pyface.tests.test_ui_traits - pyface.tests.test_widget - pyface.tests.test_window - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_about_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_about_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_about_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_about_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_about\_dialog module -======================================= - -.. automodule:: pyface.tests.test_about_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_application.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_application.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_application.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_application.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_application module -===================================== - -.. automodule:: pyface.tests.test_application - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_application_window.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_application_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_application_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_application_window.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_application\_window module -============================================= - -.. automodule:: pyface.tests.test_application_window - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_base_toolkit.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_base_toolkit.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_base_toolkit.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_base_toolkit.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_base\_toolkit module -======================================= - -.. automodule:: pyface.tests.test_base_toolkit - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_beep.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_beep.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_beep.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_beep.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_beep module -============================== - -.. automodule:: pyface.tests.test_beep - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_clipboard.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_clipboard.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_clipboard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_clipboard.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_clipboard module -=================================== - -.. automodule:: pyface.tests.test_clipboard - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_confirmation_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_confirmation_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_confirmation_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_confirmation_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_confirmation\_dialog module -============================================== - -.. automodule:: pyface.tests.test_confirmation_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_dialog module -================================ - -.. automodule:: pyface.tests.test_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_directory_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_directory_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_directory_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_directory_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_directory\_dialog module -=========================================== - -.. automodule:: pyface.tests.test_directory_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_file_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_file_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_file_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_file_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_file\_dialog module -====================================== - -.. automodule:: pyface.tests.test_file_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_gui_application.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_gui_application.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_gui_application.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_gui_application.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_gui\_application module -========================================== - -.. automodule:: pyface.tests.test_gui_application - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_heading_text.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_heading_text.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_heading_text.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_heading_text.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_heading\_text module -======================================= - -.. automodule:: pyface.tests.test_heading_text - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_image_cache.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_image_cache.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_image_cache.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_image_cache.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_image\_cache module -====================================== - -.. automodule:: pyface.tests.test_image_cache - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_image_resource.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_image_resource.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_image_resource.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_image_resource.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_image\_resource module -========================================= - -.. automodule:: pyface.tests.test_image_resource - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_message_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_message_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_message_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_message_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_message\_dialog module -========================================= - -.. automodule:: pyface.tests.test_message_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_new_toolkit.init.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_new_toolkit.init.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_new_toolkit.init.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_new_toolkit.init.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_new\_toolkit.init module -=========================================== - -.. automodule:: pyface.tests.test_new_toolkit.init - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_new_toolkit.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_new_toolkit.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_new_toolkit.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_new_toolkit.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -pyface.tests.test\_new\_toolkit package -======================================= - -.. automodule:: pyface.tests.test_new_toolkit - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.tests.test_new_toolkit.init - pyface.tests.test_new_toolkit.widget - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_new_toolkit.widget.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_new_toolkit.widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_new_toolkit.widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_new_toolkit.widget.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_new\_toolkit.widget module -============================================= - -.. automodule:: pyface.tests.test_new_toolkit.widget - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_progress_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_progress_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_progress_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_progress_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_progress\_dialog module -========================================== - -.. automodule:: pyface.tests.test_progress_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_python_editor.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_python_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_python_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_python_editor.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_python\_editor module -======================================== - -.. automodule:: pyface.tests.test_python_editor - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_python_shell.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_python_shell.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_python_shell.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_python_shell.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_python\_shell module -======================================= - -.. automodule:: pyface.tests.test_python_shell - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_resource_manager.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_resource_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_resource_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_resource_manager.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_resource\_manager module -=========================================== - -.. automodule:: pyface.tests.test_resource_manager - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_single_choice_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_single_choice_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_single_choice_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_single_choice_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_single\_choice\_dialog module -================================================ - -.. automodule:: pyface.tests.test_single_choice_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_splash_screen_log_handler.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_splash_screen_log_handler.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_splash_screen_log_handler.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_splash_screen_log_handler.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_splash\_screen\_log\_handler module -====================================================== - -.. automodule:: pyface.tests.test_splash_screen_log_handler - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_splash_screen.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_splash_screen.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_splash_screen.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_splash_screen.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_splash\_screen module -======================================== - -.. automodule:: pyface.tests.test_splash_screen - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_split_application_window.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_split_application_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_split_application_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_split_application_window.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_split\_application\_window module -==================================================== - -.. automodule:: pyface.tests.test_split_application_window - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_split_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_split_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_split_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_split_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_split\_dialog module -======================================= - -.. automodule:: pyface.tests.test_split_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_split_panel.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_split_panel.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_split_panel.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_split_panel.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_split\_panel module -====================================== - -.. automodule:: pyface.tests.test_split_panel - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_system_metrics.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_system_metrics.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_system_metrics.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_system_metrics.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_system\_metrics module -========================================= - -.. automodule:: pyface.tests.test_system_metrics - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_toolkit.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_toolkit.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_toolkit.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_toolkit.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_toolkit module -================================= - -.. automodule:: pyface.tests.test_toolkit - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_ui_traits.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_ui_traits.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_ui_traits.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_ui_traits.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_ui\_traits module -==================================== - -.. automodule:: pyface.tests.test_ui_traits - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_widget.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_widget.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_widget module -================================ - -.. automodule:: pyface.tests.test_widget - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tests.test_window.rst python-pyface-7.4.0/docs/source/api/pyface.tests.test_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.tests.test_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tests.test_window.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.tests.test\_window module -================================ - -.. automodule:: pyface.tests.test_window - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.api.rst python-pyface-7.4.0/docs/source/api/pyface.timer.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.timer.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.do_later.rst python-pyface-7.4.0/docs/source/api/pyface.timer.do_later.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.do_later.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.do_later.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.timer.do_later - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.i_timer.rst python-pyface-7.4.0/docs/source/api/pyface.timer.i_timer.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.i_timer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.i_timer.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================ .. automodule:: pyface.timer.i_timer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.rst python-pyface-7.4.0/docs/source/api/pyface.timer.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,24 +2,17 @@ ==================== .. automodule:: pyface.timer - :members: - :undoc-members: - :show-inheritance: - -Subpackages ------------ - -.. toctree:: - - pyface.timer.tests + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.timer.api pyface.timer.do_later pyface.timer.i_timer pyface.timer.timer - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.tests.rst python-pyface-7.4.0/docs/source/api/pyface.timer.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.tests.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -pyface.timer.tests package -========================== - -.. automodule:: pyface.timer.tests - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.timer.tests.test_timer - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.tests.test_timer.rst python-pyface-7.4.0/docs/source/api/pyface.timer.tests.test_timer.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.tests.test_timer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.tests.test_timer.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.timer.tests.test\_timer module -===================================== - -.. automodule:: pyface.timer.tests.test_timer - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.timer.timer.rst python-pyface-7.4.0/docs/source/api/pyface.timer.timer.rst --- python-pyface-6.1.2/docs/source/api/pyface.timer.timer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.timer.timer.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================= .. automodule:: pyface.timer.timer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.toolkit.rst python-pyface-7.4.0/docs/source/api/pyface.toolkit.rst --- python-pyface-6.1.2/docs/source/api/pyface.toolkit.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.toolkit.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================== .. automodule:: pyface.toolkit - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.api.rst python-pyface-7.4.0/docs/source/api/pyface.tree.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================== .. automodule:: pyface.tree.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.node_event.rst python-pyface-7.4.0/docs/source/api/pyface.tree.node_event.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.node_event.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.node_event.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.tree.node_event - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.node_manager.rst python-pyface-7.4.0/docs/source/api/pyface.tree.node_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.node_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.node_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.tree.node_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.node_monitor.rst python-pyface-7.4.0/docs/source/api/pyface.tree.node_monitor.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.node_monitor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.node_monitor.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ================================ .. automodule:: pyface.tree.node_monitor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.node_tree_model.rst python-pyface-7.4.0/docs/source/api/pyface.tree.node_tree_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.node_tree_model.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.node_tree_model.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.tree.node_tree_model - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.node_tree.rst python-pyface-7.4.0/docs/source/api/pyface.tree.node_tree.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.node_tree.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.node_tree.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.tree.node_tree - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.node_type.rst python-pyface-7.4.0/docs/source/api/pyface.tree.node_type.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.node_type.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.node_type.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.tree.node_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.rst python-pyface-7.4.0/docs/source/api/pyface.tree.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,14 +2,15 @@ =================== .. automodule:: pyface.tree - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.tree.api pyface.tree.node_event @@ -22,4 +23,3 @@ pyface.tree.trait_list_node_type pyface.tree.tree pyface.tree.tree_model - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.trait_dict_node_type.rst python-pyface-7.4.0/docs/source/api/pyface.tree.trait_dict_node_type.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.trait_dict_node_type.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.trait_dict_node_type.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.tree.trait_dict_node_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.trait_list_node_type.rst python-pyface-7.4.0/docs/source/api/pyface.tree.trait_list_node_type.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.trait_list_node_type.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.trait_list_node_type.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.tree.trait_list_node_type - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.tree_model.rst python-pyface-7.4.0/docs/source/api/pyface.tree.tree_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.tree_model.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.tree_model.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.tree.tree_model - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.tree.tree.rst python-pyface-7.4.0/docs/source/api/pyface.tree.tree.rst --- python-pyface-6.1.2/docs/source/api/pyface.tree.tree.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.tree.tree.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================= .. automodule:: pyface.tree.tree - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.ui_traits.rst python-pyface-7.4.0/docs/source/api/pyface.ui_traits.rst --- python-pyface-6.1.2/docs/source/api/pyface.ui_traits.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.ui_traits.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================== .. automodule:: pyface.ui_traits - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.abstract_command.rst python-pyface-7.4.0/docs/source/api/pyface.undo.abstract_command.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.abstract_command.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.abstract_command.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.abstract\_command module +==================================== + +.. automodule:: pyface.undo.abstract_command + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.action.abstract_command_stack_action.rst python-pyface-7.4.0/docs/source/api/pyface.undo.action.abstract_command_stack_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.action.abstract_command_stack_action.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.action.abstract_command_stack_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.action.abstract\_command\_stack\_action module +========================================================== + +.. automodule:: pyface.undo.action.abstract_command_stack_action + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.action.api.rst python-pyface-7.4.0/docs/source/api/pyface.undo.action.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.action.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.action.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.action.api module +============================= + +.. automodule:: pyface.undo.action.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.action.command_action.rst python-pyface-7.4.0/docs/source/api/pyface.undo.action.command_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.action.command_action.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.action.command_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.action.command\_action module +========================================= + +.. automodule:: pyface.undo.action.command_action + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.action.redo_action.rst python-pyface-7.4.0/docs/source/api/pyface.undo.action.redo_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.action.redo_action.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.action.redo_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.action.redo\_action module +====================================== + +.. automodule:: pyface.undo.action.redo_action + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.action.rst python-pyface-7.4.0/docs/source/api/pyface.undo.action.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.action.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,19 @@ +pyface.undo.action package +========================== + +.. automodule:: pyface.undo.action + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.undo.action.abstract_command_stack_action + pyface.undo.action.api + pyface.undo.action.command_action + pyface.undo.action.redo_action + pyface.undo.action.undo_action diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.action.undo_action.rst python-pyface-7.4.0/docs/source/api/pyface.undo.action.undo_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.action.undo_action.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.action.undo_action.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.action.undo\_action module +====================================== + +.. automodule:: pyface.undo.action.undo_action + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.api.rst python-pyface-7.4.0/docs/source/api/pyface.undo.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.api.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.api module +====================== + +.. automodule:: pyface.undo.api + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.command_stack.rst python-pyface-7.4.0/docs/source/api/pyface.undo.command_stack.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.command_stack.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.command_stack.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.command\_stack module +================================= + +.. automodule:: pyface.undo.command_stack + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.i_command.rst python-pyface-7.4.0/docs/source/api/pyface.undo.i_command.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.i_command.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.i_command.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.i\_command module +============================= + +.. automodule:: pyface.undo.i_command + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.i_command_stack.rst python-pyface-7.4.0/docs/source/api/pyface.undo.i_command_stack.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.i_command_stack.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.i_command_stack.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.i\_command\_stack module +==================================== + +.. automodule:: pyface.undo.i_command_stack + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.i_undo_manager.rst python-pyface-7.4.0/docs/source/api/pyface.undo.i_undo_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.i_undo_manager.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.i_undo_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.i\_undo\_manager module +=================================== + +.. automodule:: pyface.undo.i_undo_manager + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.rst python-pyface-7.4.0/docs/source/api/pyface.undo.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,29 @@ +pyface.undo package +=================== + +.. automodule:: pyface.undo + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + pyface.undo.action + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + pyface.undo.abstract_command + pyface.undo.api + pyface.undo.command_stack + pyface.undo.i_command + pyface.undo.i_command_stack + pyface.undo.i_undo_manager + pyface.undo.undo_manager diff -Nru python-pyface-6.1.2/docs/source/api/pyface.undo.undo_manager.rst python-pyface-7.4.0/docs/source/api/pyface.undo.undo_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.undo.undo_manager.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.undo.undo_manager.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.undo.undo\_manager module +================================ + +.. automodule:: pyface.undo.undo_manager + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.color_helpers.rst python-pyface-7.4.0/docs/source/api/pyface.util.color_helpers.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.color_helpers.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.color_helpers.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.color\_helpers module +================================= + +.. automodule:: pyface.util.color_helpers + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.color_parser.rst python-pyface-7.4.0/docs/source/api/pyface.util.color_parser.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.color_parser.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.color_parser.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.color\_parser module +================================ + +.. automodule:: pyface.util.color_parser + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.event_loop_helper.rst python-pyface-7.4.0/docs/source/api/pyface.util.event_loop_helper.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.event_loop_helper.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.event_loop_helper.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.event\_loop\_helper module +====================================== + +.. automodule:: pyface.util.event_loop_helper + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.font_helper.rst python-pyface-7.4.0/docs/source/api/pyface.util.font_helper.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.font_helper.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.font_helper.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.font\_helper module -=============================== - -.. automodule:: pyface.util.font_helper - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.font_parser.rst python-pyface-7.4.0/docs/source/api/pyface.util.font_parser.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.font_parser.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.font_parser.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.font\_parser module +=============================== + +.. automodule:: pyface.util.font_parser + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.grid.api.rst python-pyface-7.4.0/docs/source/api/pyface.util.grid.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.grid.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.grid.api.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.grid.api module -=========================== - -.. automodule:: pyface.util.grid.api - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid_column.rst python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid_column.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid_column.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid_column.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.grid.grid\_column module -==================================== - -.. automodule:: pyface.util.grid.grid_column - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid_model.rst python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid_model.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid_model.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid_model.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.grid.grid\_model module -=================================== - -.. automodule:: pyface.util.grid.grid_model - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid_row.rst python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid_row.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid_row.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid_row.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.grid.grid\_row module -================================= - -.. automodule:: pyface.util.grid.grid_row - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid.rst python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.grid.grid.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.grid.grid.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.grid.grid module -============================ - -.. automodule:: pyface.util.grid.grid - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.grid.rst python-pyface-7.4.0/docs/source/api/pyface.util.grid.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.grid.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.grid.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -pyface.util.grid package -======================== - -.. automodule:: pyface.util.grid - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.util.grid.api - pyface.util.grid.grid - pyface.util.grid.grid_column - pyface.util.grid.grid_model - pyface.util.grid.grid_row - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.guisupport.rst python-pyface-7.4.0/docs/source/api/pyface.util.guisupport.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.guisupport.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.guisupport.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.util.guisupport - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.gui_test_assistant.rst python-pyface-7.4.0/docs/source/api/pyface.util.gui_test_assistant.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.gui_test_assistant.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.gui_test_assistant.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.gui\_test\_assistant module +======================================= + +.. automodule:: pyface.util.gui_test_assistant + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.id_helper.rst python-pyface-7.4.0/docs/source/api/pyface.util.id_helper.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.id_helper.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.id_helper.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ============================= .. automodule:: pyface.util.id_helper - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.image_helpers.rst python-pyface-7.4.0/docs/source/api/pyface.util.image_helpers.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.image_helpers.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.image_helpers.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.image\_helpers module +================================= + +.. automodule:: pyface.util.image_helpers + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.modal_dialog_tester.rst python-pyface-7.4.0/docs/source/api/pyface.util.modal_dialog_tester.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.modal_dialog_tester.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.modal_dialog_tester.rst 2022-02-02 10:01:05.000000000 +0000 @@ -0,0 +1,7 @@ +pyface.util.modal\_dialog\_tester module +======================================== + +.. automodule:: pyface.util.modal_dialog_tester + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.python_stc.rst python-pyface-7.4.0/docs/source/api/pyface.util.python_stc.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.python_stc.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.python_stc.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.python\_stc module -============================== - -.. automodule:: pyface.util.python_stc - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.rst python-pyface-7.4.0/docs/source/api/pyface.util.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,26 +2,23 @@ =================== .. automodule:: pyface.util - :members: - :undoc-members: - :show-inheritance: - -Subpackages ------------ - -.. toctree:: - - pyface.util.grid - pyface.util.tests + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 - pyface.util.font_helper + pyface.util.color_helpers + pyface.util.color_parser + pyface.util.event_loop_helper + pyface.util.font_parser + pyface.util.gui_test_assistant pyface.util.guisupport pyface.util.id_helper - pyface.util.python_stc + pyface.util.image_helpers + pyface.util.modal_dialog_tester pyface.util.testing - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.testing.rst python-pyface-7.4.0/docs/source/api/pyface.util.testing.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.testing.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.testing.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ========================== .. automodule:: pyface.util.testing - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.tests.rst python-pyface-7.4.0/docs/source/api/pyface.util.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.tests.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -pyface.util.tests package -========================= - -.. automodule:: pyface.util.tests - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.util.tests.test_id_helper - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.util.tests.test_id_helper.rst python-pyface-7.4.0/docs/source/api/pyface.util.tests.test_id_helper.rst --- python-pyface-6.1.2/docs/source/api/pyface.util.tests.test_id_helper.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.util.tests.test_id_helper.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.util.tests.test\_id\_helper module -========================================= - -.. automodule:: pyface.util.tests.test_id_helper - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.api.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.api.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ======================== .. automodule:: pyface.viewer.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.column_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.column_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.column_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.column_provider.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.viewer.column_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.content_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.content_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.content_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.content_provider.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.viewer.content_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.content_viewer.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.content_viewer.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.content_viewer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.content_viewer.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.viewer.content_viewer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.default_tree_content_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.default_tree_content_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.default_tree_content_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.default_tree_content_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ===================================================== .. automodule:: pyface.viewer.default_tree_content_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.label_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.label_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.label_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.label_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.viewer.label_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,14 +2,15 @@ ===================== .. automodule:: pyface.viewer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.viewer.api pyface.viewer.column_provider @@ -28,4 +29,3 @@ pyface.viewer.viewer pyface.viewer.viewer_filter pyface.viewer.viewer_sorter - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.table_column_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.table_column_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.table_column_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.table_column_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================ .. automodule:: pyface.viewer.table_column_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.table_content_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.table_content_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.table_content_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.table_content_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================= .. automodule:: pyface.viewer.table_content_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.table_label_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.table_label_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.table_label_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.table_label_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =========================================== .. automodule:: pyface.viewer.table_label_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.table_viewer.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.table_viewer.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.table_viewer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.table_viewer.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.viewer.table_viewer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_content_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_content_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_content_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_content_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================ .. automodule:: pyface.viewer.tree_content_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_item.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_item.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_item.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.viewer.tree_item - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_label_provider.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_label_provider.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_label_provider.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_label_provider.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.viewer.tree_label_provider - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_viewer.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_viewer.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.tree_viewer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.tree_viewer.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.viewer.tree_viewer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.viewer_filter.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.viewer_filter.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.viewer_filter.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.viewer_filter.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.viewer.viewer_filter - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.viewer.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.viewer.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.viewer.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.viewer.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.viewer.viewer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.viewer.viewer_sorter.rst python-pyface-7.4.0/docs/source/api/pyface.viewer.viewer_sorter.rst --- python-pyface-6.1.2/docs/source/api/pyface.viewer.viewer_sorter.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.viewer.viewer_sorter.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.viewer.viewer_sorter - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.widget.rst python-pyface-7.4.0/docs/source/api/pyface.widget.rst --- python-pyface-6.1.2/docs/source/api/pyface.widget.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.widget.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================== .. automodule:: pyface.widget - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.window.rst python-pyface-7.4.0/docs/source/api/pyface.window.rst --- python-pyface-6.1.2/docs/source/api/pyface.window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.window.rst 2022-02-02 10:01:05.000000000 +0000 @@ -2,6 +2,6 @@ ==================== .. automodule:: pyface.window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.api.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.api.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ======================== .. automodule:: pyface.wizard.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.chained_wizard_controller.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.chained_wizard_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.chained_wizard_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.chained_wizard_controller.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================ .. automodule:: pyface.wizard.chained_wizard_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.chained_wizard.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.chained_wizard.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.chained_wizard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.chained_wizard.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.wizard.chained_wizard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.i_wizard_controller.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.i_wizard_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.i_wizard_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.i_wizard_controller.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.wizard.i_wizard_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.i_wizard_page.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.i_wizard_page.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.i_wizard_page.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.i_wizard_page.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.wizard.i_wizard_page - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.i_wizard.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.i_wizard.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.i_wizard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.i_wizard.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.wizard.i_wizard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,14 +2,15 @@ ===================== .. automodule:: pyface.wizard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.wizard.api pyface.wizard.chained_wizard @@ -22,4 +23,3 @@ pyface.wizard.wizard pyface.wizard.wizard_controller pyface.wizard.wizard_page - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.simple_wizard_controller.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.simple_wizard_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.simple_wizard_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.simple_wizard_controller.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =============================================== .. automodule:: pyface.wizard.simple_wizard_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.simple_wizard.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.simple_wizard.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.simple_wizard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.simple_wizard.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.wizard.simple_wizard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.wizard_controller.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.wizard_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.wizard_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.wizard_controller.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.wizard.wizard_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.wizard_page.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.wizard_page.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.wizard_page.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.wizard_page.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.wizard.wizard_page - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.wizard.wizard.rst python-pyface-7.4.0/docs/source/api/pyface.wizard.wizard.rst --- python-pyface-6.1.2/docs/source/api/pyface.wizard.wizard.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.wizard.wizard.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.wizard.wizard - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.action_controller.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.action_controller.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.action_controller.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.action_controller.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================= .. automodule:: pyface.workbench.action.action_controller - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.api.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.api.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================== .. automodule:: pyface.workbench.action.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.delete_user_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.delete_user_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.delete_user_perspective_action.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.delete_user_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================================ .. automodule:: pyface.workbench.action.delete_user_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.menu_bar_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.menu_bar_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.menu_bar_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.menu_bar_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================= .. automodule:: pyface.workbench.action.menu_bar_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.new_user_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.new_user_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.new_user_perspective_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.new_user_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================================= .. automodule:: pyface.workbench.action.new_user_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.perspective_menu_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.perspective_menu_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.perspective_menu_manager.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.perspective_menu_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================================= .. automodule:: pyface.workbench.action.perspective_menu_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.rename_user_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.rename_user_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.rename_user_perspective_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.rename_user_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================================ .. automodule:: pyface.workbench.action.rename_user_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.reset_active_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.reset_active_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.reset_active_perspective_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.reset_active_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================================= .. automodule:: pyface.workbench.action.reset_active_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.reset_all_perspectives_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.reset_all_perspectives_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.reset_all_perspectives_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.reset_all_perspectives_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =============================================================== .. automodule:: pyface.workbench.action.reset_all_perspectives_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,14 +2,15 @@ =============================== .. automodule:: pyface.workbench.action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.workbench.action.action_controller pyface.workbench.action.api @@ -31,4 +32,3 @@ pyface.workbench.action.view_chooser pyface.workbench.action.view_menu_manager pyface.workbench.action.workbench_action - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.save_as_user_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.save_as_user_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.save_as_user_perspective_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.save_as_user_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================================== .. automodule:: pyface.workbench.action.save_as_user_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.set_active_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.set_active_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.set_active_perspective_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.set_active_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =============================================================== .. automodule:: pyface.workbench.action.set_active_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.setattr_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.setattr_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.setattr_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.setattr_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================== .. automodule:: pyface.workbench.action.setattr_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.show_view_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.show_view_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.show_view_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.show_view_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================= .. automodule:: pyface.workbench.action.show_view_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.toggle_view_visibility_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.toggle_view_visibility_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.toggle_view_visibility_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.toggle_view_visibility_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =============================================================== .. automodule:: pyface.workbench.action.toggle_view_visibility_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.tool_bar_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.tool_bar_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.tool_bar_manager.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.tool_bar_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================= .. automodule:: pyface.workbench.action.tool_bar_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.user_perspective_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.user_perspective_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.user_perspective_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.user_perspective_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ======================================================== .. automodule:: pyface.workbench.action.user_perspective_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.user_perspective_name.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.user_perspective_name.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.user_perspective_name.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.user_perspective_name.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ====================================================== .. automodule:: pyface.workbench.action.user_perspective_name - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.view_chooser.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.view_chooser.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.view_chooser.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.view_chooser.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================ .. automodule:: pyface.workbench.action.view_chooser - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.view_menu_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.view_menu_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.view_menu_manager.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.view_menu_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================== .. automodule:: pyface.workbench.action.view_menu_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.action.workbench_action.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.action.workbench_action.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.action.workbench_action.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.action.workbench_action.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================ .. automodule:: pyface.workbench.action.workbench_action - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.api.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.api.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.api.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =========================== .. automodule:: pyface.workbench.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.debug.api.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.debug.api.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.debug.api.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.debug.api.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.workbench.debug.api - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.debug.debug_view.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.debug.debug_view.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.debug.debug_view.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.debug.debug_view.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.workbench.debug.debug_view - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.debug.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.debug.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.debug.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.debug.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,15 +2,15 @@ ============================== .. automodule:: pyface.workbench.debug - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.workbench.debug.api pyface.workbench.debug.debug_view - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.editor_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.editor_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.editor_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.editor_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ======================================= .. automodule:: pyface.workbench.editor_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.editor.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.editor.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================== .. automodule:: pyface.workbench.editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_editor_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_editor_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_editor_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_editor_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.workbench.i_editor_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_editor.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_editor.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.workbench.i_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_perspective_item.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_perspective_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_perspective_item.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_perspective_item.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================================ .. automodule:: pyface.workbench.i_perspective_item - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_perspective.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_perspective.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_perspective.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_perspective.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ====================================== .. automodule:: pyface.workbench.i_perspective - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_view.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_view.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_view.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_view.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =============================== .. automodule:: pyface.workbench.i_view - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_workbench_part.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_workbench_part.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_workbench_part.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_workbench_part.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.workbench.i_workbench_part - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_workbench.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_workbench.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_workbench.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_workbench.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ==================================== .. automodule:: pyface.workbench.i_workbench - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.i_workbench_window_layout.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.i_workbench_window_layout.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.i_workbench_window_layout.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.i_workbench_window_layout.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ==================================================== .. automodule:: pyface.workbench.i_workbench_window_layout - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.perspective_item.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.perspective_item.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.perspective_item.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.perspective_item.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.workbench.perspective_item - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.perspective.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.perspective.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.perspective.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.perspective.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ =================================== .. automodule:: pyface.workbench.perspective - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,23 +2,24 @@ ======================== .. automodule:: pyface.workbench - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Subpackages ----------- .. toctree:: + :maxdepth: 4 - pyface.workbench.action - pyface.workbench.debug - pyface.workbench.tests + pyface.workbench.action + pyface.workbench.debug Submodules ---------- .. toctree:: + :maxdepth: 4 pyface.workbench.api pyface.workbench.editor @@ -42,4 +43,3 @@ pyface.workbench.workbench_window pyface.workbench.workbench_window_layout pyface.workbench.workbench_window_memento - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.tests.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.tests.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.tests.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.tests.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -pyface.workbench.tests package -============================== - -.. automodule:: pyface.workbench.tests - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - -.. toctree:: - - pyface.workbench.tests.test_workbench_window - diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.tests.test_workbench_window.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.tests.test_workbench_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.tests.test_workbench_window.rst 2019-05-08 14:16:25.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.tests.test_workbench_window.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.workbench.tests.test\_workbench\_window module -===================================================== - -.. automodule:: pyface.workbench.tests.test_workbench_window - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.traits_ui_editor.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.traits_ui_editor.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.traits_ui_editor.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.traits_ui_editor.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================== .. automodule:: pyface.workbench.traits_ui_editor - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.traits_ui_view.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.traits_ui_view.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.traits_ui_view.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.traits_ui_view.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ======================================== .. automodule:: pyface.workbench.traits_ui_view - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.user_perspective_manager.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.user_perspective_manager.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.user_perspective_manager.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.user_perspective_manager.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================== .. automodule:: pyface.workbench.user_perspective_manager - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.view.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.view.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.view.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.view.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ============================ .. automodule:: pyface.workbench.view - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.window_event.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.window_event.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.window_event.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.window_event.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ===================================== .. automodule:: pyface.workbench.window_event - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================= .. automodule:: pyface.workbench.workbench - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench_window_layout.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench_window_layout.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench_window_layout.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench_window_layout.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================= .. automodule:: pyface.workbench.workbench_window_layout - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench_window_memento.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench_window_memento.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench_window_memento.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench_window_memento.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ================================================== .. automodule:: pyface.workbench.workbench_window_memento - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench_window.rst python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench_window.rst --- python-pyface-6.1.2/docs/source/api/pyface.workbench.workbench_window.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.workbench.workbench_window.rst 2022-02-02 10:01:06.000000000 +0000 @@ -2,6 +2,6 @@ ========================================= .. automodule:: pyface.workbench.workbench_window - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/api/pyface.xrc_dialog.rst python-pyface-7.4.0/docs/source/api/pyface.xrc_dialog.rst --- python-pyface-6.1.2/docs/source/api/pyface.xrc_dialog.rst 2019-05-08 14:16:24.000000000 +0000 +++ python-pyface-7.4.0/docs/source/api/pyface.xrc_dialog.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -pyface.xrc\_dialog module -========================= - -.. automodule:: pyface.xrc_dialog - :members: - :undoc-members: - :show-inheritance: diff -Nru python-pyface-6.1.2/docs/source/applications.rst python-pyface-7.4.0/docs/source/applications.rst --- python-pyface-6.1.2/docs/source/applications.rst 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/applications.rst 2022-02-01 12:21:15.000000000 +0000 @@ -74,7 +74,7 @@ location = Str("world") def _run(self): - super(HelloApplication, self)._run() + super()._run() print("Hello "+self.location) def main(): @@ -199,7 +199,7 @@ name='&Help', ) ) - return window + return window def main(): app = GUIApplication( diff -Nru python-pyface-6.1.2/docs/source/conf.py python-pyface-7.4.0/docs/source/conf.py --- python-pyface-6.1.2/docs/source/conf.py 2019-05-03 13:53:35.000000000 +0000 +++ python-pyface-7.4.0/docs/source/conf.py 2022-02-01 12:21:15.000000000 +0000 @@ -11,13 +11,9 @@ # All configuration values have a default value; values that are commented out # serve to show the default value. -import sys, os - -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -#sys.path.append(os.path.abspath('some/directory')) -sys.path.insert(0, os.path.abspath('./sphinxext')) +import os +import runpy +import sys # General configuration # --------------------- @@ -27,6 +23,9 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', + 'sphinx.ext.intersphinx', + # Link to code in sphinx generated API docs + "sphinx.ext.viewcode", 'traits.util.trait_documenter' ] @@ -41,14 +40,14 @@ # General substitutions. project = 'pyface' -copyright = '2008-2016, Enthought' +copyright = '2008-2022, Enthought' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. -d = {} -with open(os.path.join('..', '..', 'pyface', '_version.py')) as fp: - exec (fp.read(), d) -version = release = d['full_version'] +version_py = os.path.join('..', '..', 'pyface', '_version.py') +version_content = runpy.run_path(version_py) +version = ".".join(version_content["version"].split(".", 2)[:2]) +release = version # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -163,14 +162,6 @@ html_favicon = "et.png" html_style = 'default.css' -# Useful aliases to avoid repeating long URLs. -extlinks = { - 'github-examples': ( - 'https://github.com/enthought/pyface/tree/master/examples/%s', - 'github-examples' - ) -} - # Options for LaTeX output # ------------------------ @@ -229,3 +220,18 @@ 'IPython.frontend.wx', 'IPython.frontend.wx.wx_frontend', ] + + +def autodoc_skip_member(app, what, name, obj, skip, options): + # Skip load_tests + return skip or name == "load_tests" + + +def setup(app): + app.connect('autodoc-skip-member', autodoc_skip_member) + +intersphinx_mapping = { + "traits": ("http://docs.enthought.com/traits", None), + "traitsui": ("http://docs.enthought.com/traitsui", None), + "python": ("https://docs.python.org/3", None), +} diff -Nru python-pyface-6.1.2/docs/source/data_view.rst python-pyface-7.4.0/docs/source/data_view.rst --- python-pyface-6.1.2/docs/source/data_view.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/data_view.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,430 @@ + +.. _data-view: + +Pyface DataViews +================= + +The Pyface DataView API allows visualization of hierarchical and +non-hierarchical tabular data. + +.. note:: + As of Pyface 7.1.0, the public API for DataView is provisional and may + change in the future minor releases through until Pyface 8.0 + +.. See enthought/pyface#756 for removing the note. + +Indexing +-------- + +The DataView API has a consistent way of indexing that uses tuples of integers +to represent the rows and columns, as illustrated below: + +.. figure:: images/data_view_indices.png + :scale: 50 + :alt: an illustration of data view indices + + How DataView Indexing Works. + +A row index corresponds to a list of integer indexes at each level of the +hierarchy, so the empty tuple ``()`` represents the root of the hierarchy, +the tuples ``(0,)`` and ``(1,)`` give the two child rows of the root, while +``(0, 1)`` is the second child row of the first child of the root, and so on. + +Column indices follow a similar pattern, but only have the root and one level +of child indices. + +When interpreting these values, the root row ``()`` corresponds to the +*column* headers, the root column ``()`` corresponds to the *row* headers. +The root row and column indices together refer to the cell in the top-left +corner. + +Selections +~~~~~~~~~~ + +Implementers of the |IDataViewWidget| interface provide a |selection| trait +that holds a list of tuples of selected row and column index values. This +trait is settable, so changes made to the trait are reflected in the selection +in the view. + +The |selection_type| trait describes what gets selected when a user clicks +on a cell. It defaults to ``row``, which selects entire rows with one click, +but implementations may optionally support ``item`` and ``column`` selection +as well. + +In ``row`` selection type, the column values are all equal ``()`` (in other +words, the indices of the appropriate row header), and users setting the +values should adhere to that expectation. + +.. figure:: images/row_selection_type.png + :scale: 50 + :alt: an illustration of row selection type + + Row selection type. + + This corresponds to the |selection| being set equal to + ``[((0,), ()), ((1, 0), ()), ((1, 2), ())]``. + +The ``column`` selection type only selects the column values that are children +of a particular parent row, and so the row provided is that parent row. Code +which sets the value of the selection should adhere to that expectation. + +.. figure:: images/column_selection_type.png + :scale: 50 + :alt: an illustration of column selection type + + Column selection type. + + This corresponds to the |selection| being set equal to + ``[((), (2,)), ((0,), (0,)), ((1,), (3,))]``. + +The ``item`` selection type potentially allows any index, specified by both, +row and column indices. This can include row and column headers provided that +the view supports selecting them (which is likely dependent on the underlying +toolkit and platform's capabilities); in these cases the selected values are +just the values in the header cells, not the entire row or column. + +The |selection_mode| trait describes the behaviour of selections as the user +interacts with them. It defaults to ``extended``, which allows the user to +extend the selection by shift-clicking or other similar platform-dependent +interactions, but can also take the value ``single``, which restricts the +user to at most one selected thing. + +A change to either the |selection_type| or the |selection_mode| results in the +|selection| be cleared. + +Note: with the current implementations, the |selection| list should not be +mutated, rather the entire list should be replaced on every change. This +restriction may be relaxed in the future. + + +Drag and Drop +------------- + +The |IDataViewWidget| interface provides hooks to support dragging the +selected values out of the table, or dropping objects onto the data view. +To provide cross-platform and cross-toolkit compatibility, drag and drop +operations require the data that is being exported or imported to be +converted to or from a bytestring in some MIME type. + +The DataView infrastructure provides a |DataFormat| named tuple to +encapsulate the process of converting different data objects to bytes. +For string objects this conversion might be as simple as encoding the +text as UTF-8 and declaring it to be a ``text/plain`` MIME type, but for +more complex structures there is serialization and deserialization which +needs to occur. The |DataFormat| objects are expected to provide the +mimetype of the data, a function to serialize an object, and a function +to deserialize bytes. + +In practice the sorts of objects being dragged and dropped, can be +classified as simple scalar values (such as might occur when the selection +is a single item), 1D collections of values (such as might occur when +multiple items are selected, or a single row or column is selected), +or 2D collections of values (such as might occur for extended row or +column selections). + +The DataView api provides a standard data formats for plain text, CSV, +and .npy format for scalar, 1D and 2D exports; HTML and JSON formats +for scalar values, as well as standard serializers and deserializers +for users to create build their own |DataFormat| instances if the defaults +do not match the needs. + +Dragging +~~~~~~~~ + +To allow dragging the selection, the |exporters| trait should hold a list +of |AbstractDataExporter| instances. This class provides methods to access +the values to be exported from the selected indices, as well as a reference +to a |DataFormat| that will perform the actual serialization and provides +the MIME type. + +In practice, users will usually use a standard data exporter, such as the +|ItemExporter| or |RowExporter|. Some care should be taken that +the data exporter provides data in the shape that the |DataFormat| expects. +For example, the |ItemExporter| works best when paired with scalar data +formats. In many cases all that is needed to enable dragging data from a +DataViewWidget is to configure it appropriately: + +.. code-block:: python + + control = DataViewWidget( + ..., + selection_mode='extended', + exporters=[ + RowExporter(format=table_format), + RowExporter(format=csv_format), + ], + ... + ) + +When multiple exporters are provided, _all_ of the supported formats are +exported as part of the drag operation, and it is up to the target program +to decide which of the supplied formats it can best handle, if any. + +Dropping +~~~~~~~~ + +The |IDataViewWidget| supports dropping of objects via the |IDropHandler| +interface supported by other widgets. Developers using DataViews can +handle dropped data by providing a list of |IDropHandler| instances which +tell the underlying code whether the objects being dropped can be dropped, +and if so, how to handle the drop operation. + +For example, to handle files being dropped onto the DataView, a DataView could +use the generic |FileDropHandler| class, coupled with a callback to load the +data from the dropped file. + +.. code-block:: python + + control = DataViewWidget( + ..., + drop_handlers=[ + FileDropHandler( + extensions=['.csv', '.tsv', '.npy'], + open_file=self.load_data, + ) + ], + ... + ) + +When multiple drop handlers are supplied, the first one which says it can +handle the dropped objects is the one which is used. + +There are currently no specific drop handlers for supporting dragging +data within the table, but this can be supported by custom drop handlers +that use toolkit code to interact with the underlying toolkit objects. + + +Index Managers +-------------- + +These indices need to be converted to and from whatever system the backend +toolkit uses for indexing and tracking rows. This conversion is handled +by an |AbstractIndexManager| instance. Pyface provides two of these which +efficiently handle the two common cases: |TupleIndexManager| is designed to +handle general hierarchical models, but needs to cache mementos for all rows +with children (and on Wx, for all rows); the |IntIndexManager| can only handle +non-hierarchical tables, but does so without needing any additional memory +allocation. + +Unless you are creating a toolkit model or widget that uses the DataView +infrastructure it is sufficient to simply know to use the |IntIndexManager| +when you know that the data will always be a flat table, and |TupleIndexManager| +otherwise. + + +Data Models +----------- + +Data to be viewed needs to be exposed to the DataView infrastructure by +creating a data model for it. This is a class that implements the +interface of |AbstractDataModel| to display values from a dictionary. + +.. figure:: images/dict_data_model.png + :scale: 50 + :alt: an illustration of the DictDataModel + + The DictDataModel example. + +The basic traits for the model might look like this: + +.. literalinclude:: examples/dict_data_model.py + :start-at: class DictDataModel + :end-at: index_manager = + +The base |AbstractDataModel| class requires you to provide an index manager +so we use an |IntIndexManager| because the data is non-hierarchical for this +model. + +Data Structure +~~~~~~~~~~~~~~ + +The |get_column_count| method needs to be implemented to tell the toolkit +how many columns are in the data model. For the dict model, keys are +displayed in the row headers, so there is just one column displaying the +value: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def get_column_count + :end-at: return + +We can signal to the toolkit that certain rows can never have children +via the |can_have_children| method. The dict data model is +non-hierarchical, so the root has children but no other rows will ever +have children: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def can_have_children + :end-at: return + +We need to tell the toolkit how many child rows a particular row has, +which is done via the |get_row_count| method. In this example, only the +root has children, and the number of child rows of the root is the length +of the dictionary: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def get_row_count + :end-at: return 0 + +Data Values +~~~~~~~~~~~ + +The |get_value| method is used to return the raw value for each location. +To get the values of the dict data model, we need to determine from the row +and column index whether or not the cell is a column header and whether +it corresponds to the keys or the values. The code looks like this: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def get_value + :end-at: return value + +Conversion of values into data channels is done by providing a value type +for each cell that implements the |AbstractValueType| interface. The +|get_value_type| method is expected to provide an appropriate data +type for each item in the table. For this data model we have three value +types: the column headers, the keys and the values. + +.. literalinclude:: examples/dict_data_model.py + :start-at: #: The header data + :lines: 1-8 + +The default values of these traits are defined to be |TextValue| instances. +Users of the model can provide different value types when instantiating, +for example if the values are known to all be integers then |IntValue| +could be used instead for the ``value_type`` trait:: + + model = DictDataModel(value_type=IntValue()) + +The |get_value_type| method uses the indices to select the appropriate +value types: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def get_value_type + :end-at: return self.value_type + +The |AbstractValueType| interface provides getters (and in some cases setters) +for various data channels the most obvious of these is the text to display +in an item, but channels allow checked state, image, color and tooltips +to also be associated with a value. How (or even if) these values are +displayed or used is up to the implementation of the |IDataViewWidget|. + +As noted above, the DataView API provides a number of pre-definited value +type implementations that cover common cases, but where they do not meet the +needs of a particular design, developers should create their own +implementations with the desired properties. + +Invalid Values +~~~~~~~~~~~~~~ + +If no valid value can be generated for some *expected* reason, value +generation code can raise a |DataViewGetError| exception. This error +will be handled and silently ignored by the DataView code, and no value +will be displayed. Any other errors raised by value generation are +assumed to be unexpected and will be logged and re-raised, which is +likely to cause an application crash. + +Handling Updates +~~~~~~~~~~~~~~~~ + +The |AbstractDataModel| class expects that when the data changes, one of +two trait Events are fired. If a value is changed, or the value type is +updated, but the number of rows and columns is unaffected, then the +``values_changed`` trait should be fired with a tuple:: + + (start_row_index, start_column_index, end_row_index, end_column_index) + +If a major change has occurred, or if the size, shape or layout of the data +has changed, then the ``structure_changed`` event should be fired with a +simple ``True`` value. + +While it is possible that a data model could require users of the model to +manually fire these events (and for some opaque, non-traits data structures, +this may be necessary), where possible it makes sense to use trait observers +to automatically fire these events when a change occurs. + +For example, we want to listen for changes in the dictionary and its items. +It is simplest in this case to just indicate that the entire model needs +updating by firing the ``structure_changed`` event [#]_: + +.. literalinclude:: examples/dict_data_model.py + :start-at: @observe('data.items') + :end-at: self.structure_changed + +Changes to the value types also should fire update events, but usually +these are simply changes to the data, rather than changes to the structure +of the table. All value types have an updated event which is fired when +any state of the type changes. We can observe these, compute which +indices are affected, and fire the appropriate event. + +.. literalinclude:: examples/dict_data_model.py + :start-at: @observe('header_value_type.updated') + :lines: 1-11 + +Editing Values +~~~~~~~~~~~~~~ + +A model can flag values as being modifiable by implementing the +|can_set_value| function. The default implementation simply returns +``False`` for all items, but subclasses can override this to permit +modification of the values. For example, to allow modification of the +values of the dictionary, we could write: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def can_set_value + :end-at: return + +A corresponding |set_value| method is needed to actually perform the changes +to the underlying values. If for some reason it is impossible to set the +value (eg. an invalid value is supplied, or |set_value| is called with an +inappropriate row or column value, then a |DataViewSetError| should be +raised: + +.. literalinclude:: examples/dict_data_model.py + :start-at: def set_value + :end-at: raise + +Even though a data value may be modifiable at the data model level, the +value types also have the ability to control whether or not the value is +editable. For example, subclasses of |EditableValue|, such as |TextValue| +and |IntValue| have an ``is_editable`` trait that controls whether the +value should be editable in the view (presuming that the underlying value +can be set). Other value types can simply prevent editing by ensuring that +the |has_editor_value| method returns ``False``. + +.. rubric:: Footnotes + +.. [#] A more sophisticated implementation might try to work out + whether the total number of items has changed, and if not, the + location of the first and last changes in at least some of the + change events, and then fire ``values_changed``. For simplicty + we don't try to do that in this example. + + +.. |AbstractIndexManager| replace:: :py:class:`~pyface.data_view.index_manager.AbstractIndexManager` +.. |AbstractDataModel| replace:: :py:class:`~pyface.data_view.abstract_data_model.AbstractDataModel` +.. |AbstractDataExporter| replace:: :py:class:`~pyface.data_view.abstract_data_exporter.AbstractDataExporter` +.. |AbstractValueType| replace:: :py:class:`~pyface.data_view.abstract_value_type.AbstractValueType` +.. |DataFormat| replace:: :py:class:`~pyface.data_view.i_data_wrapper.DataFormat` +.. |DataViewGetError| replace:: :py:class:`~pyface.data_view.data_view_errors.DataViewGetError` +.. |DataViewSetError| replace:: :py:class:`~pyface.data_view.data_view_errors.DataViewSetError` +.. |EditableValue| replace:: :py:class:`~pyface.data_view.value_types.editable_value.EditableValue` +.. |FileDropHandler| replace:: :py:class:`~pyface.drop_handler.FileDropHandler` +.. |IDataViewWidget| replace:: :py:class:`~pyface.data_view.i_data_view_widget.IDataViewWidget` +.. |IDropHandler| replace:: :py:class:`~pyface.i_drop_handler.IDropHandler` +.. |IntIndexManager| replace:: :py:class:`~pyface.data_view.index_manager.IntIndexManager` +.. |IntValue| replace:: :py:class:`~pyface.data_view.value_types.numeric_value.IntValue` +.. |ItemExporter| replace:: :py:class:`~pyface.data_view.exporters.item_exporter.ItemExporter` +.. |RowExporter| replace:: :py:class:`~pyface.data_view.exporters.row_exporter.RowExporter` +.. |TextValue| replace:: :py:class:`~pyface.data_view.value_types.text_value.TextValue` +.. |TupleIndexManager| replace:: :py:class:`~pyface.data_view.index_manager.TupleIndexManager` +.. |can_have_children| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.can_have_children` +.. |can_set_value| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.can_set_value` +.. |get_column_count| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.get_column_count` +.. |get_row_count| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.get_row_count` +.. |get_value| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.get_value` +.. |get_value_type| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.get_value` +.. |has_editor_value| replace:: :py:meth:`~pyface.data_view.abstract_value_type.AbstractValueType.has_editor_value` +.. |exporters| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.exporters` +.. |selection| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.selection` +.. |selection_mode| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.selection_mode` +.. |selection_type| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.selection_type` +.. |set_value| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.set_value` diff -Nru python-pyface-6.1.2/docs/source/examples/data_model_indices.py python-pyface-7.4.0/docs/source/examples/data_model_indices.py --- python-pyface-6.1.2/docs/source/examples/data_model_indices.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/examples/data_model_indices.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,76 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Instance, Int, List + +from pyface.data_view.api import AbstractDataModel, TupleIndexManager + + +class IndexDataModel(AbstractDataModel): + """ A data model that displays the indices of the cell. """ + + index_manager = Instance(TupleIndexManager, ()) + + shape = List(Int, [2, 3, 4]) + + def get_column_count(self): + return self.shape[-1] + + def can_have_children(self, row): + return len(row) < len(self.shape) - 1 + + def get_row_count(self, row): + if len(row) == len(self.shape) - 1: + return 0 + else: + return self.shape[len(row)] + + def get_value(self, row, column): + return "{} {}".format(row, column) + + def get_value_type(self, row, column): + return TextValue(is_editable=False) + + +if __name__ == '__main__': + from pyface.api import ApplicationWindow, GUI + from pyface.data_view.i_data_view_widget import IDataViewWidget + from pyface.data_view.data_view_widget import DataViewWidget + from pyface.data_view.value_types.api import TextValue + + class MainWindow(ApplicationWindow): + """ The main application window. """ + + data_view = Instance(IDataViewWidget) + + def _create_contents(self, parent): + """ Creates the left hand side or top depending on the style. """ + + self.data_view = DataViewWidget( + parent=parent, + data_model=IndexDataModel(), + ) + self.data_view._create() + return self.data_view.control + + def destroy(self): + self.data_view.destroy() + super().destroy() + + # Create the GUI (this does NOT start the GUI event loop). + gui = GUI() + + # Create and open the main window. + window = MainWindow() + window.open() + window.data_view.observe(print, "selection") + + # Start the GUI event loop! + gui.start_event_loop() diff -Nru python-pyface-6.1.2/docs/source/examples/dict_data_model.py python-pyface-7.4.0/docs/source/examples/dict_data_model.py --- python-pyface-6.1.2/docs/source/examples/dict_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/examples/dict_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,152 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Dict, Instance, Str, observe + +from pyface.data_view.api import ( + AbstractDataModel, AbstractValueType, DataViewSetError, IntIndexManager +) + + +class DictDataModel(AbstractDataModel): + """ A data model that provides data from a dictionary. """ + + #: The dictionary containing the data. + data = Dict() + + #: The index manager. Because the data is flat, we use the + #: IntIndexManager. + index_manager = Instance(IntIndexManager, ()) + + #: The text to display in the key column header. + keys_header = Str("Keys") + + #: The text to display in the values column header. + values_header = Str("Values") + + #: The header data channels. + header_value_type = Instance(AbstractValueType) + + #: The key column data channels. + key_value_type = Instance(AbstractValueType) + + #: The value column data channels. + value_type = Instance(AbstractValueType) + + def get_column_count(self): + return 1 + + def can_have_children(self, row): + return len(row) == 0 + + def get_row_count(self, row): + if len(row) == 0: + return len(self.data) + return 0 + + def get_value(self, row, column): + if len(row) == 0: + # this is a column header + if len(column) == 0: + # title of the row headers + return self.keys_header + else: + return self.values_header + else: + row_index = row[0] + key, value = list(self.data.items())[row_index] + if len(column) == 0: + # the is a row header, so get the key + return key + else: + return value + + def get_value_type(self, row, column): + if len(row) == 0: + return self.header_value_type + elif len(column) == 0: + return self.key_value_type + else: + return self.value_type + + def can_set_value(self, row, column): + return len(row) != 0 and len(column) != 0 + + def set_value(self, row, column, value): + if self.can_set_value(row, column): + row_index = row[0] + key = list(self.data)[row_index] + self.data[key] = value + else: + raise DataViewSetError() + + @observe('header_value_type.updated') + def header_values_updated(self, event): + self.values_changed = ([], [], [], [0]) + + @observe('key_value_type.updated') + def key_values_updated(self, event): + self.values_changed = ([0], [], [len(self.data) - 1], []) + + @observe('value_type.updated') + def values_updated(self, event): + self.values_changed = ([0], [0], [len(self.data) - 1], [0]) + + @observe('data.items') + def data_updated(self, event): + self.structure_changed = True + + def _header_value_type_default(self): + return TextValue(is_editable=False) + + def _key_value_type_default(self): + return TextValue(is_editable=False) + + def _value_type_default(self): + return TextValue(is_editable=False) + + +if __name__ == '__main__': + from pyface.api import ApplicationWindow, GUI + from pyface.data_view.i_data_view_widget import IDataViewWidget + from pyface.data_view.data_view_widget import DataViewWidget + from pyface.data_view.value_types.api import IntValue, TextValue + + class MainWindow(ApplicationWindow): + """ The main application window. """ + + data_view = Instance(IDataViewWidget) + + def _create_contents(self, parent): + """ Creates the left hand side or top depending on the style. """ + + self.data_view = DataViewWidget( + parent=parent, + data_model=DictDataModel( + data={'one': 1, 'two': 2, 'three': 3}, + value_type=IntValue(), + ), + ) + self.data_view._create() + return self.data_view.control + + def destroy(self): + self.data_view.destroy() + super().destroy() + + # Create the GUI (this does NOT start the GUI event loop). + gui = GUI() + + # Create and open the main window. + window = MainWindow() + window.open() + + # Start the GUI event loop! + gui.start_event_loop() diff -Nru python-pyface-6.1.2/docs/source/fields.rst python-pyface-7.4.0/docs/source/fields.rst --- python-pyface-6.1.2/docs/source/fields.rst 2019-05-03 11:52:40.000000000 +0000 +++ python-pyface-7.4.0/docs/source/fields.rst 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,5 @@ +.. _fields: + ====== Fields ====== @@ -13,11 +15,10 @@ field without needing to know anything about the underlying toolkit event signalling mechanisms. -All fields also provide traits for setting the -:py:attr:`~pyface.fields.i_field.IField.tooltip` and -:py:attr:`~pyface.fields.i_field.IField.context_menu` of the field. Tooltips -expect unicode text values, and context menus should be -:py:class:`~pyface.action.menu_manager.MenuManager` instances. +All fields also provide a trait for setting the +:py:attr:`~pyface.fields.i_field.IField.context_menu` of the field. Context +menus should be :py:class:`~pyface.action.menu_manager.MenuManager` +instances. TextField ========= @@ -54,10 +55,38 @@ ========== The :py:class:`~pyface.fields.i_combo_field.IComboField` interface has an arbitrary -:py:attr:`~pyface.fields.i_spin_field.IComboField.value` that must come from a list -of valid :py:attr:`~pyface.fields.i_spin_field.IComboField.values`. For non-text -values, a :py:attr:`~pyface.fields.i_spin_field.IComboField.formatter` function +:py:attr:`~pyface.fields.i_combo_field.IComboField.value` that must come from a list +of valid :py:attr:`~pyface.fields.i_combo_field.IComboField.values`. For non-text +values, a :py:attr:`~pyface.fields.i_combo_field.IComboField.formatter` function should be provided - this defaults to either :py:func:`str` (Python 3+) or :py:func:`unicode` (Python 2). +TimeField +========== +The :py:class:`~pyface.fields.i_time_field.ITimeField` interface has a +:py:class:`datetime.time` :py:attr:`~pyface.fields.i_time_field.ITimeField.value`. +This value defaults to the current time. + +ToggleField and Subclasses +========================== + +The :py:class:`~pyface.fields.i_toggle_field.IToggleField` interface holds a +boolean :py:attr:`~pyface.fields.i_toggle_field.IToggleField.value` that is +toggled between ``True`` and ``False`` by the widget. The interface is +implemented by several different concrete classes with different appearances +but similar behaviour: + +- :py:class:`~pyface.fields.toggle_field.CheckBoxField` +- :py:class:`~pyface.fields.toggle_field.RadioButtonField` +- :py:class:`~pyface.fields.toggle_field.ToggleButtonField` + +There is an abstract class :py:class:`~pyface.fields.toggle_field.ToggleField` +which implements much of the behaviour and is suitable for use by custom +implementations to toggling behaviour. + +All :py:class:`~pyface.fields.i_toggle_field.IToggleField` implementations +have can have label text set via the +:py:attr:`~pyface.fields.i_toggle_field.IToggleField.text` trait, and in the +Qt backend they can have an image for an +:py:attr:`~pyface.fields.i_toggle_field.IToggleField.icon`. Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/about_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/about_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/color_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/color_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/column_selection_type.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/column_selection_type.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/confirmation_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/confirmation_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/data_view_indices.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/data_view_indices.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/dict_data_model.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/dict_data_model.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/error_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/error_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/information_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/information_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/progress_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/progress_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/row_selection_type.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/row_selection_type.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/single_choice_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/single_choice_dialog.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/docs/source/images/warning_dialog.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/docs/source/images/warning_dialog.png differ diff -Nru python-pyface-6.1.2/docs/source/image_types.rst python-pyface-7.4.0/docs/source/image_types.rst --- python-pyface-6.1.2/docs/source/image_types.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/image_types.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,196 @@ +Image Types +=========== + +Pyface's image model presumes that there are three different types of +image objects provided by a toolkit: + +Images + The "image" image type is optimized for pixel + access and manipulation. For the Qt toolkit this is the + :class:`~pyface.qt.QtGui.QImage` class, while for the Wx toolkit it is + :class:`wx.Image`. + +Bitmap + The "bitmap" image type is optimized for rendering + to the screen. For the Qt toolkit this is the + :class:`~pyface.qt.QtGui.QPixmap` class, while for the Wx toolkit it is + :class:`wx.Bitmap`. + +Icon + The "icon" image type is really a collection of related images that + represent different states of a GUI, such as disabled, selected, + activated and so on. For the Qt toolkit this is the + :class:`~pyface.qt.QtGui.QIcon` class, while for the Wx toolkit it is + :class:`wx.Icon`. + +Additionally, there may be other sources of images, such as images stored in +numpy arrays of appropriate shape and dtype, PIL/Pillow images, and images +stored in files, both user-supplied and stored as resources with a Python +library. + +:class:`~pyface.i_image.IImage` Interface +----------------------------------------- + +To handle all of these different notions of an "image", Pyface has a base +interface :class:`~pyface.i_image.IImage` that exposes methods for creating +each toolkit image type from underlying image data. There are corresponding +concrete implementations of the interface for Numpy arrays and image resources. + +Additionally, there is an :class:`~pyface.ui_traits.Image` trait that is +designed to accept any :class:`~pyface.i_image.IImage` as a value, but to +also accept the name of a resource or file to load into an image. + +:class:`~pyface.array_image.ArrayImage` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This implementation of :class:`~pyface.i_image.IImage` wraps an NxMx3 or +NxMx4 numpy array of unsigned bytes which it treats as RGB or RGBA image +data. When converting to toolkit objects, the data is copied. + +:class:`~pyface.image_resource.ImageResource` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An image resource is an image stored as part of a Python package, either as +a file or inside a zip file or similar, and so guaranteed to be available +for use in an application for GUI components. Pyface provides the +:class:`~pyface.image_resource.ImageResource` class and associated resource +discovery machinery to support this use-case. + +Resources are specified to the :class:`~pyface.image_resource.ImageResource` +by filename (adding standard image file extensions if not present in the name). +The Pyface resource system searches for files matching the given name in these +places: + +- in "images" directories inside the current Python package +- in "images.zip" files inside the current Python package +- in directories given as explicit search paths + +If an image of a particular size is requested, the system will also look for +directories with names of the form ``images/{width}x{height}`` and will use +any matching image from these preferentially. + +The most common way to specify images for use in button icons or complex +TraitsUI table and tree data structures is by adding an "images" directory +next to the module using the image, for example:: + + my_package/ + my_module.py + images/ + my_image.png + +The image code like the following in my_module.py will work: + +.. code-block:: python + + from pyface.api import ImageResource + from pyface.actions.api import Action + + img_res = ImageResource("my_image") + action = Action(image="my_image") + +When using this approach, remember that image files will need to be added +to the ``package_data`` in ``setup.py`` or they will not be shipped alongside +the code. + +:mod:`~pyface.util.image_helpers` Module +---------------------------------------- + +Since there is a lot of shared functionality between the various +implementations of the :class:`~pyface.i_image.IImage` interface, the +:mod:`pyface.util.image_helpers` module provides a number of functions and +other objects to perform lower-level tasks, such as converting between toolkit +types. + +Implementers of new toolkits will likely want to write their own versions of +these, and writers of new concrete :class:`~pyface.i_image.IImage` +implementations may want to make use of them to simplify the implementation of +the interface. + +:class:`~pyface.image.image.ImageLibrary` +----------------------------------------- + +The :class:`~pyface.image_resource.ImageResource` system is built around +supplying image files which are local to the place where they are being used. +Sometimes, however, you want your images to be available throughout the +application. Pyface provides the :class:`~pyface.image.image.ImageLibrary` +to allow for this use case. + +The :class:`~pyface.image.image.ImageLibrary` is a global object that +holds a catalog of :class:`~pyface.image.image.ImageVolume` objects that in +turn contain images and associated metadata. Image volumes are either +directories or zipfiles containing images and metadata files. + +Accessing Images and Image Metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you give an :class:`~pyface.ui_traits.Image` an image name which +starts with ``@``, then it will interpret the name to be the id of an +image in the :class:`~pyface.image.image.ImageLibrary` of the form +``@:``. Alternatively, you can ask the image +library directly for an image resource that corresponds to the id via the +:meth:`~pyface.image.image.ImageLibrary.image_resource` method: + +.. code-block:: python + + from pyface.image.image import ImageLibrary + from pyface.actions.api import Action + + red_ball_image = ImageLibrary.image_resource("@icons:red_ball") + action = Action(image=red_ball_image) + +Each image has an :class:`~pyface.image.image.ImageInfo` metadata object +associated with it which can be obtained via the +:meth:`~pyface.image.image.ImageLibrary.image_info` method. A list of all +known image metadata can be obtained from the +:attr:`~pyface.image.image.ImageLibrary.images` trait. + +:class:`~pyface.image.image.ImageVolume` Instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Image volumes are represented by the :class:`~pyface.image.image.ImageVolume` +class. Image volumes may have multiple aliases in addition to their primary +name, and these aliases can be used as part of the id of an image. The +volume which stores an image can be found using the +:meth:`~pyface.image.image.ImageLibrary.find_volume` method. The +:attr:`~pyface.image.image.ImageLibrary.catalog` and +:attr:`~pyface.image.image.ImageLibrary.aliases` attributes are dictionaries +that map names to volume objects. + +By default the :class:`~pyface.image.image.ImageLibrary` is initialized +with: + +- an ``"application"`` volume which is assumed to be in a "library" directory + next to the application executable (ie. whatever is in ``sys.argv[0]`` + which is usually the main module's path); +- the ``std`` and ``icons`` volumes in ``pyface/image/library`` +- any path listed in the ``TRAITS_IMAGES`` environment variable which contains + a volume. + +Additional volumes can be added programatically by calling +:meth:`~pyface.image.image.ImageLibrary.add_volume` with the path of a zipfile +or directory. Alternatively :meth:`~pyface.image.image.ImageLibrary.add_path` +can be used to add volume by giving a name for the volume and a directory path +to use. If no path is provided in either case, the library will look for an +``images`` directory next to the current module. + +Just as with image libraries, the +:meth:`~pyface.image.image.ImageVolume.image_resource` method returns an +:class:`~pyface.image_resource.ImageResource` instance for the specified image +name. Additionally, the bytes of the actual image file are available through +:meth:`~pyface.image.image.ImageVolume.image_data`. + +Image volumes have a :attr:`~pyface.image.image.ImageVolume.info` trait that +holds a list of :class:`~pyface.image.image.ImageVolumeInfo` instances. These +hold metadata about groups of images, including copyright and licensing +information. The :attr:`~pyface.image.image.ImageVolume.category` and +:attr:`~pyface.image.image.ImageVolume.keywords` traits hold additional +information about the volume itself. + +Image volumes are designed to reflect the actual contents of the directories +or zipfile that they refer to. The +:meth:`~pyface.image.image.ImageVolume.update` method clears and reloads the +data from disk. The :meth:`~pyface.image.image.ImageVolume.save` method saves +any changes made by the user into the data file. + +Images stored in image volumes which are zipfiles are extracted to temporary +files as needed for actual use. diff -Nru python-pyface-6.1.2/docs/source/index.rst python-pyface-7.4.0/docs/source/index.rst --- python-pyface-6.1.2/docs/source/index.rst 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/docs/source/index.rst 2022-02-01 15:52:57.000000000 +0000 @@ -2,44 +2,37 @@ Welcome to the Pyface Documentation! ==================================== -Pyface contains a toolkit-independent GUI abstraction layers, used to support -the "visualization" features of the Traits package. Thus, you can write code -in the abstraction of the Pyface API and the selected toolkit and backend take -care of the details of displaying them. +Pyface contains toolkit-independent GUI abstraction layers, used to support +the TraitsUI package. Thus, you can write code in the abstraction of the +Pyface API and the selected toolkit and backend take care of the details of +displaying them. Pyface ====== Pyface enables programmers to interact with generic GUI objects, such as an "MDI Application Window", rather than with raw GUI widgets. (Pyface is named by -analogy to JFace in Java.) Traits uses Pyface to implement views and editors +analogy to JFace in Java.) TraitsUI uses Pyface to implement views and editors for displaying and editing Traits-based objects. Toolkit Backends ================ -Traits and Pyface define APIs that are independent of any GUI toolkit. However, -in order to actually produce user interfaces with them, you must install a -supported Python-based GUI toolkit and the appropriate toolkit-specific backend -project. Conversely, if you wish to use Traits without a UI, a "null" backend -is automatically used in the absence of a real backend. - -Currently, the supported GUI toolkits are - -* wxPython (>= 2.8, including experimental support for WxPython 3.0) -* PySide -* PyQt (Qt4 only, but Qt5 support is in development) - -While all toolkits funtion with Pyface, integration with wxPython is currently -more complete. Future development, however, will be more focused on -supporting Qt. - -The default toolkit if none is supplied is ``qt4``. This changed from ``wx`` in -Pyface 5.0. - -NOTE: Although the code in this library is BSD licensed, when the PyQt backend -is used the more restrictive terms of PyQt's GPL or proprietary licensing will -likely apply to your code. +TraitsUI and Pyface define APIs that are independent of any GUI toolkit. +However, in order to actually produce user interfaces with them, you must +install a supported Python-based GUI toolkit and the appropriate +toolkit-specific backend project. Conversely, a "null" backend is +automatically used in the absence of a real backend. + +Currently, the GUI toolkits are + +* PySide2 (stable) and PySide6 (experimental) +* PyQt5 (stable) and PyQt6 (in development) +* wxPython 4 (experimental) + +NOTE: Although the code in this library is BSD licensed, when a PyQt backend +is used, the more restrictive terms of PyQt's GPL or proprietary licensing +will likely apply to your code. Toolkit Backend Selection ------------------------- @@ -50,12 +43,12 @@ The supported values of **ETSConfig.toolkit** are: -* 'qt4': `PyQt `_, which provides Python - bindings for the `Qt `_ framework version 4. -* 'wx': `wxPython `_, which provides Python bindings +* 'qt4' or 'qt': PySide2, PySide6 or `PyQt `_, + which provide Python bindings for the `Qt `_ framework. +* 'wx': `wxPython 4 `_, which provides Python bindings for the `wxWidgets `_ toolkit. * 'null': A do-nothing toolkit, for situations where neither of the other - toolkits is installed, but Traits is needed for non-UI purposes. + toolkits is installed. The default behavior of Pyface is to search for available toolkit-specific packages in the order listed, and uses the first one it finds. The programmer or @@ -75,11 +68,16 @@ ======== .. toctree:: - :maxdepth: 2 + :maxdepth: 3 Overview Toolkits - Submodules + Widgets Pyface Applications + Pyface Trait Types + Images in Pyface + Standard Dialogs + Submodules + Testing API Documentation Change Log diff -Nru python-pyface-6.1.2/docs/source/overview.rst python-pyface-7.4.0/docs/source/overview.rst --- python-pyface-6.1.2/docs/source/overview.rst 2019-06-14 10:36:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/overview.rst 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,5 @@ +.. _overview: + ======== Overview ======== @@ -188,7 +190,7 @@ """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( @@ -229,7 +231,7 @@ if self.control: try: self._editor.save() - except IOError, e: + except IOError: # If you are trying to save to a file that doesn't exist, # open up a FileDialog with a 'save as' action. dlg = FileDialog( diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/AUTHORS.txt python-pyface-7.4.0/docs/source/sphinxext/refactordoc/AUTHORS.txt --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/AUTHORS.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/AUTHORS.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -Ioannis Tziakos is the main developer and maintainer of the refactor_doc -sphinx extention. - -Historical notes: ------------------ - -The refactor_doc extention started while working on the Enaml project -with Chris Colbert, Robert Kern, Corran Webster and David Wyde at -Enthought. - -Many people at Enthought have provided feedback and given suggestions and -fixes. diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/base_doc.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/base_doc.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/base_doc.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/base_doc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,355 +0,0 @@ -# -*- coding: UTF-8 -*- -#------------------------------------------------------------------------------ -# file: base_doc.py -# License: LICENSE.TXT -# Author: Ioannis Tziakos -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -import re - -from fields import Field -from line_functions import is_empty, get_indent, fix_backspace - -#------------------------------------------------------------------------------ -# Classes -#------------------------------------------------------------------------------ - -class BaseDoc(object): - """Base abstract docstring refactoring class. - - The class' main purpose is to parse the dosctring and find the - sections that need to be refactored. It also provides a number of - methods to help with the refactoring. Subclasses should provide - the methods responsible for refactoring the sections. - - Attributes - ---------- - docstring : list - A list of strings (lines) that holds docstrings - - index : int - The current zero-based line number of the docstring that is - proccessed. - - verbose : bool - When set the class prints a lot of info about the proccess - during runtime. - - headers : dict - The sections that the class refactors. Each entry in the - dictionary should have as key the name of the section in the - form that it appears in the docstrings. The value should be - the postfix of the method, in the subclasses, that is - responsible for refactoring (e.g. {'Methods': 'method'}). - - """ - - def __init__(self, lines, headers = None, verbose=False): - """ Initialize the class - - The method setups the class attributes and starts parsing the - docstring to find and refactor the sections. - - Arguments - --------- - lines : list of strings - The docstring to refactor - - headers : dict - The sections for which the class has custom refactor methods. - Each entry in the dictionary should have as key the name of - the section in the form that it appears in the docstrings. - The value should be the postfix of the method, in the - subclasses, that is responsible for refactoring (e.g. - {'Methods': 'method'}). - - verbose : bool - When set the class logs info about the proccess - during runtime. - - """ - try: - self._docstring = lines.splitlines() - except AttributeError: - self._docstring = lines - self.verbose = verbose - self.headers = {} if headers is None else headers - self.index = 0 - self.parse() - - def parse(self): - """ Parse the docstring. - - The docstring is parsed for sections. If a section is found then - the corresponding refactoring method is called. - - """ - self.index = 0 - self.seek_to_next_non_empty_line() - while not self.eol: - header = self.is_section() - if header: - self.remove_if_empty(self.index + 2) # Remove space after header - self._refactor(header) - else: - self.index += 1 - self.seek_to_next_non_empty_line() - - def _refactor(self, header): - """Call the heading refactor method. - - The name of the refctoring method is constructed using the form - _refactor_
. Where
is the value corresponding to - ``self.headers[header]``. If there is no custom method for the - section then the self._refactor_header() is called with the - found header name as input. - - """ - refactor_postfix = self.headers.get(header, 'header') - method_name = ''.join(('_refactor_', refactor_postfix)) - method = getattr(self, method_name) - method(header) - - def _refactor_header(self, header): - """ Refactor the header section using the rubric directive. - - The method has been tested and supports refactoring single word - headers, two word headers and headers that include a backslash - ''\''. - - Arguments - --------- - header : string - The header string to use with the rubric directive. - - """ - index = self.index - indent = get_indent(self.peek()) - self.remove_lines(index, 2) - descriptions = [] - header = fix_backspace(header) - descriptions += [indent + '.. rubric:: {0}'.format(header), ''] - self.insert_lines(descriptions, index) - self.index += len(descriptions) - return descriptions - - - def extract_fields(self, indent='', field_type=None): - """Extract the fields from the docstring - - Parse the fields in the description of a section into tuples of - name, type and description in a list of strings. The parsed lines - are also removed from original list. - - Arguments - --------- - indent : str, optional - the indent argument is used to make sure that only the lines - with the same indent are considered when checking for a - field header line. The value is used to define the field - checking function. - - field_check : function - Optional function to use for checking if the next line is a - field. The signature of the function is ``foo(line)`` and it - should return ``True`` if the line contains a valid field - The default function is checking for fields of the following - formats:: - - : - - or:: - - : - - Where the name has to be one word. - - Returns - ------- - parameters : list of tuples - list of parsed parameter tuples as returned from the - :meth:`~BaseDocstring.parse_field` method. - - """ - field_type = Field if (field_type is None) else field_type - is_field = field_type.is_field - fields = [] - while (not self.eol) and (is_field(self.peek(), indent) or - is_field(self.peek(1), indent)): - self.remove_if_empty(self.index) - field_block = self.get_next_block() - field = field_type.parse(field_block) - fields.append(field) - return fields - - def get_next_block(self): - """ Get the next field block from the docstring. - - The method reads the next block in the docstring. The first line - assumed to be the field header and the following lines to belong to - the description:: - -
- - - The end of the field is designated by a line with the same indent - as the field header or two empty lines are found in sequence. Thus, - there are two valid field layouts: - - 1. No lines between fields:: - - - - - - - 2. One line between fields:: - - - - - - - - """ - start = self.index - field_header = self.read() - indent = get_indent(field_header) + ' ' - field = [field_header] - while (not self.eol): - peek_0 = self.peek() - peek_1 = self.peek(1) - if (is_empty(peek_0) and (not peek_1.startswith(indent))) \ - or \ - ((not is_empty(peek_0)) and (not peek_0.startswith(indent))): - break - else: - line = self.read() - field.append(line.rstrip()) - - self.remove_lines(start, len(field)) - self.index = start - return field - - def is_section(self): - """Check if the line defines a section. - - """ - if self.eol: - return False - - header = self.peek() - line2 = self.peek(1) - if self.verbose: - print 'current line is: {0} at index {1}'.format(header, self.index) - - # check for underline type format - underline = re.match(r'\s*\S+\s*\Z', line2) - if underline is None: - return False - # is the nextline an rst underline? - striped_header = header.rstrip() - expected_underline1 = re.sub(r'[A-Za-z\\]|\b\s', '-', striped_header) - expected_underline2 = re.sub(r'[A-Za-z\\]|\b\s', '=', striped_header) - if ((underline.group().rstrip() == expected_underline1) or - (underline.group().rstrip() == expected_underline2)): - return header.strip() - else: - return False - - def insert_lines(self, lines, index): - """ Insert refactored lines - - Arguments - --------- - new_lines : list - The list of lines to insert - - index : int - Index to start the insertion - """ - docstring = self.docstring - for line in reversed(lines): - docstring.insert(index, line) - - def seek_to_next_non_empty_line(self): - """ Goto the next non_empty line - - """ - docstring = self.docstring - for line in docstring[self.index:]: - if not is_empty(line): - break - self.index += 1 - - - def get_next_paragraph(self): - """ Get the next paragraph designated by an empty line. - - """ - docstring = self.docstring - lines = [] - start = self.index - while (not self.eol) and (not is_empty(self.peek())): - line = self.read() - lines.append(line) - del docstring[start:self.index] - return lines - - def read(self): - """ Return the next line and advance the index. - - """ - index = self.index - line = self._docstring[index] - self.index += 1 - return line - - def remove_lines(self, index, count=1): - """ Removes the lines for the docstring - - """ - docstring = self.docstring - del docstring[index:(index + count)] - - def remove_if_empty(self, index=None): - """ Remove the line from the docstring if it is empty. - - """ - if is_empty(self.docstring[index]): - self.remove_lines(index) - - def peek(self, ahead=0): - """ Peek ahead a number of lines - - The function retrieves the line that is ahead of the current - index. If the index is at the end of the list then it returns an - empty string. - - Arguments - --------- - ahead : int - The number of lines to look ahead. - - - """ - position = self.index + ahead - try: - line = self.docstring[position] - except IndexError: - line = '' - return line - - @property - def eol(self): - return self.index >= len(self.docstring) - - @property - def docstring(self): - """ Get the docstring lines. - - """ - return self._docstring - diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/CHANGELOG.txt python-pyface-7.4.0/docs/source/sphinxext/refactordoc/CHANGELOG.txt --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/CHANGELOG.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/CHANGELOG.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -Change Log -========== - -Version 0.2dev (current) ------------------------- - -- Draft version of the documentation -- Removed depedancy to docscrape.py -- Refactor_doc is now a valid sphinx extention - -26/10/2011 - -Early Versions --------------- - -An early copy of the refactor_doc` can be found in the enaml documentation -source directory. The module is named ``enamldoc`` and uses the Reader -class that is in the docscrape.py file of the numpydoc package. \ No newline at end of file diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/class_doc.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/class_doc.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/class_doc.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/class_doc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -# -*- coding: UTF-8 -*- -#------------------------------------------------------------------------------ -# file: class_doc.py -# License: LICENSE.TXT -# Author: Ioannis Tziakos -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -from base_doc import BaseDoc -from line_functions import get_indent, replace_at, add_indent -from fields import (max_header_length, max_desc_length, - max_name_length, MethodField, AttributeField) - - -class ClassDoc(BaseDoc): - """Docstring refactoring for classes""" - - def __init__(self, lines, headers=None, verbose=False): - - if headers is None: - headers = { - 'Attributes': 'attributes', - 'Methods': 'methods', - 'See Also': 'header', - 'Abstract Methods': 'methods', - 'Notes':'notes' - } - - super(ClassDoc, self).__init__(lines, headers, verbose) - return - - def _refactor_attributes(self, header): - """Refactor the attributes section to sphinx friendly format""" - - if self.verbose: - print '{0} Section'.format(header) - - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - fields = self.extract_fields(indent, AttributeField) - - descriptions = [] - for field in fields: - descriptions += field.to_rst(len(indent)) - self.insert_lines(descriptions[:-1], index) - self.index += len(descriptions) - return - - def _refactor_methods(self, header): - """Refactor the methods section to sphinx friendly format. - - """ - if self.verbose: - print '{0} section'.format(header) - - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - method_fields = self.extract_fields(indent, MethodField) - - lines = [] - if len(method_fields) > 0 : - name_length = max_name_length(method_fields) - method_length = max_header_length(method_fields) - desc_length = max_desc_length(method_fields) - - first_column = len(indent) - second_column = first_column + method_length + name_length + 13 - first_column_str = '=' * (method_length + name_length + 12) - second_column_str = '=' * desc_length - - border = '{0}{1} {2}'.format(indent, - first_column_str, - second_column_str) - length = len(border) - empty = length * ' ' - headings = empty[:] - headings = replace_at('Methods', headings, first_column) - headings = replace_at('Description', headings, second_column) - lines.append(border) - lines.append(headings) - lines.append(border) - for field in method_fields: - lines += field.to_rst(length, first_column, second_column) - lines.append(border) - - lines = [line.rstrip() for line in lines] - self.insert_lines(lines, index) - self.index += len(lines) - return - - def _refactor_notes(self, header): - """Refactor the note section to use the rst ``.. note`` directive. - - """ - descriptions = [] - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - paragraph = self.get_next_paragraph() - descriptions.append(indent + '.. note::') - descriptions += add_indent(paragraph) - self.insert_lines(descriptions, index) - self.index += len(descriptions) - return descriptions diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/enaml_decl_doc.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/enaml_decl_doc.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/enaml_decl_doc.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/enaml_decl_doc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -# -*- coding: UTF-8 -*- -#------------------------------------------------------------------------------ -# file: class_doc.py -# License: LICENSE.TXT -# Author: Ioannis Tziakos -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -from base_doc import BaseDoc -from line_functions import get_indent, replace_at, add_indent, fix_backspace -from fields import AttributeField - - -class EnamlDeclDoc(BaseDoc): - """Docstring refactoring for classes""" - - def __init__(self, lines, headers=None, verbose=False): - - if headers is None: - headers = {'Input Attributes': 'attributes', - 'Synchronized Attributes': 'attributes', - 'Output Attributes': 'attributes', - 'Public Attributes': 'attributes'} - - super(EnamlDeclDoc, self).__init__(lines, headers, verbose) - return - - def _refactor_attributes(self, header): - """Refactor the attributes section to sphinx friendly format""" - - if self.verbose: - print '{0} Section'.format(header) - - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - fields = self.extract_fields(indent, AttributeField) - header = fix_backspace(header) - lines = [indent + ':{0}:'.format(header), ''] - for field in fields: - lines += field.to_rst(len(indent) + 4) - self.insert_lines(lines[:-1], index) - self.index += len(lines) - return - - def _refactor_notes(self, header): - """Refactor the note section to use the rst ``.. note`` directive. - - """ - descriptions = [] - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - paragraph = self.get_next_paragraph() - descriptions.append(indent + '.. note::') - descriptions += add_indent(paragraph) - self.insert_lines(descriptions, index) - self.index += len(descriptions) - return descriptions diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/fields.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/fields.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/fields.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/fields.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,285 +0,0 @@ -# -*- coding: UTF-8 -*- -#------------------------------------------------------------------------------ -# file: fields.py -# License: LICENSE.TXT -# Author: Ioannis Tziakos -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -import collections -import re - -from line_functions import add_indent, is_empty, remove_indent, replace_at - - -class Field(collections.namedtuple('Field', ('name','signature','desc'))): - """ A docstring field. - - The class is based on the nametuple class and represents the logic - to check, parse and refactor a docstring field. - - Attributes - ---------- - name : str - The name if the field, usssualy the name of a parameter of atrribute. - - signature : str - The signature of the field. Commonly is the class type of an argument - or the signature of a function. - - desc : str - The description of the field. Given the type of the field this is a - single paragraph or a block of rst source. - - """ - - @classmethod - def is_field(cls, line, indent=''): - """ Check if the line is a field header. - """ - regex = indent + r'\*?\*?\w+\s:(\s+|$)' - match = re.match(regex, line) - return match - - @classmethod - def parse(cls, lines): - """Parse a field definition for a set of lines. - - The field is assumed to be in one of the following formats:: - - : - - - or:: - - : - - - or:: - - - - - Arguments - --------- - lines : - docstring lines of the field without any empty lines before or - after. - - Returns - ------- - field : - Field or subclass of Field - - """ - header = lines[0].strip() - if ' :' in header: - arg_name, arg_type = re.split('\s\:\s?', header, maxsplit=1) - else: - arg_name, arg_type = header, '' - if len(lines) > 1: - lines = [line.rstrip() for line in lines] - return cls(arg_name.strip(), arg_type.strip(), lines[1:]) - else: - return cls(arg_name.strip(), arg_type.strip(), ['']) - - - def to_rst(self, indent=4): - """ Outputs field in rst as an itme in a definition list. - - Arguments - --------- - indent : int - The indent to use for the decription block. - - - Returns - ------- - lines : list - A list of string lines of formated rst. - - Example - ------- - - >>> Field('Ioannis', 'Ιωάννης', 'Is the greek guy.') - >>> print Field.to_rst() - Ioannis (Ιωάννης) - Is the greek guy. - - """ - lines = [] - header = '{0} ({1})'.format(self.name, self.signature) - lines.append(header) - lines += add_indent(self.desc, indent) - return lines - -class AttributeField(Field): - """ Field for the argument function docstrings """ - - def to_rst(self, indent=4): - """ Outputs field in rst using the ``:param:`` role. - - Arguments - --------- - indent : int - The indent to use for the decription block. - - Example - ------- - - >>> Field('indent', 'int', 'The indent to use for the decription block.') - >>> print Field.to_rst() - :param indent: The indent to use for the description block - :type indent: int - - """ - lines = [] - _type = self.signature - annotation = '{0} :annotation: = {1}' - type_str = '' if is_empty(_type) else annotation.format(indent * ' ', _type) - directive = '{0}.. attribute:: {1}' - lines += [directive.format(indent * ' ', self.name), type_str] - if type_str != '': - lines.append('') - lines += self.desc - lines.append('') - return lines - - -class ArgumentField(Field): - """ Field for the argument function docstrings """ - - def to_rst(self, indent=4): - """ Outputs field in rst using the ``:param:`` role. - - Arguments - --------- - indent : int - The indent to use for the decription block. - - Example - ------- - - >>> Field('indent', 'int', 'The indent to use for the decription block.') - >>> print Field.to_rst() - :param indent: The indent to use for the description block - :type indent: int - - """ - lines = [] - name = self.name.replace('*','\*') # Fix cases like *args and **kwargs - indent_str = ' ' * indent - param_str = '{0}:param {1}: {2}'.format(indent_str, name, self.desc[0].strip()) - type_str = '{0}:type {1}: {2}'.format(indent_str, name, self.signature) - lines.append(param_str) - lines += self.desc[1:] - if len(self.signature) > 0: - lines.append(type_str) - return lines - -class ListItemField(Field): - """ Field that in rst is formated as an item in the list ignoring any - field.type information. - - """ - - def to_rst(self, indent=4, prefix=''): - """ Outputs field in rst using as items in an list. - - Arguments - --------- - indent : int - The indent to use for the decription block. - - prefix : str - The prefix to use. For example if the item is part of a numbered - list then ``prefix='# '``. - - Example - ------- - - - Note - ---- - The field descrption is reformated into a line. - - """ - indent_str = ' ' * indent - rst_pattern = '{0}{1}**{2}**{3}' if is_empty(self.desc[0]) else \ - '{0}{1}**{2}** -- {3}' - description = '' if is_empty(self.desc[0]) else \ - ' '.join(remove_indent(self.desc)) - return [rst_pattern.format(indent_str, prefix, self.name, description)] - - -class ListItemWithTypeField(Field): - """ Field for the return section of the function docstrings """ - def to_rst(self, indent=4, prefix=''): - indent_str = ' ' * indent - _type = '' if self.signature == '' else '({0})'.format(self.signature) - rst_pattern = '{0}{1}**{2}** {3}{4}' if is_empty(self.desc[0]) else \ - '{0}{1}**{2}** {3} -- {4}' - description = '' if is_empty(self.desc[0]) else \ - ' '.join(remove_indent(self.desc)) - return [rst_pattern.format(indent_str, prefix, self.name, _type, description)] - - -class FunctionField(Field): - """ A field that represents a function """ - - @classmethod - def is_field(cls, line, indent=''): - regex = indent + r'\w+\(.*\)\s*' - match = re.match(regex, line) - return match - - def to_rst(self, length, first_column, second_column): - split_result = re.split('\((.*)\)', self.name) - method_name = split_result[0] - method_text = ':meth:`{0} <{1}>`'.format(self.name, method_name) - summary = ' '.join([line.strip() for line in self.desc]) - line = ' ' * length - line = replace_at(method_text, line, first_column) - line = replace_at(summary, line, second_column) - return [line] - -MethodField = FunctionField - -#------------------------------------------------------------------------------ -# Functions to work with fields -#------------------------------------------------------------------------------ - -def max_name_length(method_fields): - """ Find the max length of the function name in a list of method fields. - - Arguments - --------- - fields : list - The list of the parsed fields. - - """ - return max([field[0].find('(') for field in method_fields]) - -def max_header_length(fields): - """ Find the max length of the header in a list of fields. - - Arguments - --------- - fields : list - The list of the parsed fields. - - """ - return max([len(field[0]) for field in fields]) - -def max_desc_length(fields): - """ Find the max length of the description in a list of fields. - - Arguments - --------- - fields : list - The list of the parsed fields. - - """ - return max([len(' '.join([line.strip() for line in field[2]])) - for field in fields]) diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/function_doc.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/function_doc.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/function_doc.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/function_doc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -# -*- coding: UTF-8 -*- -#------------------------------------------------------------------------------ -# file: function_doc.py -# License: LICENSE.TXT -# Author: Ioannis Tziakos -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -from base_doc import BaseDoc -from line_functions import get_indent, add_indent -from fields import ArgumentField, ListItemWithTypeField, ListItemField - - -class FunctionDoc(BaseDoc): - """Docstring refactoring for functions""" - - def __init__(self, lines, headers=None, verbose=False): - - if headers is None: - headers = {'Returns': 'returns', 'Arguments': 'arguments', - 'Parameters': 'arguments', 'Raises': 'raises', - 'Yields': 'returns', 'Notes':'notes'} - - super(FunctionDoc, self).__init__(lines, headers, verbose) - return - - def _refactor_returns(self, header): - """Refactor the return section to sphinx friendly format. - - """ - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - fields = self.extract_fields(indent, field_type=ListItemWithTypeField) - lines = [indent + ':returns:'] - prefix = '' if len(fields) == 1 else '- ' - for field in fields: - lines += field.to_rst(len(indent) + 4, prefix) - self.insert_lines(lines, index) - self.index += len(lines) - return - - def _refactor_raises(self, header): - """Refactor the raises section to sphinx friendly format""" - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - fields = self.extract_fields(indent, field_type=ListItemField) - lines = [indent + ':raises:'] - prefix = '' if len(fields) == 1 else '- ' - for field in fields: - lines += field.to_rst(len(indent) + 4, prefix) - self.insert_lines(lines, index) - self.index += len(lines) - return - - def _refactor_arguments(self, header): - """Refactor the argument section to sphinx friendly format - """ - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - fields = self.extract_fields(indent, field_type=ArgumentField) - lines = [] - for field in fields: - lines += field.to_rst(len(indent)) - self.insert_lines(lines, index) - self.index += len(lines) - return - - def _refactor_notes(self, header): - """Refactor the argument section to sphinx friendly format. - - """ - if self.verbose: - print 'Refactoring Notes' - - descriptions = [] - index = self.index - self.remove_lines(index, 2) - indent = get_indent(self.peek()) - paragraph = self.get_next_paragraph() - descriptions.append(indent + '.. note::') - descriptions += add_indent(paragraph) - self.insert_lines(descriptions, index) - self.index += len(descriptions) - return descriptions - diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/__init__.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/__init__.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -#------------------------------------------------------------------------------ -# file: refactor_doc.py -# License: LICENSE.TXT -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -from function_doc import FunctionDoc -from class_doc import ClassDoc -from enaml_decl_doc import EnamlDeclDoc - -#------------------------------------------------------------------------------ -# Extension definition -#------------------------------------------------------------------------------ -def refactor_docstring(app, what, name, obj, options, lines): - - verbose = False - # if 'component.Component' in name: - # verbose = True - - if ('class' in what): - ClassDoc(lines, verbose=verbose) - elif ('function' in what) or ('method' in what): - FunctionDoc(lines, verbose=verbose) - elif ('enaml_decl' in what): - EnamlDeclDoc(lines, verbose=verbose) - -def setup(app): - app.setup_extension('sphinx.ext.autodoc') - app.connect('autodoc-process-docstring', refactor_docstring) diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/LICENSE.txt python-pyface-7.4.0/docs/source/sphinxext/refactordoc/LICENSE.txt --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -This software is OSI Certified Open Source Software. -OSI Certified is a certification mark of the Open Source Initiative. - -Copyright (c) 2006, Enthought, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Enthought, Inc. nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/line_functions.py python-pyface-7.4.0/docs/source/sphinxext/refactordoc/line_functions.py --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/line_functions.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/line_functions.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -# -*- coding: UTF-8 -*- -#------------------------------------------------------------------------------ -# file: line_functions.py -# License: LICENSE.TXT -# Author: Ioannis Tziakos -# -# Copyright (c) 2011, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -#!/usr/bin/env python -import re - -#------------------------------------------------------------------------------ -# Precompiled regexes -#------------------------------------------------------------------------------ -indent_regex = re.compile(r'\s+') - -#------------------------------------------------------------------------------ -# Functions to manage indention -#------------------------------------------------------------------------------ - -def add_indent(lines, indent=4): - """ Add spaces to indent a list of lines. - - Arguments - --------- - lines : list - The list of strings to indent. - - indent : int - The number of spaces to add. - - Returns - ------- - lines : list - The indented strings (lines). - - .. note:: Empty strings are not changed - - """ - indent_str = ' ' * indent - output = [] - for line in lines: - if is_empty(line): - output.append(line) - else: - output.append(indent_str + line) - return output - -def remove_indent(lines): - """ Remove all indentation from the lines. - - """ - return [line.lstrip() for line in lines] - -def get_indent(line): - """ Return the indent portion of the line. - - """ - indent = indent_regex.match(line) - if indent is None: - return '' - else: - return indent.group() - -#------------------------------------------------------------------------------ -# Functions to detect line type -#------------------------------------------------------------------------------ - -def is_empty(line): - return not line.strip() - -#------------------------------------------------------------------------------ -# Functions to adjust strings -#------------------------------------------------------------------------------ - -def fix_star(word): - return word.replace('*','\*') - -def fix_backspace(word): - return word.replace('\\', '\\\\') - -def replace_at(word, line, index): - """ Replace the text in-line. - - The text in line is replaced (not inserted) with the word. The - replacement starts at the provided index. The result is cliped to - the input length - - Arguments - --------- - word : str - The text to copy into the line. - - line : str - The line where the copy takes place. - - index : int - The index to start coping. - - Returns - ------- - result : str - line of text with the text replaced. - - """ - word_length = len(word) - result = line[:index] + word + line[(index + word_length):] - return result[:len(line)] - - diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/README.txt python-pyface-7.4.0/docs/source/sphinxext/refactordoc/README.txt --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/README.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/README.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -RefactorDoc: Docstring refactor shpinx extension -================================================ - -The RefactorDoc extension parses the function and class docstrings as -they are retrieved by the autodoc extension and refactors the section -blocks into sphinx friendly rst. The extension shares similarities with -alternatives (such as numpydoc) but aims at reflacting the original form -of the docstring. - -Key aims of RefactorDoc are: - - - Do not change the order of sections. - - Allow sphinx directives between (and inside) section blocks. - - Easier to debug (native support for debugging) and extend. - -Repository ----------- - -The RefactorDoc extension lives at Github. You can clone the repository -using:: - - git clone ... - -Installation ------------- - -Please copy the ``refactor_doc.py`` in the source directory -(or a sub_directory) of your documentation and update your ``conf.py`` -as follows: - - - Add the path where the extension exists in the python path:: - - sys.path.insert(0, os.path.abspath('/extension')) - - - Add refactor-doc to the extensions variable:: - - extensions = [..., - 'refactor_doc', - ..., - ] diff -Nru python-pyface-6.1.2/docs/source/sphinxext/refactordoc/TODO.txt python-pyface-7.4.0/docs/source/sphinxext/refactordoc/TODO.txt --- python-pyface-6.1.2/docs/source/sphinxext/refactordoc/TODO.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/sphinxext/refactordoc/TODO.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -Enhancements: - * Update documentation to the latest changes - * Allow directives to exist inside sections (done for arguments). - * Add error messages when formating is wrong. - * Clean up code. diff -Nru python-pyface-6.1.2/docs/source/standard_dialogs.rst python-pyface-7.4.0/docs/source/standard_dialogs.rst --- python-pyface-6.1.2/docs/source/standard_dialogs.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/standard_dialogs.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,219 @@ +.. _standard_dialogs: + +================ +Standard Dialogs +================ + +In addition to being able to create custom dialogs based on the |IDialog| +interface, Pyface exposes a number of standard toolkit and OS dialogs. + +About Dialog +============ + +.. figure:: images/about_dialog.png + :scale: 50 + :alt: an about dialog + + An About Dialog + +A dialog that displays a simple message about the application, along with a +logo and copyright information. This is designed to be suitable to be called +from an "About" menu item. + +Color Dialog +============ + +.. figure:: images/color_dialog.png + :scale: 50 + :alt: a color dialog on Mac OS + + A Color Dialog on Mac OS + +A dialog that takes a |Color| object and displays a standard OS color-picker +dialog with an option that allows the setting of transparency/alpha if needed. + +A convenience function |get_color| opens the dialog and returns a color value. + +Confirmation Dialog +=================== + +.. figure:: images/confirmation_dialog.png + :scale: 50 + :alt: a confirmation dialog + + A Confirmation Dialog + +A dialog that presents a message and allows the user to select a Yes/No +response, with the option to present a Cancel button as well. The default +button, as well as the labels for the buttons can be controlled. +The result value is one of the pyface constants of |CANCEL|, |YES| or +|NO|. + +A convenience function |confirm| provides a simple API for opening a dialog +and returning the result. + +Directory Dialog +================ + +A dialog that shows the standard OS file selection dialog for directories only, +as well as whether the dialog should follow "Open" or "Save As" conventions for +selecting a directory. Other options allow specifying a default path. + +Typical usage for a save dialog looks like this:: + + # display a directory dialog for opening a directory + dialog = DirectoryDialog(parent=None) + if dialog.open() == OK: + print(f"Open {dialog.path}") + + +File Dialog +=========== + +A dialog that shows the standard OS file selection dialog, with options to +filter by file type as well as whether the dialog should follow "Open" or +"Save As" conventions. Other options allow specifying a default directory and +file, and wildcards for filtering file type extensions. + +Typical usage for a save dialog looks like this:: + + # display a file dialog for saving a Python file + dialog = FileDialog(parent=None, action="save as", wildcard="*.py") + if dialog.open() == OK: + print(f"Save to {dialog.path}") + + +Font Dialog +=========== + +A dialog that takes a |Font| object and displays a standard OS font selection. +A convenience function |get_font| opens the dialog and returns a font value. + + +Message Dialog +============== + +.. figure:: images/error_dialog.png + :scale: 50 + :alt: an error message dialog + + An Error Message Dialog + +A dialog that shows the standard OS message dialog, where the user is only +presented with the ability to confirm the message. The dialog has a "severity" +trait which can take the values of ``"information"``, ``"warning"`` or +``"error"`` and displays an appropriate icon along with the message. + +There are convenience functions |information|, |warning| and |error| that +create a dialog of the appropriate severity level. + +Progress Dialog +=============== + +.. figure:: images/progress_dialog.png + :scale: 50 + :alt: a progress dialog + + A Progress Dialog + +This class displays a progress bar in a dialog, together with a message and +various additonal optional information, such as time estimates. The progress +dialog is designed to integrate with long-running computations that are using +the main thread, and make an effort to keep the UI at least minimally +responsive:: + + def create_thumbnails(paths, size=(128, 128)): + progress = ProgressDialog( + title="Creating Thumbnails...", + max=len(paths), + ) + progress.open() + + for i, path in enumerate(paths): + progress.message = f"Processing: {path}" + file, ext = os.path.splitext(path) + with Image.open(path) as im: + im.thumbnail(size) + im.save(file + "_thumbnail.png") + (cont, skip) = progress.update(i) + if not cont or skip: + break + + progress.update(len(paths)) + +It can also be used with Python's standard library |Executor| classes to take +advantage of potential parallelism:: + + def create_thumbnail(path, size): + file, ext = os.path.splitext(path) + with Image.open(path) as im: + im.thumbnail(size) + im.save(file + "_thumbnail.png") + return path + + def create_thumbnails(paths, size=(128, 128)): + executor = ThreadPoolExecutor() + progress = ProgressDialog( + title="Creating Thumbnails...", + max=len(paths), + ) + progress.open() + + for i, path in enumerate(map(create_thumbnail, paths)): + progress.message = f"Processing: {path}" + (cont, skip) = progress.update(i) + if not cont or skip: + break + + executor.shutdown(cancel_futures=True) + + progress.update(len(paths)) + +This is in contrast with the situation where operations are handled using a +"fire-and-forget" process where computations are dispatched in the background, +with any opportunity for updates coming via callbacks. Since these sorts of +jobs run in the background, a dialog (particularly a modal dialog), may not +provide the best UX. + +This dialog is well-suited to situations where there are many steps to +perform, each of which takes a small amount of time. If instead there is only +one step that can take a long time to perform, or if the total number of steps +isn't known, setting the min and max to 0 shows an indeterminate busy indicator +in the place of the bar. + +The progress dialog has the option to provide a cancel button that the user +can use to stop the underlying process. + +Single-Choice Dialog +==================== + +.. figure:: images/single_choice_dialog.png + :scale: 50 + :alt: a single-choice dialog + + A Single-Choice Dialog + +A dialog which presents a list-box with a set of choices for the user and +permits the selection of one of them, or to cancel. There is also a +|choose_one| convenience function which takes a list of options and either the +user's selection or ``None`` if nothing is selected. + + +.. |CANCEL| replace:: :py:obj:`~pyface.constants.CANCEL` +.. |Color| replace:: :py:class:`~pyface.color.Color` +.. |Executor| replace:: :py:class:`~concurrent.futures.Executor` +.. |Font| replace:: :py:class:`~pyface.font.Font` +.. |IDialog| replace:: :py:class:`~pyface.i_dialog.IDialog` +.. |NO| replace:: :py:obj:`~pyface.constants.NO` +.. |OK| replace:: :py:obj:`~pyface.constants.OK` +.. |YES| replace:: :py:obj:`~pyface.constants.YES` +.. |choose_one| replace:: :py:func:`~pyface.single_choice_dialog.choose_one` +.. |confirm| replace:: :py:func:`~pyface.confirmation_dialog.confirm` +.. |error| replace:: :py:func:`~pyface.message_dialog.error` +.. |get_color| replace:: :py:func:`~pyface.color_dialog.get_color` +.. |get_font| replace:: :py:func:`~pyface.font_dialog.get_font` +.. |information| replace:: :py:func:`~pyface.message_dialog.information` +.. |open| replace:: :py:meth:`~pyface.i_dialog.IDialog.open` +.. |warning| replace:: :py:func:`~pyface.message_dialog.warning` +.. |_create_buttons| replace:: :py:meth:`~pyface.i_dialog.IDialog._create_buttons` +.. |_create_dialog_area| replace:: :py:meth:`~pyface.i_dialog.IDialog._create_dialog_area` diff -Nru python-pyface-6.1.2/docs/source/submodules.rst python-pyface-7.4.0/docs/source/submodules.rst --- python-pyface-6.1.2/docs/source/submodules.rst 2019-05-03 11:52:40.000000000 +0000 +++ python-pyface-7.4.0/docs/source/submodules.rst 2022-02-01 12:21:15.000000000 +0000 @@ -5,5 +5,7 @@ .. toctree:: :maxdepth: 2 + Data View Fields Timers + Undo diff -Nru python-pyface-6.1.2/docs/source/testing.rst python-pyface-7.4.0/docs/source/testing.rst --- python-pyface-6.1.2/docs/source/testing.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/testing.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,249 @@ +.. _testing: + +=========== +GUI Testing +=========== + +GUI testing involves a slew of difficulties on top of what is normally +encountered when writing typical unit tests. This is a result of having to deal +with the underlying event loop that drives the GUI. Consider a scenario in +which you have a simple :class:`~traits.has_traits.HasTraits` class and you call +:meth:`~traits.has_traits.HasTraits.configure_traits` on it to start the +application. Typically python execution stops at this point and control is +passed to the underlying event loop. In a test context though, we need +direct access to that event loop. To simulate and test GUI interactions we need +to be able to post events to the event loop, process them manually, and then +make assertions about the desired behavior. Even further, the event loop is +global state. Therefore, great care needs to be taken in each test to pick up +after itself to avoid interactions with other tests. This is still necessary +even if the test fails. + +Pyface provides a few utilities that are useful in this process. Namely, +:class:`~pyface.util.GuiTestAssistant` and +:class:`~pyface.util.ModalDialogTester`. + + +GuiTestAssistant +================ + +.. note:: + + GuiTestAssistant is currently only available on Qt. + +:class:`GuiTestAssistant` is a mixin class intended to augment +:class:`python:unittest.TestCase`. In fact, it inherits from +:class:`~traits.testing.unittest_tools.UnittestTools` from Traits and thus +gives access to methods like +:meth:`~traits.testing.unittest_tools.UnittestTools.assertTraitChanges`. See the +`Traits Testing documentation `_ +for more. + +:class:`GuiTestAssistant` holds a reference to a :class:`pyface.gui.GUI` object +(for api details see the interface :class:`~pyface.i_gui.IGUI`) which is what +gives the low level access to the event loop. :class:`pyface.gui.GUI` provides methods such as +:meth:`~pyface.i_gui.IGUI.start_event_loop`, +:meth:`~pyface.i_gui.IGUI.stop_event_loop`, +:meth:`~pyface.i_gui.IGUI.process_events`, +:meth:`~pyface.i_gui.IGUI.invoke_later`, and +:meth:`~pyface.i_gui.IGUI.set_trait_later`. This is accessible via the +:attr:`gui` attribute on :class:`GuiTestAssistant`. + +What :class:`GuiTestAssistant` provides that is novel, is effectively better +control to ensure that your tests clean up after themselves. For example, +:class:`GuiTestAssistant` provides standard :meth:`setUp` and :meth:`tearDown` +methods which try to clean up existing UI state and empty the event loop even +if a test fails. In addition, the methods typically have timeouts so that the +test will fail rather than blocking forever in the case something has gone +wrong. Effectively, the class aims to remember to do the overhead to ensure +your tests don't cause trouble, and at the same time give you the low level +event loop access needed to write your GUI tests. + + +This class provides the following methods (some of them being context managers): + +- :meth:`event_loop` + + Context Manager + + Takes an integer ``repeat`` parameter and artificially replicates the event + loop by calling :meth:`sendPostedEvents` and :meth:`processEvents` ``repeat`` + number of times. + +- :meth:`event_loop_until_condition` + + Context Manager + + Runs the real Qt event loop until the provided condition evaluates to True. + +- :meth:`event_loop_until_traits_change` + + Context Manager + + Run the real application event loop until a change notification for all of + the specified traits is received. + +- :meth:`event_loop_with_timeout` + + Context Manager + + Helper context manager to send all posted events to the event queue + and wait for them to be processed. + + This differs from the `event_loop()` context manager in that it + starts the real event loop rather than emulating it with + ``QApplication.processEvents()`` + +- :meth:`assertTraitChangesInEventLoop` + + Context Manager + + Runs the real Qt event loop, collecting trait change events until + the provided condition evaluates to True. + +- :meth:`delete_widget` + + Context Manager + + Runs the real Qt event loop until the widget provided has been + deleted. + +- :meth:`find_qt_widget` + + Takes parameters ``start``, ``type_`` and ``test``. Recursively walks the Qt + widget tree from Qt widget ``start`` until it finds a widget of type ``type_`` + (a QWidget subclass) that satisfies the provided ``test`` method. + + Note: This method is known to be finicky / linked to sporadic seg faults. + The TraitsUI :class:`~traitsui.testing.tester.ui_tester.UITester` is often + an easier to use, safer alternative if working with a TraitsUI based + application. + +- :meth:`assertEventuallyTrueInGui` + + Assert that the given condition becomes true if we run the GUI + event loop for long enough. + + This assertion runs the real Qt event loop, polling the condition + and returning as soon as the condition becomes true. If the condition + does not become true within the given timeout, the assertion fails. + +.. warning:: + Some care needs to be taken with the various methods that run the event + loop while waiting for a condition function to return true, such as + :meth:`event_loop_until_condition` and :meth:`assertEventuallyTrueInGui`. + These work by running the real application event loop and polling for the + state of the condition being tested. If the condition being tested is + transient, it is possible that it may switch from False to True and back to + False in between polling checks, and so fail to detect that the condition + occurred. + + When writing tests that use these methods, you should be careful to test + for conditions that once True, remains True. + +For a very simple example consider this (slightly modified) test from pyface's +own test suite. + +:: + + import unittest + + from pyface.api import Window + from pyface.util.gui_test_assistant import GuiTestAssistant + + class TestWindow(unittest.TestCase, GuiTestAssistant): + def setUp(self): + GuiTestAssistant.setUp(self) + self.window = Window() + + def tearDown(self): + if self.window.control is not None: + with self.delete_widget(self.window.control): + self.window.destroy() + self.window = None + GuiTestAssistant.tearDown(self) + + def test_open_close(self): + # test that opening works as expected + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): + with self.event_loop(): + self.window.open() + + # test that closing works as expected with a different approach + with self.event_loop_until_traits_change( + self.window, "closing", "closed"): + self.window.close() + + +ModalDialogTester +================= + +.. note:: + + ModalDialogTester is currently only available on Qt. + +:class:`ModalDialogTester` is, as the name suggests, intended specifically for +use testing modal dialogs. Modal dialogs are dialogs which sit on top of the +main content of the application, and effectively demand interaction. The +rest of the UI is blocked until the dialog is addressed. These require special +care to test and :class:`GuiTestAssistant` doesen't provide this functionality. +When testing modal dialog related code the main recommendation for doing so is +try to avoid it. If you can, try testing the dialog in a non-modal fashion. Or, +if possible for your use case, use :mod:`python:unittest.mock` to patch the +class or its "open" method with a dummy implementation that returns a useful +result. If you absolutely do need to test the real modal dialog in a modal +fashion, :class:`ModalDialogTester` aims to help make this as easy as possible. + +To use it, instantiate a :class:`ModalDialogTester` instance, passing it a +function taking no arguments which when called opens the modal dialog. From +there you can call the :meth:`open_and_run` method on the tester object just +instantiated, and pass in a ``when_opened`` callable which will take the tester +object as its sole argument. This method first calls the function to open the +dialog and then subsequently the ``when_opened`` callable. In the body of the +``when_opened`` callable is where you define the interactions with the modal +dialog you want to be performed during the test. You can use the +:meth:`get_dialog_widget` method on the tester object (accesible since the +tester is passed as an argument to ``when_opened``) to get access to the UI for +the dialog. Then interactions can be performed using methods such as +:meth:`find_qt_widget`, :meth:`click_widget`, etc. Alternatively, if working +with a TraitsUI application, you could use the TraitsUI +:class:`~traitsui.testing.tester.ui_tester.UITester` to perform these interactions (see the +`TraitsUI Testing documentation `_). +If doing so, it is important to remember to set the :attr:`auto_process_events` +attribute on the :class:`~traitsui.testing.tester.ui_tester.UITester` to False. +This prevents :class:`~traitsui.testing.tester.ui_tester.UITester` and +:class:`ModalDialogTester` from both trying to drive the event loop +simultaneously, which can lead to very strange, difficult to diagnose, bugs. +Finally, you should ensure that your ``when_opened`` callable will close the +dialog. You don't want to leave the dialog open and blocking (there are +timeouts in place as a safety net, but neverthelesss). +:class:`ModalDialogTester` provides a method :meth:`close` for this purpose. +To verify the dailog was indeed opened once, you can run +``self.assertTrue(tester.dialog_was_opened)``. + +Additionally, :class:`ModalDialogTester` provides a context manager +:meth:`capture_error` to be used inside the event loop. When errors or failures +occur they could be missed by :mod:`python:unittest`, but this catches them. +These can then be checked with the :meth:`assert_no_errors_collected` method. + +For a very simple example consider this (slightly modified) test from pyface's +own test suite. + +:: + + import unittest + + from pyface.api import Dialog, OK + from pyface.util.modal_dialog_tester import ModalDialogTester + + class TestDialog(unittest.TestCase): + + def test_accept(self): + dialog = Dialog() + # test that accept works as expected + tester = ModalDialogTester(dialog.open) + tester.open_and_run(when_opened=lambda x: x.close(accept=True)) + + self.assertTrue(tester.dialog_was_opened) + self.assertEqual(tester.result, OK) + self.assertEqual(dialog.return_code, OK) diff -Nru python-pyface-6.1.2/docs/source/timer.rst python-pyface-7.4.0/docs/source/timer.rst --- python-pyface-6.1.2/docs/source/timer.rst 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/timer.rst 2022-02-01 12:21:15.000000000 +0000 @@ -103,7 +103,7 @@ gets performed). For example, a timer which repeats every 0.5 seconds and runs no more than 10 -times and for no longer than 10 seconds can be started like this: +times and for no longer than 10 seconds can be started like this - .. code-block:: python @@ -112,7 +112,7 @@ :py:class:`~pyface.timer.timer.PyfaceTimer` also provides two convenience class methods for creating and starting a timer in one line. The above example -could instead be written as:: +could instead be written as - .. code-block:: python @@ -121,7 +121,7 @@ For the common case of a "single-shot" timer that is only performed once, there is the :py:meth:`~pyface.timer.timer.PyfaceTimer.single_shot` class method that creates a timer that will be called once after the specified -interval:: +interval - .. code-block:: python @@ -165,16 +165,21 @@ Another common use case is that you want a Traits Event to be fired periodically or at some future time. This permits many possible listeners to be called from the same timer, and have them be turned on and turned off -dynamically, if desired. The :py:class:`~pyface.timer.timer.EventTimer` -provides this functionality via its -:py:class:`~pyface.timer.timer.EventTimer.timeout` event trait. +dynamically, if desired, via +`Traits' observe `_ +(note that observe listeners are required to take a single event argument). +The :py:class:`~pyface.timer.timer.EventTimer` provides this functionality via +its :py:class:`~pyface.timer.timer.EventTimer.timeout` event trait. .. code-block:: python from pyface.timer.api import EventTimer + def print_time(event): + print("The time is {}".format(datetime.datetime.now())) + timer = EventTimer(interval=0.5, expire=60) - timer.on_trait_change(print_time, 'timeout') + timer.observe(print_time, 'timeout') timer.start() diff -Nru python-pyface-6.1.2/docs/source/toolkits.rst python-pyface-7.4.0/docs/source/toolkits.rst --- python-pyface-6.1.2/docs/source/toolkits.rst 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/docs/source/toolkits.rst 2022-02-01 15:52:57.000000000 +0000 @@ -47,7 +47,7 @@ backends may use their own objects. The toolkit object for the toolkit that has been selected can be found as :py:obj:`pyface.toolkit.toolkit_object`. -This is a callable object which expects to be given the an identifier for the +This is a callable object which expects to be given an identifier for the widget in the form of a relative module name and the object name, separated by a ``':'``. This is most often used when creating new widget types for Pyface. The API module for the new widget class typically looks something like this:: @@ -63,7 +63,7 @@ :py:mod:`pyface.ui.wx.my_package.my_widget`. If no matching object is found, the toolkit will return a special -:py:class:`Undefined` class that will raise :py:exception:`NotImplementedError` +:py:class:`Undefined` class that will raise :py:exc:`NotImplementedError` when instantiated. The basic toolkit implementation provides two other features which may be of @@ -76,9 +76,9 @@ Toolkit Entrypoints =================== -Pyface uses the standard ``pkg_resources`` "entry point" system to allow other -libraries to contribute new toolkit implementations to Pyface. The toolkit -selection process discussed above looks for things contributed to the +Pyface uses the standard ``importlib_metadata`` "entry point" system to allow +other libraries to contribute new toolkit implementations to Pyface. The +toolkit selection process discussed above looks for things contributed to the ``pyface.toolkits`` entry point. These are specified in the ``setup.py`` of the third party library, something like this:: diff -Nru python-pyface-6.1.2/docs/source/traits.rst python-pyface-7.4.0/docs/source/traits.rst --- python-pyface-6.1.2/docs/source/traits.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/traits.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,71 @@ +.. _ui-trait-types: + +=========== +Trait Types +=========== + +Pyface defines a number of custom Trait types that represent quantities and +objects that are useful in the context of graphical user interfaces. + +Colors +====== + +When working with user interfaces, it is common to want to be able to specify +the color to use in part of the UI. Each toolkit usually has its own way of +representing colors, and so the ability to specify a color in a +toolkit-independent way that can be converted to a toolkit-specific +representation is important. This is particularly so when you want to allow +the user to specify a color. + +Pyface provides a |Color| class and a corresponding |PyfaceColor| trait-type +that allows this sort of representation. Internally, the |Color| class +stores colors as a tuple of red, green, blue and alpha values which range +from 0.0 through to 1.0, inclusive. Helper properties allow the user to +specify individual channel values, as well as specify colors in alternate +color spaces, such as HSV or HLS:: + + Color(rgba=(0.4, 0.2, 0.6, 0.8)) + Color(red=0.4, green=0.2, blue=0.6, alpha=0.8) + Color(hls=(0.2, 0.5, 0.8)) + +|Color| instances can also be created via the |Color.from_str| method +which allow specification of colors via CSS-style color strings, such as:: + + Color.from_str("aquamarine") + Color.from_str("#662244") + +All standard web colors are understood, as well as hexadecimal RGB(A) with +1, 2 or 4 hex digits per channel. + +|Color| instances are mutable, as their intended use is as values stored +in |PyfaceColor| trait classes which can be modified and listened to. The +|PyfaceColor| validator understands string descriptions of colors, and will +accept them as values when initializing or modifying the trait:: + + class Style(HasStrictTraits): + + color = PyfaceColor("#442266FF") + + @observe('color.rgba') + def color_changed(self, event): + print('The color has changed to {}'.format(self.color)) + + shape = Style(color='orange') + shape.color.blue = 0.8 + shape.color = "rebeccapurple" + +For interactions with the toolkit, the |Color.from_toolkit| and +|Color.to_toolkit| methods allow conversion to and from the appropriate +toolkit color objects, such as Qt's :py:class:`QColor` or +:py:class:`wx.Colour`. These are most likely to be needed by internal +Pyface functionality, and should not be needed by developers who are +building applications on top of Pyface. + +It is intended that this trait will eventually replace the ``Color`` +trait from TraitsUI. + +.. |Color| replace:: :py:class:`~pyface.color.Color` +.. |Color.from_str| replace:: :py:meth:`~pyface.color.Color.from_str` +.. |Color.from_toolkit| replace:: :py:meth:`~pyface.color.Color.from_toolkit` +.. |Color.to_toolkit| replace:: :py:meth:`~pyface.color.Color.to_toolkit` +.. |PyfaceColor| replace:: :py:class:`~pyface.ui_traits.PyfaceColor` diff -Nru python-pyface-6.1.2/docs/source/undo.rst python-pyface-7.4.0/docs/source/undo.rst --- python-pyface-6.1.2/docs/source/undo.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/undo.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,72 @@ +Undo Framework +============== + +The Undo Framework is a component of the Enthought Tool Suite that provides +developers with an API that implements the standard pattern for do/undo/redo +commands. + +The framework is completely configurable. Alternate implementations of all +major components can be provided if necessary. + + +Framework Concepts +------------------ + +The following are the concepts supported by the framework. + +- Command + + A command is an application defined operation that can be done (i.e. + executed), undone (i.e. reverted) and redone (i.e. repeated). + + A command operates on some data and maintains sufficient state to allow it to + revert or repeat a change to the data. + + Commands may be merged so that potentially long sequences of similar + commands (e.g. to add a character to some text) can be collapsed into a + single command (e.g. to add a word to some text). + +- Macro + + A macro is a sequence of commands that is treated as a single command when + being undone or redone. + +- Command Stack + + A command is done by pushing it onto a command stack. The last command can + be undone and redone by calling appropriate command stack methods. It is + also possible to move the stack's position to any point and the command stack + will ensure that commands are undone or redone as required. + + A command stack maintains a *clean* state which is updated as commands are + done and undone. It may be explicitly set, for example when the data being + manipulated by the commands is saved to disk. + + PyFace actions are provided as wrappers around command stack methods + to implement common menu items. + +- Undo Manager + + An undo manager is responsible for one or more command stacks and maintains + a reference to the currently active stack. It provides convenience undo and + redo methods that operate on the currently active stack. + + An undo manager ensures that each command execution is allocated a unique + sequence number, irrespective of which command stack it is pushed to. Using + this it is possible to synchronise multiple command stacks and restore them + to a particular point in time. + + An undo manager will generate an event whenever the clean state of the active + stack changes. This can be used to maintain some sort of GUI status + indicator to tell the user that their data has been modified since it was + last saved. + +Typically an application will have one undo manager and one undo stack for +each data type that can be edited. However this is not a requirement: how the +command stack's in particular are organised and linked (with the user +manager's sequence number) can need careful thought so as not to confuse the +user - particularly in a plugin based application that may have many editors. + +To support this typical usage the PyFace ``Workbench`` class has an +``undo_manager`` trait and the PyFace ``Editor`` class has a ``command_stack`` +trait. Both are lazy loaded so can be completely ignored if they are not used. diff -Nru python-pyface-6.1.2/docs/source/widgets.rst python-pyface-7.4.0/docs/source/widgets.rst --- python-pyface-6.1.2/docs/source/widgets.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/docs/source/widgets.rst 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,149 @@ +.. _widgets: + +======= +Widgets +======= + +.. py:currentmodule:: pyface + +A Widget in Pyface is an object wraps a control from the underlying toolkit +and provides standard traits and methods that abstract the differences +between the interfaces presented by the toolkit widgets. + +The public API for every widget is provided by the appropriate interface +class: :class:`~pyface.i_widget.IWidget` and its subclasses such as +:class:`~pyface.i_application_window.IApplicationWindow` and +:class:`~pyface.fields.i_text_field.ITextField`. + +The actual implementation is provided by the corresponding toolkit-dependent +:class:`~pyface.widget.Widget` subclass, but with shared functionality split +into separate mixins: :class:`~pyface.i_widget.MWidget` and so on, following +the general design described in the :ref:`overview` section. + +Lifecycle +========= + +When a :class:`~pyface.widget.Widget` instance is created only the traits +are initialized. The underlying toolkit widget is not created until the +widget's :meth:`~pyface.widget.Widget._create` method is called, which is +responsible for creating the toolkit object, configuring it, and connecting +handlers that react to changes at the toolkit level or on the traits, +keeping the state of the Python class synchronised with the state of the +underlying toolkit object. The :meth:`~pyface.widget.Widget._create` method +calls the :meth:`~pyface.widget.Widget._create_control` method to create the +actual toolkit control and configure it, and also calls the +:meth:`~pyface.widget.Widget._add_event_listeners` method to handle hooking +up the different types of change handlers. Some widget subclasses may do +additional things in their :meth:`~pyface.widget.Widget._create` methods, +as appropriate for their purpose. + +To destroy the toolkit object, the :meth:`~pyface.widget.Widget.destroy` +method is called which cleans up the code that listens for changes to the +object, and destroys the underlying toolkit control (and, for most toolkits, +any children). The :meth:`~pyface.widget.Widget.destroy` method +calls the :meth:`~pyface.widget.Widget._remove_event_listeners` method to +handle unhooking of change handlers. Some widget subclasses may do +additional things in their :meth:`~pyface.widget.Widget.destroy` methods. + +Basic Interface +=============== + +Every widget has a :attr:`~pyface.i_widget.IWidget.control` trait which +holds a reference to the toolkit control that is actually doing the work. +Additionally, there is a :attr:`~pyface.i_widget.IWidget.parent` trait +which references the toolkit control which is the parent of the widget's +control, however this may be ``None`` for top-level windows. + +Additionally, every widget has two states associated with it: it can be +visible or hidden, and it can be enabled or disabled. The visibile +state is accessible via the :attr:`~pyface.i_widget.IWidget.visible` +trait, or by calling :meth:`~pyface.i_widget.IWidget.show`. Similarly +the enabled state is accessible via the +:attr:`~pyface.i_widget.IWidget.enabled` trait, or by calling +:meth:`~pyface.i_widget.IWidget.enable`. + +Widgets also provide a trait :attr:`~pyface.i_widget.IWidget.tooltip` that +provides a string to display to the user when they hover the mouse over the +widget. If the string is empty then there will be no tooltip displayed. + +IWidget Subclasses +================== + +The key subclasses/subinterfaces of :class:`~pyface.i_widget.IWidget` include +the following. + +IWindow +------- + +The :class:`~pyface.i_window.IWindow` interface represents a top-level +window, and so provides additional traits such as +:attr:`~pyface.i_window.IWindow.size`, +:attr:`~pyface.i_window.IWindow.position` and +:attr:`~pyface.i_window.IWindow.title`, as well as events for the +window's lifecycle and user interactions. + +The :meth:`~pyface.i_window.IWindow.open` and +:meth:`~pyface.i_window.IWindow.close` methods provide the standard +way of bring up non-modal windows and closing them with the option of +a user veto. + +Additionally there are convenience methods that create message dialogs +correctly parented to the window. + +IDialog +------- + +The :class:`~pyface.i_dialog.IDialog` interface represents short-lived, +usually modal, dialog windows. Often these are standard system dialogs, +in which case the class is responsible for configuring and invoking them, +but it also includes subclasses which have custom content. + +For modal dialogs, :meth:`~pyface.i_dialog.IDialog.open` is blocking, +and returns the :attr:`~pyface.i_dialog.IDialog.return_code` of the +dialog after the user is done. Typical usage looks like:: + + dialog = MyModalDialog() + if dialog.open() == OK: + # do something based on dialog state + ... + else: + # user cancelled, clean-up if needed and stop + ... + +For custom dialogs, there are protected methods for creating the contents +of the dialog and its buttons. In most cases you will need to, at a minimum, +override the :meth:`~pyface.i_dialog.IDialog._create_dialog_area` method to +populate the widgets inside the main dialog. + +Pyface provides a number of standard dialog classes and convenience functions +to access them. + +.. toctree:: + :maxdepth: 2 + + standard_dialogs.rst + +IApplicationWindow +------------------ + +The :class:`~pyface.i_application_window.IApplicationWindow` interface +represents a main window of an application with the associated additional +features which go with these, such as menubars, toolbars, and status bars. +Users can supply +:attr:`~pyface.i_application_window.IApplicationWindow.menu_bar_manager`, +:attr:`~pyface.i_application_window.IApplicationWindow.status_bar_manager` +and :attr:`~pyface.i_application_window.IApplicationWindow.tool_bar_managers` +objects to provide the functionality, and the concrete implementations will +use these to create the toolkit-level objects. + +Writers of custom subclasses will have to override the +:meth:`~pyface.i_application_window.IApplicationWindow._create_contents` +method to populate the actual contents of the window. + +IField +------ + +The :class:`~pyface.fields.i_field.IField` interface is the base for widgets +which present a value to be edited by the user, such as text fields, sliders, +combo-boxes and so on. The interface is described in more detail in the +section on :ref:`fields`. diff -Nru python-pyface-6.1.2/doc-src-requirements.txt python-pyface-7.4.0/doc-src-requirements.txt --- python-pyface-6.1.2/doc-src-requirements.txt 2019-05-03 13:53:35.000000000 +0000 +++ python-pyface-7.4.0/doc-src-requirements.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -git+http://github.com/enthought/enthought-sphinx-theme.git#egg=enthought_sphinx_theme diff -Nru python-pyface-6.1.2/examples/application/hello_world/hello_application.py python-pyface-7.4.0/examples/application/hello_world/hello_application.py --- python-pyface-6.1.2/examples/application/hello_world/hello_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/hello_world/hello_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2018 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Example Command-Line Application @@ -16,7 +17,6 @@ computation. """ -from __future__ import print_function import argparse from pyface.application import Application @@ -40,7 +40,7 @@ id = "example_hello_application" def _run(self): - super(HelloApplication, self)._run() + super()._run() print("Hello " + self.location) @@ -50,8 +50,8 @@ parser = argparse.ArgumentParser(description=app.description) parser.add_argument( - 'location', - nargs='?', + "location", + nargs="?", default=app.location, help="the location to greet", ) @@ -60,5 +60,5 @@ app.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru python-pyface-6.1.2/examples/application/python_editor/images/image_LICENSE.txt python-pyface-7.4.0/examples/application/python_editor/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/application/python_editor/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_editor/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -PSF Trademark https://www.python.org/community/logos/ -Oxygen CC-SA 3.0 http://creativecommons.org/licenses/by-sa/3.0/ - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- - document_new.png | Oxygen - document_open.png | Oxygen - document_save.png | Oxygen - python_icon.png | PSF - python_logo.png | PSF diff -Nru python-pyface-6.1.2/examples/application/python_editor/python_browser_pane.py python-pyface-7.4.0/examples/application/python_editor/python_browser_pane.py --- python-pyface-6.1.2/examples/application/python_editor/python_browser_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_editor/python_browser_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,6 @@ -# Standard library imports. import os.path -# Enthought library imports. + from pyface.tasks.api import TraitsDockPane from traits.api import Event, File, List, Str from traitsui.api import View, Item, FileEditor @@ -14,30 +13,30 @@ # FileBrowserPane interface ---------------------------------------------- # Fired when a file is double-clicked. - activated = Event + activated = Event() # The list of wildcard filters for filenames. - filters = List(Str, ['*.py']) + filters = List(Str, ["*.py"]) # The currently selected file. - selected_file = File(os.path.expanduser('~')) + selected_file = File(os.path.expanduser("~")) # TaskPane interface ----------------------------------------------------- - id = 'example.python_browser_pane' - name = 'File Browser' + id = "example.python_browser_pane" + name = "File Browser" # The view used to construct the dock pane's widget. view = View( Item( - 'selected_file', + "selected_file", editor=FileEditor( - dclick_name='activated', - filter_name='filters', - root_path=os.path.expanduser('~'), + dclick_name="activated", + filter_name="filters", + root_path=os.path.expanduser("~"), ), - style='custom', - show_label=False + style="custom", + show_label=False, ), resizable=True, ) diff -Nru python-pyface-6.1.2/examples/application/python_editor/python_editor_application.py python-pyface-7.4.0/examples/application/python_editor/python_editor_application.py --- python-pyface-6.1.2/examples/application/python_editor/python_editor_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_editor/python_editor_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-18 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Example Tasks Application @@ -33,39 +34,35 @@ description=( "An example Tasks application that provides a Python editor." ), - icon='python_icon', - logo='python_logo', + icon="python_icon", + logo="python_logo", task_factories=[ TaskFactory( - id='example.python_editor_task', + id="example.python_editor_task", name="Python Editor", - factory=PythonEditorTask + factory=PythonEditorTask, ) ], ) # get file names from arguments parser = argparse.ArgumentParser(description=app.description) - parser.add_argument( - 'files', - nargs='*', - help="the files to open", - ) + parser.add_argument("files", nargs="*", help="the files to open") namespace = parser.parse_args() if len(namespace.files) == 0: - namespace.files.append(u'') + namespace.files.append("") # set up callback to open files once app is up and running - def open_files(): + def open_files(event): """ Open files once app is active. """ for path in namespace.files: app.active_task.create_editor(path) - app.on_trait_change(open_files, 'application_initialized') + app.observe(open_files, "application_initialized") # invoke the mainloop app.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru python-pyface-6.1.2/examples/application/python_editor/python_editor.py python-pyface-7.4.0/examples/application/python_editor/python_editor.py --- python-pyface-6.1.2/examples/application/python_editor/python_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_editor/python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-18 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Example Python Editor @@ -22,10 +23,21 @@ from pyface.tasks.api import TraitsEditor from traits.api import ( - Bool, File, HasStrictTraits, Int, Property, Unicode, cached_property + Bool, + File, + HasStrictTraits, + Int, + Property, + Str, + cached_property, ) from traitsui.api import ( - CodeEditor, Item, OKCancelButtons, RangeEditor, UndoHistory, View + CodeEditor, + Item, + OKCancelButtons, + RangeEditor, + UndoHistory, + View, ) @@ -33,16 +45,16 @@ """ A simple line number dialog. """ #: The total number of lines. - max_line = Int + max_line = Int() #: The entered line. - line = Int + line = Int() traits_view = View( Item( "line", - label=u'Line Number:', - editor=RangeEditor(low=1, high_name='max_line', mode='spinner'), + label="Line Number:", + editor=RangeEditor(low=1, high_name="max_line", mode="spinner"), ), buttons=OKCancelButtons, ) @@ -52,13 +64,13 @@ """ Tasks Editor that provides a code editor via TraitsUI """ #: The Python code being edited. - code = Unicode + code = Str() #: Whether or not undo operation is possible. - can_undo = Property(Bool, depends_on='ui.history.undoable') + can_undo = Property(Bool, observe="ui.history.undoable") #: Whether or not redo operation is possible. - can_redo = Property(Bool, depends_on='ui.history.redoable') + can_redo = Property(Bool, observe="ui.history.redoable") #: The current cursor line. line = Int(1) @@ -67,32 +79,32 @@ column = Int(1) #: The currently selected text, if any. - selection = Unicode + selection = Str() #: The length of the currently selected text. - selection_length = Property(Int, depends_on='selection') + selection_length = Property(Int, observe="selection") #: The start of the currently selected text, if any. - selection_start = Int + selection_start = Int() #: The end of the currently selected text, if any. - selection_end = Int + selection_end = Int() #: The position of the last save in the history. - _last_save = Int + _last_save = Int() # IEditor traits --------------------------------------------------------- #: The file being edited. - obj = File + obj = File() #: The editor's user-visible name. - name = Property(Unicode, depends_on='obj') + name = Property(Str, observe="obj") #: The tooltip for the editor. - tooltip = Property(Unicode, depends_on='obj') + tooltip = Property(Str, observe="obj") - dirty = Property(Bool, depends_on=['obj', '_last_save', 'ui.history.now']) + dirty = Property(Bool, observe=["obj", "_last_save", "ui.history.now"]) # ------------------------------------------------------------------------- # PythonTextEditor interface @@ -119,7 +131,7 @@ self.code = fp.read() self.obj = path else: - self.code = u"" + self.code = "" if self.ui is not None: # reset history @@ -154,7 +166,7 @@ """ Ask the use for a line number and jump to that line. """ max_line = len(self.code.splitlines()) + 1 dialog = LineNumberDialog(max_line=max_line, line=self.line) - ui = dialog.edit_traits(kind='livemodal') + ui = dialog.edit_traits(kind="livemodal") if ui.result: self.column = 1 self.line = dialog.line @@ -172,7 +184,7 @@ def create(self, parent): """ Create and set the toolkit-specific contents of the editor. """ - super(PythonEditor, self).create(parent) + super().create(parent) self.ui.history = UndoHistory() self._last_save = 0 @@ -186,7 +198,7 @@ This is True if there is no file path, or history is not at last save point. """ - return self.obj == '' or self._last_save != self.ui.history.now + return self.obj == "" or self._last_save != self.ui.history.now def _get_can_undo(self): """ Whether or not undo operations can be performed. @@ -230,14 +242,14 @@ traits_view = View( Item( - 'code', + "code", show_label=False, editor=CodeEditor( - selected_text='selection', - selected_start_pos='selection_start', - selected_end_pos='selection_end', - line='line', - column='column', - ) - ), + selected_text="selection", + selected_start_pos="selection_start", + selected_end_pos="selection_end", + line="line", + column="column", + ), + ) ) diff -Nru python-pyface-6.1.2/examples/application/python_editor/python_editor_task.py python-pyface-7.4.0/examples/application/python_editor/python_editor_task.py --- python-pyface-6.1.2/examples/application/python_editor/python_editor_task.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_editor/python_editor_task.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-18 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Example Python Editor Task @@ -22,31 +23,48 @@ import webbrowser from pyface.api import ( - CANCEL, ConfirmationDialog, FileDialog, ImageResource, OK, YES, error + CANCEL, + ConfirmationDialog, + FileDialog, + ImageResource, + OK, + YES, + error, ) from pyface.action.api import Action, StatusBarManager +from pyface.action.schema.api import SGroup, SMenu, SMenuBar, SToolBar from pyface.tasks.api import ( - EditorAreaPane, IEditor, IEditorAreaPane, PaneItem, Task, TaskLayout + EditorAreaPane, + IEditor, + IEditorAreaPane, + PaneItem, + Task, + TaskLayout, ) from pyface.tasks.action.api import ( - DockPaneToggleGroup, EditorAction, SGroup, SMenu, SMenuBar, SToolBar, - TaskAction + DockPaneToggleGroup, + EditorAction, + TaskAction, ) from traits.api import ( - Instance, Property, Unicode, cached_property, on_trait_change + Instance, + Property, + Str, + cached_property, + observe, ) from python_browser_pane import PythonBrowserPane from python_editor import PythonEditor -PYTHON_DOCS = 'https://docs.python.org/{}.{}'.format(*sys.version_info[:2]) +PYTHON_DOCS = "https://docs.python.org/{}.{}".format(*sys.version_info[:2]) class OpenURLAction(Action): """ An action that opens a web page in the system's default browser. """ #: The URL to open. - url = Unicode + url = Str() def perform(self, event=None): """ Open a URL in a web browser. """ @@ -63,14 +81,14 @@ # 'Task' traits ----------------------------------------------------------- #: The unique id of the task. - id = 'example.python_editor_task' + id = "example.python_editor_task" #: The human-readable name of the task. - name = u"Python Editor" + name = "Python Editor" #: The currently active editor in the editor area, if any. active_editor = Property( - Instance(IEditor), depends_on='editor_area.active_editor' + Instance(IEditor), observe="editor_area.active_editor" ) #: The editor area for this task. @@ -80,108 +98,104 @@ menu_bar = SMenuBar( SMenu( SGroup( - TaskAction(name='New', method='new', accelerator='Ctrl+N'), - id='new_group', + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="new_group", ), SGroup( TaskAction( - name='Open...', method='open', accelerator='Ctrl+O' + name="Open...", method="open", accelerator="Ctrl+O" ), - id='open_group', + id="open_group", ), SGroup( TaskAction( - name='Save', - method='save', - accelerator='Ctrl+S', - enabled_name='active_editor.dirty' + name="Save", + method="save", + accelerator="Ctrl+S", + enabled_name="active_editor.dirty", ), TaskAction( - name='Save As...', - method='save_as', - accelerator='Ctrl+Shift+S' + name="Save As...", + method="save_as", + accelerator="Ctrl+Shift+S", ), - id='save_group', + id="save_group", ), SGroup( TaskAction( - name='Close Editor', - method='close_editor', - accelerator='Ctrl+W', + name="Close Editor", + method="close_editor", + accelerator="Ctrl+W", ), - id='close_group', + id="close_group", ), - id='File', - name='&File', + id="File", + name="&File", ), SMenu( SGroup( EditorAction( - name='Undo', - method='undo', - enabled_name='can_undo', - accelerator='Ctrl+Z', + name="Undo", + method="undo", + enabled_name="can_undo", + accelerator="Ctrl+Z", ), EditorAction( - name='Redo', - method='redo', - enabled_name='can_redo', - accelerator='Ctrl+Shift+Z', + name="Redo", + method="redo", + enabled_name="can_redo", + accelerator="Ctrl+Shift+Z", ), - id='undo_group', + id="undo_group", ), SGroup( EditorAction( - name='Go to Line...', - method='go_to_line', - accelerator='Ctrl+G', + name="Go to Line...", + method="go_to_line", + accelerator="Ctrl+G", ), - id='search_group', + id="search_group", ), - id='Edit', - name='&Edit', - ), - SMenu( - DockPaneToggleGroup(), - id='View', - name='&View', + id="Edit", + name="&Edit", ), + SMenu(DockPaneToggleGroup(), id="View", name="&View"), SMenu( SGroup( OpenURLAction( - name='Python Documentation', - id='python_docs', + name="Python Documentation", + id="python_docs", url=PYTHON_DOCS, ), id="documentation_group", ), - id='Help', - name='&Help', - ) + id="Help", + name="&Help", + ), ) #: The tool bars for the task. tool_bars = [ SToolBar( TaskAction( - method='new', - tooltip='New file', - image=ImageResource('document_new'), + method="new", + tooltip="New file", + image=ImageResource("document_new"), ), TaskAction( - method='open', - tooltip='Open a file', - image=ImageResource('document_open') + method="open", + tooltip="Open a file", + image=ImageResource("document_open"), ), TaskAction( - method='save', - tooltip='Save the current file', - image=ImageResource('document_save'), - enabled_name='active_editor.dirty' + method="save", + tooltip="Save the current file", + image=ImageResource("document_save"), + enabled_name="active_editor.dirty", ), image_size=(16, 16), - show_tool_names=False - ), + show_tool_names=False, + ) ] #: The status bar for the window when this task is active. @@ -191,7 +205,7 @@ # 'PythonEditorTask' interface. # ------------------------------------------------------------------------- - def create_editor(self, path=''): + def create_editor(self, path=""): """ Create a new editor in the editor pane. Parameters @@ -201,11 +215,9 @@ """ if path: path = os.path.abspath(path) - use_existing = (path != '') + use_existing = path != "" self.editor_area.edit( - path, - factory=PythonEditor, - use_existing=use_existing, + path, factory=PythonEditor, use_existing=use_existing ) if path: self.active_editor.load() @@ -226,7 +238,7 @@ def open(self): """ Shows a dialog to open a Python file. """ - dialog = FileDialog(parent=self.window.control, wildcard='*.py') + dialog = FileDialog(parent=self.window.control, wildcard="*.py") if dialog.open() == OK: self.create_editor(dialog.path) @@ -247,9 +259,7 @@ # If you are trying to save to a file that doesn't exist, open up a # FileDialog with a 'save as' action. dialog = FileDialog( - parent=self.window.control, - action='save as', - wildcard='*.py', + parent=self.window.control, action="save as", wildcard="*.py" ) if dialog.open() == OK: editor.save(dialog.path) @@ -265,7 +275,7 @@ """ The default layout with the browser pane on the left. """ return TaskLayout( - left=PaneItem('example.python_browser_pane', width=200) + left=PaneItem("example.python_browser_pane", width=200) ) def create_central_pane(self): @@ -279,11 +289,12 @@ """ browser = PythonBrowserPane() - def handler(path): + def handler(event): + path = event.new if os.path.isfile(path): return self.create_editor(path) - browser.on_trait_change(handler, 'activated') + browser.observe(handler, "activated") return [browser] # ------------------------------------------------------------------------- @@ -302,13 +313,13 @@ if not dirty_editors: return True - message = 'You have unsaved files. Would you like to save them?' + message = "You have unsaved files. Would you like to save them?" dialog = ConfirmationDialog( parent=self.window.control, message=message, cancel=True, default=CANCEL, - title='Save Changes?' + title="Save Changes?", ) result = dialog.open() if result == CANCEL: @@ -320,15 +331,16 @@ # Trait change handlers -------------------------------------------------- - @on_trait_change('window:closing') + @observe("window:closing") def _prompt_on_close(self, event): """ Prompt the user to save when exiting. """ close = self._prompt_for_save() - event.veto = not close + window = event.new + window.veto = not close - @on_trait_change('active_editor.name') - def _change_title(self): + @observe("active_editor.name") + def _change_title(self, event): """ Update the window title when the active editor changes. """ if self.window.active_task == self: @@ -337,8 +349,8 @@ else: self.window.title = self.name - @on_trait_change('active_editor.[line,column,selection_length]') - def _update_status(self): + @observe("active_editor.[line,column,selection_length]") + def _update_status(self, event): if self.active_editor is not None: editor = self.active_editor if editor.selection_length: diff -Nru python-pyface-6.1.2/examples/application/python_shell/images/image_LICENSE.txt python-pyface-7.4.0/examples/application/python_shell/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/application/python_shell/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_shell/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -PSF Trademark https://www.python.org/community/logos/ - -Files and orginal authors: ----------------------------------------------------------------------------- - python_icon.png | PSF - python_logo.png | PSF diff -Nru python-pyface-6.1.2/examples/application/python_shell/python_shell_application.py python-pyface-7.4.0/examples/application/python_shell/python_shell_application.py --- python-pyface-6.1.2/examples/application/python_shell/python_shell_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_shell/python_shell_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-18 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Example GUI Application @@ -23,12 +24,20 @@ from pyface.api import GUIApplication from pyface.action.api import ( - AboutAction, CloseActiveWindowAction, CreateWindowAction, ExitAction, - Group, MenuBarManager, MenuManager + AboutAction, + CloseActiveWindowAction, + CreateWindowAction, + ExitAction, + Group, + MenuBarManager, + MenuManager, ) from python_shell_window import ( - OpenURLAction, PYTHON_DOCS, PythonShellWindow, RunFileAction + OpenURLAction, + PYTHON_DOCS, + PythonShellWindow, + RunFileAction, ) @@ -41,42 +50,33 @@ # than just overriding. window.menu_bar_manager = MenuBarManager( MenuManager( - Group( - CreateWindowAction(application=application), - id='new_group', - ), + Group(CreateWindowAction(application=application), id="new_group"), Group( CloseActiveWindowAction(application=application), ExitAction(application=application), - id='close_group', + id="close_group", ), - name='&File', - id='File', + name="&File", + id="File", ), MenuManager( - Group( - RunFileAction(window=window), - id='run_group', - ), - name='&Run', - id='Run', + Group(RunFileAction(window=window), id="run_group"), + name="&Run", + id="Run", ), MenuManager( Group( OpenURLAction( - name='Python Documentation', - id='python_docs', + name="Python Documentation", + id="python_docs", url=PYTHON_DOCS, ), id="documentation_group", ), - Group( - AboutAction(application=application), - id='about_group', - ), - name='&Help', - id='Help', - ) + Group(AboutAction(application=application), id="about_group"), + name="&Help", + id="Help", + ), ) return window @@ -87,8 +87,8 @@ id="example_python_shell_application", name="Python Shell", description="An example application that provides a Python shell.", - icon='python_icon', - logo='python_logo', + icon="python_icon", + logo="python_logo", window_factory=create_python_shell_window, ) @@ -99,5 +99,5 @@ app.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru python-pyface-6.1.2/examples/application/python_shell/python_shell_window.py python-pyface-7.4.0/examples/application/python_shell/python_shell_window.py --- python-pyface-6.1.2/examples/application/python_shell/python_shell_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application/python_shell/python_shell_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-18 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Example Python Shell Window @@ -19,30 +20,41 @@ import webbrowser from pyface.api import ( - ApplicationWindow, FileDialog, ImageResource, OK, PythonShell, error + ApplicationWindow, + FileDialog, + ImageResource, + OK, + PythonShell, + error, ) from pyface.action.api import ( - Action, CloseWindowAction, Group, MenuBarManager, MenuManager, - StatusBarManager, WindowAction + Action, + CloseWindowAction, + Group, + MenuBarManager, + MenuManager, + StatusBarManager, + WindowAction, ) -from traits.api import Instance, Unicode +from traits.api import Instance, Str -PYTHON_DOCS = 'https://docs.python.org/{}.{}'.format(*sys.version_info[:2]) +PYTHON_DOCS = "https://docs.python.org/{}.{}".format(*sys.version_info[:2]) class RunFileAction(WindowAction): """ Action that calls the do_run_file method of a PythonShellWindow """ - name = 'Run File...' - accelerator = 'Ctrl+R' - method = 'do_run_file' - window = Instance('PythonShellWindow') + + name = "Run File..." + accelerator = "Ctrl+R" + method = "do_run_file" + window = Instance("PythonShellWindow") class OpenURLAction(Action): """ An action that opens a web page in the system's default browser. """ #: The URL to open. - url = Unicode + url = Str() def perform(self, event=None): """ Open a URL in a web browser. """ @@ -62,7 +74,7 @@ icon = ImageResource("python_icon") #: The Python shell widget to use. - shell = Instance('pyface.i_python_shell.IPythonShell') + shell = Instance("pyface.i_python_shell.IPythonShell") def do_run_file(self): """ Run a file selected by the user. """ @@ -79,32 +91,26 @@ def _menu_bar_manager_default(self): menu_bar = MenuBarManager( MenuManager( - Group( - CloseWindowAction(window=self), - id='close_group', - ), - name='&File', - id='File', + Group(CloseWindowAction(window=self), id="close_group"), + name="&File", + id="File", ), MenuManager( - Group( - RunFileAction(window=self), - id='run_group', - ), - name='&Run', - id='Run', + Group(RunFileAction(window=self), id="run_group"), + name="&Run", + id="Run", ), MenuManager( Group( OpenURLAction( - name='Python Documentation', - id='python_docs', + name="Python Documentation", + id="python_docs", url=PYTHON_DOCS, ), id="documentation_group", ), - name='&Help', - id='Help', + name="&Help", + id="Help", ), ) return menu_bar @@ -113,8 +119,9 @@ return StatusBarManager() -if __name__ == '__main__': +if __name__ == "__main__": from pyface.api import GUI + window = PythonShellWindow() window.open() GUI().start_event_loop() diff -Nru python-pyface-6.1.2/examples/application_window.py python-pyface-7.4.0/examples/application_window.py --- python-pyface-6.1.2/examples/application_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Application window example. """ -# Enthought library imports. from pyface.api import ApplicationWindow, GUI from pyface.action.api import Action, MenuManager, MenuBarManager from pyface.action.api import StatusBarManager, ToolBarManager, Group @@ -23,53 +19,53 @@ class MainWindow(ApplicationWindow): """ The main application window. """ - ########################################################################### + #: The initial size of the application window. + size = (800, 600) + + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Create an action that exits the application. - exit_action = Action(name='E&xit', on_perform=self.close) + exit_action = Action(name="E&xit", on_perform=self.close) self.exit_action = exit_action - + # Test action to toggle visibility of exit action and some action groups - test_action = Action(name='&Toggle', on_perform=self.toggle) + test_action = Action(name="&Toggle", on_perform=self.toggle) # Add a menu bar. self.menu_bar_manager = MenuBarManager( - MenuManager(exit_action, name='&File') + MenuManager(exit_action, name="&File") ) # Add some tool bars, with the first one subdivided into action groups self.tool_bar_managers = [ ToolBarManager( - Group(exit_action, exit_action, id='a'), - Group(id='b'), # empty, so will remain hidden - Group(exit_action, exit_action, id='c'), - Group(exit_action, test_action, exit_action, id='d'), - name='Tool Bar 1', show_tool_names=False + Group(exit_action, exit_action, id="a"), + Group(id="b"), # empty, so will remain hidden + Group(exit_action, exit_action, id="c"), + Group(exit_action, test_action, exit_action, id="d"), + name="Tool Bar 1", + show_tool_names=True, ), - ToolBarManager( - exit_action, name='Tool Bar 2', show_tool_names=False + exit_action, name="Tool Bar 2", show_tool_names=True ), - ToolBarManager( - test_action, name='Tool Bar 3', show_tool_names=False + test_action, name="Tool Bar 3", show_tool_names=True ), ] # Add a status bar. self.status_bar_manager = StatusBarManager() - self.status_bar_manager.message = 'Example application window' + self.status_bar_manager.message = "Example application window" - return - def toggle(self): """ Toggle the visibility of the exit action and of the first 3 groups in the first toolbar, which contain only exit actions. @@ -81,7 +77,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -91,5 +87,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/chained_wizard.py python-pyface-7.4.0/examples/chained_wizard.py --- python-pyface-6.1.2/examples/chained_wizard.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/chained_wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,107 +1,105 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Chained wizard example. """ +# Thanks for using Enthought open source! -from __future__ import print_function +""" Chained wizard example. """ -# Standard library imports. -import os -import sys -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import HasTraits, Str -# Enthought library imports. from pyface.api import GUI, OK from pyface.wizard.api import ChainedWizard, Wizard, WizardPage -from traits.api import Color, HasTraits, Int, Str +from pyface.ui_traits import TraitsUIColor as Color + class Details(HasTraits): """ Some test data. """ - name = Str + name = Str() color = Color class SimpleWizardPage(WizardPage): """ A simple wizard page. """ - #### 'SimpleWizardPage' interface ######################################### + # 'SimpleWizardPage' interface ----------------------------------------- # The page color. color = Color - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_page_content(self, parent): """ Create the wizard page. """ details = Details(color=self.color) - details.on_trait_change(self._on_name_changed, 'name') + details.observe(self._on_name_changed, "name") - return details.edit_traits(parent=parent, kind='subpanel').control + return details.edit_traits(parent=parent, kind="subpanel").control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_name_changed(self, new): + def _on_name_changed(self, event): """ Called when the name has been changed. """ - self.complete = len(new.strip()) > 0 + self.complete = len(event.new.strip()) > 0 return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() wizard = ChainedWizard( - parent = None, - title = 'Chained wizard root', - pages = [ - SimpleWizardPage(id='foo', color='red', - heading="The Red Page", - subheading="The default color on this page is red.") - ] + parent=None, + title="Chained wizard root", + pages=[ + SimpleWizardPage( + id="foo", + color="red", + heading="The Red Page", + subheading="The default color on this page is red.", + ) + ], ) next_wizard = Wizard( - parent = None, - title = 'Chained wizard child.', - pages = [ - SimpleWizardPage(id='bar', color='yellow', - heading="The Yellow Page", - subheading="The default color on this page is yellow."), - SimpleWizardPage(id='baz', color='green', - heading="The Green Page", - subheading="The default color on this page is green.") - ] + parent=None, + title="Chained wizard child.", + pages=[ + SimpleWizardPage( + id="bar", + color="yellow", + heading="The Yellow Page", + subheading="The default color on this page is yellow.", + ), + SimpleWizardPage( + id="baz", + color="green", + heading="The Green Page", + subheading="The default color on this page is green.", + ), + ], ) wizard.next_wizard = next_wizard # Open the wizard. if wizard.open() == OK: - print('Wizard completed successfully') + print("Wizard completed successfully") else: - print('Wizard cancelled') - -#### EOF ###################################################################### + print("Wizard cancelled") diff -Nru python-pyface-6.1.2/examples/data_view/array_example.py python-pyface-7.4.0/examples/data_view/array_example.py --- python-pyface-6.1.2/examples/data_view/array_example.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/data_view/array_example.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,92 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Example showing DataView for ArrayDataModel. """ + +import logging + +import numpy as np + +from traits.api import Array, Instance, observe + +from pyface.api import ApplicationWindow, GUI +from pyface.data_view.api import ( + DataViewWidget, IDataViewWidget, table_format, csv_format, npy_format +) +from pyface.data_view.data_models.api import ArrayDataModel +from pyface.data_view.exporters.api import RowExporter +from pyface.data_view.value_types.api import FloatValue +from pyface.drop_handler import FileDropHandler + + +logger = logging.getLogger(__name__) + + +class MainWindow(ApplicationWindow): + """ The main application window. """ + + data = Array() + + data_view = Instance(IDataViewWidget) + + def load_data(self, path): + try: + self.data = np.load(path) + except Exception: + logger.exception() + + def _create_contents(self, parent): + """ Creates the left hand side or top depending on the style. """ + + self.data_view = DataViewWidget( + parent=parent, + data_model=ArrayDataModel( + data=self.data, + value_type=FloatValue(), + ), + selection_mode='extended', + exporters=[ + RowExporter(format=table_format), + RowExporter(format=csv_format), + RowExporter(format=npy_format), + ], + drop_handlers=[ + FileDropHandler(extensions=['.npy'], open_file=self.load_data), + ], + ) + self.data_view._create() + return self.data_view.control + + def destroy(self): + self.data_view.destroy() + super().destroy() + + @observe('data') + def _data_updated(self, event): + if self.data_view is not None: + self.data_view.data_model.data = self.data + + def _data_default(self): + return np.random.uniform(size=(10000, 10, 10))*1000000 + + +# Application entry point. +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + # Create the GUI (this does NOT start the GUI event loop). + gui = GUI() + + # Create and open the main window. + window = MainWindow() + window.open() + + # Start the GUI event loop! + gui.start_event_loop() diff -Nru python-pyface-6.1.2/examples/data_view/column_data_model.py python-pyface-7.4.0/examples/data_view/column_data_model.py --- python-pyface-6.1.2/examples/data_view/column_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/data_view/column_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,224 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from abc import abstractmethod + +from traits.api import ( + ABCHasStrictTraits, Event, HasTraits, Instance, + List, Str, observe +) +from traits.trait_base import xgetattr, xsetattr + +from pyface.data_view.api import ( + AbstractDataModel, AbstractValueType, DataViewSetError, TupleIndexManager +) +from pyface.data_view.value_types.api import TextValue + + +class AbstractRowInfo(ABCHasStrictTraits): + """ Configuration for a data row in a ColumnDataModel. + """ + + #: The text to display in the first column. + title = Str() + + #: The child rows of this row, if any. + rows = List(Instance('AbstractRowInfo')) + + #: The value type of the data stored in this row. + title_type = Instance( + AbstractValueType, + factory=TextValue, + kw={'is_editable': False}, + ) + + #: The value type of the data stored in this row. + value_type = Instance(AbstractValueType) + + #: An event fired when the row or its children update. The payload is + #: a tuple of whether the title or value changed (or both), and the + #: row_index affected. + updated = Event + + def __iter__(self): + yield self + for row in self.rows: + yield from row + + @abstractmethod + def get_value(self, obj): + raise NotImplementedError() + + @abstractmethod + def can_set_value(self, obj): + raise NotImplementedError() + + def set_value(self, obj): + return + + @abstractmethod + def get_observable(self, obj): + raise NotImplementedError() + + # trait observers + + @observe('title,title_type.updated') + def title_updated(self, event): + self.updated = (self, 'title', []) + + @observe('value_type.updated') + def value_type_updated(self, event): + self.updated = (self, 'value', []) + + @observe('rows.items') + def rows_updated(self, event): + self.updated = (self, 'rows', []) + + @observe('rows:items:updated') + def row_item_updated(self, event): + row = event.object + row_info, part, row_index = event.new + row_index = [self.rows.index(row)] + row_index + self.updated = (row_info, part, row_index) + + +class HasTraitsRowInfo(AbstractRowInfo): + """ RowInfo that presents a named trait value. + """ + + #: The extended trait name of the trait holding the value. + value = Str() + + def get_value(self, obj): + return xgetattr(obj, self.value, None) + + def can_set_value(self, obj): + return self.value != '' + + def set_value(self, obj, value): + if not self.value: + return + xsetattr(obj, self.value, value) + + def get_observable(self): + return self.value + + @observe('value') + def value_type_updated(self, event): + self.updated = (self, 'value', []) + + +class DictRowInfo(AbstractRowInfo): + """ RowInfo that presents an item in a dictionary. + + The attribute ``value`` should reference a dictionary trait on a + has traits object. + """ + + #: The extended trait name of the dictionary holding the values. + value = Str() + + #: The key holding the value. + key = Str() + + def get_value(self, obj): + data = xgetattr(obj, self.value, None) + return data.get(self.key, None) + + def can_set_value(self, obj): + return self.value != '' + + def set_value(self, obj, value): + data = xgetattr(obj, self.value, None) + data[self.key] = value + + def get_observable(self): + return self.value + '.items' + + @observe('value,key') + def value_type_updated(self, event): + self.updated = (self, 'value', []) + + +class ColumnDataModel(AbstractDataModel): + """ A data model that presents a list of objects as columns. + """ + + #: A list of objects to display in columns. + data = List(Instance(HasTraits)) + + #: An object which describes how to map data for each row. + row_info = Instance(AbstractRowInfo) + + #: The index manager that helps convert toolkit indices to data view + #: indices. + index_manager = Instance(TupleIndexManager, args=()) + + def get_column_count(self): + return len(self.data) + + def can_have_children(self, row): + if len(row) == 0: + return True + row_info = self._row_info_object(row) + return len(row_info.rows) != 0 + + def get_row_count(self, row): + row_info = self._row_info_object(row) + return len(row_info.rows) + + def get_value(self, row, column): + row_info = self._row_info_object(row) + if len(column) == 0: + return row_info.title + obj = self.data[column[0]] + return row_info.get_value(obj) + + def can_set_value(self, row, column): + """ Whether the value in the indicated row and column can be set. + + This returns False for row headers, but True for all other values. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + if len(column) == 0: + return False + else: + return True + + def set_value(self, row, column, value): + row_info = self._row_info_object(row) + if len(column) == 0: + raise DataViewSetError("Cannot set value for row header.") + obj = self.data[column[0]] + row_info.set_value(obj, value) + + def get_value_type(self, row, column): + row_info = self._row_info_object(row) + if len(column) == 0: + return row_info.title_type + else: + return row_info.value_type + + def _row_info_object(self, row): + row_info = self.row_info + for index in row: + row_info = row_info.rows[index] + return row_info diff -Nru python-pyface-6.1.2/examples/data_view/column_example.py python-pyface-7.4.0/examples/data_view/column_example.py --- python-pyface-6.1.2/examples/data_view/column_example.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/data_view/column_example.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,185 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Example showing DataView for ColumnDataModel using row info. """ + +import logging + +from traits.api import Bool, Dict, HasStrictTraits, Instance, Int, Str, List + +from pyface.api import ApplicationWindow, GUI, Image, ImageResource +from pyface.data_view.api import ( + DataViewWidget, IDataViewWidget, table_format, csv_format +) +from pyface.data_view.exporters.api import RowExporter +from pyface.data_view.value_types.api import ( + BoolValue, EnumValue, ColorValue, IntValue, TextValue, no_value +) +from pyface.ui_traits import PyfaceColor + +from column_data_model import ( + AbstractRowInfo, ColumnDataModel, HasTraitsRowInfo +) +from example_data import ( + any_name, family_name, favorite_color, age, street, city, country +) + +logger = logging.getLogger(__name__) + + +flags = { + 'Canada': ImageResource('ca.png'), + 'UK': ImageResource('gb.png'), + 'USA': ImageResource('us.png'), +} + + +class Address(HasStrictTraits): + + street = Str() + + city = Str() + + country = Str() + + +class Person(HasStrictTraits): + + name = Str() + + age = Int() + + favorite_color = PyfaceColor() + + contacted = Bool() + + address = Instance(Address) + + +row_info = HasTraitsRowInfo( + title='People', + value='name', + value_type=TextValue(), + rows=[ + HasTraitsRowInfo( + title="Age", + value="age", + value_type=IntValue(minimum=0), + ), + HasTraitsRowInfo( + title="Favorite Color", + value="favorite_color", + value_type=ColorValue(), + ), + HasTraitsRowInfo( + title="Contacted", + value="contacted", + value_type=BoolValue(true_text="Yes", false_text="No"), + ), + HasTraitsRowInfo( + title="Address", + value_type=no_value, + value='address', + rows=[ + HasTraitsRowInfo( + title="Street", + value="address.street", + value_type=TextValue(), + ), + HasTraitsRowInfo( + title="City", + value="address.city", + value_type=TextValue(), + ), + HasTraitsRowInfo( + title="Country", + value="address.country", + value_type=EnumValue( + values=sorted(flags.keys()), + images=flags.get, + ), + ), + ], + ), + ], +) + + +class MainWindow(ApplicationWindow): + """ The main application window. """ + + data = List(Instance(Person)) + + row_info = Instance(AbstractRowInfo) + + data_view = Instance(IDataViewWidget) + + def _create_contents(self, parent): + """ Creates the left hand side or top depending on the style. """ + + self.data_view = DataViewWidget( + parent=parent, + data_model=ColumnDataModel( + data=self.data, + row_info=self.row_info, + ), + selection_mode='extended', + exporters=[ + RowExporter( + format=table_format, + column_headers=True, + row_headers=True, + ), + RowExporter( + format=csv_format, + column_headers=True, + ), + ] + ) + self.data_view._create() + return self.data_view.control + + def _data_default(self): + logger.info("Initializing data") + people = [ + Person( + name='%s %s' % (any_name(), family_name()), + age=age(), + favorite_color=favorite_color(), + address=Address( + street=street(), + city=city(), + country=country(), + ), + ) + for i in range(100) + ] + logger.info("Data initialized") + return people + + def destroy(self): + self.data_view.destroy() + super().destroy() + + +# Application entry point. +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + # Create the GUI (this does NOT start the GUI event loop). + gui = GUI() + + # Create and open the main window. + window = MainWindow(row_info=row_info) + window.open() + + # Start the GUI event loop! + gui.start_event_loop() + logger.info("Shutting down") diff -Nru python-pyface-6.1.2/examples/data_view/example_data.py python-pyface-7.4.0/examples/data_view/example_data.py --- python-pyface-6.1.2/examples/data_view/example_data.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/data_view/example_data.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,98 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Example data generation functions + +This module provides some routines for randomly generating data for use +in examples. +""" + +from random import choice, randint + +from pyface.color import Color + + +male_names = [ + 'Michael', 'Edward', 'Timothy', 'James', 'George', 'Ralph', 'David', + 'Martin', 'Bryce', 'Richard', 'Eric', 'Travis', 'Robert', 'Bryan', + 'Alan', 'Harold', 'John', 'Stephen', 'Gael', 'Frederic', 'Eli', 'Scott', + 'Samuel', 'Alexander', 'Tobias', 'Sven', 'Peter', 'Albert', 'Thomas', + 'Horatio', 'Julius', 'Henry', 'Walter', 'Woodrow', 'Dylan', 'Elmer', +] + +female_names = [ + 'Leah', 'Jaya', 'Katrina', 'Vibha', 'Diane', 'Lisa', 'Jean', 'Alice', + 'Rebecca', 'Delia', 'Christine', 'Marie', 'Dorothy', 'Ellen', 'Victoria', + 'Elizabeth', 'Margaret', 'Joyce', 'Sally', 'Ethel', 'Esther', 'Suzanne', + 'Monica', 'Hortense', 'Samantha', 'Tabitha', 'Judith', 'Ariel', 'Helen', + 'Mary', 'Jane', 'Janet', 'Jennifer', 'Rita', 'Rena', 'Rianna', +] + +all_names = male_names + female_names + +family_names = [ + 'Jones', 'Smith', 'Thompson', 'Hayes', 'Thomas', 'Boyle', "O'Reilly", + 'Lebowski', 'Lennon', 'Starr', 'McCartney', 'Harrison', 'Harrelson', + 'Steinbeck', 'Rand', 'Hemingway', 'Zhivago', 'Clemens', 'Heinlien', + 'Farmer', 'Niven', 'Van Vogt', 'Sturbridge', 'Washington', 'Adams', + 'Bush', 'Kennedy', 'Ford', 'Lincoln', 'Jackson', 'Johnson', + 'Eisenhower', 'Truman', 'Roosevelt', 'Wilson', 'Coolidge', 'Mack', + 'Moon', 'Monroe', 'Springsteen', 'Rigby', "O'Neil", 'Philips', + 'Clinton', 'Clapton', 'Santana', 'Midler', 'Flack', 'Conner', 'Bond', + 'Seinfeld', 'Costanza', 'Kramer', 'Falk', 'Moore', 'Cramdon', 'Baird', + 'Baer', 'Spears', 'Simmons', 'Roberts', 'Michaels', 'Stuart', + 'Montague', 'Miller', +] + +favorite_colors = [ + Color(hsv=(hue/100, 1.0, 1.0)) + for hue in range(100) +] + + +def any_name(): + return choice(all_names) + + +def family_name(): + return choice(family_names) + + +def age(): + return randint(15, 72) + + +def favorite_color(): + return choice(favorite_colors) + + +def street(): + number = randint(11, 999) + text_1 = choice([ + 'Spring', 'Summer', 'Moonlight', 'Winding', 'Windy', 'Whispering', + 'Falling', 'Roaring', 'Hummingbird', 'Mockingbird', 'Bluebird', + 'Robin', 'Babbling', 'Cedar', 'Pine', 'Ash', 'Maple', 'Oak', 'Birch', + 'Cherry', 'Blossom', 'Rosewood', 'Apple', 'Peach', 'Blackberry', + 'Strawberry', 'Starlight', 'Wilderness', 'Dappled', 'Beaver', 'Acorn', + 'Pecan', 'Pheasant', 'Owl' + ]) + text_2 = choice([ + 'Way', 'Lane', 'Boulevard', 'Street', 'Drive', 'Circle', 'Avenue', + 'Trail', + ]) + return '%d %s %s' % (number, text_1, text_2) + + +def city(): + return choice(['Boston', 'Bristol', 'Cambridge', 'Newcastle', 'York']) + + +def country(): + return choice(['Canada', 'USA', 'UK']) Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/examples/data_view/images/ca.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/examples/data_view/images/ca.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/examples/data_view/images/gb.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/examples/data_view/images/gb.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/examples/data_view/images/us.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/examples/data_view/images/us.png differ diff -Nru python-pyface-6.1.2/examples/data_view/row_table_example.py python-pyface-7.4.0/examples/data_view/row_table_example.py --- python-pyface-6.1.2/examples/data_view/row_table_example.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/data_view/row_table_example.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,159 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import logging + +from traits.api import Bool, Dict, HasStrictTraits, Instance, Int, Str, List + +from pyface.api import ApplicationWindow, GUI, Image, ImageResource +from pyface.ui_traits import PyfaceColor +from pyface.data_view.data_models.api import ( + AttributeDataAccessor, RowTableDataModel +) +from pyface.data_view.api import DataViewWidget, IDataViewWidget +from pyface.data_view.value_types.api import ( + BoolValue, EnumValue, ColorValue, IntValue, TextValue +) + +from example_data import ( + any_name, family_name, favorite_color, age, street, city, country +) + + +logger = logging.getLogger(__name__) + + +flags = { + 'Canada': ImageResource('ca.png'), + 'UK': ImageResource('gb.png'), + 'USA': ImageResource('us.png'), +} + + +# The data model + +class Address(HasStrictTraits): + + street = Str() + + city = Str() + + country = Str() + + +class Person(HasStrictTraits): + + name = Str() + + age = Int() + + favorite_color = PyfaceColor() + + contacted = Bool() + + address = Instance(Address, ()) + + +row_header_data = AttributeDataAccessor( + title='People', + attr='name', + value_type=TextValue(), +) + +column_data = [ + AttributeDataAccessor( + attr="age", + value_type=IntValue(minimum=0), + ), + AttributeDataAccessor( + attr="favorite_color", + value_type=ColorValue(), + ), + AttributeDataAccessor( + attr="contacted", + value_type=BoolValue(), + ), + AttributeDataAccessor( + attr="address.street", + value_type=TextValue(), + ), + AttributeDataAccessor( + attr="address.city", + value_type=TextValue(), + ), + AttributeDataAccessor( + attr="address.country", + value_type=EnumValue( + values=['Canada', 'UK', 'USA'], + images=flags.get, + ), + ), +] + + +class MainWindow(ApplicationWindow): + """ The main application window. """ + + #: A collection of People. + data = List(Instance(Person)) + + #: The data view widget. + data_view = Instance(IDataViewWidget) + + def _create_contents(self, parent): + """ Creates the left hand side or top depending on the style. """ + + self.data_view = DataViewWidget( + parent=parent, + data_model=RowTableDataModel( + data=self.data, + row_header_data=row_header_data, + column_data=column_data + ), + ) + self.data_view._create() + return self.data_view.control + + def _data_default(self): + logger.info("Initializing data") + people = [ + Person( + name='%s %s' % (any_name(), family_name()), + age=age(), + favorite_color=favorite_color(), + address=Address( + street=street(), + city=city(), + country=country(), + ), + ) + for i in range(10000) + ] + logger.info("Data initialized") + return people + + def destroy(self): + self.data_view.destroy() + super().destroy() + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + + # Create the GUI (this does NOT start the GUI event loop). + gui = GUI() + + # Create and open the main window. + window = MainWindow() + window.open() + + # Start the GUI event loop! + gui.start_event_loop() + logger.info("Shutting down") diff -Nru python-pyface-6.1.2/examples/dialog.py python-pyface-7.4.0/examples/dialog.py --- python-pyface-6.1.2/examples/dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Dialog example. """ -from __future__ import print_function - -# Standard library imports. -import os, sys - -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) - -# Enthought library imports. -from pyface.api import (ApplicationWindow, GUI, YES, choose_one, confirm, - error, information, warning) -from pyface.action.api import Action, MenuBarManager, MenuManager - - -class MainWindow(ApplicationWindow): - """ The main application window. """ - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """ Creates a new application window. """ - - # Base class constructor. - super(MainWindow, self).__init__(**traits) - - # Add a menu bar. - self.menu_bar_manager = MenuBarManager( - MenuManager( - Action(name='E&xit', on_perform=self._on_exit), - name = '&File', - ) - ) - - return - - ########################################################################### - # Private interface. - ########################################################################### - - def _on_exit(self): - """ Called when the exit action is invoked. """ - - parent = self.control - - print(choose_one(parent, "Make a choice", ['one', 'two', 'three'])) - - information(parent, 'Going...') - warning(parent, 'Going......') - error(parent, 'Gone!') - - if confirm(parent, 'Should I exit?') == YES: - self.close() - - -# Application entry point. -if __name__ == '__main__': - # Create the GUI (this does NOT start the GUI event loop). - gui = GUI() - - # Create and open the main window. - window = MainWindow() - window.open() - - # Start the GUI event loop! - gui.start_event_loop() diff -Nru python-pyface-6.1.2/examples/dialogs/color_dialog.py python-pyface-7.4.0/examples/dialogs/color_dialog.py --- python-pyface-6.1.2/examples/dialogs/color_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/dialogs/color_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,16 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.color_dialog import get_color + +# display a color dialog and get the result +color = get_color(None, color='red') +if color is not None: + print(color.hex()) diff -Nru python-pyface-6.1.2/examples/dialogs/dialog.py python-pyface-7.4.0/examples/dialogs/dialog.py --- python-pyface-6.1.2/examples/dialogs/dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/dialogs/dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,78 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Dialog example. """ + + +from pyface.api import ( + ApplicationWindow, + GUI, + YES, + choose_one, + confirm, + error, + information, + warning, +) +from pyface.action.api import Action, MenuBarManager, MenuManager + + +class MainWindow(ApplicationWindow): + """ The main application window. """ + + # ------------------------------------------------------------------------ + # 'object' interface. + # ------------------------------------------------------------------------ + + def __init__(self, **traits): + """ Creates a new application window. """ + + # Base class constructor. + super().__init__(**traits) + + # Add a menu bar. + self.menu_bar_manager = MenuBarManager( + MenuManager( + Action(name="E&xit", on_perform=self._on_exit), name="&File" + ) + ) + + return + + # ------------------------------------------------------------------------ + # Private interface. + # ------------------------------------------------------------------------ + + def _on_exit(self): + """ Called when the exit action is invoked. """ + + parent = self.control + + print(choose_one(parent, "Make a choice", ["one", "two", "three"])) + + information(parent, "Going...") + warning(parent, "Going......") + error(parent, "Gone!") + + if confirm(parent, "Should I exit?") == YES: + self.close() + + +# Application entry point. +if __name__ == "__main__": + # Create the GUI (this does NOT start the GUI event loop). + gui = GUI() + + # Create and open the main window. + window = MainWindow() + window.open() + + # Start the GUI event loop! + gui.start_event_loop() diff -Nru python-pyface-6.1.2/examples/dialogs/directory_dialog.py python-pyface-7.4.0/examples/dialogs/directory_dialog.py --- python-pyface-6.1.2/examples/dialogs/directory_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/dialogs/directory_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,17 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.api import DirectoryDialog, OK + + +# display a directory dialog for opening a directory +dialog = DirectoryDialog(parent=None) +if dialog.open() == OK: + print(f"Open {dialog.path}") diff -Nru python-pyface-6.1.2/examples/dialogs/file_dialog.py python-pyface-7.4.0/examples/dialogs/file_dialog.py --- python-pyface-6.1.2/examples/dialogs/file_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/dialogs/file_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,17 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.api import FileDialog, OK + + +# display a file dialog for saving a Python file +dialog = FileDialog(parent=None, action="save as", wildcard="*.py") +if dialog.open() == OK: + print(f"Save to {dialog.path}") diff -Nru python-pyface-6.1.2/examples/dialogs/font_dialog.py python-pyface-7.4.0/examples/dialogs/font_dialog.py --- python-pyface-6.1.2/examples/dialogs/font_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/dialogs/font_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,16 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.font_dialog import get_font + +# display a font dialog and get the result +font = get_font(None, font='12 pt Helvetica sans-serif') +if font is not None: + print(font) diff -Nru python-pyface-6.1.2/examples/dialogs/progress_dialog.py python-pyface-7.4.0/examples/dialogs/progress_dialog.py --- python-pyface-6.1.2/examples/dialogs/progress_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/dialogs/progress_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,70 @@ +# +# simple example of its use +# + + +import time +from pyface.api import GUI, ApplicationWindow, ProgressDialog +from pyface.action.api import Action, MenuManager, MenuBarManager + + +def task_func(t): + progress = ProgressDialog( + title="progress", + message="counting to %d" % t, + max=t, + show_time=True, + can_cancel=True, + ) + progress.open() + + for i in range(0, t + 1): + time.sleep(1) + print(i) + (cont, skip) = progress.update(i) + if not cont or skip: + break + + progress.update(t) + + +def _main(): + task_func(10) + + +class MainWindow(ApplicationWindow): + """ The main application window. """ + + # -------------------------------------------------------------------- + # 'object' interface. + # -------------------------------------------------------------------- + + def __init__(self, **traits): + """ Creates a new application window. """ + + # Base class constructor. + super().__init__(**traits) + + # Add a menu bar. + self.menu_bar_manager = MenuBarManager( + MenuManager( + Action(name="E&xit", on_perform=self.close), + Action(name="DoIt", on_perform=_main), + name="&File", + ) + ) + + return + + +if __name__ == "__main__": + gui = GUI() + + # Create and open the main window. + window = MainWindow() + window.open() + + _main() + + # Start the GUI event loop! + gui.start_event_loop() diff -Nru python-pyface-6.1.2/examples/dock/dock_test2.py python-pyface-7.4.0/examples/dock/dock_test2.py --- python-pyface-6.1.2/examples/dock/dock_test2.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/dock/dock_test2.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,66 +1,61 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Test the DockWindow. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Written by: David C. Morrill -# -# Date: 10/20/2005 -# -# (c) Copyright 2005 by Enthought, Inc. -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! import sys -from traits.api \ - import * +from traits.api import * -from traitsui.api \ - import * +from traitsui.api import * -from traitsui.menu \ - import * +from traitsui.menu import * -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'TestDock' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class TestDock ( HasPrivateTraits ): +class TestDock(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - button1 = Button - button2 = Button - button3 = Button - button4 = Button - button5 = Button - button6 = Button + button1 = Button() + button2 = Button() + button3 = Button() + button4 = Button() + button5 = Button() + button6 = Button() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- + + view = View( + ["button1"], + ["button2"], + ["button3"], + ["button4"], + ["button5"], + ["button6"], + title="DockWindow Test", + resizable=True, + width=0.5, + height=0.5, + buttons=NoButtons, + ) - view = View( [ 'button1' ], - [ 'button2' ], - [ 'button3' ], - [ 'button4' ], - [ 'button5' ], - [ 'button6' ], - title = 'DockWindow Test', - resizable = True, - width = 0.5, - height = 0.5, - buttons = NoButtons ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Run the test program: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": TestDock().configure_traits() diff -Nru python-pyface-6.1.2/examples/dock/dock_test3.py python-pyface-7.4.0/examples/dock/dock_test3.py --- python-pyface-6.1.2/examples/dock/dock_test3.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/examples/dock/dock_test3.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,96 +1,89 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Test the DockWindow. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Written by: David C. Morrill -# -# Date: 10/20/2005 -# -# (c) Copyright 2005 by Enthought, Inc. -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! import sys -from traits.api \ - import * +from traits.api import * -from traitsui.api \ - import * +from traitsui.api import * -from traitsui.menu \ - import * +from traitsui.menu import * -from pyface.image_resource \ - import ImageResource +from pyface.image_resource import ImageResource -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Constants: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -image1 = ImageResource( 'folder' ) -image2 = ImageResource( 'gear' ) +image1 = ImageResource("folder") +image2 = ImageResource("gear") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'TestDock' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class TestDock ( HasPrivateTraits ): +class TestDock(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - button1 = Button - button2 = Button - button3 = Button - button4 = Button - button5 = Button - button6 = Button - button7 = Button - button8 = Button - button9 = Button - button10 = Button - button11 = Button - button12 = Button - code1 = Code - code2 = Code + button1 = Button() + button2 = Button() + button3 = Button() + button4 = Button() + button5 = Button() + button6 = Button() + button7 = Button() + button8 = Button() + button9 = Button() + button10 = Button() + button11 = Button() + button12 = Button() + code1 = Code() + code2 = Code() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- view = View( HSplit( VSplit( - Tabbed( 'button1', 'button2', image = image1 ), - Tabbed( 'button3', 'button4', image = image2 ) + Tabbed("button1", "button2", image=image1), + Tabbed("button3", "button4", image=image2), ), - Tabbed( VSplit( 'button5', 'button6' ), - Tabbed( 'button7', 'button8' ), - HSplit( 'button9', 'button10' ), - Group( 'code1@', '|<>', image = image1 ), - Group( 'code2@', '|<>', image = image2 ), - Group( 'button11', 'button12' ) + Tabbed( + VSplit("button5", "button6"), + Tabbed("button7", "button8"), + HSplit("button9", "button10"), + Group("code1@", "|<>", image=image1), + Group("code2@", "|<>", image=image2), + Group("button11", "button12"), ), - id = 'dock_window' + id="dock_window", ), - title = 'DockWindow Test', - id = 'pyface.dock.dock_test3', - dock = 'horizontal', - resizable = True, - width = 0.5, - height = 0.5, - buttons = NoButtons + title="DockWindow Test", + id="pyface.dock.dock_test3", + dock="horizontal", + resizable=True, + width=0.5, + height=0.5, + buttons=NoButtons, ) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Run the test program: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": TestDock().configure_traits() diff -Nru python-pyface-6.1.2/examples/dock/dock_test.py python-pyface-7.4.0/examples/dock/dock_test.py --- python-pyface-6.1.2/examples/dock/dock_test.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/dock/dock_test.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,132 +1,152 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Test the DockWindow. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Written by: David C. Morrill -# -# Date: 10/20/2005 -# -# (c) Copyright 2005 by Enthought, Inc. -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! import wx import sys -from traits.api \ - import * +from traits.api import * -from traitsui.api \ - import * +from traitsui.api import * -from traitsui.menu \ - import * +from traitsui.menu import * -from pyface.dock.api \ - import * +from pyface.dock.api import * -from pyface.image_resource \ - import ImageResource +from pyface.image_resource import ImageResource -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Global data: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # DockControl style to use: -style1 = 'horizontal' -style2 = 'vertical' +style1 = "horizontal" +style2 = "vertical" -image1 = ImageResource( 'folder' ) -image2 = ImageResource( 'gear' ) +image1 = ImageResource("folder") +image2 = ImageResource("gear") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Creates a DockWindow as a Traits UI widget: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -def create_dock_window ( parent, editor ): + +def create_dock_window(parent, editor): """ Creates a window for editing a workflow canvas. """ - window = DockWindow( parent ).control - button1 = wx.Button( window, -1, 'Button 1' ) - button2 = wx.Button( window, -1, 'Button 2' ) - button3 = wx.Button( window, -1, 'Button 3' ) - button4 = wx.Button( window, -1, 'Button 4' ) - button5 = wx.Button( window, -1, 'Button 5' ) - button6 = wx.Button( window, -1, 'Button 6' ) - sizer = DockSizer( contents = - [ DockControl( name = 'Button 1', - image = image1, - closeable = True, - control = button1, - style = style1 ), - [ DockControl( name = 'Button 2', - image = image1, - closeable = True, - height = 400, - control = button2, - style = style1 ), - ( [ DockControl( name = 'Button 3', - image = image2, - resizable = False, - control = button3, - style = style2 ), - DockControl( name = 'Button 4', - image = image2, - resizable = False, - control = button4, - style = style2 ) ], - [ DockControl( name = 'Button 5', - resizable = False, - control = button5, - style = style2 ), - DockControl( name = 'Button 6', - resizable = False, - control = button6, - style = style2 ) ] ) - ] - ] ) - window.SetSizer( sizer ) - window.SetAutoLayout( True ) + window = DockWindow(parent).control + button1 = wx.Button(window, -1, "Button 1") + button2 = wx.Button(window, -1, "Button 2") + button3 = wx.Button(window, -1, "Button 3") + button4 = wx.Button(window, -1, "Button 4") + button5 = wx.Button(window, -1, "Button 5") + button6 = wx.Button(window, -1, "Button 6") + sizer = DockSizer( + contents=[ + DockControl( + name="Button 1", + image=image1, + closeable=True, + control=button1, + style=style1, + ), + [ + DockControl( + name="Button 2", + image=image1, + closeable=True, + height=400, + control=button2, + style=style1, + ), + ( + [ + DockControl( + name="Button 3", + image=image2, + resizable=False, + control=button3, + style=style2, + ), + DockControl( + name="Button 4", + image=image2, + resizable=False, + control=button4, + style=style2, + ), + ], + [ + DockControl( + name="Button 5", + resizable=False, + control=button5, + style=style2, + ), + DockControl( + name="Button 6", + resizable=False, + control=button6, + style=style2, + ), + ], + ), + ], + ] + ) + window.SetSizer(sizer) + window.SetAutoLayout(True) return window -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'TestDock' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class TestDock ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class TestDock(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - dummy = Int + dummy = Int() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- + + view = View( + [ + Item( + "dummy", + resizable=True, + editor=CustomEditor(create_dock_window), + ), + "|<>", + ], + title="DockWindow Test", + resizable=True, + width=0.5, + height=0.5, + buttons=NoButtons, + ) - view = View( [ Item( 'dummy', - resizable = True, - editor = CustomEditor( create_dock_window ) ), - '|<>' ], - title = 'DockWindow Test', - resizable = True, - width = 0.5, - height = 0.5, - buttons = NoButtons ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Run the test program: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -if __name__ == '__main__': - if len( sys.argv ) > 1: +if __name__ == "__main__": + if len(sys.argv) > 1: style1 = style2 = sys.argv[1] - if len( sys.argv ) > 2: + if len(sys.argv) > 2: style2 = sys.argv[2] TestDock().configure_traits() diff -Nru python-pyface-6.1.2/examples/dock/drag_test.py python-pyface-7.4.0/examples/dock/drag_test.py --- python-pyface-6.1.2/examples/dock/drag_test.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/dock/drag_test.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,203 +1,231 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Test the DockWindow. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Written by: David C. Morrill -# -# Date: 10/20/2005 -# -# (c) Copyright 2005 by Enthought, Inc. -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! import wx -from traits.api \ - import * +from traits.api import * -from traitsui.api \ - import * +from traitsui.api import * -from traitsui.menu \ - import * +from traitsui.menu import * -from traitsui.dockable_view_element \ - import DockableViewElement +from traitsui.dockable_view_element import DockableViewElement -from pyface.image_resource \ - import ImageResource +from pyface.image_resource import ImageResource -from pyface.dock.api \ - import * +from pyface.dock.api import * -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Global data: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # DockControl style to use: -style = 'tab' +style = "tab" -image1 = ImageResource( 'folder' ) -image2 = ImageResource( 'gear' ) +image1 = ImageResource("folder") +image2 = ImageResource("gear") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'AnEditor' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class AnEditor ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class AnEditor(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - code1 = Code - code2 = Code - name = Str( 'Mike Noggins' ) - address = Str( '1313 Drury Lane' ) - shell = PythonValue + code1 = Code() + code2 = Code() + name = Str("Mike Noggins") + address = Str("1313 Drury Lane") + shell = PythonValue - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- + + traits_view = View( + VSplit( + VGroup("code1@", "|<>"), + VGroup("code2@", "|<>"), + VGroup("name", "address"), + VGroup("shell", "|{Python Shell}<>"), + export="editor", + show_labels=False, + ), + kind="subpanel", + resizable=True, + buttons=NoButtons, + dock="horizontal", + ) - traits_view = View( VSplit( VGroup( 'code1@', '|<>' ), - VGroup( 'code2@', '|<>' ), - VGroup( 'name', 'address' ), - VGroup( 'shell', '|{Python Shell}<>' ), - export = 'editor', - show_labels = False ), - kind = 'subpanel', - resizable = True, - buttons = NoButtons, - dock = 'horizontal' ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'AView' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class AView ( HasPrivateTraits ): +class AView(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - code1 = Code - code2 = Code + code1 = Code() + code2 = Code() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - traits_view = View( VSplit( 'code1@', 'code2@', - show_labels = False ), - imports = [ 'editor' ], - dock = 'horizontal', - kind = 'subpanel' ) + traits_view = View( + VSplit("code1@", "code2@", show_labels=False), + imports=["editor"], + dock="horizontal", + kind="subpanel", + ) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Creates a DockWindow as a Traits UI widget: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -def create_dock_window ( parent, editor ): +def create_dock_window(parent, editor): """ Creates a window for editing a workflow canvas. """ try: - main = DockWindow( parent ).control - view_uis = [ AView().edit_traits( parent = main ) for i in range( 6 ) ] - views = [ ui.control for ui in view_uis ] - edit = DockWindow( main ).control - editors = [ AnEditor().edit_traits( parent = edit ) for i in range( 6 ) ] - controls = [] - for i in range( 6 ): - dockable = DockableViewElement( ui = editors[i] ) - controls.append( DockControl( - name = 'Editor %d' % (i + 1), - image = image1, - closeable = True, - on_close = dockable.close_dock_control, - control = editors[i].control, - export = 'editor', - dockable = dockable, - style = style ) ) - edit_sizer = DockSizer( contents = [ tuple( controls ) ] ) - dve0 = DockableViewElement( ui = view_uis[0] ) - dve1 = DockableViewElement( ui = view_uis[1] ) - main_sizer = DockSizer( contents = - [ [ DockControl( name = 'View 1', - image = image1, - closeable = True, - on_close = dve0.close_dock_control, - dockable = dve0, - control = views[0], - style = style ), - DockControl( name = 'View 2', - image = image1, - closeable = True, - on_close = dve1.close_dock_control, - dockable = dve1, - height = 400, - control = views[1], - style = style ) ], - [ DockControl( name = 'Editors', - image = image1, - control = edit, - style = 'fixed' ), - [ DockControl( name = 'View 3', - image = image2, - control = views[2], - style = style ), - DockControl( name = 'View 4', - image = image2, - control = views[3], - style = style ) ] ], - [ DockControl( name = 'View 5', - control = views[4], - style = style ), - DockControl( name = 'View 6', - control = views[5], - style = style ) ] ] ) - edit.SetSizer( edit_sizer ) - main.SetSizer( main_sizer ) + main = DockWindow(parent).control + view_uis = [AView().edit_traits(parent=main) for i in range(6)] + views = [ui.control for ui in view_uis] + edit = DockWindow(main).control + editors = [AnEditor().edit_traits(parent=edit) for i in range(6)] + controls = [] + for i in range(6): + dockable = DockableViewElement(ui=editors[i]) + controls.append( + DockControl( + name="Editor %d" % (i + 1), + image=image1, + closeable=True, + on_close=dockable.close_dock_control, + control=editors[i].control, + export="editor", + dockable=dockable, + style=style, + ) + ) + edit_sizer = DockSizer(contents=[tuple(controls)]) + dve0 = DockableViewElement(ui=view_uis[0]) + dve1 = DockableViewElement(ui=view_uis[1]) + main_sizer = DockSizer( + contents=[ + [ + DockControl( + name="View 1", + image=image1, + closeable=True, + on_close=dve0.close_dock_control, + dockable=dve0, + control=views[0], + style=style, + ), + DockControl( + name="View 2", + image=image1, + closeable=True, + on_close=dve1.close_dock_control, + dockable=dve1, + height=400, + control=views[1], + style=style, + ), + ], + [ + DockControl( + name="Editors", + image=image1, + control=edit, + style="fixed", + ), + [ + DockControl( + name="View 3", + image=image2, + control=views[2], + style=style, + ), + DockControl( + name="View 4", + image=image2, + control=views[3], + style=style, + ), + ], + ], + [ + DockControl(name="View 5", control=views[4], style=style), + DockControl(name="View 6", control=views[5], style=style), + ], + ] + ) + edit.SetSizer(edit_sizer) + main.SetSizer(main_sizer) - return main + return main except: import traceback + traceback.print_exc() raise -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'EnvisageDock' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class EnvisageDock ( HasPrivateTraits ): +class EnvisageDock(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - dummy = Int + dummy = Int() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- + + view = View( + [ + Item( + "dummy", + resizable=True, + editor=CustomEditor(create_dock_window), + ), + "|<>", + ], + title="Envisage DockWindow Mock-up", + resizable=True, + width=1.00, + height=1.00, + buttons=NoButtons, + ) - view = View( [ Item( 'dummy', - resizable = True, - editor = CustomEditor( create_dock_window ) ), - '|<>' ], - title = 'Envisage DockWindow Mock-up', - resizable = True, - width = 1.00, - height = 1.00, - buttons = NoButtons ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Run the test program: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": EnvisageDock().configure_traits() diff -Nru python-pyface-6.1.2/examples/dock/envisage_test.py python-pyface-7.4.0/examples/dock/envisage_test.py --- python-pyface-6.1.2/examples/dock/envisage_test.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/dock/envisage_test.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,131 +1,146 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Test the DockWindow. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Written by: David C. Morrill -# -# Date: 10/20/2005 -# -# (c) Copyright 2005 by Enthought, Inc. -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! import wx -from traits.api \ - import * +from traits.api import * -from traitsui.api \ - import * +from traitsui.api import * -from traitsui.menu \ - import * +from traitsui.menu import * -from pyface.image_resource \ - import ImageResource +from pyface.image_resource import ImageResource -from pyface.dock.api \ - import * +from pyface.dock.api import * -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Global data: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # DockControl style to use: -style = 'tab' +style = "tab" -image1 = ImageResource( 'folder' ) -image2 = ImageResource( 'gear' ) +image1 = ImageResource("folder") +image2 = ImageResource("gear") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Creates a DockWindow as a Traits UI widget: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -def create_dock_window ( parent, editor ): + +def create_dock_window(parent, editor): """ Creates a window for editing a workflow canvas. """ - main = DockWindow( parent ).control - views = [ wx.Button( main, -1, 'View %d' % (i + 1) ) - for i in range( 6 ) ] - edit = DockWindow( main ).control - editors = [ wx.Button( edit, -1, 'Editor %d' % (i + 1) ) - for i in range( 6 ) ] - controls = [ DockControl( name = 'Editor %d' % (i + 1), - image = image1, - closeable = True, - control = editors[i], - style = style ) - for i in range( 6 ) ] - controls[0].export = 'any' - edit_sizer = DockSizer( contents = [ tuple( controls ) ] ) - main_sizer = DockSizer( contents = - [ [ DockControl( name = 'View 1', - image = image1, - closeable = True, - control = views[0], - style = style ), - DockControl( name = 'View 2', - image = image1, - closeable = True, - height = 400, - control = views[1], - style = style ) ], - [ DockControl( name = 'Editors', - image = image1, - control = edit, - style = 'fixed' ), - [ DockControl( name = 'View 3', - image = image2, - control = views[2], - style = style ), - DockControl( name = 'View 4', - image = image2, - control = views[3], - style = style ) ] ], - [ DockControl( name = 'View 5', - control = views[4], - style = style ), - DockControl( name = 'View 6', - control = views[5], - style = style ) ] ] ) - edit.SetSizer( edit_sizer ) - main.SetSizer( main_sizer ) + main = DockWindow(parent).control + views = [wx.Button(main, -1, "View %d" % (i + 1)) for i in range(6)] + edit = DockWindow(main).control + editors = [wx.Button(edit, -1, "Editor %d" % (i + 1)) for i in range(6)] + controls = [ + DockControl( + name="Editor %d" % (i + 1), + image=image1, + closeable=True, + control=editors[i], + style=style, + ) + for i in range(6) + ] + controls[0].export = "any" + edit_sizer = DockSizer(contents=[tuple(controls)]) + main_sizer = DockSizer( + contents=[ + [ + DockControl( + name="View 1", + image=image1, + closeable=True, + control=views[0], + style=style, + ), + DockControl( + name="View 2", + image=image1, + closeable=True, + height=400, + control=views[1], + style=style, + ), + ], + [ + DockControl( + name="Editors", image=image1, control=edit, style="fixed" + ), + [ + DockControl( + name="View 3", + image=image2, + control=views[2], + style=style, + ), + DockControl( + name="View 4", + image=image2, + control=views[3], + style=style, + ), + ], + ], + [ + DockControl(name="View 5", control=views[4], style=style), + DockControl(name="View 6", control=views[5], style=style), + ], + ] + ) + edit.SetSizer(edit_sizer) + main.SetSizer(main_sizer) return main -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'EnvisageDock' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class EnvisageDock ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class EnvisageDock(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - dummy = Int + dummy = Int() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- + + view = View( + [ + Item( + "dummy", + resizable=True, + editor=CustomEditor(create_dock_window), + ), + "|<>", + ], + title="Envisage DockWindow Mock-up", + resizable=True, + width=1.00, + height=1.00, + buttons=NoButtons, + ) - view = View( [ Item( 'dummy', - resizable = True, - editor = CustomEditor( create_dock_window ) ), - '|<>' ], - title = 'Envisage DockWindow Mock-up', - resizable = True, - width = 1.00, - height = 1.00, - buttons = NoButtons ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Run the test program: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": EnvisageDock().configure_traits() diff -Nru python-pyface-6.1.2/examples/dock/images/image_LICENSE.txt python-pyface-7.4.0/examples/dock/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/dock/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/dock/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Nuvola LGPL image_LICENSE_Nuvola.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -examples/dock/images: - folder.png | Nuvola - gear.png | Nuvola diff -Nru python-pyface-6.1.2/examples/expandable.py python-pyface-7.4.0/examples/expandable.py --- python-pyface-6.1.2/examples/expandable.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/expandable.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,60 +1,52 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Expandable example. """ +# Thanks for using Enthought open source! +""" ExpandablePanel example. -# Standard library imports. -import os, sys +This is currently only supported by the WxPython toolkit. +""" -# Major package imports. -import wx +import os -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import Float, Str -# Enthought library imports. from pyface.api import GUI, PythonShell, SplitApplicationWindow from pyface.expandable_panel import ExpandablePanel -from traits.api import Float, Str -# Local imports. from file_tree import FileTree class MainWindow(SplitApplicationWindow): """ The main application window. """ - #### 'SplitApplicationWindow' interface ################################### + # 'SplitApplicationWindow' interface ----------------------------------- # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the window is split. - direction = Str('vertical') + direction = Str("vertical") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the left hand side or top depending on the split. """ - self._expandable = expandable = ExpandablePanel(parent) + self._expandable = expandable = ExpandablePanel(parent, create=False) + self._expandable.create() for i in range(10): panel = self._create_content(expandable.control) - expandable.add_panel('Panel %d' % i, panel) + expandable.add_panel("Panel %d" % i, panel) return expandable.control @@ -64,14 +56,14 @@ widget = self._expandable self._python_shell = PythonShell(parent) - self._python_shell.bind('widget', widget) - self._python_shell.bind('w', widget) + self._python_shell.bind("widget", widget) + self._python_shell.bind("w", widget) return self._python_shell.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_content(self, parent): """ Create some context for an expandable panel. """ @@ -82,7 +74,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -92,5 +84,3 @@ # Start the GUI event loop. gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/explorer.py python-pyface-7.4.0/examples/explorer.py --- python-pyface-6.1.2/examples/explorer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/explorer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,34 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A file explorer example. """ +# Thanks for using Enthought open source! -from __future__ import print_function +""" A file explorer example. + Note: This demo only works on the wx backend. +""" -# Standard library imports. -import os, sys -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +import os -# Enthought library imports. -from pyface.api import ApplicationWindow, GUI, PythonShell, SplashScreen +from pyface.api import GUI, PythonShell, SplashScreen from pyface.api import SplitApplicationWindow, SplitPanel -from pyface.action.api import Action, Group, MenuBarManager, MenuManager +from pyface.action.api import Action, MenuBarManager, MenuManager from pyface.action.api import Separator, StatusBarManager, ToolBarManager from traits.api import Float, Str -# Local imports. from file_filters import AllowOnlyFolders from file_sorters import FileSorter from file_table_viewer import FileTableViewer @@ -38,12 +30,12 @@ class ExampleAction(Action): """ An example action. """ - accelerator = Str('Ctrl-K') + accelerator = Str("Ctrl-K") def perform(self): """ Performs the action. """ - print('Performing', self.name) + print("Performing", self.name) return @@ -51,32 +43,32 @@ class MainWindow(SplitApplicationWindow): """ The main application window. """ - #### 'SplitApplicationWindow' interface ################################### + # 'SplitApplicationWindow' interface ----------------------------------- # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the panel is split. - direction = Str('vertical') + direction = Str("vertical") - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Create the window's menu, tool and status bars. self._create_action_bars() return - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the left hand side or top depending on the style. """ @@ -87,57 +79,56 @@ """ Creates the panel containing the selected preference page. """ self._rhs = SplitPanel( - parent = parent, - lhs = self._create_file_table, - rhs = self._create_python_shell, - direction = 'horizontal' + parent=parent, + lhs=self._create_file_table, + rhs=self._create_python_shell, + direction="horizontal", ) return self._rhs.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_action_bars(self): """ Creates the window's menu, tool and status bars. """ # Common actions. - highest = Action(name='Highest', style='radio') - higher = Action(name='Higher', style='radio', checked=True) - lower = Action(name='Lower', style='radio') - lowest = Action(name='Lowest', style='radio') + highest = Action(name="Highest", style="radio") + higher = Action(name="Higher", style="radio", checked=True) + lower = Action(name="Lower", style="radio") + lowest = Action(name="Lowest", style="radio") self._actions = [highest, higher, lower, lowest] # Menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - ExampleAction(name='Foogle'), + ExampleAction(name="Foogle"), Separator(), highest, higher, lower, lowest, Separator(), - Action(name='E&xit', on_perform=self.close), - - name = '&File', + Action(name="E&xit", on_perform=self.close), + name="&File", ) ) # Tool bar. self.tool_bar_manager = ToolBarManager( - ExampleAction(name='Foo'), + ExampleAction(name="Foo"), Separator(), - ExampleAction(name='Bar'), + ExampleAction(name="Bar"), Separator(), - ExampleAction(name='Baz'), + ExampleAction(name="Baz"), Separator(), highest, higher, lower, - lowest + lowest, ) # Status bar. @@ -150,11 +141,11 @@ self._tree_viewer = tree_viewer = FileTreeViewer( parent, - input = os.path.abspath(os.curdir), - filters = [AllowOnlyFolders()] + input=os.path.abspath(os.curdir), + filters=[AllowOnlyFolders()], ) - tree_viewer.on_trait_change(self._on_selection_changed, 'selection') + tree_viewer.observe(self._on_selection_changed, "selection") return tree_viewer.control @@ -162,9 +153,7 @@ """ Creates the file table. """ self._table_viewer = table_viewer = FileTableViewer( - parent, - sorter = FileSorter(), - odd_row_background = "white" + parent, sorter=FileSorter(), odd_row_background="white" ) return table_viewer.control @@ -173,18 +162,18 @@ """ Creates the Python shell. """ self._python_shell = python_shell = PythonShell(parent) - python_shell.bind('widget', self._tree_viewer) - python_shell.bind('w', self._tree_viewer) - python_shell.bind('window', self) - python_shell.bind('actions', self._actions) + python_shell.bind("widget", self._tree_viewer) + python_shell.bind("w", self._tree_viewer) + python_shell.bind("window", self) + python_shell.bind("actions", self._actions) return python_shell.control - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_selection_changed(self, selection): + def _on_selection_changed(self, event): """ Called when the selection in the tree is changed. """ - + selection = event.new if len(selection) > 0: self._table_viewer.input = selection[0] @@ -192,7 +181,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI and put up a splash screen (this does NOT start the GUI # event loop). gui = GUI(splash_screen=SplashScreen()) @@ -203,5 +192,3 @@ # Start the GUI event loop. gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/file_filters.py python-pyface-7.4.0/examples/file_filters.py --- python-pyface-6.1.2/examples/file_filters.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/file_filters.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Filters for local file system viewers. """ -# Standard library imports. from os.path import isdir -# Enthought library imports. + from pyface.viewer.api import ViewerFilter @@ -34,5 +30,3 @@ """ return isdir(element) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/examples/file_node_tree.py python-pyface-7.4.0/examples/file_node_tree.py --- python-pyface-6.1.2/examples/file_node_tree.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/file_node_tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,29 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A file system tree. """ -# Standard library imports. from os import listdir from os.path import basename, isdir, isfile, join -# Enthought library imports. + from pyface.tree.api import NodeManager, NodeType class FileNode(NodeType): """ Node type for files. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeType' interface. - ########################################################################### + # ------------------------------------------------------------------------ def is_type_for(self, node): """ Returns True if this node type recognizes a node. """ @@ -48,9 +44,9 @@ class FolderNode(NodeType): """ Node type for folders. """ - ######################################################################### + # ------------------------------------------------------------------------ # 'NodeType' interface. - ######################################################################### + # ------------------------------------------------------------------------ def is_type_for(self, node): """ Returns True if this node type recognizes a node. """ @@ -82,5 +78,3 @@ node_manager = NodeManager() node_manager.add_node_type(FileNode()) node_manager.add_node_type(FolderNode()) - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/file_sorters.py python-pyface-7.4.0/examples/file_sorters.py --- python-pyface-6.1.2/examples/file_sorters.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/file_sorters.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Sorters for local file system viewers. """ -# Standard library imports. from os.path import isdir -# Enthought library imports. + from pyface.viewer.api import ViewerSorter @@ -43,5 +39,3 @@ category = 1 return category - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/examples/file_table_viewer.py python-pyface-7.4.0/examples/file_table_viewer.py --- python-pyface-6.1.2/examples/file_table_viewer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/file_table_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,33 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A table viewer for local file systems. """ -# Standard library imports. from os import listdir, stat from os.path import basename, isdir, join from time import localtime, strftime -# Enthought library imports. + from pyface.api import ImageResource from pyface.viewer.api import TableColumnProvider, TableContentProvider from pyface.viewer.api import TableLabelProvider, TableViewer from traits.api import Instance, Str - class FileTableContentProvider(TableContentProvider): """ A table content provider for local file systems. """ - ######################################################################### + # ------------------------------------------------------------------------ # 'TableContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_elements(self, element): """ Returns the label for an element. """ @@ -52,14 +47,14 @@ """ A table label provider for local file systems. """ # The icon used to represent 'folder' elements. - FOLDER = ImageResource('closed_folder') + FOLDER = ImageResource("closed_folder") # The icon used to represent 'document' elements. - DOCUMENT = ImageResource('document') + DOCUMENT = ImageResource("document") - ########################################################################### + # ------------------------------------------------------------------------ # 'TableLabelProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, viewer, element, column_index=0): """ Returns the filename of the label image for an element. """ @@ -81,11 +76,11 @@ label = basename(element) elif column_index == 1: - label = str(int(details.st_size) / 1000) + ' KB' + label = str(int(details.st_size) // 1000) + " KB" else: # Format is: mm/dd/yyyy HH:MM AM eg. '12/31/2004 12:00 PM' - label = strftime('%m/%d/%Y %I:%M %p', localtime(details.st_mtime)) + label = strftime("%m/%d/%Y %I:%M %p", localtime(details.st_mtime)) return label @@ -93,24 +88,24 @@ class FileTableColumnProvider(TableColumnProvider): """ A table column provider for local file systems. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self): """ Creates a new column provider. """ # Column labels. - self._column_labels = ['Name', 'Size', 'Date Modified'] + self._column_labels = ["Name", "Size", "Date Modified"] # The number of columns. self.column_count = len(self._column_labels) return - ########################################################################### + # ------------------------------------------------------------------------ # 'TableColumnProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_label(self, viewer, column_index): """ Returns the label for a column. """ @@ -125,17 +120,18 @@ """ if column_index == 1: - alignment = 'right' + alignment = "right" else: - alignment = 'left' + alignment = "left" return alignment + class FileTableViewer(TableViewer): """ A table viewer for local file systems. """ - #### 'TableViewer' interface ############################################## + # 'TableViewer' interface ---------------------------------------------- # The column provider. column_provider = Instance(FileTableColumnProvider, ()) @@ -146,5 +142,3 @@ # The label provider provides, err, the labels for the items in the tree # (a label can have text and/or an image). label_provider = Instance(FileTableLabelProvider, ()) - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/file_tree.py python-pyface-7.4.0/examples/file_tree.py --- python-pyface-6.1.2/examples/file_tree.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/file_tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A file system tree. """ -# Standard library imports. from os import listdir from os.path import basename, isdir, join -# Enthought library imports. + from pyface.api import ImageResource from pyface.tree.api import Tree, TreeModel from traits.api import Instance @@ -28,17 +24,17 @@ """ A tree model for local file systems. """ # The image used to represent folders that are NOT expanded. - CLOSED_FOLDER = ImageResource('closed_folder') + CLOSED_FOLDER = ImageResource("closed_folder") # The image used to represent folders that ARE expanded. - OPEN_FOLDER = ImageResource('open_folder') + OPEN_FOLDER = ImageResource("open_folder") # The image used to represent documents (ie. NON-'folder') nodes. - DOCUMENT = ImageResource('document') + DOCUMENT = ImageResource("document") - ######################################################################### + # ------------------------------------------------------------------------ # 'TreeModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_children(self, node): """ Returns the children of a node. """ @@ -80,9 +76,7 @@ class FileTree(Tree): """ A file system tree. """ - #### 'Tree' interface ##################################################### + # 'Tree' interface ----------------------------------------------------- # The model that provides the data for the tree. model = Instance(FileTreeModel, ()) - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/file_tree_viewer.py python-pyface-7.4.0/examples/file_tree_viewer.py --- python-pyface-6.1.2/examples/file_tree_viewer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/file_tree_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A tree viewer for local file systems. """ -# Standard library imports. from os import listdir from os.path import basename, dirname, isdir, join -# Enthought library imports. + from pyface.api import ImageResource from pyface.viewer.api import TreeContentProvider, TreeLabelProvider from pyface.viewer.api import TreeViewer @@ -28,9 +24,9 @@ class FileTreeContentProvider(TreeContentProvider): """ A tree content provider for local file systems. """ - ######################################################################### + # ------------------------------------------------------------------------ # 'TreeContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_parent(self, element): """ Returns the parent of an element. """ @@ -63,34 +59,28 @@ """ A tree label provider for local file systems. """ # The image used to represent folders that are NOT expanded. - CLOSED_FOLDER = ImageResource('closed_folder') + CLOSED_FOLDER = ImageResource("closed_folder") # The image used to represent folders that ARE expanded. - OPEN_FOLDER = ImageResource('open_folder') + OPEN_FOLDER = ImageResource("open_folder") # The image used to represent documents (ie. NON-'folder') elements. - DOCUMENT = ImageResource('document') + DOCUMENT = ImageResource("document") - ########################################################################### + # ------------------------------------------------------------------------ # 'TreeLabelProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, viewer, element): """ Returns the filename of the label image for an element. """ - - selected = viewer.is_selected(element) expanded = viewer.is_expanded(element) - if isdir(element): if expanded: image = self.OPEN_FOLDER - else: image = self.CLOSED_FOLDER - else: image = self.DOCUMENT - return image def get_text(self, viewer, element): @@ -102,7 +92,7 @@ class FileTreeViewer(TreeViewer): """ A tree viewer for local file systems. """ - #### 'TreeViewer' interface ############################################### + # 'TreeViewer' interface ----------------------------------------------- # The content provider provides the actual tree data. content_provider = Instance(FileTreeContentProvider, ()) @@ -110,5 +100,3 @@ # The label provider provides, err, the labels for the items in the tree # (a label can have text and/or an image). label_provider = Instance(FileTreeLabelProvider, ()) - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/grid.py python-pyface-7.4.0/examples/grid.py --- python-pyface-6.1.2/examples/grid.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/grid.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,89 +1,96 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Expandable example. """ - +# Thanks for using Enthought open source! -# Standard library imports. -import os, sys +""" Grid example. -# Major package imports. -import wx +This is currently only supported by the WxPython toolkit. +""" +import os -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import Float, Str +from traits.observation.api import match -# Enthought library imports. from pyface.api import GUI, PythonShell, SplitApplicationWindow -from pyface.ui.wx.grid.api import Grid, TraitGridModel, \ - SimpleGridModel, GridRow, GridColumn, TraitGridColumn -from traits.api import Float, Str +from pyface.ui.wx.grid.api import ( + Grid, + TraitGridModel, + GridRow, + GridColumn, + TraitGridColumn, +) + +from file_tree import FileTree class MainWindow(SplitApplicationWindow): """ The main application window. """ - #### 'SplitApplicationWindow' interface ################################### + # 'SplitApplicationWindow' interface ----------------------------------- # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the window is split. - direction = Str('vertical') + direction = Str("vertical") # The data used to create the SimpleGridModel - data = [['bob', 1, True, Float], - ['sarah', 45, True, Str], - ['jonas', -3, False, direction]] - - rows = [GridRow(name='Row 1'), - GridRow(name='Row 2'), - GridRow(name='Row 3')] - - cols = [GridColumn(name='Name'), - GridColumn(name='Index', read_only=True), - GridColumn(name='Veracity'), - GridColumn(name='Object')] + data = [ + ["bob", 1, True, Float], + ["sarah", 45, True, Str], + ["jonas", -3, False, direction], + ] + + rows = [ + GridRow(name="Row 1"), + GridRow(name="Row 2"), + GridRow(name="Row 3"), + ] + + cols = [ + GridColumn(name="Name"), + GridColumn(name="Index", read_only=True), + GridColumn(name="Veracity"), + GridColumn(name="Object"), + ] # The data used to create the TraitGridModel - trait_data = [GridRow(name='Bob', index=1, veracity=True, object=Float), - GridRow(name='Sarah', index=45, veracity=True, object=Str), - GridRow(name='Jonas', index=-3, veracity=False, object=direction)] - - trait_col = [TraitGridColumn(name='name', label='Name'), - TraitGridColumn(name='index', label='Index', read_only=True), - TraitGridColumn(name='veracity', label='Veracity'), - TraitGridColumn(name='object', label='Object')] + trait_data = [ + GridRow(name="Bob", index=1, veracity=True, object=Float), + GridRow(name="Sarah", index=45, veracity=True, object=Str), + GridRow(name="Jonas", index=-3, veracity=False, object=direction), + ] + + trait_col = [ + TraitGridColumn(name="name", label="Name"), + TraitGridColumn(name="index", label="Index", read_only=True), + TraitGridColumn(name="veracity", label="Veracity"), + TraitGridColumn(name="object", label="Object"), + ] - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the left hand side or top depending on the split. """ - #self._model = model = SimpleGridModel(data = self.data, - # rows = self.rows, - # columns = self.cols) - - self._model = model = TraitGridModel(data = self.trait_data, - columns = self.trait_col, - row_name_trait = 'name') - - self._grid = grid = Grid(parent, model = model) - - - self._grid.on_trait_change(self._on_grid_anytrait_changed) + self._model = model = TraitGridModel( + data=self.trait_data, columns=self.trait_col, row_name_trait="name" + ) + + self._grid = grid = Grid(parent, model=model) + + self._grid.observe( + self._on_grid_anytrait_changed, + match(lambda name, ctrait: True) # listen to all traits + ) return grid.control @@ -93,14 +100,14 @@ widget = self._grid self._python_shell = PythonShell(parent) - self._python_shell.bind('widget', widget) - self._python_shell.bind('w', widget) + self._python_shell.bind("widget", widget) + self._python_shell.bind("w", widget) return self._python_shell.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_content(self, parent): """ Create some context for an expandable panel. """ @@ -109,18 +116,18 @@ return tree.control - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_grid_anytrait_changed(self, tree, trait_name, old, new): + def _on_grid_anytrait_changed(self, event): """ Called when any trait on the tree has changed. """ - print 'trait', trait_name, 'value', new + print("trait", event.name, "value", event.new) return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -130,5 +137,3 @@ # Start the GUI event loop. gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/hello_world.py python-pyface-7.4.0/examples/hello_world.py --- python-pyface-6.1.2/examples/hello_world.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/hello_world.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,35 +1,32 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2009, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Hello world example. """ -# Enthought library imports. + from pyface.api import ApplicationWindow, GUI, HeadingText class MainWindow(ApplicationWindow): """ The main application window. """ - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'IWindow' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # The window title. - title = 'Hello World' + title = "Hello World" - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'IApplicationWindow' interface. - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def _create_contents(self, parent): """ Create the editor. """ @@ -52,5 +49,5 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru python-pyface-6.1.2/examples/images/image_LICENSE.txt python-pyface-7.4.0/examples/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -GV (Gael Varoquaux) Public Domain N/A -Nuvola LGPL image_LICENSE_Nuvola.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -examples/images: - closed_folder_24x24.png | Nuvola - closed_folder.png | Nuvola - document.png | GV - open_folder.png | Nuvola diff -Nru python-pyface-6.1.2/examples/image_widget.py python-pyface-7.4.0/examples/image_widget.py --- python-pyface-6.1.2/examples/image_widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/image_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,31 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Application window example. """ - +# Thanks for using Enthought open source! -# Standard library imports. -import os, sys +""" ImageWidget example. -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +This is only supported by the WxPython toolkit. +""" -# Major package imports. import wx import wx.html import wx.lib.wxpTag -# Enthought library imports. + from pyface.api import ApplicationWindow, GUI, ImageResource, ImageWidget from pyface.action.api import Action, MenuManager, MenuBarManager @@ -43,32 +35,32 @@ PART = """""" + class MainWindow(ApplicationWindow): """ The main application window. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - Action(name='E&xit', on_perform=self.close), - name = '&File', + Action(name="E&xit", on_perform=self.close), name="&File" ) ) return - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Creates the window contents. @@ -89,7 +81,7 @@ # Create the HTML. parts = [] for i in range(N): - parts.append(PART % str(wxid+i)) + parts.append(PART % str(wxid + i)) html = HTML % "".join(parts) @@ -99,7 +91,7 @@ # Initialize all embedded wx controls. for i in range(N): - self._initialize_window(html_window, wxid+i) + self._initialize_window(html_window, wxid + i) sizer.Add(html_window, 1, wx.EXPAND) @@ -108,9 +100,9 @@ return panel - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _initialize_window(self, parent, wxid): """ Initialize the window with the specified Id. """ @@ -120,14 +112,14 @@ window.SetSizer(sizer) window.SetAutoLayout(True) - window.SetBackgroundColour('white') + window.SetBackgroundColour("white") window.SetWindowStyleFlag(wx.CLIP_CHILDREN) window.Refresh() - image = ImageResource('closed_folder_24x24') + image = ImageResource("closed_folder_24x24") bitmap = image.create_image().ConvertToBitmap() image_widget = ImageWidget(window, bitmap=bitmap) - image_widget.control.SetBackgroundColour('white') + image_widget.control.SetBackgroundColour("white") sizer.Add(image_widget.control, 0, wx.EXPAND) text = wx.StaticText(window, -1, "Blah", style=wx.ALIGN_CENTRE) @@ -140,7 +132,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -150,5 +142,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/ipython_shell.py python-pyface-7.4.0/examples/ipython_shell.py --- python-pyface-6.1.2/examples/ipython_shell.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/ipython_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,16 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2009, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ IPython widget example. """ -# Enthought library imports. + from pyface.api import ApplicationWindow, GUI from pyface.ipython_widget import IPythonWidget @@ -21,17 +18,17 @@ class MainWindow(ApplicationWindow): """ The main application window. """ - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------- # The size of the window. size = (640, 480) # The window title. - title = 'IPython' + title = "IPython" - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplication' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create the editor. """ @@ -42,7 +39,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI. gui = GUI() @@ -52,5 +49,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/mdi_application_window.py python-pyface-7.4.0/examples/mdi_application_window.py --- python-pyface-6.1.2/examples/mdi_application_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/mdi_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Application window example. """ - +# Thanks for using Enthought open source! -# Standard library imports. -import os, sys +""" Application window example. -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +This example only works with the WxPython toolkit. +""" -# Enthought library imports. from pyface.api import MDIApplicationWindow, MDIWindowMenu, GUI from pyface.action.api import Action, MenuManager, MenuBarManager @@ -28,24 +20,22 @@ class MainWindow(MDIApplicationWindow): """ The main application window. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - Action(name='E&xit', on_perform=self.close), - name = '&File', + Action(name="E&xit", on_perform=self.close), name="&File" ), - - MDIWindowMenu(self) + MDIWindowMenu(self), ) # Set the size of the window @@ -55,7 +45,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -70,5 +60,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/menu_manager.py python-pyface-7.4.0/examples/menu_manager.py --- python-pyface-6.1.2/examples/menu_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,122 +1,96 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from __future__ import print_function - -# Standard library imports. -import os, sys +# Thanks for using Enthought open source! -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +""" Menu Manager example. """ -# Local imports. from pyface.action.api import Action from pyface.action.api import Group, MenuManager, Separator file_menu = MenuManager( Group( - Action(name='New Project...'), - Action(name='Open Project...'), - - id = 'OpenGroup', - ), - - Group( - Action(name='Close Project'), - Action(name='Close Active Editor'), - - id = 'CloseGroup' + Action(name="New Project..."), + Action(name="Open Project..."), + id="OpenGroup", ), - Group( - Action(name='Export to HTML...'), - Action(name='Print...'), - - id = 'ExportGroup' + Action(name="Close Project"), + Action(name="Close Active Editor"), + id="CloseGroup", ), - Group( - Action(name='Exit'), - - id = 'ExitGroup' + Action(name="Export to HTML..."), + Action(name="Print..."), + id="ExportGroup", ), + Group(Action(name="Exit"), id="ExitGroup"), ) file_menu.dump() -############################################################################### +# ---------------------------------------------------------------------------- -file_menu = MenuManager( - Action(name='New Project...'), - Action(name='Open Project...'), +file_menu = MenuManager( + Action(name="New Project..."), + Action(name="Open Project..."), Separator(), - - Action(name='Close Project'), - Action(name='Close Active Editor'), - + Action(name="Close Project"), + Action(name="Close Active Editor"), Separator(), - - Action(name='Export to HTML...'), - Action(name='Print...'), - + Action(name="Export to HTML..."), + Action(name="Print..."), Separator(), - - Action(name='Exit'), + Action(name="Exit"), ) file_menu.dump() -############################################################################### - +# ---------------------------------------------------------------------------- def new_project(): - print('new project') + print("new project") + def open_project(): - print('open project') + print("open project") + def close_project(): - print('close project') + print("close project") + def close_active_editor(): - print('close active editor') + print("close active editor") + def export_to_html(): - print('export to html') + print("export to html") + def printit(): - print('print') + print("print") + def exit(): - print('exit') + print("exit") file_menu = MenuManager( open_project, - Separator(), - close_project, close_active_editor, - Separator(), - export_to_html, printit, - Separator(), - exit, ) file_menu.dump() diff -Nru python-pyface-6.1.2/examples/multi_toolbar_window.py python-pyface-7.4.0/examples/multi_toolbar_window.py --- python-pyface-6.1.2/examples/multi_toolbar_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/multi_toolbar_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,34 +1,31 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Mulit-tool bar example. """ +# Thanks for using Enthought open source! + +""" Mulit-tool bar example. +Note: This demo only works on the wx backend. +""" -# Standard library imports. -import os, sys -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +import sys # FIXME: This is a hack to disable the AUI module which causes the example to # not layout correctly. try: import wx - sys.modules['wx.aui'] = None + + sys.modules["wx.aui"] = None except: pass -# Enthought library imports. + from pyface.api import MultiToolbarWindow, GUI from pyface.action.api import Action, MenuManager, MenuBarManager from pyface.action.api import ToolBarManager @@ -37,49 +34,48 @@ class MainWindow(MultiToolbarWindow): """ The main application window. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - Action(name='E&xit', on_perform=self.close), - name = '&File', + Action(name="E&xit", on_perform=self.close), name="&File" ) ) # Add a menu bar at each location. self.add_tool_bar( - ToolBarManager(Action(name='Foo'), orientation='horizontal') + ToolBarManager(Action(name="Foo"), orientation="horizontal") ) self.add_tool_bar( - ToolBarManager(Action(name='Bar'), orientation='horizontal'), - location = 'bottom' + ToolBarManager(Action(name="Bar"), orientation="horizontal"), + location="bottom", ) self.add_tool_bar( - ToolBarManager(Action(name='Baz'), orientation='vertical'), - location = 'left' + ToolBarManager(Action(name="Baz"), orientation="vertical"), + location="left", ) self.add_tool_bar( - ToolBarManager(Action(name='Buz'), orientation='vertical'), - location = 'right' + ToolBarManager(Action(name="Buz"), orientation="vertical"), + location="right", ) return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -89,5 +85,3 @@ # Start the GUI event loop. gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/node_tree.py python-pyface-7.4.0/examples/node_tree.py --- python-pyface-6.1.2/examples/node_tree.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/node_tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,49 +1,40 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Node tree example. """ +# Thanks for using Enthought open source! -from __future__ import print_function +""" Node tree example. """ -# Standard library imports. -import os, sys +import os -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import Float, Str +from traits.observation.api import match -# Enthought library imports. from pyface.api import GUI, PythonShell, SplitApplicationWindow -from traits.api import Float, Str +from pyface.tree.api import NodeTree, NodeTreeModel -# Local imports. from file_node_tree import node_manager -from pyface.tree.api import NodeTree, NodeTreeModel class MainWindow(SplitApplicationWindow): """ The main application window. """ - #### 'SplitApplicationWindow' interface ################################### + # 'SplitApplicationWindow' interface ----------------------------------- # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the window is split. - direction = Str('vertical') + direction = Str("vertical") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the left hand side or top depending on the split. """ @@ -52,7 +43,10 @@ model.root = os.path.abspath(os.curdir) self._tree = NodeTree(parent, model=model) - self._tree.on_trait_change(self._on_tree_anytrait_changed) + self._tree.observe( + self._on_tree_anytrait_changed, + match(lambda name, ctrait: True) # listen to all traits + ) return self._tree.control @@ -60,27 +54,27 @@ """ Creates the right hand side or bottom depending on the split. """ self._python_shell = PythonShell(parent) - self._python_shell.bind('widget', self._tree) - self._python_shell.bind('w', self._tree) + self._python_shell.bind("widget", self._tree) + self._python_shell.bind("w", self._tree) return self._python_shell.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_tree_anytrait_changed(self, tree, trait_name, old, new): + def _on_tree_anytrait_changed(self, event): """ Called when any trait on the tree has changed. """ - print('trait', trait_name, 'value', new) + print("trait", event.name, "value", event.new) return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -90,5 +84,3 @@ # Start the GUI event loop. gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/progress_dialog.py python-pyface-7.4.0/examples/progress_dialog.py --- python-pyface-6.1.2/examples/progress_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/progress_dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -# -# simple example of its use -# -from __future__ import print_function - -import time -from pyface.api import GUI, ApplicationWindow, ProgressDialog -from pyface.action.api import Action, MenuManager, MenuBarManager - -def task_func(t): - progress = ProgressDialog(title="progress", message="counting to %d"%t, max=t, show_time=True, can_cancel=True) - progress.open() - - for i in range(0,t+1): - time.sleep(1) - print(i) - (cont, skip) = progress.update(i) - if not cont or skip: - break - - progress.update(t) - -def _main(): - task_func(10) - -class MainWindow(ApplicationWindow): - """ The main application window. """ - - ###################################################################### - # 'object' interface. - ###################################################################### - - def __init__(self, **traits): - """ Creates a new application window. """ - - # Base class constructor. - super(MainWindow, self).__init__(**traits) - - # Add a menu bar. - self.menu_bar_manager = MenuBarManager( - MenuManager( - Action(name='E&xit', on_perform=self.close), - Action(name='DoIt', on_perform=_main), - name = '&File', - ) - ) - - return - - -if __name__ == "__main__": - gui = GUI() - - # Create and open the main window. - window = MainWindow() - window.open() - - _main() - - # Start the GUI event loop! - gui.start_event_loop() diff -Nru python-pyface-6.1.2/examples/python_editor.py python-pyface-7.4.0/examples/python_editor.py --- python-pyface-6.1.2/examples/python_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,24 @@ -#------------------------------------------------------------------------------ # Copyright (c) 2007, Riverbank Computing Limited. # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Riverbank Computing Limited. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Python editor example. """ -from __future__ import print_function, unicode_literals -from pyface.api import ( - ApplicationWindow, FileDialog, GUI, OK, PythonEditor -) +from pyface.api import ApplicationWindow, FileDialog, GUI, OK, PythonEditor from pyface.action.api import ( - Action, FieldAction, Group, MenuManager, MenuBarManager, - ToolBarManager + Action, + FieldAction, + Group, + MenuManager, + MenuBarManager, + ToolBarManager, ) from pyface.fields.api import ComboField from pyface.toolkit import toolkit_object @@ -29,84 +27,75 @@ class MainWindow(ApplicationWindow): """ The main application window. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( Group( Action( - name='&Open...', - accelerator='Ctrl+O', - on_perform=self.on_open_file + name="&Open...", + accelerator="Ctrl+O", + on_perform=self.on_open_file, ), Action( - name='&Save', - accelerator='Ctrl+S', - on_perform=self.on_save_file + name="&Save", + accelerator="Ctrl+S", + on_perform=self.on_save_file, ), - id='document_group', + id="document_group", ), Action( - name='&Close', - accelerator='Ctrl+W', - on_perform=self.close + name="&Close", accelerator="Ctrl+W", on_perform=self.close ), - name='&File') + name="&File", + ) ) # Add a tool bar if we are using qt4 - wx has layout issues - if toolkit_object.toolkit == 'qt4': + if toolkit_object.toolkit == "qt4": from pygments.styles import STYLE_MAP + styles = list(STYLE_MAP) self.tool_bar_manager = ToolBarManager( Group( - Action( - name='Open...', - on_perform=self.on_open_file - ), - Action( - name='Save', - on_perform=self.on_save_file - ), - Action( - name='Close', - on_perform=self.close - ), - id='document_group', + Action(name="Open...", on_perform=self.on_open_file), + Action(name="Save", on_perform=self.on_save_file), + Action(name="Close", on_perform=self.close), + id="document_group", ), Group( Action( name="Lines", - style='toggle', + style="toggle", on_perform=self.on_show_line_numbers, checked=True, ), FieldAction( - name='Style', + name="Style", field_type=ComboField, field_defaults={ - 'values': styles, - 'value': 'default', - 'tooltip': 'Style', + "values": styles, + "value": "default", + "tooltip": "Style", }, on_perform=self.on_style_changed, ), - ) + ), ) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplication' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create the editor. """ @@ -115,15 +104,15 @@ return self._editor.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def on_open_file(self): """ Open a new file. """ if self.control: - dlg = FileDialog(parent=self.control, wildcard='*.py') + dlg = FileDialog(parent=self.control, wildcard="*.py") if dlg.open() == OK: self._editor.path = dlg.path @@ -137,8 +126,9 @@ except IOError: # If you are trying to save to a file that doesn't exist, # open up a FileDialog with a 'save as' action. - dlg = FileDialog(parent=self.control, action='save as', - wildcard="*.py") + dlg = FileDialog( + parent=self.control, action="save as", wildcard="*.py" + ) if dlg.open() == OK: self._editor.save(dlg.path) @@ -158,7 +148,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI. gui = GUI() diff -Nru python-pyface-6.1.2/examples/python_shell.py python-pyface-7.4.0/examples/python_shell.py --- python-pyface-6.1.2/examples/python_shell.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/python_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,16 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2009, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Python shell example. """ -# Enthought library imports. + from pyface.api import ApplicationWindow, GUI, PythonShell from pyface.action.api import Action, MenuManager, MenuBarManager @@ -21,17 +18,17 @@ class MainWindow(ApplicationWindow): """ The main application window. """ - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------- # The size of the window. size = (640, 480) # The window title. - title = 'Python' + title = "Python" - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplication' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create the editor. """ @@ -42,7 +39,7 @@ # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI. gui = GUI() @@ -52,5 +49,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/splash_screen.py python-pyface-7.4.0/examples/splash_screen.py --- python-pyface-6.1.2/examples/splash_screen.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/splash_screen.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,5 @@ """Example of using a splash screen (and the use of pyface Timer).""" -from __future__ import print_function import time @@ -10,7 +9,7 @@ from traits.api import Any, Int -splash_screen = SplashScreen(image=ImageResource('images/splash')) +splash_screen = SplashScreen(image=ImageResource("images/splash")) class MainWindow(ApplicationWindow): @@ -20,21 +19,21 @@ my_timer = Any() # Count each time the timer task executes. - counter = Int + counter = Int() def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - Action(name='Start Timer', on_perform=self._start_timer), - Action(name='Stop Timer', on_perform=self._stop_timer), - Action(name='E&xit', on_perform=self.close), - name = '&File', + Action(name="Start Timer", on_perform=self._start_timer), + Action(name="Stop Timer", on_perform=self._stop_timer), + Action(name="E&xit", on_perform=self.close), + name="&File", ) ) @@ -55,7 +54,6 @@ if self.my_timer is not None: self.my_timer.Stop() - def _timer_task(self): """The method run periodically by the timer.""" @@ -71,7 +69,10 @@ window = MainWindow() # Simulate a busy window initialization. - time.sleep(5) + for i in range(5): + gui.process_events() + time.sleep(1) + window.open() # Start the GUI event loop! diff -Nru python-pyface-6.1.2/examples/tasks/advanced/example_panes.py python-pyface-7.4.0/examples/tasks/advanced/example_panes.py --- python-pyface-6.1.2/examples/tasks/advanced/example_panes.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/example_panes.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,6 @@ -# Standard library imports. import os.path -# Enthought library imports. + from pyface.tasks.api import TraitsDockPane from traits.api import Event, File, List, Str from traitsui.api import View, Item, FileEditor @@ -11,40 +10,43 @@ """ A simple file browser pane. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'example.file_browser_pane' - name = 'File Browser' + id = "example.file_browser_pane" + name = "File Browser" - #### FileBrowserPane interface ############################################ + # FileBrowserPane interface -------------------------------------------- # Fired when a file is double-clicked. - activated = Event + activated = Event() # The list of wildcard filters for filenames. filters = List(Str) # The currently selected file. - selected_file = File(os.path.expanduser('~')) + selected_file = File(os.path.expanduser("~")) # The view used to construct the dock pane's widget. - view = View(Item('selected_file', - editor=FileEditor(dclick_name='activated', - filter_name='filters'), - style='custom', - show_label=False), - resizable=True) + view = View( + Item( + "selected_file", + editor=FileEditor(dclick_name="activated", filter_name="filters"), + style="custom", + show_label=False, + ), + resizable=True, + ) class PythonScriptBrowserPane(FileBrowserPane): """ A file browser pane restricted to Python scripts. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'example.python_script_browser_pane' - name = 'Script Browser' + id = "example.python_script_browser_pane" + name = "Script Browser" - #### FileBrowserPane interface ############################################ + # FileBrowserPane interface -------------------------------------------- - filters = [ '*.py' ] + filters = ["*.py"] diff -Nru python-pyface-6.1.2/examples/tasks/advanced/example_task.py python-pyface-7.4.0/examples/tasks/advanced/example_task.py --- python-pyface-6.1.2/examples/tasks/advanced/example_task.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/example_task.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,65 +1,98 @@ -# Enthought library imports. -from pyface.tasks.api import Task, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction -from pyface.api import ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from traits.api import on_trait_change, Property, Instance +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, +) +from pyface.tasks.action.api import DockPaneToggleGroup, TaskAction +from traits.api import observe, Property, Instance -# Local imports. from example_panes import PythonScriptBrowserPane from python_editor import PythonEditor + class ExampleTask(Task): """ A simple task for editing Python code. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - TaskAction(name='Open...', method='open', - accelerator='Ctrl+O'), - TaskAction(name='Save', method='save', - accelerator='Ctrl+S'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - TaskAction(method='open', - tooltip='Open a file', - image=ImageResource('document_open')), - TaskAction(method='save', - tooltip='Save the current file', - image=ImageResource('document_save')), - image_size = (32, 32), show_tool_names=False), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + TaskAction(name="Open...", method="open", accelerator="Ctrl+O"), + TaskAction(name="Save", method="save", accelerator="Ctrl+S"), + id="File", + name="&File", + ), + SMenu(DockPaneToggleGroup(), id="View", name="&View"), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + TaskAction( + method="open", + tooltip="Open a file", + image=ImageResource("document_open"), + ), + TaskAction( + method="save", + tooltip="Save the current file", + image=ImageResource("document_save"), + ), + image_size=(32, 32), + show_tool_names=False, + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): - return TaskLayout( - left=PaneItem('example.python_script_browser_pane')) + return TaskLayout(left=PaneItem("example.python_script_browser_pane")) def activated(self): """ Overriden to set the window's title. """ return - filename = self.active_editor.path if self.active_editor else '' - self.window.title = filename if filename else 'Untitled' + filename = self.active_editor.path if self.active_editor else "" + self.window.title = filename if filename else "Untitled" def create_central_pane(self): """ Create the central pane: the script editor. @@ -71,13 +104,13 @@ """ Create the file browser and connect to its double click event. """ browser = PythonScriptBrowserPane() - handler = lambda: self._open_file(browser.selected_file) - browser.on_trait_change(handler, 'activated') - return [ browser ] + handler = lambda _: self._open_file(browser.selected_file) + browser.observe(handler, "activated") + return [browser] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -90,7 +123,7 @@ def open(self): """ Shows a dialog to open a file. """ - dialog = FileDialog(parent=self.window.control, wildcard='*.py') + dialog = FileDialog(parent=self.window.control, wildcard="*.py") if dialog.open() == OK: self._open_file(dialog.path) @@ -104,17 +137,18 @@ except IOError: # If you are trying to save to a file that doesn't exist, open up a # FileDialog with a 'save as' action. - dialog = FileDialog(parent=self.window.control, - action='save as', wildcard='*.py') + dialog = FileDialog( + parent=self.window.control, action="save as", wildcard="*.py" + ) if dialog.open() == OK: editor.save(dialog.path) else: return False return True - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _open_file(self, filename): """ Opens the file at the specified path in the editor. @@ -128,15 +162,23 @@ """ Prompts the user to save if necessary. Returns whether the dialog was cancelled. """ - dirty_editors = dict([(editor.name, editor) - for editor in self.editor_area.editors - if editor.dirty]) + dirty_editors = dict( + [ + (editor.name, editor) + for editor in self.editor_area.editors + if editor.dirty + ] + ) if not dirty_editors.keys(): return True - message = 'You have unsaved files. Would you like to save them?' - dialog = ConfirmationDialog(parent=self.window.control, - message=message, cancel=True, - default=CANCEL, title='Save Changes?') + message = "You have unsaved files. Would you like to save them?" + dialog = ConfirmationDialog( + parent=self.window.control, + message=message, + cancel=True, + default=CANCEL, + title="Save Changes?", + ) result = dialog.open() if result == CANCEL: return False @@ -145,16 +187,17 @@ editor.save(editor.path) return True - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - @on_trait_change('window:closing') + @observe("window:closing") def _prompt_on_close(self, event): """ Prompt the user to save when exiting. """ close = self._prompt_for_save() - event.veto = not close + window = event.new + window.veto = not close - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: diff -Nru python-pyface-6.1.2/examples/tasks/advanced/images/image_LICENSE.txt python-pyface-7.4.0/examples/tasks/advanced/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/tasks/advanced/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Enthought BSD 3-Clause LICENSE.txt -Oxygen CC-SA 3.0 http://creativecommons.org/licenses/by-sa/3.0/ - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- - document_new.png | Oxygen - document_open.png | Oxygen - document_save.png | Oxygen diff -Nru python-pyface-6.1.2/examples/tasks/advanced/i_python_editor.py python-pyface-7.4.0/examples/tasks/advanced/i_python_editor.py --- python-pyface-6.1.2/examples/tasks/advanced/i_python_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/i_python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,52 +1,48 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A widget for editing Python code. """ -# Enthought library imports. -from traits.api import Bool, Event, Instance, File, Interface, Unicode +from traits.api import Bool, Event, Instance, File, Interface, Str from pyface.tasks.i_editor import IEditor -# Local imports. + from pyface.key_pressed_event import KeyPressedEvent class IPythonEditor(IEditor): """ A widget for editing Python code. """ - #### 'IPythonEditor' interface ############################################ + # 'IPythonEditor' interface -------------------------------------------- # Object being editor is a file obj = Instance(File) # The pathname of the file being edited. - path = Unicode + path = Str() # Should line numbers be shown in the margin? show_line_numbers = Bool(True) - #### Events #### + # Events ---- # The contents of the editor has changed. - changed = Event + changed = Event() # A key has been pressed. key_pressed = Event(KeyPressedEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'IPythonEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def load(self, path=None): """ Loads the contents of the editor. """ diff -Nru python-pyface-6.1.2/examples/tasks/advanced/python_editor_qt4.py python-pyface-7.4.0/examples/tasks/advanced/python_editor_qt4.py --- python-pyface-6.1.2/examples/tasks/advanced/python_editor_qt4.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/python_editor_qt4.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,54 +1,54 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Standard library imports. import sys from os.path import basename -# Major package imports. + from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, Event, Instance, File, Unicode, Property, provides + +from traits.api import ( + Bool, Event, File, Instance, observe, Property, provides, Str +) from pyface.tasks.api import Editor -# Local imports. + from i_python_editor import IPythonEditor from pyface.key_pressed_event import KeyPressedEvent + @provides(IPythonEditor) class PythonEditor(Editor): """ The toolkit specific implementation of a PythonEditor. See the IPythonEditor interface for the API documentation. """ - #### 'IPythonEditor' interface ############################################ + # 'IPythonEditor' interface -------------------------------------------- obj = Instance(File) - path = Unicode + path = Str() dirty = Bool(False) - name = Property(Unicode, depends_on='path') + name = Property(Str, observe="path") - tooltip = Property(Unicode, depends_on='path') + tooltip = Property(Str, observe="path") show_line_numbers = Bool(True) - #### Events #### + # Events ---- - changed = Event + changed = Event() key_pressed = Event(KeyPressedEvent) @@ -56,11 +56,11 @@ return self.path def _get_name(self): - return basename(self.path) or 'Untitled' + return basename(self.path) or "Untitled" - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): self.control = self._create_control(parent) @@ -73,11 +73,11 @@ # We will have no path for a new script. if len(path) > 0: - f = open(self.path, 'r') + f = open(self.path, "r") text = f.read() f.close() else: - text = '' + text = "" self.control.code.setPlainText(text) self.dirty = False @@ -88,7 +88,7 @@ if path is None: path = self.path - f = open(path, 'w') + f = open(path, "w") f.write(self.control.code.toPlainText()) f.close() @@ -98,33 +98,38 @@ """ Selects the specified line. """ self.control.code.set_line_column(lineno, 0) - self.control.code.moveCursor(QtGui.QTextCursor.EndOfLine, - QtGui.QTextCursor.KeepAnchor) + self.control.code.moveCursor( + QtGui.QTextCursor.MoveOperation.EndOfLine, QtGui.QTextCursor.MoveMode.KeepAnchor + ) - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _path_changed(self): + @observe('path') + def _path_updated(self, event): if self.control is not None: self.load() - def _show_line_numbers_changed(self): + @observe('show_line_numbers') + def _show_line_numbers_updated(self, event=None): if self.control is not None: self.control.code.line_number_widget.setVisible( - self.show_line_numbers) + self.show_line_numbers + ) self.control.code.update_line_number_width() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the toolkit-specific control for the widget. """ from pyface.ui.qt4.code_editor.code_widget import AdvancedCodeWidget + self.control = control = AdvancedCodeWidget(parent) - self._show_line_numbers_changed() + self._show_line_numbers_updated() # Install event filter to trap key presses. event_filter = PythonEditorEventFilter(self, self.control) @@ -158,20 +163,26 @@ """ def __init__(self, editor, parent): - super(PythonEditorEventFilter, self).__init__(parent) + super().__init__(parent) self.__editor = editor def eventFilter(self, obj, event): """ Reimplemented to trap key presses. """ - if self.__editor.control and obj == self.__editor.control and \ - event.type() == QtCore.QEvent.FocusOut: + if ( + self.__editor.control + and obj == self.__editor.control + and event.type() == QtCore.QEvent.Type.FocusOut + ): # Hack for Traits UI compatibility. self.__editor.control.lostFocus.emit() - elif self.__editor.control and obj == self.__editor.control.code and \ - event.type() == QtCore.QEvent.KeyPress: - # Pyface doesn't seem to be Unicode aware. Only keep the key code + elif ( + self.__editor.control + and obj == self.__editor.control.code + and event.type() == QtCore.QEvent.Type.KeyPress + ): + # Pyface doesn't seem to be Str aware. Only keep the key code # if it corresponds to a single Latin1 character. kstr = event.text() try: @@ -181,13 +192,18 @@ mods = event.modifiers() self.key_pressed = KeyPressedEvent( - alt_down = ((mods & QtCore.Qt.AltModifier) == - QtCore.Qt.AltModifier), - control_down = ((mods & QtCore.Qt.ControlModifier) == - QtCore.Qt.ControlModifier), - shift_down = ((mods & QtCore.Qt.ShiftModifier) == - QtCore.Qt.ShiftModifier), - key_code = kcode, - event = event) + alt_down=( + (mods & QtCore.Qt.KeyboardModifier.AltModifier) == QtCore.Qt.KeyboardModifier.AltModifier + ), + control_down=( + (mods & QtCore.Qt.KeyboardModifier.ControlModifier) + == QtCore.Qt.KeyboardModifier.ControlModifier + ), + shift_down=( + (mods & QtCore.Qt.KeyboardModifier.ShiftModifier) == QtCore.Qt.KeyboardModifier.ShiftModifier + ), + key_code=kcode, + event=event, + ) - return super(PythonEditorEventFilter, self).eventFilter(obj, event) + return super().eventFilter(obj, event) diff -Nru python-pyface-6.1.2/examples/tasks/advanced/python_editor_wx.py python-pyface-7.4.0/examples/tasks/advanced/python_editor_wx.py --- python-pyface-6.1.2/examples/tasks/advanced/python_editor_wx.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/python_editor_wx.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,56 +1,56 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Standard library imports. import sys from os.path import basename -# Major package imports. + import wx import wx.stc -# Enthought library imports. -from traits.api import Bool, Event, Instance, File, Unicode, Property, provides + +from traits.api import ( + Bool, Event, File, Instance, observe, Property, provides, Str +) from pyface.tasks.api import Editor from pyface.wx.python_stc import PythonSTC, faces -# Local imports. + from i_python_editor import IPythonEditor from pyface.key_pressed_event import KeyPressedEvent + @provides(IPythonEditor) class PythonEditor(Editor): """ The toolkit specific implementation of a StyledTextEditor. See the IStyledTextEditor interface for the API documentation. """ - #### 'IPythonEditor' interface ############################################ + # 'IPythonEditor' interface -------------------------------------------- obj = Instance(File) - path = Unicode + path = Str() dirty = Bool(False) - name = Property(Unicode, depends_on='path') + name = Property(Str, observe="path") - tooltip = Property(Unicode, depends_on='path') + tooltip = Property(Str, observe="path") show_line_numbers = Bool(True) - #### Events #### + # Events ---- - changed = Event + changed = Event() key_pressed = Event(KeyPressedEvent) @@ -58,11 +58,11 @@ return self.path def _get_name(self): - return basename(self.path) or 'Untitled' + return basename(self.path) or "Untitled" - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): self.control = self._create_control(parent) @@ -75,11 +75,11 @@ # We will have no path for a new script. if len(path) > 0: - f = open(self.path, 'r') + f = open(self.path, "r") text = f.read() f.close() else: - text = '' + text = "" self.control.SetText(text) self.dirty = False @@ -90,9 +90,8 @@ if path is None: path = self.path - f = file(path, 'w') - f.write(self.control.GetText()) - f.close() + with open(path, "w") as f: + f.write(self.control.GetText()) self.dirty = False @@ -100,27 +99,29 @@ """ Selects the specified line. """ start = self.control.PositionFromLine(lineno) - end = self.control.GetLineEndPosition(lineno) + end = self.control.GetLineEndPosition(lineno) self.control.SetSelection(start, end) def set_style(self, n, fore, back): self.control.StyleSetForeground(n, fore) - #self.StyleSetBackground(n, '#c0c0c0') - #self.StyleSetBackground(n, '#ffffff') + # self.StyleSetBackground(n, '#c0c0c0') + # self.StyleSetBackground(n, '#ffffff') self.control.StyleSetBackground(n, back) self.control.StyleSetFaceName(n, "courier new") - self.control.StyleSetSize(n, faces['size']) + self.control.StyleSetSize(n, faces["size"]) - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _path_changed(self): + @observe('path') + def _path_updated(self, event): if self.control is not None: self.load() - def _show_line_numbers_changed(self): + @observe('show_line_numbers') + def _show_line_numbers_updated(self, event=None): if self.control is not None: c = self.control if self.show_line_numbers: @@ -129,16 +130,9 @@ else: c.SetMarginWidth(1, 4) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### - - def _create_control(self, parent): - """ Creates the toolkit-specific control for the widget. - """ - from pyface.ui.qt4.code_editor.code_widget import AdvancedCodeWidget - self.control = control = AdvancedCodeWidget(parent) - self._show_line_numbers_changed() + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the toolkit-specific control for the widget. """ @@ -170,22 +164,22 @@ stc.SetIndent(4) # Line ending mode. - stc.SetEOLMode(wx.stc.STC_EOL_LF) # Unix - #self.SetEOLMode(wx.stc.STC_EOL_CR) # Apple Mac - #self.SetEOLMode(wx.stc.STC_EOL_CRLF) # Windows + stc.SetEOLMode(wx.stc.STC_EOL_LF) # Unix + # self.SetEOLMode(wx.stc.STC_EOL_CR) # Apple Mac + # self.SetEOLMode(wx.stc.STC_EOL_CRLF) # Windows - ########################################## + # ---------------------------------------- # Global styles for all languages. - ########################################## + # ---------------------------------------- self.set_style(wx.stc.STC_STYLE_DEFAULT, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_CONTROLCHAR, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_BRACELIGHT, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_BRACEBAD, "#000000", "#ffffff") - ########################################## + # ---------------------------------------- # Python styles. - ########################################## + # ---------------------------------------- # White space self.set_style(wx.stc.STC_P_DEFAULT, "#000000", "#ffffff") @@ -229,29 +223,31 @@ # End of line where string is not closed self.set_style(wx.stc.STC_P_STRINGEOL, "#000000", "#ffffff") - ########################################## + # ---------------------------------------- # Events. - ########################################## + # ---------------------------------------- # By default, the will fire EVT_STC_CHANGE evented for all mask values # (STC_MODEVENTMASKALL). This generates too many events. - stc.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT | - wx.stc.STC_MOD_DELETETEXT | - wx.stc.STC_PERFORMED_UNDO | - wx.stc.STC_PERFORMED_REDO) + stc.SetModEventMask( + wx.stc.STC_MOD_INSERTTEXT + | wx.stc.STC_MOD_DELETETEXT + | wx.stc.STC_PERFORMED_UNDO + | wx.stc.STC_PERFORMED_REDO + ) # Listen for changes to the file. wx.stc.EVT_STC_CHANGE(stc, stc.GetId(), self._on_stc_changed) # Listen for key press events. - wx.EVT_CHAR(stc, self._on_char) + stc.Bind(wx.EVT_CHAR, self._on_char) # Load the editor's contents. self.load() return stc - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_stc_changed(self, event): """ Called whenever a change is made to the text of the document. """ @@ -268,11 +264,11 @@ """ Called whenever a change is made to the text of the document. """ self.key_pressed = KeyPressedEvent( - alt_down = event.m_altDown == 1, - control_down = event.m_controlDown == 1, - shift_down = event.m_shiftDown == 1, - key_code = event.m_keyCode, - event = event + alt_down=event.altDown, + control_down=event.controlDown, + shift_down=event.shiftDown, + key_code=event.KeyCode, + event=event, ) # Give other event handlers a chance. diff -Nru python-pyface-6.1.2/examples/tasks/advanced/run.py python-pyface-7.4.0/examples/tasks/advanced/run.py --- python-pyface-6.1.2/examples/tasks/advanced/run.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/advanced/run.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,16 @@ """ -Simple example of a task application creating tasks and panes from traits -components. +Simple example of a task application creating tasks and panes from traits +components. -Note: Run it with +Note: Run it with $ ETS_TOOLKIT='qt4' python run.py as the wx backend is not supported yet for the TaskWindow. """ -# Enthought library imports. + from pyface.api import GUI from pyface.tasks.api import TaskWindow -# Local imports. + from example_task import ExampleTask @@ -32,6 +32,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/basic/example_panes.py python-pyface-7.4.0/examples/tasks/basic/example_panes.py --- python-pyface-6.1.2/examples/tasks/basic/example_panes.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/basic/example_panes.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,6 @@ -# Standard library imports. import os.path -# Enthought library imports. + from pyface.api import PythonEditor from pyface.tasks.api import TaskPane, TraitsDockPane from traits.api import Event, File, Instance, List, Str @@ -12,61 +11,64 @@ """ A simple file browser pane. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'example.file_browser_pane' - name = 'File Browser' + id = "example.file_browser_pane" + name = "File Browser" - #### FileBrowserPane interface ############################################ + # FileBrowserPane interface -------------------------------------------- # Fired when a file is double-clicked. - activated = Event + activated = Event() # The list of wildcard filters for filenames. filters = List(Str) # The currently selected file. - selected_file = File(os.path.expanduser('~')) + selected_file = File(os.path.expanduser("~")) # The view used to construct the dock pane's widget. - view = View(Item('selected_file', - editor=FileEditor(dclick_name='activated', - filter_name='filters'), - style='custom', - show_label=False), - resizable=True) + view = View( + Item( + "selected_file", + editor=FileEditor(dclick_name="activated", filter_name="filters"), + style="custom", + show_label=False, + ), + resizable=True, + ) class PythonScriptBrowserPane(FileBrowserPane): """ A file browser pane restricted to Python scripts. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'example.python_script_browser_pane' - name = 'Script Browser' + id = "example.python_script_browser_pane" + name = "Script Browser" - #### FileBrowserPane interface ############################################ + # FileBrowserPane interface -------------------------------------------- - filters = [ '*.py' ] + filters = ["*.py"] class PythonEditorPane(TaskPane): """ A wrapper around the Pyface Python editor. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'example.python_editor_pane' - name = 'Python Editor' + id = "example.python_editor_pane" + name = "Python Editor" - #### PythonEditorPane interface ########################################### + # PythonEditorPane interface ------------------------------------------- editor = Instance(PythonEditor) - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): self.editor = PythonEditor(parent) diff -Nru python-pyface-6.1.2/examples/tasks/basic/example_task.py python-pyface-7.4.0/examples/tasks/basic/example_task.py --- python-pyface-6.1.2/examples/tasks/basic/example_task.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/basic/example_task.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,12 +1,27 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) from pyface.tasks.api import Task, TaskLayout, PaneItem -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction -from pyface.api import ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from traits.api import on_trait_change +from pyface.tasks.action.api import DockPaneToggleGroup, TaskAction +from traits.api import observe + -# Local imports. from example_panes import PythonEditorPane, PythonScriptBrowserPane @@ -14,42 +29,51 @@ """ A simple task for editing Python code. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Python Script Editor' + id = "example.example_task" + name = "Python Script Editor" - #default_layout = TaskLayout( + # default_layout = TaskLayout( # left=PaneItem('example.python_script_browser_pane')) - menu_bar = SMenuBar(SMenu(TaskAction(name='Open...', method='open', - accelerator='Ctrl+O'), - TaskAction(name='Save', method='save', - accelerator='Ctrl+S'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='open', - tooltip='Open a file', - image=ImageResource('document_open')), - TaskAction(method='save', - tooltip='Save the current file', - image=ImageResource('document_save'))) ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="Open...", method="open", accelerator="Ctrl+O"), + TaskAction(name="Save", method="save", accelerator="Ctrl+S"), + id="File", + name="&File", + ), + SMenu(DockPaneToggleGroup(), id="View", name="&View"), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="open", + tooltip="Open a file", + image=ImageResource("document_open"), + ), + TaskAction( + method="save", + tooltip="Save the current file", + image=ImageResource("document_save"), + ), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): - return TaskLayout( - left=PaneItem('example.python_script_browser_pane')) + return TaskLayout(left=PaneItem("example.python_script_browser_pane")) def activated(self): """ Overriden to set the window's title. """ filename = self.window.central_pane.editor.path - self.window.title = filename if filename else 'Untitled' + self.window.title = filename if filename else "Untitled" def create_central_pane(self): """ Create the central pane: the script editor. @@ -60,18 +84,18 @@ """ Create the file browser and connect to its double click event. """ browser = PythonScriptBrowserPane() - handler = lambda: self._open_file(browser.selected_file) - browser.on_trait_change(handler, 'activated') - return [ browser ] + handler = lambda _: self._open_file(browser.selected_file) + browser.observe(handler, "activated") + return [browser] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Shows a dialog to open a file. """ - dialog = FileDialog(parent=self.window.control, wildcard='*.py') + dialog = FileDialog(parent=self.window.control, wildcard="*.py") if dialog.open() == OK: self._open_file(dialog.path) @@ -85,17 +109,18 @@ except IOError: # If you are trying to save to a file that doesn't exist, open up a # FileDialog with a 'save as' action. - dialog = FileDialog(parent=self.window.control, - action='save as', wildcard='*.py') + dialog = FileDialog( + parent=self.window.control, action="save as", wildcard="*.py" + ) if dialog.open() == OK: editor.save(dialog.path) else: return False return True - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _open_file(self, filename): """ Opens the file at the specified path in the editor. @@ -109,11 +134,17 @@ was cancelled. """ if self.window.central_pane.editor.dirty: - message = 'The current file has unsaved changes. ' \ - 'Do you want to save your changes?' - dialog = ConfirmationDialog(parent=self.window.control, - message=message, cancel=True, - default=CANCEL, title='Save Changes?') + message = ( + "The current file has unsaved changes. " + "Do you want to save your changes?" + ) + dialog = ConfirmationDialog( + parent=self.window.control, + message=message, + cancel=True, + default=CANCEL, + title="Save Changes?", + ) result = dialog.open() if result == CANCEL: return False @@ -122,9 +153,10 @@ return self._prompt_for_save() return True - @on_trait_change('window:closing') + @observe("window:closing") def _prompt_on_close(self, event): """ Prompt the user to save when exiting. """ + window = event.new if not self._prompt_for_save(): - event.veto = True + window.veto = True diff -Nru python-pyface-6.1.2/examples/tasks/basic/images/image_LICENSE.txt python-pyface-7.4.0/examples/tasks/basic/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/tasks/basic/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/basic/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Enthought BSD 3-Clause LICENSE.txt -Oxygen CC-SA 3.0 http://creativecommons.org/licenses/by-sa/3.0/ - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- - document_open.png | Oxygen - document_save.png | Oxygen diff -Nru python-pyface-6.1.2/examples/tasks/basic/run.py python-pyface-7.4.0/examples/tasks/basic/run.py --- python-pyface-6.1.2/examples/tasks/basic/run.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/basic/run.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,16 @@ """ -Simple example of a task application creating tasks and panes from traits -components. +Simple example of a task application creating tasks and panes from traits +components. -Note: Run it with +Note: Run it with $ ETS_TOOLKIT='qt4' python run.py as the wx backend is not supported yet for the TaskWindow. """ -# Enthought library imports. + from pyface.api import GUI from pyface.tasks.api import TaskWindow -# Local imports. + from example_task import ExampleTask @@ -32,6 +32,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/enaml/employee.py python-pyface-7.4.0/examples/tasks/enaml/employee.py --- python-pyface-6.1.2/examples/tasks/enaml/employee.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/enaml/employee.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -import datetime - -from traits.api import HasTraits, Str, Int, Instance, Tuple, Date, Property - - -class Person(HasTraits): - """ A simple class representing a person object. - - """ - # The last name of the person as a string - last_name = Str - - # The first name of the person as a string - first_name = Str - - # The date of birth of the person - dob = Date(datetime.date(1970, 1, 1)) - - # The age of the person computed from their dob - age = Property(Int, depends_on='dob') - - # This method is called when the age of the person needs to - # be computed - def _get_age(self): - today = datetime.date.today() - dob = self.dob - age = today.year - dob.year - birthday_this_year = dob.replace(year=today.year) - if today < birthday_this_year: - age -= 1 - return age - - -class Employer(Person): - """ An employer is a person who runs a company. - - """ - # The name of the company - company_name = Str - - -class Employee(Person): - """ An employee is person with a boss and a phone number. - - """ - # The employee's boss - boss = Instance(Employer) - - # The employee's phone number as a tuple of 3 ints - phone = Tuple(Int, Int, Int) - - # This method is called automatically by traits to get the - # default value for the phone number. - def _phone_default(self): - return (555, 555, 5555) - - # This method will be called automatically by traits when the - # employee's phone number changes - def _phone_changed(self, val): - print 'received new phone number for %s: %s' % (self.first_name, val) - - -if __name__ == '__main__': - # Create an employee with a boss - boss_john = Employer(first_name='John', last_name='Paw', - company_name="Packrat's Cats") - employee_mary = Employee(first_name='Mary', last_name='Sue', - boss=boss_john) - - # Import our Enaml EmployeeView - import enaml - with enaml.imports(): - from employee_view import EmployeeView - - # Create a view and show it. - view = EmployeeView(employee=employee_mary) - #view.show() - diff -Nru python-pyface-6.1.2/examples/tasks/enaml/employee_view.enaml python-pyface-7.4.0/examples/tasks/enaml/employee_view.enaml --- python-pyface-6.1.2/examples/tasks/enaml/employee_view.enaml 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/enaml/employee_view.enaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -from enaml.layout.api import align, vbox -from enaml.widgets.api import CheckBox, Container, DateSelector, Field, Form, \ - GroupBox, Label - - -enamldef EmployeeForm(Form): - attr employee - attr show_employer: bool = False - Label: - text = "First name:" - Field: - text := employee.first_name - Label: - text = "Last name:" - Field: - text := employee.last_name - Label: - text = 'Date of Birth:' - DateSelector: - date := employee.dob - Label: - text = 'Age:' - Label: - text << str(employee.age) - Label: - text = 'Password' - Field: - echo_mode << 'password' if not pw_cb.checked else 'normal' - text :: print 'Password:', text - Label: - text = 'Show Password:' - CheckBox: pw_cb: - checked = False - Label: - text = 'Edit Employer Details:' - CheckBox: - checked := show_employer - - -enamldef EmployerForm(Form): - attr employer - Label: - text = "Company:" - Field: - text << employer.company_name - Label: - text = "Reporting Manager:" - Field: - text << "%s %s" % (employer.first_name, employer.last_name) - - -enamldef EmployeeView(Container): main: - attr employee - - constraints = [ - vbox(top_box, bottom_box), - align('midline', top_form, bottom_form), - ] - GroupBox: top_box: - title = "Personal Details" - EmployeeForm: top_form: - # We access the employee object through the identifier - # 'main' here, because the EmployeeForm also has an - # 'employee' attribute declared, and that would be - # found first. - employee = main.employee - GroupBox: bottom_box: - title = "Employer Details" - EmployerForm: bottom_form: - enabled << top_form.show_employer - employer << employee.boss diff -Nru python-pyface-6.1.2/examples/tasks/enaml/empty_form.enaml python-pyface-7.4.0/examples/tasks/enaml/empty_form.enaml --- python-pyface-6.1.2/examples/tasks/enaml/empty_form.enaml 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/enaml/empty_form.enaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ -from enaml.widgets.api import Field, Form, Label - -enamldef EmptyForm(Form): - Label: - text = "First name:" - Field: - pass - Label: - text = "Last name:" - Field: - pass - Label: - text = "Address:" - Field: - pass - Label: - text = "" - Field: - pass - Label: - text = "City:" - Field: - pass - Label: - text = "State:" - Field: - pass - Label: - text = "Postal Code:" - Field: - pass - Label: - text = "Country:" - Field: - pass - Label: - text = "Phone number:" - Field: - pass - Label: - text = "Email:" - Field: - pass - Label: - text = "Confirm email:" - Field: - pass - Label: - text = "Confirm email:" - Field: - pass - Label: - text = "Confirm email:" - Field: - pass - Label: - text = "Confirm email:" - Field: - pass - Label: - text = "Confirm email:" - Field: - pass - Label: - text = "Confirm email:" - Field: - pass diff -Nru python-pyface-6.1.2/examples/tasks/enaml/enaml_panes.py python-pyface-7.4.0/examples/tasks/enaml/enaml_panes.py --- python-pyface-6.1.2/examples/tasks/enaml/enaml_panes.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/enaml/enaml_panes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ - -# Enthought library imports. -from pyface.tasks.api import EnamlDockPane, EnamlTaskPane -import traits_enaml - - -class DummyDockPane(EnamlDockPane): - id = 'example.dummy_dock_pane' - name = 'Dummy Dock' - - def create_component(self): - with traits_enaml.imports(): - from empty_form import EmptyForm - return EmptyForm() - - -class DummyTaskPane(EnamlTaskPane): - - #### TaskPane interface ################################################### - - id = 'example.dummy_task_pane' - name = 'Dummy Task' - - ########################################################################### - # 'ITaskPane' interface. - ########################################################################### - - def create_component(self): - with traits_enaml.imports(): - from employee_view import EmployeeView - from employee import Employer, Employee - - boss_john = Employer(first_name='John', last_name='Paw', - company_name="Packrat's Cats") - employee_mary = Employee(first_name='Mary', last_name='Sue', - boss=boss_john) - - view = EmployeeView(employee=employee_mary) - return view diff -Nru python-pyface-6.1.2/examples/tasks/enaml/enaml_task.py python-pyface-7.4.0/examples/tasks/enaml/enaml_task.py --- python-pyface-6.1.2/examples/tasks/enaml/enaml_task.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/enaml/enaml_task.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ - -# Enthought library imports. -from pyface.tasks.api import Task, TaskLayout, PaneItem - -# Local imports. -from enaml_panes import DummyTaskPane, DummyDockPane - - -class EnamlTask(Task): - """ A simple task for demonstrating the use of Enaml in Tasks. - """ - - #### Task interface ####################################################### - - id = 'example.enaml_task' - name = 'Enaml Demo' - - - ########################################################################### - # 'Task' interface. - ########################################################################### - - def _default_layout_default(self): - return TaskLayout( - left=PaneItem('example.dummy_dock_pane')) - - def create_central_pane(self): - """ Create the central pane: the script editor. - """ - return DummyTaskPane() - - def create_dock_panes(self): - """ Create the file browser and connect to its double click event. - """ - return [DummyDockPane()] - diff -Nru python-pyface-6.1.2/examples/tasks/enaml/run.py python-pyface-7.4.0/examples/tasks/enaml/run.py --- python-pyface-6.1.2/examples/tasks/enaml/run.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/enaml/run.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ - -# Enthought library imports. -from pyface.api import GUI -from pyface.tasks.api import TaskWindow - -# Local imports. -from enaml_task import EnamlTask - - -def main(argv): - """ A simple example of using Tasks. - """ - # Create the GUI (this does NOT start the GUI event loop). - from traits.etsconfig.api import ETSConfig - ETSConfig.toolkit = 'qt4' - - from enaml.qt.qt_application import QtApplication - app = QtApplication() - - gui = GUI() - - # Create a Task and add it to a TaskWindow. - task = EnamlTask() - window = TaskWindow(size=(800, 600)) - window.add_task(task) - - # Show the window. - window.open() - - # Start the GUI event loop. - gui.start_event_loop() - - -if __name__ == '__main__': - import sys - main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/mac-menubar-switching.py python-pyface-7.4.0/examples/tasks/steps/mac-menubar-switching.py --- python-pyface-6.1.2/examples/tasks/steps/mac-menubar-switching.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/mac-menubar-switching.py 2022-02-01 12:21:15.000000000 +0000 @@ -7,45 +7,82 @@ incorrect tying of the controls to SecondTask because the class attributes were shared between both classes. """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane, HSplitter, VSplitter -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, + HSplitter, + VSplitter, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance + class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### - + # ------------------------------------------------------------------------ + def _menu_bar_default(self): - return SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) + return SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), + TaskToggleGroup(), + id="View", + name="&View", + ), + ) def create_central_pane(self): """ Create the central pane: the script editor. @@ -53,9 +90,9 @@ self.editor_area = EditorAreaPane() return self.editor_area - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -65,43 +102,60 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### - + # ------------------------------------------------------------------------ + def _menu_bar_default(self): - return SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View'), - SMenu(TaskAction(name='Item 1', method='item1'), - TaskAction(name='Item 2', method='item2'), - id='Task2', name='&Task2'),) + return SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), + TaskToggleGroup(), + id="View", + name="&View", + ), + SMenu( + TaskAction(name="Item 1", method="item1"), + TaskAction(name="Item 2", method="item2"), + id="Task2", + name="&Task2", + ), + ) + - def main(argv): """ A simple example of using Tasks. """ @@ -123,6 +177,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step1.py python-pyface-7.4.0/examples/tasks/steps/step1.py --- python-pyface-6.1.2/examples/tasks/steps/step1.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step1.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,23 @@ """ Simple demo of tasks used to develop the wx support for tasks. """ -# Enthought library imports. + from pyface.api import GUI from pyface.tasks.api import Task, TaskWindow, EditorAreaPane + class BlankTask(Task): """ A task that does nothing """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.blank_task' - name = 'Blank' + id = "example.blank_task" + name = "Blank" - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_central_pane(self): """ Create the central pane: the script editor. @@ -24,6 +25,7 @@ self.editor_area = EditorAreaPane() return self.editor_area + def main(argv): """ A simple example of using Tasks. """ @@ -42,6 +44,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step2.py python-pyface-7.4.0/examples/tasks/steps/step2.py --- python-pyface-6.1.2/examples/tasks/steps/step2.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step2.py 2022-02-01 12:21:15.000000000 +0000 @@ -3,43 +3,72 @@ Create and delete editors in the main notebook area. Also adds menu and toolbar support """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, +) +from traits.api import Property, Instance + class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu(DockPaneToggleGroup(), id="View", name="&View"), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_central_pane(self): """ Create the central pane: the script editor. @@ -47,9 +76,9 @@ self.editor_area = EditorAreaPane() return self.editor_area - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -59,13 +88,14 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + def main(argv): """ A simple example of using Tasks. """ @@ -84,6 +114,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step3.py python-pyface-7.4.0/examples/tasks/steps/step3.py --- python-pyface-6.1.2/examples/tasks/steps/step3.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step3.py 2022-02-01 12:21:15.000000000 +0000 @@ -2,56 +2,83 @@ Create and delete task panes """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, +) +from pyface.tasks.action.api import DockPaneToggleGroup, TaskAction +from traits.api import Property, Instance + class ExamplePane(DockPane): """ A simple file browser pane. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.example_pane" + name = "Example Pane" - id = 'steps.example_pane' - name = 'Example Pane' class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu(DockPaneToggleGroup(), id="View", name="&View"), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): - return TaskLayout( - top=PaneItem('steps.example_pane')) + return TaskLayout(top=PaneItem("steps.example_pane")) def create_central_pane(self): """ Create the central pane: the script editor. @@ -63,11 +90,11 @@ """ Create the file browser and connect to its double click event. """ pane = ExamplePane() - return [ pane ] + return [pane] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -77,13 +104,14 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + def main(argv): """ A simple example of using Tasks. """ @@ -102,6 +130,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step4.py python-pyface-7.4.0/examples/tasks/steps/step4.py --- python-pyface-6.1.2/examples/tasks/steps/step4.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step4.py 2022-02-01 12:21:15.000000000 +0000 @@ -7,57 +7,89 @@ incorrect tying of the controls to SecondTask because the class attributes were shared between both classes. """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance + class ExamplePane(DockPane): """ A simple file browser pane. """ - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.example_pane" + name = "Example Pane" - id = 'steps.example_pane' - name = 'Example Pane' class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): - return TaskLayout( - top=PaneItem('steps.example_pane')) + return TaskLayout(top=PaneItem("steps.example_pane")) def create_central_pane(self): """ Create the central pane: the script editor. @@ -69,11 +101,11 @@ """ Create the file browser and connect to its double click event. """ pane = ExamplePane() - return [ pane ] + return [pane] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -83,41 +115,52 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): - return TaskLayout( - bottom=PaneItem('steps.example_pane')) + return TaskLayout(bottom=PaneItem("steps.example_pane")) + def main(argv): """ A simple example of using Tasks. @@ -139,6 +182,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step5.py python-pyface-7.4.0/examples/tasks/steps/step5.py --- python-pyface-6.1.2/examples/tasks/steps/step5.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step5.py 2022-02-01 12:21:15.000000000 +0000 @@ -7,68 +7,107 @@ incorrect tying of the controls to SecondTask because the class attributes were shared between both classes. """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane, Tabbed -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, + Tabbed, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance + class FirstPane(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.first_pane" + name = "First Pane" - id = 'steps.first_pane' - name = 'First Pane' class SecondPane(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.second_pane" + name = "Second Pane" - id = 'steps.second_pane' - name = 'Second Pane' class ThirdPane(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.third_pane" + name = "Third Pane" - id = 'steps.third_pane' - name = 'Third Pane' class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( - top=Tabbed(PaneItem('steps.first_pane'), - PaneItem('steps.second_pane'), - PaneItem('steps.third_pane'))) + top=Tabbed( + PaneItem("steps.first_pane"), + PaneItem("steps.second_pane"), + PaneItem("steps.third_pane"), + ) + ) def create_central_pane(self): """ Create the central pane: the script editor. @@ -79,11 +118,11 @@ def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ - return [ FirstPane(), SecondPane(), ThirdPane() ] + return [FirstPane(), SecondPane(), ThirdPane()] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -93,43 +132,58 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( - left=Tabbed(PaneItem('steps.first_pane'), - PaneItem('steps.second_pane'), - PaneItem('steps.third_pane'))) + left=Tabbed( + PaneItem("steps.first_pane"), + PaneItem("steps.second_pane"), + PaneItem("steps.third_pane"), + ) + ) + def main(argv): """ A simple example of using Tasks. @@ -151,6 +205,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step6a.py python-pyface-7.4.0/examples/tasks/steps/step6a.py --- python-pyface-6.1.2/examples/tasks/steps/step6a.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step6a.py 2022-02-01 12:21:15.000000000 +0000 @@ -7,99 +7,143 @@ incorrect tying of the controls to SecondTask because the class attributes were shared between both classes. """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane, HSplitter, VSplitter -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, + HSplitter, + VSplitter, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance + class Pane1(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane1" + name = "Pane 1" - id = 'steps.pane1' - name = 'Pane 1' class Pane2(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane2" + name = "Pane 2" - id = 'steps.pane2' - name = 'Pane 2' class Pane3(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane3" + name = "Pane 3" - id = 'steps.pane3' - name = 'Pane 3' class Pane4(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane4" + name = "Pane 4" - id = 'steps.pane4' - name = 'Pane 4' class Pane5(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane5" + name = "Pane 5" - id = 'steps.pane5' - name = 'Pane 5' class Pane6(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane6" + name = "Pane 6" - id = 'steps.pane6' - name = 'Pane 6' class Pane7(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane7" + name = "Pane 7" - id = 'steps.pane7' - name = 'Pane 7' class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( top=VSplitter( HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2'), - PaneItem('steps.pane3')), + PaneItem("steps.pane1"), + PaneItem("steps.pane2"), + PaneItem("steps.pane3"), + ), HSplitter( - PaneItem('steps.pane4'), - PaneItem('steps.pane5'), - PaneItem('steps.pane6')), - )) + PaneItem("steps.pane4"), + PaneItem("steps.pane5"), + PaneItem("steps.pane6"), + ), + ) + ) def create_central_pane(self): """ Create the central pane: the script editor. @@ -110,11 +154,11 @@ def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ - return [ Pane1(), Pane2(), Pane3(), Pane4(), Pane5(), Pane6() ] + return [Pane1(), Pane2(), Pane3(), Pane4(), Pane5(), Pane6()] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -124,51 +168,66 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( left=VSplitter( HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2'), - PaneItem('steps.pane3')), + PaneItem("steps.pane1"), + PaneItem("steps.pane2"), + PaneItem("steps.pane3"), + ), HSplitter( - PaneItem('steps.pane4'), - PaneItem('steps.pane5'), - PaneItem('steps.pane6')), - )) - + PaneItem("steps.pane4"), + PaneItem("steps.pane5"), + PaneItem("steps.pane6"), + ), + ) + ) + + def main(argv): """ A simple example of using Tasks. """ @@ -189,6 +248,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step6.py python-pyface-7.4.0/examples/tasks/steps/step6.py --- python-pyface-6.1.2/examples/tasks/steps/step6.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step6.py 2022-02-01 12:21:15.000000000 +0000 @@ -7,94 +7,136 @@ incorrect tying of the controls to SecondTask because the class attributes were shared between both classes. """ -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane, HSplitter, VSplitter -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, + HSplitter, + VSplitter, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance + class Pane1(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane1" + name = "Pane 1" - id = 'steps.pane1' - name = 'Pane 1' class Pane2(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane2" + name = "Pane 2" - id = 'steps.pane2' - name = 'Pane 2' class Pane3(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane3" + name = "Pane 3" - id = 'steps.pane3' - name = 'Pane 3' class Pane4(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane4" + name = "Pane 4" - id = 'steps.pane4' - name = 'Pane 4' class Pane5(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane5" + name = "Pane 5" - id = 'steps.pane5' - name = 'Pane 5' class Pane6(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane6" + name = "Pane 6" - id = 'steps.pane6' - name = 'Pane 6' class Pane7(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane7" + name = "Pane 7" - id = 'steps.pane7' - name = 'Pane 7' class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( top=HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2'), - PaneItem('steps.pane3'), - )) + PaneItem("steps.pane1"), + PaneItem("steps.pane2"), + PaneItem("steps.pane3"), + ) + ) def create_central_pane(self): """ Create the central pane: the script editor. @@ -105,11 +147,11 @@ def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ - return [ Pane1(), Pane2(), Pane3() ] + return [Pane1(), Pane2(), Pane3()] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -119,46 +161,59 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( left=VSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2'), - PaneItem('steps.pane3'), - )) - + PaneItem("steps.pane1"), + PaneItem("steps.pane2"), + PaneItem("steps.pane3"), + ) + ) + + def main(argv): """ A simple example of using Tasks. """ @@ -179,6 +234,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step7.py python-pyface-7.4.0/examples/tasks/steps/step7.py --- python-pyface-6.1.2/examples/tasks/steps/step7.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step7.py 2022-02-01 12:21:15.000000000 +0000 @@ -9,83 +9,125 @@ """ import logging + logging.basicConfig(level=logging.DEBUG) import wx -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane, HSplitter, VSplitter -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance, Str, List + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, + HSplitter, + VSplitter, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance, Str, List class Pane1(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane1" + name = "Pane 1" - id = 'steps.pane1' - name = 'Pane 1' class Pane2(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'steps.pane2' - name = 'Pane 2' + id = "steps.pane2" + name = "Pane 2" - #### FileBrowserPane interface ############################################ + # FileBrowserPane interface -------------------------------------------- # The list of wildcard filters for filenames. filters = List(Str) - + def create_contents(self, parent): - control = wx.GenericDirCtrl(parent, -1, size=(200,-1), style=wx.NO_BORDER) + control = wx.GenericDirCtrl( + parent, -1, size=(200, -1), style=wx.NO_BORDER + ) control.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.on_selected) return control - + def on_selected(self, evt): selected_file = self.control.GetFilePath() - wx.CallAfter(self.task.window.application.load_file, selected_file, self.task, in_current_window=True) + wx.CallAfter( + self.task.window.application.load_file, + selected_file, + self.task, + in_current_window=True, + ) + class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( top=VSplitter( - HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2')), - )) + HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2")) + ) + ) def create_central_pane(self): """ Create the central pane: the script editor. @@ -96,11 +138,11 @@ def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ - return [ Pane1(), Pane2() ] + return [Pane1(), Pane2()] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -110,48 +152,62 @@ self.editor_area.activate_editor(editor) self.activated() - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( left=VSplitter( - HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2')), - )) - + HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2")) + ) + ) + + def main(argv): """ A simple example of using Tasks. """ @@ -172,6 +228,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/tasks/steps/step8.py python-pyface-7.4.0/examples/tasks/steps/step8.py --- python-pyface-6.1.2/examples/tasks/steps/step8.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tasks/steps/step8.py 2022-02-01 12:21:15.000000000 +0000 @@ -9,87 +9,131 @@ """ import logging + logging.basicConfig(level=logging.DEBUG) import wx -# Enthought library imports. -from pyface.api import GUI, ConfirmationDialog, FileDialog, \ - ImageResource, YES, OK, CANCEL -from pyface.tasks.api import Task, TaskWindow, TaskLayout, PaneItem, IEditor, \ - IEditorAreaPane, EditorAreaPane, Editor, DockPane, HSplitter, VSplitter -from pyface.tasks.action.api import DockPaneToggleGroup, SMenuBar, \ - SMenu, SToolBar, TaskAction, TaskToggleGroup -from traits.api import on_trait_change, Property, Instance, Str, List + +from pyface.action.schema.api import SMenu, SMenuBar, SToolBar +from pyface.api import ( + GUI, + ConfirmationDialog, + FileDialog, + ImageResource, + YES, + OK, + CANCEL, +) +from pyface.tasks.api import ( + Task, + TaskWindow, + TaskLayout, + PaneItem, + IEditor, + IEditorAreaPane, + EditorAreaPane, + Editor, + DockPane, + HSplitter, + VSplitter, +) +from pyface.tasks.action.api import ( + DockPaneToggleGroup, + TaskAction, + TaskToggleGroup, +) +from traits.api import Property, Instance, Str, List class Pane1(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- + + id = "steps.pane1" + name = "Pane 1" - id = 'steps.pane1' - name = 'Pane 1' class Pane2(DockPane): - #### TaskPane interface ################################################### + # TaskPane interface --------------------------------------------------- - id = 'steps.pane2' - name = 'Pane 2' + id = "steps.pane2" + name = "Pane 2" - #### FileBrowserPane interface ############################################ + # FileBrowserPane interface -------------------------------------------- # The list of wildcard filters for filenames. filters = List(Str) - + def create_contents(self, parent): - control = wx.GenericDirCtrl(parent, -1, size=(200,-1), style=wx.NO_BORDER) + control = wx.GenericDirCtrl( + parent, -1, size=(200, -1), style=wx.NO_BORDER + ) control.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.on_selected) return control - + def on_selected(self, evt): selected_file = self.control.GetFilePath() - wx.CallAfter(self.task.window.application.load_file, selected_file, self.task, in_current_window=True) + wx.CallAfter( + self.task.window.application.load_file, + selected_file, + self.task, + in_current_window=True, + ) + class ExampleTask(Task): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.example_task' - name = 'Multi-Tab Editor' + id = "example.example_task" + name = "Multi-Tab Editor" - active_editor = Property(Instance(IEditor), - depends_on='editor_area.active_editor') + active_editor = Property( + Instance(IEditor), observe="editor_area.active_editor" + ) editor_area = Instance(IEditorAreaPane) - menu_bar = SMenuBar(SMenu(TaskAction(name='New', method='new', - accelerator='Ctrl+N'), - TaskAction(name='Add Task', method='add_task', - accelerator='Ctrl+A'), - TaskAction(name='Remove Task', method='remove_task', - accelerator='Ctrl+R'), - id='File', name='&File'), - SMenu(DockPaneToggleGroup(), - TaskToggleGroup(), - id='View', name='&View')) - - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + menu_bar = SMenuBar( + SMenu( + TaskAction(name="New", method="new", accelerator="Ctrl+N"), + TaskAction( + name="Add Task", method="add_task", accelerator="Ctrl+A" + ), + TaskAction( + name="Remove Task", method="remove_task", accelerator="Ctrl+R" + ), + id="File", + name="&File", + ), + SMenu( + DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" + ), + ) + + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( top=VSplitter( - HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2')), - )) + HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2")) + ) + ) def create_central_pane(self): """ Create the central pane: the script editor. @@ -100,11 +144,11 @@ def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ - return [ Pane1(), Pane2() ] + return [Pane1(), Pane2()] - ########################################################################### + # ------------------------------------------------------------------------ # 'ExampleTask' interface. - ########################################################################### + # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window @@ -129,78 +173,98 @@ window.remove_task(self) window.activate_task(task) - #### Trait property getter/setters ######################################## + # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None + class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.second_task' - name = 'Second Multi-Tab Editor' + id = "example.second_task" + name = "Second Multi-Tab Editor" - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), ] + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) + ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( left=VSplitter( - HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2')), - )) + HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2")) + ) + ) + class ThirdTask(ExampleTask): """ A simple task for opening a blank editor. """ - #### Task interface ####################################################### + # Task interface ------------------------------------------------------- - id = 'example.third_task' - name = 'Third Multi-Tab Editor' + id = "example.third_task" + name = "Third Multi-Tab Editor" - tool_bars = [ SToolBar(TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - TaskAction(method='new', - tooltip='New file', - image=ImageResource('document_new')), - image_size = (32, 32)), + tool_bars = [ + SToolBar( + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + TaskAction( + method="new", + tooltip="New file", + image=ImageResource("document_new"), + ), + image_size=(32, 32), + ) ] - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( right=VSplitter( - HSplitter( - PaneItem('steps.pane1'), - PaneItem('steps.pane2')), - )) - + HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2")) + ) + ) + + def main(argv): """ A simple example of using Tasks. """ @@ -221,6 +285,7 @@ gui.start_event_loop() -if __name__ == '__main__': +if __name__ == "__main__": import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/timer.py python-pyface-7.4.0/examples/timer.py --- python-pyface-6.1.2/examples/timer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/timer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,5 +1,5 @@ """Example of using a pyface Timer.""" -from __future__ import print_function + from pyface.timer.api import Timer from pyface.api import GUI, ApplicationWindow @@ -14,21 +14,21 @@ my_timer = Any() # Count each time the timer task executes. - counter = Int + counter = Int() def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - Action(name='Start Timer', on_perform=self._start_timer), - Action(name='Stop Timer', on_perform=self._stop_timer), - Action(name='E&xit', on_perform=self.close), - name = '&File', + Action(name="Start Timer", on_perform=self._start_timer), + Action(name="Stop Timer", on_perform=self._stop_timer), + Action(name="E&xit", on_perform=self.close), + name="&File", ) ) @@ -49,7 +49,6 @@ if self.my_timer is not None: self.my_timer.Stop() - def _timer_task(self): """The method run periodically by the timer.""" diff -Nru python-pyface-6.1.2/examples/tool_palette.py python-pyface-7.4.0/examples/tool_palette.py --- python-pyface-6.1.2/examples/tool_palette.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tool_palette.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,14 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ - - +# Thanks for using Enthought open source! +""" Example of a Tool Palette. """ import wx @@ -23,46 +18,47 @@ from pyface.action.api import ToolPaletteManager + class MainWindow(ApplicationWindow): """ The main application window. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. - super(MainWindow, self).__init__(**traits) + super().__init__(**traits) # Add a menu bar. self.menu_bar_manager = MenuBarManager( MenuManager( - Action(name='E&xit', on_perform=self.close), - name = '&File', + Action(name="E&xit", on_perform=self.close), name="&File" ) ) return - def _create_contents(self, parent): """ Creates the window contents. """ actions = [] for i in range(25): - actions.append(Action(name='Foo', style='radio', image=ImageResource('document'))) + actions.append( + Action( + name="Foo", style="radio", image=ImageResource("document") + ) + ) - tool_palette = ToolPaletteManager( *actions ) + tool_palette = ToolPaletteManager(*actions) return tool_palette.create_tool_palette(parent).control - - # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() diff -Nru python-pyface-6.1.2/examples/traitsui_window.py python-pyface-7.4.0/examples/traitsui_window.py --- python-pyface-6.1.2/examples/traitsui_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/traitsui_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,16 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2009, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ IPython widget example. """ -# Enthought library imports. + from pyface.api import ApplicationWindow, GUI from traits.api import Enum, HasTraits, Instance, Int, Str from traitsui.api import View, Item @@ -23,19 +20,19 @@ """ Model class representing a person """ #: the name of the person - name = Str + name = Str() #: the age of the person age = Int(18) #: the gender of the person - gender = Enum('female', 'male') + gender = Enum("female", "male") # a default traits view view = View( - Item('name', resizable=True), - Item('age', resizable=True), - Item('gender', resizable=True), + Item("name", resizable=True), + Item("age", resizable=True), + Item("gender", resizable=True), resizable=True, ) @@ -43,29 +40,29 @@ class MainWindow(ApplicationWindow): """ The main application window. """ - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------- # The size of the window. size = (320, 240) # The window title. - title = 'TraitsUI Person' + title = "TraitsUI Person" # The traits object to display person = Instance(Person, ()) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplication' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create the editor. """ - self._ui = self.person.edit_traits(kind='panel', parent=parent) + self._ui = self.person.edit_traits(kind="panel", parent=parent) return self._ui.control # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI. gui = GUI() @@ -75,5 +72,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/tree.py python-pyface-7.4.0/examples/tree.py --- python-pyface-6.1.2/examples/tree.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,57 +1,50 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Tree example. """ -from __future__ import print_function -# Standard library imports. -import os, sys +import os -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import Float, Str +from traits.observation.api import match -# Enthought library imports. from pyface.api import GUI, PythonShell, SplitApplicationWindow -from traits.api import Float, Str -# Local imports. from file_tree import FileTree class MainWindow(SplitApplicationWindow): """ The main application window. """ - #### 'SplitApplicationWindow' interface ################################### + # 'SplitApplicationWindow' interface ----------------------------------- # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the window is split. - direction = Str('vertical') + direction = Str("vertical") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the left hand side or top depending on the split. """ - self._tree = FileTree( - parent, root=os.path.abspath(os.curdir), - ) + self._tree = FileTree(parent, root=os.path.abspath(os.curdir)) - self._tree.on_trait_change(self._on_tree_anytrait_changed) + self._tree.observe( + self._on_tree_anytrait_changed, + match(lambda name, ctrait: True) # listen to all traits + ) return self._tree.control @@ -59,27 +52,27 @@ """ Creates the right hand side or bottom depending on the split. """ self._python_shell = PythonShell(parent) - self._python_shell.bind('widget', self._tree) - self._python_shell.bind('w', self._tree) + self._python_shell.bind("widget", self._tree) + self._python_shell.bind("w", self._tree) return self._python_shell.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_tree_anytrait_changed(self, tree, trait_name, old, new): + def _on_tree_anytrait_changed(self, event): """ Called when any trait on the tree has changed. """ - print('trait', trait_name, 'value', new) + print("trait", event.name, "value", event.new) return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -89,5 +82,3 @@ # Start the GUI event loop. gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/tree_viewer.py python-pyface-7.4.0/examples/tree_viewer.py --- python-pyface-6.1.2/examples/tree_viewer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/tree_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,31 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Tree viewer example. """ -from __future__ import print_function -# Standard library imports. -import os, sys +import os -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import Float, Str +from traits.observation.api import match -# Enthought library imports. from pyface.api import GUI, PythonShell, SplitApplicationWindow -from traits.api import Float, Str -# Local imports. from file_tree_viewer import FileTreeViewer from file_sorters import FileSorter @@ -33,17 +25,17 @@ class MainWindow(SplitApplicationWindow): """ The main application window. """ - #### 'SplitApplicationWindow' interface ################################### + # 'SplitApplicationWindow' interface ----------------------------------- # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the panel is split. - direction = Str('vertical') + direction = Str("vertical") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the left hand side or top depending on the style. """ @@ -52,7 +44,10 @@ parent, input=os.path.abspath(os.curdir), sorter=FileSorter() ) - self._tree_viewer.on_trait_change(self._on_tree_anytrait_changed) + self._tree_viewer.observe( + self._on_tree_anytrait_changed, + match(lambda name, ctrait: True) # listen to all traits + ) return self._tree_viewer.control @@ -60,27 +55,27 @@ """ Creates the right hand side or bottom depending on the style. """ self._python_shell = PythonShell(parent) - self._python_shell.bind('widget', self._tree_viewer) - self._python_shell.bind('w', self._tree_viewer) + self._python_shell.bind("widget", self._tree_viewer) + self._python_shell.bind("w", self._tree_viewer) return self._python_shell.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_tree_anytrait_changed(self, viewer, trait_name, old, new): + def _on_tree_anytrait_changed(self, event): """ Called when any trait on the tree has changed. """ - print('trait', trait_name, 'value', new) + print("trait", event.name, "value", event.new) return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() @@ -90,5 +85,3 @@ # Start the GUI event loop! gui.start_event_loop() - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/examples/undo/commands.py python-pyface-7.4.0/examples/undo/commands.py --- python-pyface-6.1.2/examples/undo/commands.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/undo/commands.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,213 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ----------------------------------------------------------------------------- +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ----------------------------------------------------------------------------- + + +# Enthought library imports. +from traits.api import Instance, Int, Str +from pyface.undo.api import AbstractCommand + +# Local imports. +from model import Label + + +class LabelIncrementSizeCommand(AbstractCommand): + """ The LabelIncrementSizeCommand class is a command that increases the + size of a label's text. This command will merge multiple increments + togther. + """ + + #### 'ICommand' interface ################################################# + + # The data being operated on. + data = Instance(Label) + + # The name of the command. + name = Str("&Increment size") + + #### Private interface #################################################### + + _incremented_by = Int() + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + self.data.increment_size(1) + self._incremented_by = 1 + + def merge(self, other): + # We can merge if the other command is the same type (or a sub-type). + if isinstance(other, type(self)): + self._incremented_by += 1 + merged = True + else: + merged = False + + return merged + + def redo(self): + self.data.increment_size(self._incremented_by) + + def undo(self): + self.data.decrement_size(self._incremented_by) + + +class LabelDecrementSizeCommand(AbstractCommand): + """ The LabelDecrementSizeCommand class is a command that decreases the + size of a label's text. This command will merge multiple decrements + togther. + """ + + #### 'ICommand' interface ################################################# + + # The data being operated on. + data = Instance(Label) + + # The name of the command. + name = Str("&Decrement size") + + #### Private interface #################################################### + + _decremented_by = Int() + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + self.data.decrement_size(1) + self._decremented_by = 1 + + def merge(self, other): + # We can merge if the other command is the same type (or a sub-type). + if isinstance(other, type(self)): + self._decremented_by += 1 + merged = True + else: + merged = False + + return merged + + def redo(self): + self.data.decrement_size(self._decremented_by) + + def undo(self): + self.data.increment_size(self._decremented_by) + + +class LabelNormalFontCommand(AbstractCommand): + """ The LabelNormalFontCommand class is a command that sets a normal font + for a label's text. + """ + + #### 'ICommand' interface ################################################# + + # The data being operated on. + data = Instance(Label) + + # The name of the command. + name = Str("&Normal font") + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + # Save the old value. + self._saved = self.data.style + + # Calling redo() is a convenient way to update the model now that the + # old value is saved. + self.redo() + + def redo(self): + self.data.style = 'normal' + + def undo(self): + self.data.style = self._saved + + +class LabelBoldFontCommand(AbstractCommand): + """ The LabelNormalFontCommand class is a command that sets a bold font for + a label's text. + """ + + #### 'ICommand' interface ############################################# + + # The data being operated on. + data = Instance(Label) + + # The name of the command. + name = Str("&Bold font") + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + # Save the old value. + self._saved = self.data.style + + # Calling redo() is a convenient way to update the model now that the + # old value is saved. + self.redo() + + def redo(self): + self.data.style = 'bold' + + def undo(self): + self.data.style = self._saved + + +class LabelItalicFontCommand(AbstractCommand): + """ The LabelNormalFontCommand class is a command that sets an italic font + for a label's text. + """ + + #### 'ICommand' interface ################################################# + + # The data being operated on. + data = Instance(Label) + + # The name of the command. + name = Str("&Italic font") + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + # Save the old value. + self._saved = self.data.style + + # Calling redo() is a convenient way to update the model now that the + # old value is saved. + self.redo() + + def redo(self): + self.data.style = 'italic' + + def undo(self): + self.data.style = self._saved diff -Nru python-pyface-6.1.2/examples/undo/example_editor_manager.py python-pyface-7.4.0/examples/undo/example_editor_manager.py --- python-pyface-6.1.2/examples/undo/example_editor_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/undo/example_editor_manager.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,166 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ----------------------------------------------------------------------------- +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ----------------------------------------------------------------------------- + + +# Enthought library imports. +from traits.etsconfig.api import ETSConfig +from pyface.workbench.api import Editor, EditorManager + + +class _wxLabelEditor(Editor): + """ _wxLabelEditor is the wx implementation of a label editor. """ + + def create_control(self, parent): + import wx + + w = wx.TextCtrl(parent, style=wx.TE_RICH2) + style = w.GetDefaultStyle() + style.SetAlignment(wx.TEXT_ALIGNMENT_CENTER) + w.SetDefaultStyle(style) + + self._set_text(w) + self._set_size_and_style(w) + + self.obj.observe(self._update_text, 'text') + self.obj.observe(self._update_size, 'size') + self.obj.observe(self._update_style, 'style') + + return w + + def _name_default(self): + return self.obj.text + + def _update_text(self, event): + self._set_text(self.control) + + def _set_text(self, w): + w.SetValue("") + w.WriteText( + "%s(%d points, %s)" % ( + self.obj.text, + self.obj.size, + self.obj.style + ) + ) + + def _update_size(self, event): + self._set_size_and_style(self.control) + + def _update_style(self, event): + self._set_size_and_style(self.control) + + def _set_size_and_style(self, w): + import wx + if self.obj.style == 'normal': + style, weight = wx.NORMAL, wx.NORMAL + elif self.obj.style == 'italic': + style, weight = wx.ITALIC, wx.NORMAL + elif self.obj.style == 'bold': + style, weight = wx.NORMAL, wx.BOLD + else: + raise NotImplementedError( + "style '%s' not supported" % self.obj.style + ) + + f = wx.Font(self.obj.size, wx.ROMAN, style, weight, False) + style = wx.TextAttr("BLACK", wx.NullColour, f) + w.SetDefaultStyle(style) + self._set_text(w) + + +class _PyQt4LabelEditor(Editor): + """ _PyQt4LabelEditor is the PyQt implementation of a label editor. """ + + def create_control(self, parent): + + from pyface.qt import QtCore, QtGui + + w = QtGui.QLabel(parent) + w.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + + self._set_text(w) + self._set_size(w) + self._set_style(w) + + self.obj.observe(self._update_text, 'text') + self.obj.oberve(self._update_size, 'size') + self.obj.observe(self._update_style, 'style') + + return w + + def _name_default(self): + return self.obj.text + + def _update_text(self, event): + self._set_text(self.control) + + def _set_text(self, w): + w.setText( + "%s\n(%d points, %s)" % ( + self.obj.text, + self.obj.size, + self.obj.style + ) + ) + + def _update_size(self, event): + self._set_size(self.control) + + def _set_size(self, w): + f = w.font() + f.setPointSize(self.obj.size) + w.setFont(f) + + self._set_text(w) + + def _update_style(self, event): + self._set_style(self.control) + + def _set_style(self, w): + f = w.font() + f.setBold(self.obj.style == 'bold') + f.setItalic(self.obj.style == 'italic') + w.setFont(f) + + self._set_text(w) + + +class ExampleEditorManager(EditorManager): + """ The ExampleEditorManager class creates the example editors. """ + + def create_editor(self, window, obj, kind): + + # Create the toolkit specific editor. + tk_name = ETSConfig.toolkit + + if tk_name == 'wx': + ed = _wxLabelEditor(window=window, obj=obj) + elif tk_name == 'qt4' or tk_name == 'qt': + ed = _PyQt4LabelEditor(window=window, obj=obj) + else: + raise NotImplementedError( + "unsupported toolkit: %s" % tk_name + ) + + return ed diff -Nru python-pyface-6.1.2/examples/undo/example.py python-pyface-7.4.0/examples/undo/example.py --- python-pyface-6.1.2/examples/undo/example.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/undo/example.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,96 @@ +# (C) Copyright 2008-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ----------------------------------------------------------------------------- +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ----------------------------------------------------------------------------- + + +# Standard library imports. +import logging + +# Enthought library imports. +from pyface.api import GUI, YES +from pyface.workbench.api import Workbench +from pyface.undo.api import UndoManager + +# Local imports. +from example_undo_window import ExampleUndoWindow +from model import Label + + +# Log to stderr. +logging.getLogger().addHandler(logging.StreamHandler()) +logging.getLogger().setLevel(logging.DEBUG) + + +class ExampleUndo(Workbench): + """ The ExampleUndo class is a workbench that creates ExampleUndoWindow + windows. + """ + + #### 'Workbench' interface ################################################ + + # The factory (in this case simply a class) that is used to create + # workbench windows. + window_factory = ExampleUndoWindow + + ########################################################################### + # Private interface. + ########################################################################### + + def _exiting_changed(self, event): + """ Called when the workbench is exiting. """ + + if self.active_window.confirm('Ok to exit?') != YES: + event.veto = True + + return + + +def main(argv): + """ A simple example of using the the undo framework in a workbench. """ + + # Create the GUI. + gui = GUI() + + # Create the workbench. + workbench = ExampleUndo(state_location=gui.state_location) + + window = workbench.create_window(position=(300, 300), size=(400, 300)) + window.open() + + # Create some objects to edit. + label = Label(text="Label") + label2 = Label(text="Label2") + + # Edit the objects. + window.edit(label) + window.edit(label2) + + # Start the GUI event loop. + gui.start_event_loop() + + return + + +if __name__ == '__main__': + import sys + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/undo/example_undo_window.py python-pyface-7.4.0/examples/undo/example_undo_window.py --- python-pyface-6.1.2/examples/undo/example_undo_window.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/undo/example_undo_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,150 @@ +# (C) Copyright 2008-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ----------------------------------------------------------------------------- +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ----------------------------------------------------------------------------- + + +# Enthought library imports. +from pyface.action.api import Action, Group, MenuManager +from pyface.workbench.api import WorkbenchWindow +from pyface.workbench.action.api import MenuBarManager, ToolBarManager +from traits.api import Instance +from pyface.undo.action.api import CommandAction, RedoAction, UndoAction + +# Local imports. +from example_editor_manager import ExampleEditorManager +from commands import LabelIncrementSizeCommand, LabelDecrementSizeCommand, \ + LabelNormalFontCommand, LabelBoldFontCommand, LabelItalicFontCommand + + +class ExampleUndoWindow(WorkbenchWindow): + """ The ExampleUndoWindow class is a workbench window that contains example + editors that demonstrate the use of the undo framework. + """ + + #### Private interface #################################################### + + # The action that exits the application. + _exit_action = Instance(Action) + + # The File menu. + _file_menu = Instance(MenuManager) + + # The Label menu. + _label_menu = Instance(MenuManager) + + # The Undo menu. + _undo_menu = Instance(MenuManager) + + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait initialisers ################################################### + + def __file_menu_default(self): + """ Trait initialiser. """ + + return MenuManager(self._exit_action, name="&File") + + def __undo_menu_default(self): + """ Trait initialiser. """ + undo_manager = self.workbench.undo_manager + + undo_action = UndoAction(undo_manager=undo_manager) + redo_action = RedoAction(undo_manager=undo_manager) + + return MenuManager(undo_action, redo_action, name="&Undo") + + def __label_menu_default(self): + """ Trait initialiser. """ + + size_group = Group(CommandAction(command=LabelIncrementSizeCommand), + CommandAction(command=LabelDecrementSizeCommand)) + + normal = CommandAction(id='normal', command=LabelNormalFontCommand, + style='radio', checked=True) + bold = CommandAction(id='bold', command=LabelBoldFontCommand, + style='radio') + italic = CommandAction(id='italic', command=LabelItalicFontCommand, + style='radio') + + style_group = Group(normal, bold, italic, id='style') + + return MenuManager(size_group, style_group, name="&Label") + + def __exit_action_default(self): + """ Trait initialiser. """ + + return Action(name="E&xit", on_perform=self.workbench.exit) + + def _editor_manager_default(self): + """ Trait initialiser. """ + + return ExampleEditorManager() + + def _menu_bar_manager_default(self): + """ Trait initialiser. """ + + return MenuBarManager( + self._file_menu, + self._label_menu, + self._undo_menu, + window=self + ) + + def _tool_bar_manager_default(self): + """ Trait initialiser. """ + + return ToolBarManager(self._exit_action, show_tool_names=False) + + def _active_editor_changed(self, old, new): + """ Trait handler. """ + + # Tell the undo manager about the new command stack. + if old is not None: + old.command_stack.undo_manager.active_stack = None + + if new is not None: + new.command_stack.undo_manager.active_stack = new.command_stack + + # Walk the label editor menu. + for grp in self._label_menu.groups: + for itm in grp.items: + action = itm.action + + # Enable the action and set the command stack and data if there + # is a new editor. + if new is not None: + action.enabled = True + action.command_stack = new.command_stack + action.data = new.obj + + # FIXME v3: We should just be able to check the menu option + # corresponding to the style trait - but that doesn't seem + # to uncheck the other options in the group. Even then the + # first switch to another editor doesn't update the menus + # (though subsequent ones do). + if grp.id == 'style': + action.checked = (action.data.style == action.id) + else: + action.enabled = False diff -Nru python-pyface-6.1.2/examples/undo/model.py python-pyface-7.4.0/examples/undo/model.py --- python-pyface-6.1.2/examples/undo/model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/examples/undo/model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,56 @@ +# (C) Copyright 2008-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ----------------------------------------------------------------------------- +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ----------------------------------------------------------------------------- + + +# Enthought library imports. +from traits.api import Enum, HasTraits, Int, Str + + +class Label(HasTraits): + """The Label class implements the data model for a label.""" + + #### 'Label' interface #################################################### + + # The name. + name = Str() + + # The size in points. + size = Int(18) + + # The style. + style = Enum('normal', 'bold', 'italic') + + ########################################################################### + # 'Label' interface. + ########################################################################### + + def increment_size(self, by): + """Increment the current font size.""" + + self.size += by + + def decrement_size(self, by): + """Decrement the current font size.""" + + self.size -= by diff -Nru python-pyface-6.1.2/examples/wizard.py python-pyface-7.4.0/examples/wizard.py --- python-pyface-6.1.2/examples/wizard.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,99 +1,95 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Wizard example. """ - -from __future__ import print_function +# Thanks for using Enthought open source! -# Standard library imports. -import os -import sys +""" Wizard example. """ -# Put the Enthought library on the Python path. -sys.path.append(os.path.abspath(r'..\..\..')) +from traits.api import HasTraits, Str -# Enthought library imports. from pyface.api import GUI, OK from pyface.wizard.api import SimpleWizard, WizardPage -from traits.api import Color, HasTraits, Int, Str +from pyface.ui_traits import TraitsUIColor as Color class Details(HasTraits): """ Some test data. """ - name = Str + name = Str() color = Color class SimpleWizardPage(WizardPage): """ A simple wizard page. """ - #### 'SimpleWizardPage' interface ######################################### + # 'SimpleWizardPage' interface ----------------------------------------- # The page color. color = Color - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_page_content(self, parent): """ Create the wizard page. """ details = Details(color=self.color) - details.on_trait_change(self._on_name_changed, 'name') + details.observe(self._on_name_changed, "name") - return details.edit_traits(parent=parent, kind='subpanel').control + return details.edit_traits(parent=parent, kind="subpanel").control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_name_changed(self, new): + def _on_name_changed(self, event): """ Called when the name has been changed. """ - self.complete = len(new.strip()) > 0 + self.complete = len(event.new.strip()) > 0 return # Application entry point. -if __name__ == '__main__': +if __name__ == "__main__": # Create the GUI (this does NOT start the GUI event loop). gui = GUI() wizard = SimpleWizard( - parent = None, - title = 'Create something magical', - pages = [ - SimpleWizardPage(id='foo', color='red', - heading="The Red Page", - subheading="The default color on this page is red."), - SimpleWizardPage(id='bar', color='yellow', - heading="The Yellow Page", - subheading="The default color on this page is yellow."), - SimpleWizardPage(id='baz', color='green', - heading="The Green Page", - subheading="The default color on this page is green.") - ] + parent=None, + title="Create something magical", + pages=[ + SimpleWizardPage( + id="foo", + color="red", + heading="The Red Page", + subheading="The default color on this page is red.", + ), + SimpleWizardPage( + id="bar", + color="yellow", + heading="The Yellow Page", + subheading="The default color on this page is yellow.", + ), + SimpleWizardPage( + id="baz", + color="green", + heading="The Green Page", + subheading="The default color on this page is green.", + ), + ], ) # Create and open the wizard. if wizard.open() == OK: - print('Wizard completed successfully') + print("Wizard completed successfully") else: - print('Wizard cancelled') - -##### EOF ##################################################################### + print("Wizard cancelled") diff -Nru python-pyface-6.1.2/examples/workbench/black_view.py python-pyface-7.4.0/examples/workbench/black_view.py --- python-pyface-6.1.2/examples/workbench/black_view.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/black_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,17 @@ """ A view containing a black panel! """ -# Local imports. from color_view import ColorView class BlackView(ColorView): """ A view containing a black panel! """ - #### 'IView' interface #################################################### + # 'IView' interface ---------------------------------------------------- # The view's name. - name = 'Black' + name = "Black" # The default position of the view relative to the item specified in the # 'relative_to' trait. - position = 'top' - -#### EOF ###################################################################### + position = "top" diff -Nru python-pyface-6.1.2/examples/workbench/blue_view.py python-pyface-7.4.0/examples/workbench/blue_view.py --- python-pyface-6.1.2/examples/workbench/blue_view.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/blue_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,17 @@ """ A view containing a blue panel! """ -# Local imports. from color_view import ColorView class BlueView(ColorView): """ A view containing a blue panel! """ - #### 'IView' interface #################################################### + # 'IView' interface ---------------------------------------------------- # The view's name. - name = 'Blue' + name = "Blue" # The default position of the view relative to the item specified in the # 'relative_to' trait. - position = 'bottom' - -#### EOF ###################################################################### + position = "bottom" diff -Nru python-pyface-6.1.2/examples/workbench/color_view.py python-pyface-7.4.0/examples/workbench/color_view.py --- python-pyface-6.1.2/examples/workbench/color_view.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/color_view.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,7 +1,6 @@ """ A view containing a colored panel! """ -# Enthought library imports. from traits.etsconfig.api import ETSConfig from pyface.workbench.api import View @@ -9,16 +8,16 @@ class ColorView(View): """ A view containing a colored panel! """ - #### 'IView' interface #################################################### + # 'IView' interface ---------------------------------------------------- # The category that the view belongs to. - category = 'Color' + category = "Color" - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _id_default(self): """ Trait initializer. """ @@ -29,7 +28,7 @@ # name and class name. return self.name - #### Methods ############################################################## + # Methods -------------------------------------------------------------- def create_control(self, parent): """ Creates the toolkit-specific control that represents the view. @@ -38,17 +37,17 @@ """ - method = getattr(self, '_%s_create_control' % ETSConfig.toolkit, None) + method = getattr(self, "_%s_create_control" % ETSConfig.toolkit, None) if method is None: - raise SystemError('Unknown toolkit %s', ETSConfig.toolkit) + raise SystemError("Unknown toolkit %s", ETSConfig.toolkit) color = self.name.lower() return method(parent, color) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _wx_create_control(self, parent, color): """ Create a wx version of the control. """ @@ -68,10 +67,8 @@ widget = QtGui.QWidget(parent) palette = widget.palette() - palette.setColor(QtGui.QPalette.Window, QtGui.QColor(color)) + palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor(color)) widget.setPalette(palette) widget.setAutoFillBackground(True) return widget - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/examples/workbench/example_workbench.py python-pyface-7.4.0/examples/workbench/example_workbench.py --- python-pyface-6.1.2/examples/workbench/example_workbench.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/example_workbench.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,30 @@ """ A simple example of using the workbench. """ -# Enthought library imports. from pyface.api import YES from pyface.workbench.api import Workbench -# Local imports. + from example_workbench_window import ExampleWorkbenchWindow class ExampleWorkbench(Workbench): """ A simple example of using the workbench. """ - #### 'Workbench' interface ################################################ + # 'Workbench' interface ------------------------------------------------ # The factory (in this case simply a class!) that is used to create # workbench windows. window_factory = ExampleWorkbenchWindow - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _exiting_changed(self, event): """ Called when the workbench is exiting. """ - if self.confirm('Ok to exit?') != YES: + if self.confirm("Ok to exit?") != YES: event.veto = True return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/examples/workbench/example_workbench_window.py python-pyface-7.4.0/examples/workbench/example_workbench_window.py --- python-pyface-6.1.2/examples/workbench/example_workbench_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/example_workbench_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,6 @@ """ A simple example of using the workbench window. """ -# Enthought library imports. from pyface.action.api import Action, MenuManager from pyface.workbench.api import EditorManager, WorkbenchWindow from pyface.workbench.api import Perspective, PerspectiveItem @@ -10,7 +9,7 @@ from pyface.workbench.action.api import ViewMenuManager from traits.api import Callable, List, Instance -# Local imports. + from black_view import BlackView from blue_view import BlueView from green_view import GreenView @@ -22,9 +21,9 @@ class ExampleEditorManager(EditorManager): """ An editor manager that supports the editor memento protocol. """ - ####################################################################### + # --------------------------------------------------------------------- # 'IEditorManager' interface. - ####################################################################### + # --------------------------------------------------------------------- def get_editor_memento(self, editor): """ Return the state of the editor contents. """ @@ -46,38 +45,37 @@ class ExampleWorkbenchWindow(WorkbenchWindow): """ A simple example of using the workbench window. """ - #### 'WorkbenchWindow' interface ########################################## + # 'WorkbenchWindow' interface ------------------------------------------ # The available perspectives. perspectives = [ Perspective( - name = 'Foo', - contents = [ - PerspectiveItem(id='Black', position='bottom', height=0.1), - PerspectiveItem(id='Debug', position='left', width=0.25) - ] + name="Foo", + contents=[ + PerspectiveItem(id="Black", position="bottom", height=0.1), + PerspectiveItem(id="Debug", position="left", width=0.25), + ], ), - Perspective( - name = 'Bar', - contents = [ - PerspectiveItem(id='Black', position='top'), - PerspectiveItem(id='Blue', position='bottom'), - PerspectiveItem(id='Green', position='left'), - PerspectiveItem(id='Red', position='right'), - PerspectiveItem(id='Debug', position='left') - ] - ) + name="Bar", + contents=[ + PerspectiveItem(id="Black", position="top"), + PerspectiveItem(id="Blue", position="bottom"), + PerspectiveItem(id="Green", position="left"), + PerspectiveItem(id="Red", position="right"), + PerspectiveItem(id="Debug", position="left"), + ], + ), ] - #### 'ExampleWorkbenchWindow' interface ################################### + # 'ExampleWorkbenchWindow' interface ----------------------------------- # The view factories. # # fixme: This should be part of the standadr 'WorkbenchWindow'! view_factories = List(Callable) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # The Exit action. _exit_action = Instance(Action) @@ -85,11 +83,11 @@ # The New Person action. _new_person_action = Instance(Action) - ########################################################################### + # ------------------------------------------------------------------------ # 'ApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _editor_manager_default(self): """ Trait initializer. @@ -103,10 +101,12 @@ """ Trait initializer. """ file_menu = MenuManager( - self._new_person_action, self._exit_action, - name='&File', id='FileMenu' + self._new_person_action, + self._exit_action, + name="&File", + id="FileMenu", ) - view_menu = ViewMenuManager(name='&View', id='ViewMenu', window=self) + view_menu = ViewMenuManager(name="&View", id="ViewMenu", window=self) return MenuBarManager(file_menu, view_menu, window=self) @@ -117,19 +117,18 @@ # allowed! tool_bar_managers = [ ToolBarManager( - self._exit_action, show_tool_names = False, name=str(i) + self._exit_action, show_tool_names=False, name=str(i) ) - for i in range(5) ] return tool_bar_managers - ########################################################################### + # ------------------------------------------------------------------------ # 'WorkbenchWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _view_factories_default(self): """ Trait initializer. """ @@ -146,27 +145,25 @@ # reference to its toolkit-specific control etc.). return [factory(window=self) for factory in self.view_factories] - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def __exit_action_default(self): """ Trait initializer. """ - return Action(name='E&xit', on_perform=self.workbench.exit) + return Action(name="E&xit", on_perform=self.workbench.exit) def __new_person_action_default(self): """ Trait initializer. """ - return Action(name='New Person', on_perform=self._new_person) + return Action(name="New Person", on_perform=self._new_person) def _new_person(self): """ Create a new person. """ from person import Person - self.workbench.edit(Person(name='New', age=100)) + self.workbench.edit(Person(name="New", age=100)) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/examples/workbench/green_view.py python-pyface-7.4.0/examples/workbench/green_view.py --- python-pyface-6.1.2/examples/workbench/green_view.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/green_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,17 @@ """ A view containing a green panel! """ -# Local imports. from color_view import ColorView class GreenView(ColorView): """ A view containing a green panel! """ - #### 'IView' interface #################################################### + # 'IView' interface ---------------------------------------------------- # The view's name. - name = 'Green' + name = "Green" # The default position of the view relative to the item specified in the # 'relative_to' trait. - position = 'left' - -#### EOF ###################################################################### + position = "left" diff -Nru python-pyface-6.1.2/examples/workbench/images/image_LICENSE.txt python-pyface-7.4.0/examples/workbench/images/image_LICENSE.txt --- python-pyface-6.1.2/examples/workbench/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Enthought BSD 3-Clause LICENSE.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -examples/workbench/images: - example.ico | Enthought diff -Nru python-pyface-6.1.2/examples/workbench/person.py python-pyface-7.4.0/examples/workbench/person.py --- python-pyface-6.1.2/examples/workbench/person.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/person.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,6 @@ """ A simple example of some object model. """ -# Enthought library imports. from traits.api import HasTraits, Int, Str @@ -9,18 +8,16 @@ """ A simple example of some object model! """ # Age in years. - age = Int + age = Int() # Name. - name = Str + name = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __str__(self): """ Return an informal string representation of the object. """ return self.name - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/examples/workbench/red_view.py python-pyface-7.4.0/examples/workbench/red_view.py --- python-pyface-6.1.2/examples/workbench/red_view.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/red_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,17 @@ """ A view containing a red panel! """ -# Local imports. from color_view import ColorView class RedView(ColorView): """ A view containing a red panel! """ - #### 'IView' interface #################################################### + # 'IView' interface ---------------------------------------------------- # The view's name. - name = 'Red' + name = "Red" # The default position of the view relative to the item specified in the # 'relative_to' trait. - position = 'right' - -#### EOF ###################################################################### + position = "right" diff -Nru python-pyface-6.1.2/examples/workbench/run.py python-pyface-7.4.0/examples/workbench/run.py --- python-pyface-6.1.2/examples/workbench/run.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/run.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,19 @@ """ Run the workbench example. """ -# Standard library imports. import logging -# Enthought library imports. + from pyface.api import GUI -# Local imports. + from example_workbench import ExampleWorkbench from person import Person # Log to stderr. logger = logging.getLogger() -logger.addHandler(logging.StreamHandler(open('example_workbench.log', 'w'))) +logger.addHandler(logging.StreamHandler(open("example_workbench.log", "w"))) logger.setLevel(logging.DEBUG) @@ -25,8 +24,8 @@ gui = GUI() # Create some objects to edit. - fred = Person(name='fred', age=42) - wilma = Person(name='wilma', age=35) + fred = Person(name="fred", age=42) + wilma = Person(name="wilma", age=35) # Create the workbench. # @@ -38,20 +37,22 @@ workbench = ExampleWorkbench(state_location=gui.state_location) # Create some workbench windows. - x = 300; y = 300 + x = 300 + y = 300 for i in range(2): window = workbench.create_window(position=(x, y), size=(800, 600)) window.open() # Edit the objects if they weren't restored from a previous session. - if window.get_editor_by_id('fred') is None: + if window.get_editor_by_id("fred") is None: window.edit(fred) - if window.get_editor_by_id('wilma') is None: + if window.get_editor_by_id("wilma") is None: window.edit(wilma) # Cascade the windows. - x += 100; y += 100 + x += 100 + y += 100 # Start the GUI event loop. gui.start_event_loop() @@ -59,7 +60,7 @@ return -if __name__ == '__main__': - import sys; main(sys.argv) +if __name__ == "__main__": + import sys -#### EOF ###################################################################### + main(sys.argv) diff -Nru python-pyface-6.1.2/examples/workbench/yellow_view.py python-pyface-7.4.0/examples/workbench/yellow_view.py --- python-pyface-6.1.2/examples/workbench/yellow_view.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/examples/workbench/yellow_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,22 @@ """ A view containing a yellow panel! """ -# Local imports. from color_view import ColorView class YellowView(ColorView): """ A view containing a yellow panel! """ - #### 'IView' interface #################################################### + # 'IView' interface ---------------------------------------------------- # The view's name. - name = 'Yellow' + name = "Yellow" # The default position of the view relative to the item specified in the # 'relative_to' trait. - position = 'with' + position = "with" # The Id of the view to position this view relative to. If this is not # specified (or if no view exists with this Id) then the view will be # placed relative to the editor area. - relative_to = 'Green' - -#### EOF ###################################################################### + relative_to = "Green" diff -Nru python-pyface-6.1.2/image_LICENSE.txt python-pyface-7.4.0/image_LICENSE.txt --- python-pyface-6.1.2/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/image_LICENSE.txt 2022-02-01 12:21:15.000000000 +0000 @@ -5,76 +5,84 @@ ---------------------------------------------------------------------------- Eclipse Eclipse Public License image_LICENSE_Eclipse.txt Enthought BSD 3-Clause LICENSE.txt +FamFamFam Flags Public Domain http://www.famfamfam.com/lab/icons/flags/ GV (Gael Varoquaux) Public Domain N/A Nuvola LGPL image_LICENSE_Nuvola.txt OOo LGPL image_LICENSE_OOo.txt +Oxygen CC-SA 3.0 LICENSE-CC-BY-3.0.txt +PSF Trademark https://www.python.org/community/logos/ Unless stated in this file, icons are the work of Enthought, and are released under a 3 clause BSD license. Files and orginal authors: ---------------------------------------------------------------------------- +examples/application/python_editor/images: + document_new.png | Oxygen + document_open.png | Oxygen + document_save.png | Oxygen + python_icon.png | PSF + python_logo.png | PSF + +examples/application/python_shell/images: + python_icon.png | PSF + python_logo.png | PSF + +examples/data_view/images: + ca.png | FamFamFam Flags + gb.png | FamFamFam Flags + us.png | FamFamFam Flags + +examples/dock/images: + folder.png | Nuvola + gear.png | Nuvola + +examples/images: + closed_folder_24x24.png | Nuvola + closed_folder.png | Nuvola + document.png | GV + open_folder.png | Nuvola + +examples/tasks/advanced/images: + document_new.png | Oxygen + document_open.png | Oxygen + document_save.png | Oxygen + +examples/tasks/basic/images: + document_open.png | Oxygen + document_save.png | Oxygen + pyface/action/images: action.png | Eclipse -pyface/images: - about.jpg | Enthought - background.jpg | Enthought - caret_close.png | Enthought - caret_open.png | Enthought - close.png | GV - image_not_found.png | Nuvola - panel_gradient.png | Enthought - panel_gradient_over.png | Enthought - question.png | GV - splash.png | Enthought - pyface/dock/images: - bar_feature_changed.png | Enthought - bar_feature_disabled.png | Enthought - bar_feature_drop.png | Enthought - bar_feature_no_drop.png | Enthought - bar_feature_normal.png | Enthought close_drag.png | GV close_tab.png | GV feature_tool.png | GV - sh_bottom.png | Enthought - sh_middle.png | Enthought - sh_top.png | Enthought - shell.ico | Enthought - sv_left.png | Enthought - sv_middle.png | Enthought - sv_right.png | Enthought - tab_feature_changed.png | Enthought - tab_feature_disabled.png | Enthought - tab_feature_drop.png | Enthought tab_feature_no_drop.png | OOo tab_feature_normal.png | OOo - tab_scroll_l.png | Enthought - tab_scroll_lr.png | Enthought - tab_scroll_r.png | Enthought window.png | Eclipse -pyface/ui/wx/grid/images: - checked.png | GV +pyface/images: + about.jpg | Python Software Foundation + close.png | GV image_not_found.png | Nuvola - table_edit.png | OOo - unchecked.png | GV + question.png | GV + splash.png | Python Software Foundation -pyface/tree/images: - closed_folder.png | Nuvola - document.png | GV - open_folder.png | Nuvola +pyface/tests/images: + core.png | Nuvola -examples/images: - closed_folder_24x24.png | Nuvola +pyface/tree/images: closed_folder.png | Nuvola document.png | GV open_folder.png | Nuvola -examples/dock/images: - folder.png | Nuvola - gear.png | Nuvola +pyface/ui/wx/grid/images: + checked.png | GV + image_not_found.png | Nuvola + table_edit.png | OOo + unchecked.png | GV -examples/workbench/images: - example.ico | Enthought +pyface/ui/wx/images: + warning.png | GV diff -Nru python-pyface-6.1.2/LICENSE-CC-BY-3.0.txt python-pyface-7.4.0/LICENSE-CC-BY-3.0.txt --- python-pyface-6.1.2/LICENSE-CC-BY-3.0.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/LICENSE-CC-BY-3.0.txt 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,319 @@ +Creative Commons Legal Code + +Attribution 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR + DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(f) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + e. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + f. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + g. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + h. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + i. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(b), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(b), as requested. + b. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and (iv) , consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4 (b) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION +OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at https://creativecommons.org/. diff -Nru python-pyface-6.1.2/MANIFEST.in python-pyface-7.4.0/MANIFEST.in --- python-pyface-6.1.2/MANIFEST.in 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/MANIFEST.in 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,9 @@ -include *.txt +include image_LICENSE.txt +include image_LICENSE_Eclipse.txt +include image_LICENSE_Nuvola.txt +include image_LICENSE_OOo.txt +include LICENSE-CC-BY-3.0.txt +include LICENSE.txt include README.rst include MANIFEST.in recursive-include pyface *.py *.png *.jpg *.gif *.svg *.zip *.txt diff -Nru python-pyface-6.1.2/PKG-INFO python-pyface-7.4.0/PKG-INFO --- python-pyface-6.1.2/PKG-INFO 2019-07-22 09:15:12.000000000 +0000 +++ python-pyface-7.4.0/PKG-INFO 2022-02-02 10:48:09.000000000 +0000 @@ -1,41 +1,59 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: pyface -Version: 6.1.2 +Version: 7.4.0 Summary: traits-capable windowing framework Home-page: http://docs.enthought.com/pyface -Author: ETS Developers -Author-email: enthought-dev@enthought.com +Author: David C. Morrill, et al. +Author-email: dmorrill@enthought.com +Maintainer: ETS Developers +Maintainer-email: enthought-dev@enthought.com License: BSD Download-URL: https://github.com/enthought/pyface -Description-Content-Type: UNKNOWN Description: ========================================== - pyface: traits-capable windowing framework + Pyface: Traits-capable Windowing Framework ========================================== - .. image:: https://travis-ci.org/enthought/pyface.svg?branch=master - :target: https://travis-ci.org/enthought/pyface - - .. image:: https://ci.appveyor.com/api/projects/status/68nfb049cdq9wqd1/branch/master?svg=true - :target: https://ci.appveyor.com/project/EnthoughtOSS/pyface/branch/master - - .. image:: https://codecov.io/github/enthought/pyface/coverage.svg?branch=master - :target: https://codecov.io/github/enthought/pyface?branch=master - - - The pyface project contains a toolkit-independent GUI abstraction layer, + The Pyface project contains a toolkit-independent GUI abstraction layer, which is used to support the "visualization" features of the Traits package. Thus, you can write code in terms of the Traits API (views, items, editors, - etc.), and let pyface and your selected toolkit and back-end take care of + etc.), and let Pyface and your selected toolkit and back-end take care of the details of displaying them. The following GUI backends are supported: - - wxPython - - PyQt - - PySide + - PySide2 (stable) and PySide6 (experimental) + - PyQt5 (stable) and PyQt6 (in development) + - wxPython 4 (experimental) + + Installation + ------------ + + GUI backends are marked as optional dependencies of Pyface. Some features + or infrastructures may also require additional dependencies. + + To install with PySide2 dependencies:: + + $ pip install pyface[pyside2] + + To install with PySide6 dependencies (experimental):: + + $ pip install pyface[pyside6] - **Warning:** The default toolkit if none is supplied is ``qt4``. - This changed from ``wx`` in Pyface 5.0.. + To install with PyQt5 dependencies:: + + $ pip install pyface[pyqt5] + + To install with wxPython4 dependencies (experimental):: + + $ pip install pyface[wx] + + ``pillow`` is an optional dependency for the PILImage class:: + + $ pip install pyface[pillow] + + To install with additional test dependencies:: + + $ pip install pyface[test] Documentation ------------- @@ -49,13 +67,15 @@ Pyface depends on: - * a GUI toolkit: one of PySide, PyQt or WxPython - * `Traits `_ + * a GUI toolkit as described above + * Pygments for syntax highlighting in the Qt code editor widget. - * some widgets may have additional optional dependencies. + * some widgets may have additional optional dependencies such as NumPy or + Pillow. + Platform: Windows Platform: Linux @@ -72,10 +92,20 @@ Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Provides-Extra: wx +Provides-Extra: pyqt +Provides-Extra: pyqt5 +Provides-Extra: pyqt6 +Provides-Extra: pyside2 +Provides-Extra: pyside6 +Provides-Extra: pillow +Provides-Extra: test diff -Nru python-pyface-6.1.2/pyface/about_dialog.py python-pyface-7.4.0/pyface/about_dialog.py --- python-pyface-6.1.2/pyface/about_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/about_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a simple 'About' dialog. """ -from __future__ import absolute_import # Import the toolkit specific version. from .toolkit import toolkit_object -AboutDialog = toolkit_object('about_dialog:AboutDialog') -### EOF ####################################################################### +AboutDialog = toolkit_object("about_dialog:AboutDialog") diff -Nru python-pyface-6.1.2/pyface/action/action_controller.py python-pyface-7.4.0/pyface/action/action_controller.py --- python-pyface-6.1.2/pyface/action/action_controller.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/action_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,24 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The default action controller for menus, menu bars and tool bars. """ -# Enthought library imports. from traits.api import HasTraits class ActionController(HasTraits): """ The default action controller for menus, menu bars and tool bars. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, action, event): """ Control an action invocation. @@ -40,7 +48,7 @@ Returns ------- can_add : bool - ``True` if the action can be added to a menu/menubar. + ``True`` if the action can be added to a menu/menubar. """ return True @@ -65,7 +73,7 @@ Returns ------- can_add : bool - ``True` if the action can be added to a toolbar. + ``True`` if the action can be added to a toolbar. """ return True diff -Nru python-pyface-6.1.2/pyface/action/action_event.py python-pyface-7.4.0/pyface/action/action_event.py --- python-pyface-6.1.2/pyface/action/action_event.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/action_event.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,40 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The event passed to an action's 'perform' method. """ -# Standard library imports. import time -# Enthought library imports. -from traits.api import Float, HasTraits, Int + +from traits.api import Float, HasTraits class ActionEvent(HasTraits): """ The event passed to an action's 'perform' method. """ - #### 'ActionEvent' interface ############################################## + # 'ActionEvent' interface ---------------------------------------------# #: When the action was performed (time.time()). - when = Float + when = Float() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new action event. - Note: Every keyword argument becoames a public attribute of the event. + Note: Every keyword argument becomes a public attribute of the event. """ # Base-class constructor. - super(ActionEvent, self).__init__(**traits) + super().__init__(**traits) # When the action was performed. self.when = time.time() diff -Nru python-pyface-6.1.2/pyface/action/action_item.py python-pyface-7.4.0/pyface/action/action_item.py --- python-pyface-6.1.2/pyface/action/action_item.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/action_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,49 +1,46 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enth373ought.com/licenses/BSD.txt -# Thanks for using Enthought open source! +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ An action manager item that represents an actual action. """ -# Enthought library imports. -from traits.api import Any, Instance, List, Property, Str, on_trait_change +from traits.api import Any, Instance, List, Property, Str, observe + -# Local imports. from pyface.action.action import Action from pyface.action.action_manager_item import ActionManagerItem # Import the toolkit specific versions of the internal classes. from pyface.toolkit import toolkit_object -_MenuItem = toolkit_object('action.action_item:_MenuItem') -_Tool = toolkit_object('action.action_item:_Tool') -_PaletteTool = toolkit_object('action.action_item:_PaletteTool') + +_MenuItem = toolkit_object("action.action_item:_MenuItem") +_Tool = toolkit_object("action.action_item:_Tool") +_PaletteTool = toolkit_object("action.action_item:_PaletteTool") class ActionItem(ActionManagerItem): """ An action manager item that represents an actual action. """ - #### 'ActionManagerItem' interface ######################################## + # 'ActionManagerItem' interface ---------------------------------------- #: The item's unique identifier ('unique' in this case means unique within #: its group). id = Property(Str) - #### 'ActionItem' interface ############################################### + # 'ActionItem' interface ----------------------------------------------- #: The action! action = Instance(Action) #: The toolkit specific control created for this item. - control = Any + control = Any() #: The toolkit specific Id of the control created for this item. # @@ -51,39 +48,41 @@ #: are created as 'wxObjectPtr's which do not have Ids, and the Id is #: required to manipulate the state of a tool via the tool bar 8^( # FIXME v3: Why is this part of the public interface? - control_id = Any + control_id = Any() - #### Private interface #################################################### + # Private interface ---------------------------------------------------- #: All of the internal instances that wrap this item. _wrappers = List(Any) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManagerItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait properties ##################################################### + # Trait properties ----------------------------------------------------- def _get_id(self): return self.action.id - #### Trait change handlers ################################################ - - def _enabled_changed(self, trait_name, old, new): - self.action.enabled = new + # Trait change handlers ------------------------------------------------ - def _visible_changed(self, trait_name, old, new): - self.action.visible = new + @observe('enabled') + def _enabled_updated(self, event): + self.action.enabled = event.new + + @observe('visible') + def _visible_updated(self, event): + self.action.visible = event.new - @on_trait_change('_wrappers.control') - def _on_destroy(self, object, name, old, new): + @observe("_wrappers:items:control") + def _on_destroy(self, event): """ Handle the destruction of the wrapper. """ - if name == 'control' and new is None: - self._wrappers.remove(object) + if event.new is None: + self._wrappers.remove(event.object) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_to_menu(self, parent, menu, controller): """ Add the item to a menu. @@ -107,8 +106,9 @@ self._wrappers.append(wrapper) - def add_to_toolbar(self, parent, tool_bar, image_cache, controller, - show_labels=True): + def add_to_toolbar( + self, parent, tool_bar, image_cache, controller, show_labels=True + ): """ Adds the item to a tool bar. Parameters diff -Nru python-pyface-6.1.2/pyface/action/action_manager_item.py python-pyface-7.4.0/pyface/action/action_manager_item.py --- python-pyface-6.1.2/pyface/action/action_manager_item.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/action_manager_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all action manager items. """ -# Enthought library imports. from traits.api import Bool, HasTraits, Instance, Str @@ -33,10 +29,10 @@ #: The item's unique identifier ('unique' in this case means unique within #: its group) - id = Str + id = Str() #: The group the item belongs to. - parent = Instance('pyface.action.api.Group') + parent = Instance("pyface.action.api.Group") #: Is the item enabled? enabled = Bool(True) @@ -44,9 +40,9 @@ #: Is the item visible? visible = Bool(True) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManagerItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_to_menu(self, parent, menu, controller): """ Adds the item to a menu. @@ -60,7 +56,7 @@ controller : ActionController instance or None The controller to use. """ - raise NotImplementedError + raise NotImplementedError() def add_to_toolbar(self, parent, tool_bar, image_cache, controller): """ Adds the item to a tool bar. @@ -78,4 +74,4 @@ show_labels : bool Should the toolbar item show a label. """ - raise NotImplementedError + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/action/action_manager.py python-pyface-7.4.0/pyface/action/action_manager.py --- python-pyface-6.1.2/pyface/action/action_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/action_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all action managers. """ -# Enthought library imports. -from __future__ import print_function -from traits.api import Bool, Constant, Event, HasTraits, Instance -from traits.api import List, Property, Str +from traits.api import ( + Bool, Constant, Event, HasTraits, Instance, List, observe, Property, Str +) -# Local imports. from pyface.action.action_controller import ActionController from pyface.action.group import Group -import six class ActionManager(HasTraits): @@ -39,10 +33,10 @@ """ - #### 'ActionManager' interface ############################################ + # 'ActionManager' interface -------------------------------------------- #: The Id of the default group. - DEFAULT_GROUP = Constant('additions') + DEFAULT_GROUP = Constant("additions") #: The action controller (if any) used to control how actions are performed. controller = Instance(ActionController) @@ -54,24 +48,24 @@ groups = Property(List(Group)) #: The manager's unique identifier (if it has one). - id = Str + id = Str() #: Is the action manager visible? visible = Bool(True) - #### Events #### + # Events ---- #: fixme: We probably need more granular events than this! - changed = Event + changed = Event() - #### Private interface #################################################### + # Private interface ---------------------------------------------------- #: All of the contribution groups in the manager. _groups = List(Group) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *args, **traits): """ Creates a new action manager. @@ -92,7 +86,7 @@ If a string is passed, a Group is created with id set to the string. """ # Base class constructor. - super(ActionManager, self).__init__(**traits) + super().__init__(**traits) # The last group in every manager is the group with Id 'additions'. # @@ -104,7 +98,7 @@ for arg in args: # We allow a group to be defined by simply specifying a string (its # Id). - if isinstance(arg, six.string_types): + if isinstance(arg, str): # Create a group with the specified Id. arg = Group(id=arg) @@ -118,34 +112,30 @@ # Otherwise, the item is an action manager item so add it to the # current group. else: -## # If no group has been created then add one. This is only -## # relevant when using the 'shorthand' way to define menus. -## if group is None: -## group = Group(id='__first__') -## self.insert(-1, group) - group.append(arg) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait properties ##################################################### + # Trait properties ----------------------------------------------------- def _get_groups(self): return self._groups[:] - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - def _enabled_changed(self, trait_name, old, new): + @observe('enabled') + def _enabled_updated(self, event): for group in self._groups: - group.enabled = new + group.enabled = event.new - def _visible_changed(self, trait_name, old, new): + @observe('visible') + def _visible_updated(self, event): for group in self._groups: - group.visible = new + group.visible = event.new - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def append(self, item): """ Append an item to the manager. @@ -162,7 +152,7 @@ of groups. It the item is a string, then a group is created with the string as the ``id`` and the new group is appended to the list of groups. If the item is an ActionManagerItem then the item is - appended to the manager's defualt group. + appended to the manager's default group. """ item = self._prepare_item(item) if isinstance(item, Group): @@ -246,14 +236,14 @@ Returns the matching ActionManagerItem, or None if any component of the path is not found. """ - components = path.split('/') + components = path.split("/") # If there is only one component, then the path is just an Id so look # it up in this manager. if len(components) > 0: item = self._find_item(components[0]) if len(components) > 1 and item is not None: - item = item.find_item('/'.join(components[1:])) + item = item.find_item("/".join(components[1:])) else: item = None @@ -303,14 +293,14 @@ fn : callable A callable to apply to the tree of items and subgroups. """ - if hasattr(item, 'groups'): + if hasattr(item, "groups"): item.walk(fn) else: fn(item) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_default_group(self): """ Returns the manager's default group. @@ -347,7 +337,7 @@ item.parent = self # 2) The item is a string. - elif isinstance(item, six.string_types): + elif isinstance(item, str): # Create a group with that Id. item = Group(id=item) item.parent = self @@ -375,36 +365,36 @@ else: return None - ########################################################################### + # ------------------------------------------------------------------------ # Debugging interface. - ########################################################################### + # ------------------------------------------------------------------------ - def dump(self, indent=''): + def dump(self, indent=""): """ Render a manager! """ - print(indent, 'Manager', self.id) - indent += ' ' + print(indent, "Manager", self.id) + indent += " " for group in self._groups: self.render_group(group, indent) - def render_group(self, group, indent=''): + def render_group(self, group, indent=""): """ Render a group! """ - print(indent, 'Group', group.id) - indent += ' ' + print(indent, "Group", group.id) + indent += " " for item in group.items: if isinstance(item, Group): - print('Surely, a group cannot contain another group!!!!') + print("Surely, a group cannot contain another group!!!!") self.render_group(item, indent) else: self.render_item(item, indent) - def render_item(self, item, indent=''): + def render_item(self, item, indent=""): """ Render an item! """ - if hasattr(item, 'groups'): + if hasattr(item, "groups"): item.dump(indent) else: - print(indent, 'Item', item.id) + print(indent, "Item", item.id) diff -Nru python-pyface-6.1.2/pyface/action/action.py python-pyface-7.4.0/pyface/action/action.py --- python-pyface-6.1.2/pyface/action/action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The base class for all actions. """ from functools import partial -# Enthought library imports. + from traits.api import Bool, Callable, Enum, HasTraits, Str -from traits.api import Unicode from pyface.ui_traits import Image @@ -34,10 +30,10 @@ """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- #: Keyboard accelerator (by default the action has NO accelerator). - accelerator = Unicode + accelerator = Str() #: Is the action checked? This is only relevant if the action style is #: 'radio' or 'toggle'. @@ -46,7 +42,7 @@ #: A longer description of the action (used for context sensitive help etc). #: If no description is specified, the tooltip is used instead (and if there #: is no tooltip, then well, maybe you just hate your users ;^). - description = Unicode + description = Str() #: Is the action enabled? enabled = Bool(True) @@ -55,31 +51,31 @@ visible = Bool(True) #: The action's unique identifier (may be None). - id = Str + id = Str() #: The action's image (displayed on tool bar tools etc). image = Image #: The action's name (displayed on menus/tool bar tools etc). - name = Unicode + name = Str() #: An (optional) callable that will be invoked when the action is performed. on_perform = Callable #: The action's style. - style = Enum('push', 'radio', 'toggle', 'widget') + style = Enum("push", "radio", "toggle", "widget") #: A short description of the action used for tooltip text etc. - tooltip = Unicode + tooltip = Str() #: An (optional) callable to create the toolkit control for widget style. control_factory = Callable - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Initializers ######################################################### + # Initializers --------------------------------------------------------- def _id_default(self): """ Initializes the 'id' trait. @@ -88,7 +84,7 @@ """ return self.name - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_control(self, parent): """ Called when creating a "widget" style action. @@ -112,7 +108,7 @@ control : toolkit control A toolkit control or None. """ - if self.style == 'widget' and self.control_factory is not None: + if self.style == "widget" and self.control_factory is not None: return self.control_factory(parent, self) return None @@ -141,4 +137,4 @@ This is particularly useful for passing context to Tasks schema additions. """ - return partial(cls, *args, **kwargs) \ No newline at end of file + return partial(cls, *args, **kwargs) diff -Nru python-pyface-6.1.2/pyface/action/api.py python-pyface-7.4.0/pyface/action/api.py --- python-pyface-6.1.2/pyface/action/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,70 @@ -# Copyright (c) 2005-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import absolute_import + +""" + +API for the ``pyface.action`` subpackage. + +Actions +------- + +- :class:`~.Action` +- :class:`~.FieldAction` +- :class:`~.GUIApplicationAction` +- :class:`~.ListeningAction` +- :class:`~.TraitsUIWidgetAction` +- :class:`~.WindowAction` + +Action Controller +----------------- + +- :class:`~pyface.action.action_controller.ActionController` + +Action Event +------------ + +- :class:`~.ActionEvent` + +Action Managers +--------------- + +- :class:`~.ActionManager` +- ``MenuManager`` +- ``MenuBarManager`` +- ``StatusBarManager`` +- ``ToolBarManager`` +- ``ToolPaletteManager``, only for the Wx toolkit. + +Action Manager Items +-------------------- + +- :class:`~.ActionManagerItem` +- :class:`~.ActionItem` + +Layout support +-------------- + +- :class:`~.Group` +- :class:`~.Separator` + +Useful Application and Window actions +------------------------------------- + +- :class:`~.AboutAction` +- :class:`~.CloseActiveWindowAction` +- :class:`~.CreateWindowAction` +- :class:`~.ExitAction` +- :class:`~.CloseWindowAction` + +""" from .action import Action from .action_controller import ActionController @@ -18,8 +75,11 @@ from .field_action import FieldAction from .group import Group, Separator from .gui_application_action import ( - AboutAction, CloseActiveWindowAction, CreateWindowAction, ExitAction, - GUIApplicationAction + AboutAction, + CloseActiveWindowAction, + CreateWindowAction, + ExitAction, + GUIApplicationAction, ) from .listening_action import ListeningAction from .menu_manager import MenuManager @@ -34,5 +94,6 @@ # becomes toolkit agnostic. from traits.etsconfig.api import ETSConfig -if ETSConfig.toolkit == 'wx': + +if ETSConfig.toolkit == "wx": from .tool_palette_manager import ToolPaletteManager diff -Nru python-pyface-6.1.2/pyface/action/field_action.py python-pyface-7.4.0/pyface/action/field_action.py --- python-pyface-6.1.2/pyface/action/field_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/field_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! from traits.api import Any, Constant, Dict, Str, Type @@ -17,7 +18,7 @@ class FieldAction(Action): """ A widget action containing an IField - When the value in the field is changed, the `on_peform` method is called + When the value in the field is changed, the `on_perform` method is called with the new value as the argument. """ @@ -48,15 +49,16 @@ """ field = self.field_type(parent=parent, **self.field_defaults) field._create() - field.on_trait_change(self.value_updated, 'value') + field.observe(self.value_updated, "value") field.control._field = field return field.control - def value_updated(self, value): + def value_updated(self, event): """ Handle changes to the field value by calling perform. The event passed to `perform` has the `value` as an attribute. """ + value = event.new action_event = ActionEvent(value=value) self.perform(action_event) diff -Nru python-pyface-6.1.2/pyface/action/group.py python-pyface-7.4.0/pyface/action/group.py --- python-pyface-6.1.2/pyface/action/group.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/group.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A group of action manager items. """ from functools import partial -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, Instance, List, Property -from traits.api import Str + +from traits.api import Any, Bool, HasTraits, List, observe, Property, Str from traits.trait_base import user_name_for -# Local imports. + from pyface.action.action import Action from pyface.action.action_item import ActionItem -from pyface.action.action_manager_item import ActionManagerItem class Group(HasTraits): @@ -35,7 +30,7 @@ """ - #### 'Group' interface #### + # 'Group' interface ---- #: Is the group enabled? enabled = Bool(True) @@ -45,25 +40,25 @@ #: The group's unique identifier (only needs to be unique within the action #: manager that the group belongs to). - id = Str + id = Str() #: All of the items in the group. items = Property #: The action manager that the group belongs to. - parent = Any #Instance('pyface.action.ActionManager') + parent = Any # Instance('pyface.action.ActionManager') #: Does this group require a separator when it is visualized? separator = Bool(True) - #### Private interface #### + # Private interface ---- #: All of the items in the group. - _items = List #(ActionManagerItem) + _items = List # (ActionManagerItem) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *items, **traits): """ Creates a new menu manager. @@ -74,28 +69,29 @@ Items to add to the group. """ # Base class constructor. - super(Group, self).__init__(**traits) + super().__init__(**traits) # Add any specified items. for item in items: self.append(item) - ########################################################################### + # ------------------------------------------------------------------------ # 'Group' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait Properties ##################################################### + # Trait Properties ----------------------------------------------------- def _get_items(self): return self._items[:] - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - def _enabled_changed(self, trait_name, old, new): + @observe("enabled") + def _enabled_updated(self, event): for item in self.items: - item.enabled = new + item.enabled = event.new - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def append(self, item): """ Appends an item to the group. @@ -274,4 +270,5 @@ Hopefully, 'Separator' is more readable than 'Group'... """ + pass diff -Nru python-pyface-6.1.2/pyface/action/gui_application_action.py python-pyface-7.4.0/pyface/action/gui_application_action.py --- python-pyface-6.1.2/pyface/action/gui_application_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/gui_application_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2005-2018, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. @@ -13,13 +14,13 @@ import platform -# Enthought library imports. + from traits.api import Instance, Property, cached_property -# Local imports. + from pyface.action.listening_action import ListeningAction -IS_WINDOWS = platform.system() == 'Windows' +IS_WINDOWS = platform.system() == "Windows" class GUIApplicationAction(ListeningAction): @@ -27,12 +28,12 @@ # 'ListeningAction' interface -------------------------------------------- - object = Property(depends_on='application') + object = Property(observe="application") # 'WindowAction' interface ----------------------------------------------- #: The application that the action is associated with. - application = Instance('pyface.gui_application.GUIApplication') + application = Instance("pyface.gui_application.GUIApplication") # ------------------------------------------------------------------------ # Protected interface. @@ -44,7 +45,7 @@ def destroy(self): # Disconnect listeners to application and dependent properties. self.application = None - super(GUIApplicationAction, self).destroy() + super().destroy() class ActiveWindowAction(GUIApplicationAction): @@ -52,7 +53,7 @@ # 'ListeningAction' interface -------------------------------------------- - object = Property(depends_on='application.active_window') + object = Property(observe="application.active_window") # ------------------------------------------------------------------------ # Protected interface. @@ -66,8 +67,9 @@ class CreateWindowAction(GUIApplicationAction): """ A standard 'New Window' menu action. """ - name = u'New Window' - accelerator = 'Ctrl+N' + + name = "New Window" + accelerator = "Ctrl+N" def perform(self, event=None): window = self.application.create_window() @@ -76,19 +78,21 @@ class ExitAction(GUIApplicationAction): """ A standard 'Quit' or 'Exit' menu action. """ - accelerator = 'Alt+F4' if IS_WINDOWS else 'Ctrl+Q' - method = 'exit' + + accelerator = "Alt+F4" if IS_WINDOWS else "Ctrl+Q" + method = "exit" def _name_default(self): - return (u'Exit ' if IS_WINDOWS else u'Quit ') + self.application.name + return ("Exit " if IS_WINDOWS else "Quit ") + self.application.name class AboutAction(GUIApplicationAction): """ A standard 'About' dialog menu action. """ - method = 'do_about' + + method = "do_about" def _name_default(self): - return u"About " + self.application.name + return "About " + self.application.name class CloseActiveWindowAction(ActiveWindowAction): @@ -96,6 +100,7 @@ This method closes the active window of the application. """ - name = u'Close Window' - accelerator = 'Ctrl+W' - method = 'close' + + name = "Close Window" + accelerator = "Ctrl+W" + method = "close" diff -Nru python-pyface-6.1.2/pyface/action/images/image_LICENSE.txt python-pyface-7.4.0/pyface/action/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/action/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Eclipse Eclipse Public License image_LICENSE_Eclipse.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/action/images: - action.png | Eclipse diff -Nru python-pyface-6.1.2/pyface/action/__init__.py python-pyface-7.4.0/pyface/action/__init__.py --- python-pyface-6.1.2/pyface/action/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2005-2011, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/action/listening_action.py python-pyface-7.4.0/pyface/action/listening_action.py --- python-pyface-6.1.2/pyface/action/listening_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/listening_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,22 @@ -# Copyright (c) 2005-2018, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: -# Standard library imports. + import logging -# Enthought library imports. + from pyface.action.action import Action -from traits.api import Any, Str +from traits.api import Any, Str, observe, Undefined # Logging. logger = logging.getLogger(__name__) @@ -29,19 +30,19 @@ #: The (extended) name of the method to call. By default, the on_perform #: function will be called with the event. - method = Str + method = Str() #: The (extended) name of the attribute that determines whether the action #: is enabled. By default, the action is always enabled when an object is #: set. - enabled_name = Str + enabled_name = Str() #: The (extended) name of the attribute that determines whether the action #: is visible. By default, the action is always visible. - visible_name = Str + visible_name = Str() #: The object to which the names above apply. - object = Any + object = Any() # ------------------------------------------------------------------------- # 'Action' interface. @@ -54,12 +55,14 @@ """ if self.object: - self.object.on_trait_change( - self._enabled_update, self.enabled_name, remove=True - ) - self.object.on_trait_change( - self._visible_update, self.visible_name, remove=True - ) + if self.enabled_name: + self.object.observe( + self._enabled_update, self.enabled_name, remove=True + ) + if self.visible_name: + self.object.observe( + self._visible_update, self.visible_name, remove=True + ) def perform(self, event=None): """ Call the appropriate function. @@ -68,12 +71,12 @@ stored in the :py:attr:`method` trait. If the method is empty, then this follows the usual Action method resolution. """ - if self.method != '': + if self.method != "": method = self._get_attr(self.object, self.method) if method: method() else: - super(ListeningAction, self).perform(event) + super().perform(event) # ------------------------------------------------------------------------- # Protected interface. @@ -82,7 +85,7 @@ def _get_attr(self, obj, name, default=None): """ Perform an extended look up of a dotted name. """ try: - for attr in name.split('.'): + for attr in name.split("."): # Perform the access in the Trait name style: if the object is # None, assume it simply hasn't been initialized and don't show # the warning. @@ -97,36 +100,42 @@ # Trait change handlers -------------------------------------------------- - def _enabled_name_changed(self, old, new): + @observe('enabled_name') + def _enabled_name_updated(self, event): + old, new = event.old, event.new obj = self.object if obj is not None: if old: - obj.on_trait_change(self._enabled_update, old, remove=True) + obj.observe(self._enabled_update, old, remove=True) if new: - obj.on_trait_change(self._enabled_update, new) + obj.observe(self._enabled_update, new) self._enabled_update() - def _visible_name_changed(self, old, new): + @observe('visible_name') + def _visible_name_updated(self, event): + old, new = event.old, event.new obj = self.object if obj is not None: if old: - obj.on_trait_change(self._visible_update, old, remove=True) + obj.observe(self._visible_update, old, remove=True) if new: - obj.on_trait_change(self._visible_update, new) + obj.observe(self._visible_update, new) self._visible_update() - def _object_changed(self, old, new): - for kind in ('enabled', 'visible'): - method = getattr(self, '_%s_update' % kind) - name = getattr(self, '%s_name' % kind) + @observe('object') + def _object_updated(self, event): + old, new = event.old, event.new + for kind in ("enabled", "visible"): + method = getattr(self, "_%s_update" % kind) + name = getattr(self, "%s_name" % kind) if name: - if old: - old.on_trait_change(method, name, remove=True) + if old and old is not Undefined: + old.observe(method, name, remove=True) if new: - new.on_trait_change(method, name) - method() + new.observe(method, name) + method() - def _enabled_update(self): + def _enabled_update(self, event=None): if self.enabled_name: if self.object: self.enabled = bool( @@ -137,7 +146,7 @@ else: self.enabled = bool(self.object) - def _visible_update(self): + def _visible_update(self, event=None): if self.visible_name: if self.object: self.visible = bool( diff -Nru python-pyface-6.1.2/pyface/action/menu_bar_manager.py python-pyface-7.4.0/pyface/action/menu_bar_manager.py --- python-pyface-6.1.2/pyface/action/menu_bar_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/menu_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A menu bar manager realizes itself in errr, a menu bar control. """ +# Thanks for using Enthought open source! + +""" A toolkit-specific menu bar manager that realizes itself in a menu bar +control. + +- :attr:`~.MenuBarManager` +""" # Import the toolkit specific version. from pyface.toolkit import toolkit_object -MenuBarManager = toolkit_object('action.menu_bar_manager:MenuBarManager') -#### EOF ###################################################################### +MenuBarManager = toolkit_object("action.menu_bar_manager:MenuBarManager") diff -Nru python-pyface-6.1.2/pyface/action/menu_manager.py python-pyface-7.4.0/pyface/action/menu_manager.py --- python-pyface-6.1.2/pyface/action/menu_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A menu manager realizes itself in a menu control. """ +# Thanks for using Enthought open source! + +""" A toolkit-specific menu manager that realizes itself in a menu control. + +- :attr:`~.MenuManager` +""" # Import the toolkit specific version. from pyface.toolkit import toolkit_object -MenuManager = toolkit_object('action.menu_manager:MenuManager') -### EOF ####################################################################### +MenuManager = toolkit_object("action.menu_manager:MenuManager") diff -Nru python-pyface-6.1.2/pyface/action/schema/action_manager_builder.py python-pyface-7.4.0/pyface/action/schema/action_manager_builder.py --- python-pyface-6.1.2/pyface/action/schema/action_manager_builder.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/action_manager_builder.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,310 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Base action manager builder class + +This module provides a base class that takes a schema for an action manager +and builds the concrete action manager and its groups and items, folding in +schema additions. +""" + +import logging + +from collections import defaultdict + +from traits.api import HasTraits, Instance, List + +from .schema import Schema, ToolBarSchema +from .schema_addition import SchemaAddition +from ._topological_sort import before_after_sort + + +# Logging. +logger = logging.getLogger(__name__) + + +class ActionManagerBuilder(HasTraits): + """ Builds action managers from schemas, merging schema additions. + """ + + #: An ActionController to use with the action managers created by the + #: builder. May be None. + controller = Instance("pyface.action.action_controller.ActionController") + + #: Schema additions to apply to all managers built by this builder. + #: Any additions which do not match part of the supplied schema will be + #: ignored. + additions = List(Instance(SchemaAddition)) + + # ------------------------------------------------------------------------ + # 'ActionManagerBuilder' interface. + # ------------------------------------------------------------------------ + + def create_action_manager(self, schema): + """ Create a manager for the given schema using the additions. + + Any additions whose paths do not match the supplied + + Parameters + ---------- + schema : Schema + An Schema for an ActionManager subclass (ie. one of MenuBarSchema, + MenuSchema, or ToolBarSchema). + + Returns + ------- + manager : ActionManager + The concrete ActionManager instance corresponding to the Schema + with addtions. This does not yet have concrete toolkit widgets + associated with it: usually those will be created separately. + """ + additions_map = defaultdict(list) + for addition in self.additions: + if addition.path: + additions_map[addition.path].append(addition) + + manager = self._create_action_manager_recurse(schema, additions_map) + manager.controller = self.controller + return manager + + def get_additional_toolbar_schemas(self): + """ Get any top-level toolbars from additions. + + Unlike menus, there is no base toolbar manager, so additions which + contribute new toolbars appear with no path. It is up to the class + using the builder how it wants to handle these additional toolbars. + + Returns + ------- + schemas : list of ToolBarSchema + The additional toolbars specified in self.additions. + """ + schemas = [] + for addition in self.additions: + if not addition.path: + schema = addition.factory() + if isinstance(schema, ToolBarSchema): + schemas.append(schema) + else: + logger.error( + "Invalid top-level schema addition: %r. Only " + "ToolBar schemas can be path-less.", + schema, + ) + return schemas + + def prepare_item(self, item, path): + """ Called immediately after a concrete Pyface item has been created + (or, in the case of items that are not produced from schemas, + immediately before they are processed). + + This hook can be used to perform last-minute transformations or + configuration. Returns a concrete Pyface item. + + Parameters + ---------- + item : pyface.action item + A concrete Pyface item. + path : str + The path to the item in the Schema. + + Returns + ------- + item : pyface.action item + A modified or transformed Pyface item. + """ + return item + + # ------------------------------------------------------------------------ + # Private interface. + # ------------------------------------------------------------------------ + + def _get_ordered_schemas(self, schemas): + begin = [] + middle = [] + end = [] + + for schema in schemas: + absolute_position = getattr(schema, "absolute_position", None) + if absolute_position is None: + middle.append(schema) + elif absolute_position == "last": + end.append(schema) + else: + begin.append(schema) + + schemas = ( + before_after_sort(begin) + + before_after_sort(middle) + + before_after_sort(end) + ) + return schemas + + def _group_items_by_id(self, items): + """ Group a list of action items by their ID. + + Action items are Schemas and Groups, MenuManagers, etc. + + Return a dictionary {item_id: list_of_items}, and a list containing + all the ids ordered by their appearance in the `all_items` list. The + ordered IDs are used as a replacement for an ordered dictionary, to + keep compatibility with Python <2.7 . + + """ + + ordered_items_ids = [] + id_to_items = defaultdict(list) + + for item in items: + if item.id not in id_to_items: + ordered_items_ids.append(item.id) + id_to_items[item.id].append(item) + + return id_to_items, ordered_items_ids + + def _group_items_by_class(self, items): + """ Group a list of action items by their class. + + Action items are Schemas and Groups, MenuManagers, etc. + + Return a dictionary {item_class: list_of_items}, and a list containing + all the classes ordered by their appearance in the `all_items` list. + The ordered classes are used as a replacement for an ordered + dictionary, to keep compatibility with Python <2.7 . + + """ + + ordered_items_class = [] + class_to_items = defaultdict(list) + + for item in items: + if item.__class__ not in class_to_items: + ordered_items_class.append(item.__class__) + class_to_items[item.__class__].append(item) + + return class_to_items, ordered_items_class + + def _unpack_schema_additions(self, items): + """ Unpack additions, since they may themselves be schemas. """ + + unpacked_items = [] + + for item in items: + if isinstance(item, SchemaAddition): + unpacked_items.append(item.factory()) + else: + unpacked_items.append(item) + + return unpacked_items + + def _merge_items_with_same_path(self, id_to_items, ordered_items_ids): + """ Merge items with the same path if possible. + + Items must be subclasses of `Schema` and they must be instances of + the same class to be merged. + + """ + + merged_items = [] + for item_id in ordered_items_ids: + items_with_same_id = id_to_items[item_id] + + # Group items by class. + class_to_items, ordered_items_class = self._group_items_by_class( + items_with_same_id + ) + + for items_class in ordered_items_class: + items_with_same_class = class_to_items[items_class] + + if len(items_with_same_class) == 1: + merged_items.extend(items_with_same_class) + + else: + # Only schemas can be merged. + if issubclass(items_class, Schema): + # Merge into a single schema. + items_content = sum( + (item.items for item in items_with_same_class), [] + ) + + merged_item = items_with_same_class[0].clone_traits() + merged_item.items = items_content + merged_items.append(merged_item) + + else: + merged_items.extend(items_with_same_class) + + return merged_items + + def _preprocess_schemas(self, schema, additions, path): + """ Sort and merge a schema and a set of schema additions. """ + + # Determine the order of the items at this path. + if additions[path]: + all_items = self._get_ordered_schemas( + schema.items + additions[path] + ) + else: + all_items = schema.items + + unpacked_items = self._unpack_schema_additions(all_items) + + id_to_items, ordered_items_ids = self._group_items_by_id( + unpacked_items + ) + + merged_items = self._merge_items_with_same_path( + id_to_items, ordered_items_ids + ) + + return merged_items + + def _create_action_manager_recurse(self, schema, additions, path=""): + """ Recursively create a manager for the given schema and additions map. + + Items with the same path are merged together in a single entry if + possible (i.e., if they have the same class). + + When a list of items is merged, their children are added to a clone + of the first item in the list. As a consequence, traits like menu + names etc. are inherited from the first item. + + """ + from pyface.action.action_manager import ActionManager + + # Compute the new action path. + if path: + path = path + "/" + schema.id + else: + path = schema.id + + preprocessed_items = self._preprocess_schemas(schema, additions, path) + + # Create the actual children by calling factory items. + children = [] + for item in preprocessed_items: + if isinstance(item, Schema): + item = self._create_action_manager_recurse( + item, additions, path + ) + else: + item = self.prepare_item(item, path + "/" + item.id) + + if isinstance(item, ActionManager): + # Give even non-root action managers a reference to the + # controller so that custom Groups, MenuManagers, etc. can get + # access to the state it holds. + item.controller = self.controller + + children.append(item) + + # Finally, create the pyface.action instance for this schema. + return self.prepare_item(schema.create(children), path) diff -Nru python-pyface-6.1.2/pyface/action/schema/api.py python-pyface-7.4.0/pyface/action/schema/api.py --- python-pyface-6.1.2/pyface/action/schema/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,47 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +API for the ``pyface.action.schema`` subpackage. + +Schemas and Aliases +------------------- + +- :class:`~.ActionSchema` +- :class:`~.GroupSchema` +- :class:`~.MenuBarSchema` +- :class:`~.MenuSchema` +- :attr:`~.SGroup` +- :attr:`~.SMenu` +- :attr:`~.SMenuBar` +- :attr:`~.SToolBar` +- :class:`~.ToolBarSchema` + +Builder and Schema Additions +---------------------------- + +- :class:`~.ActionManagerBuilder` +- :class:`~.SchemaAddition` + +""" + +from .action_manager_builder import ActionManagerBuilder +from .schema import ( + ActionSchema, + GroupSchema, + MenuBarSchema, + MenuSchema, + SGroup, + SMenu, + SMenuBar, + SToolBar, + ToolBarSchema, +) +from .schema_addition import SchemaAddition diff -Nru python-pyface-6.1.2/pyface/action/schema/schema_addition.py python-pyface-7.4.0/pyface/action/schema/schema_addition.py --- python-pyface-6.1.2/pyface/action/schema/schema_addition.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/schema_addition.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,61 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Callable, HasTraits, Str, Enum + +# NOTE +# This module should never import directly or indirectly any toolkit-dependent +# code. This permits it to be used declaratively in a toolkit-agnostic way +# if needed. + + +class SchemaAddition(HasTraits): + """ An addition to an existing menu bar or tool bar schema. + """ + + #: The schema addition's identifier. This optional, but if left + #: unspecified, other schema additions will be unable to refer to this one. + id = Str() + + #: A callable to create the item. Should have the following signature: + #: callable() -> Action | ActionItem | Group | MenuManager | + #: GroupSchema | MenuSchema + #: + #: If the result is a schema, it will itself admit of extension by other + #: additions. If not, the result will be fixed. + factory = Callable + + #: A forward-slash-separated path through the action hierarchy to the menu + #: to add the action, group or menu to. For example: + #: - To add an item to the menu bar: ``path = "MenuBar"`` + #: - To add an item to the tool bar: ``path = "ToolBar"`` + #: - To add an item to a sub-menu: ``path = "MenuBar/File/New"`` + path = Str() + + #: The item appears after the item with this ID. + #: - for groups, this is the ID of another group. + #: - for menus and actions, this is the ID of another menu or action. + after = Str() + + #: The action appears before the item with this ID. + #: - for groups, this is the ID of another group. + #: - for menus and actions, this is the ID of another menu or action. + before = Str() + + #: The action appears at the absolute specified position first or last. + #: This is useful for example to keep the File menu the first menu in a + #: menubar, the help menu the last etc. + #: If multiple actions in a schema have absolute_position 'first', they + #: will appear in the same order specified; unless 'before' and 'after' + #: traits are set to sort these multiple items. + #: This trait takes precedence over 'after' and 'before', and values of + #: those traits that are not compatible with the absolute_position are + #: ignored. + absolute_position = Enum(None, "first", "last") diff -Nru python-pyface-6.1.2/pyface/action/schema/schema.py python-pyface-7.4.0/pyface/action/schema/schema.py --- python-pyface-6.1.2/pyface/action/schema/schema.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/schema.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,230 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Schema class definitions + +This module defines the Schema +""" + +from traits.api import ( + Bool, + Callable, + Enum, + HasTraits, + Instance, + List, + Property, + Str, + Tuple, + Union, +) + +from pyface.util.id_helper import get_unique_id + +# NOTE +# This module should never import directly or indirectly any toolkit-dependent +# code at the top level. This permits it to be used declaratively in a +# toolkit-agnostic way if needed. + +# Trait definitions. +SubSchema = Union( + None, + Instance("pyface.action.action.Action"), + Instance("pyface.action.action_item.ActionItem"), + Instance("pyface.action.group.Group"), + Instance("pyface.action.menu_manager.MenuManager"), + Instance("pyface.action.schema.schema.GroupSchema"), + Instance("pyface.action.schema.schema.MenuSchema"), + Instance("pyface.action.schema.schema.Schema"), +) + + +class Schema(HasTraits): + """ The abstract base class for all action schemas. + """ + + #: The schema's identifier (unique within its parent schema). + id = Str() + + #: The list of sub-items in the schema. These items can be other + #: (non-top-level) schema or concrete instances from the Pyface API. + items = List(SubSchema) + + def __init__(self, *items, **traits): + """ Creates a new schema. + """ + super().__init__(**traits) + self.items.extend(items) + + def create(self, children): + """ Create the appropriate pyface.action instance with the specified + child items. + """ + raise NotImplementedError() + + # Trait initializers --------------------------------------------------- + + def _id_default(self): + return get_unique_id(self) + + +class ActionSchema(Schema): + """ Action schema for Pyface Actions. + + An action schema cannot have children. It is used as an action factory + to make sure a larger schema (e.g., a menu schema) can be used multiple + times. Without using an ActionSchema, a reference to the action is added + to every menu created from the schema. When one of the menus is destroyed, + the action is also destroyed and is made unusable. + """ + + #: A factory for the Action instance. + action_factory = Callable() + + #: Items is overwritten to be empty and read-only to avoid assigning to + #: it by mistake. + items = Property() + + def _get_items(self): + return [] + + def create(self, children): + """ Create the appropriate Pyface Action instance. """ + + traits = dict(id=self.id) + return self.action_factory(**traits) + + # Trait initializers --------------------------------------------------- + + def _action_factory_default(self): + from pyface.action.action import Action + return Action + + +class GroupSchema(Schema): + """ A schema for a Pyface Group. + """ + + #: A factory for instantiating a pyface Group. + group_factory = Callable() + + #: Does the group require a separator when it is visualized? + separator = Bool(True) + + def create(self, children): + traits = dict(id=self.id, separator=self.separator) + return self.group_factory(*children, **traits) + + # Trait initializers --------------------------------------------------- + + def _group_factory_default(self): + from pyface.action.group import Group + return Group + + +class MenuSchema(Schema): + """ A schema for a Pyface MenuManager. + """ + + #: The menu's user visible name. + name = Str() + + #: Does the menu require a separator before the menu item? + separator = Bool(False) + + #: The default action for tool button when shown in a toolbar (Qt only) + action = Instance("pyface.action.action.Action") + + #: A factory for instantiating a pyface MenuManager. + menu_manager_factory = Callable() + + def create(self, children): + traits = dict(id=self.id, name=self.name, separator=self.separator) + if self.action: + traits["action"] = self.action + return self.menu_manager_factory(*children, **traits) + + # Trait initializers --------------------------------------------------- + + def _menu_manager_factory_default(self): + from pyface.action.menu_manager import MenuManager + return MenuManager + + +class MenuBarSchema(Schema): + """ A schema for a Pyface MenuBarManager. + """ + + #: Assign a default ID for menu bar schemas. + id = "MenuBar" + + #: A factory for instantiating a pyface MenuBarManager. + menu_bar_manager_factory = Callable() + + def create(self, children): + traits = dict(id=self.id) + return self.menu_bar_manager_factory(*children, **traits) + + # Trait initializers --------------------------------------------------- + + def _menu_bar_manager_factory_default(self): + from pyface.action.menu_bar_manager import MenuBarManager + return MenuBarManager + + +class ToolBarSchema(Schema): + """ A schema for a Pyface ToolBarManager. + """ + + #: Assign a default ID for tool bar schemas. + id = "ToolBar" + + #: The tool bar's user visible name. Note that this name may not be used on + #: all platforms. + name = Str("Tool Bar") + + #: The size of tool images (width, height). + image_size = Tuple((16, 16)) + + #: The orientation of the toolbar. + orientation = Enum("horizontal", "vertical") + + #: Should we display the horizontal divider? + show_divider = Bool(True) + + #: Should we display the name of each tool bar tool under its image? + show_tool_names = Bool(True) + + #: A factory for instantiating a pyface ToolBarManager + tool_bar_manager_factory = Callable() + + def create(self, children): + traits = dict( + id=self.id, + name=self.name, + image_size=self.image_size, + orientation=self.orientation, + show_divider=self.show_divider, + show_tool_names=self.show_tool_names, + ) + return self.tool_bar_manager_factory(*children, **traits) + + # Trait initializers --------------------------------------------------- + + def _tool_bar_manager_factory_default(self): + from pyface.action.tool_bar_manager import ToolBarManager + return ToolBarManager + + +# Convenience abbreviations. +SGroup = GroupSchema +SMenu = MenuSchema +SMenuBar = MenuBarSchema +SToolBar = ToolBarSchema diff -Nru python-pyface-6.1.2/pyface/action/schema/tests/test_action_manager_builder.py python-pyface-7.4.0/pyface/action/schema/tests/test_action_manager_builder.py --- python-pyface-6.1.2/pyface/action/schema/tests/test_action_manager_builder.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/tests/test_action_manager_builder.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,443 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from contextlib import contextmanager +import unittest + + +from pyface.action.api import ( + Action, + ActionItem, + ActionManager, + Group, + MenuManager, + MenuBarManager, +) +from pyface.action.schema.api import ( + GroupSchema, + MenuSchema, + MenuBarSchema, + SchemaAddition, +) +from ..action_manager_builder import ( + ActionManagerBuilder, +) + + +class ActionManagerBuilderTestCase(unittest.TestCase): + + # 'TestCase' protocol -------------------------------------------------# + + def setUp(self): + """ Create some dummy actions to use while testing. + """ + for i in range(1, 7): + action_id = "action%i" % i + setattr( + self, action_id, Action(id=action_id, name="Action %i" % i) + ) + + # 'ActionManagerBuilderTestCase' protocol -----------------------------# + + def assertActionElementsEqual(self, first, second): + """ Checks that two action managers are (logically) equivalent. + """ + children1 = children2 = [] + self.assertEqual(type(first), type(second)) + self.assertEqual(first.id, second.id) + + if isinstance(first, ActionItem): + self.assertEqual(first.action.name, second.action.name) + + elif isinstance(first, ActionManager): + if not isinstance(first, MenuBarManager): + self.assertEqual(first.name, second.name) + children1, children2 = first.groups, second.groups + + elif isinstance(first, Group): + self.assertEqual(first.separator, second.separator) + children1, children2 = first.items, second.items + + self.assertEqual(len(children1), len(children2)) + for i in range(len(children1)): + self.assertActionElementsEqual(children1[i], children2[i]) + + def reset_unique_ids(self): + import pyface.util.id_helper as id_helper + id_helper.object_counter = id_helper._ObjectCounter() + + @contextmanager + def unique_id_context_manager(self): + self.reset_unique_ids() + try: + yield + finally: + self.reset_unique_ids() + + # Tests ---------------------------------------------------------------- + + def test_simple_menu_bar(self): + """ Does constructing a simple menu with no additions work? + """ + schema = MenuBarSchema( + MenuSchema(self.action1, self.action2, id="File", name="&File"), + MenuSchema(self.action3, self.action4, id="Edit", name="&Edit"), + ) + builder = ActionManagerBuilder() + actual = builder.create_action_manager(schema) + desired = MenuBarManager( + MenuManager(self.action1, self.action2, id="File", name="&File"), + MenuManager(self.action3, self.action4, id="Edit", name="&Edit"), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + # Tests about schema additions ----------------------------------------- + + def test_additions_menu_bar(self): + """ Does constructing a menu with a few additions work? + """ + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, self.action2, id="FileGroup"), + id="File", + ) + ) + additions = [ + SchemaAddition( + factory=lambda: self.action3, + before="action1", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action4, + before="action1", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action5, path="MenuBar/File/FileGroup" + ), + ] + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + desired = MenuBarManager( + MenuManager( + Group( + self.action3, + self.action4, + self.action1, + self.action2, + self.action5, + id="FileGroup", + ), + id="File", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_extra_menu(self): + """ Test contributing a whole new menu to the menu bar. """ + + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), id="FileMenu" + ) + ) + + # Contributed menu. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="BarGroup"), id="DummyActionsMenu" + ) + + additions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + + desired = MenuBarManager( + MenuManager(Group(self.action1, id="FileGroup"), id="FileMenu"), + MenuManager( + Group(self.action2, id="BarGroup"), id="DummyActionsMenu" + ), + id="MenuBar", + ) + + self.assertActionElementsEqual(actual, desired) + + # Tests about merging schemas -----------------------------------------# + + def test_merging_redundant_items(self): + """ Menus and groups with matching path are merged together. """ + + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), + name="File menu number one", + id="FileMenu", + ) + ) + + # Contributed menus. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="FileGroup"), + name="File menu number two", + id="FileMenu", + ) + + additions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + + # Note that we expect the name of the menu to be inherited from + # the menu in the menu bar schema that is defined first. + desired = MenuBarManager( + MenuManager( + Group(self.action1, self.action2, id="FileGroup"), + name="File menu number one", + id="FileMenu", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_unwanted_merge(self): + """ Test that we don't have automatic merges due to forgetting to set + a schema ID. """ + + with self.unique_id_context_manager(): + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), name="File 1" + ) + ) + + # Contributed menus. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="FileGroup"), name="File 2" + ) + + additions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + + # Note that we expect the name of the menu to be inherited from + # the menu in the menu bar schema that is defined first. + desired = MenuBarManager( + MenuManager( + Group(self.action1, id="FileGroup"), + name="File 1", + id="MenuSchema_1", + ), + MenuManager( + Group(self.action2, id="FileGroup"), + name="File 2", + id="MenuSchema_2", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_merging_items_with_same_id_but_different_class(self): + """ Schemas with the same path but different types (menus, groups) + are not merged together. + + Having a group and a menu with the same path is of course bad practice, + but we need a predictable outcome. + + """ + + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), id="FileSchema" + ) + ) + + # Contributed menus. + extra_group = GroupSchema(self.action2, id="FileSchema") + + additions = [ + SchemaAddition( + path="MenuBar", + factory=(lambda: extra_group), + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + + desired = MenuBarManager( + MenuManager(Group(self.action1, id="FileGroup"), id="FileSchema"), + Group(self.action2, id="FileSchema"), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_merging_redundant_items_that_are_not_schemas(self): + """ Items that are not schemas cannot be merged, but we should + not crash, either. """ + + # Initial menu. + schema = MenuBarSchema( + # This menu is not a schema... + MenuManager(Group(self.action1, id="FileGroup"), id="FileMenu") + ) + + # Contributed menus. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="FileGroup"), id="FileMenu" + ) + + additions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + + desired = MenuBarManager( + MenuManager(Group(self.action1, id="FileGroup"), id="FileMenu"), + MenuManager(Group(self.action2, id="FileGroup"), id="FileMenu"), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + # Tests about ordering ------------------------------------------------- + + def test_absolute_ordering(self): + """ Does specifying absolute_position work? + """ + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, self.action2, id="FileGroup"), + id="File", + ) + ) + additions = [ + SchemaAddition( + factory=lambda: self.action3, + absolute_position="last", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action4, + absolute_position="first", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action5, + absolute_position="first", + path="MenuBar/File/FileGroup", + ), + ] + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + desired = MenuBarManager( + MenuManager( + Group( + self.action4, + self.action5, + self.action1, + self.action2, + self.action3, + id="FileGroup", + ), + id="File", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_absolute_and_before_after(self): + """ Does specifying absolute_position along with before, after work? + """ + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, self.action2, id="FileGroup"), + id="File", + ) + ) + additions = [ + SchemaAddition( + factory=lambda: self.action3, + id="action3", + after="action2", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action4, + after="action3", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action5, + id="action5", + absolute_position="last", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action6, + absolute_position="last", + before="action5", + path="MenuBar/File/FileGroup", + ), + ] + builder = ActionManagerBuilder(additions=additions) + actual = builder.create_action_manager(schema) + desired = MenuBarManager( + MenuManager( + Group( + self.action1, + self.action2, + self.action3, + self.action4, + self.action6, + self.action5, + id="FileGroup", + ), + id="File", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) diff -Nru python-pyface-6.1.2/pyface/action/schema/tests/test_topological_sort.py python-pyface-7.4.0/pyface/action/schema/tests/test_topological_sort.py --- python-pyface-6.1.2/pyface/action/schema/tests/test_topological_sort.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/tests/test_topological_sort.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,103 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + + +from traits.api import HasTraits, Int +from pyface.action.schema._topological_sort import ( + before_after_sort, + topological_sort, +) + + +class TestItem(HasTraits): + + id = Int() + before = Int() + after = Int() + + def __init__(self, id, **traits): + super().__init__(id=id, **traits) + + def __hash__(self): + return hash(self.id) + + def __eq__(self, other): + return self.id == other.id + + def __repr__(self): + return repr(self.id) + + +class TopologicalSortTestCase(unittest.TestCase): + def test_before_after_sort_1(self): + """ Does the before-after sort work? + """ + items = [ + TestItem(1), + TestItem(2), + TestItem(3, before=2), + TestItem(4, after=1), + TestItem(5), + ] + actual = before_after_sort(items) + desired = [ + TestItem(1), + TestItem(3), + TestItem(4), + TestItem(2), + TestItem(5), + ] + self.assertEqual(actual, desired) + + def test_before_after_sort_2(self): + """ Does the before-after sort work when both 'before' and 'after' + are set? + """ + items = [ + TestItem(1), + TestItem(2), + TestItem(3), + TestItem(4, after=2, before=3), + ] + actual = before_after_sort(items) + desired = [TestItem(1), TestItem(2), TestItem(4), TestItem(3)] + self.assertEqual(actual, desired) + + def test_before_after_sort_3(self): + """ Does the degenerate case for the before-after sort work? + """ + actual = before_after_sort([TestItem(1)]) + desired = [TestItem(1)] + self.assertEqual(actual, desired) + + def test_topological_sort_1(self): + """ Does a basic topological sort work? + """ + pairs = [(1, 2), (3, 5), (4, 6), (1, 3), (1, 4), (1, 6), (2, 4)] + result, has_cycles = topological_sort(pairs) + self.assertTrue(not has_cycles) + self.assertEqual(result, [1, 2, 3, 4, 5, 6]) + + def test_topological_sort_2(self): + """ Does another basic topological sort work? + """ + pairs = [(1, 2), (1, 3), (2, 4), (3, 4), (5, 6), (4, 5)] + result, has_cycles = topological_sort(pairs) + self.assertTrue(not has_cycles) + self.assertEqual(result, [1, 2, 3, 4, 5, 6]) + + def test_topological_sort_3(self): + """ Does cycle detection work? + """ + pairs = [(1, 2), (2, 3), (3, 1)] + result, has_cycles = topological_sort(pairs) + self.assertTrue(has_cycles) diff -Nru python-pyface-6.1.2/pyface/action/schema/_topological_sort.py python-pyface-7.4.0/pyface/action/schema/_topological_sort.py --- python-pyface-6.1.2/pyface/action/schema/_topological_sort.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/schema/_topological_sort.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,102 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from collections import OrderedDict, defaultdict +import logging + +# Logging. +logger = logging.getLogger(__name__) + + +def before_after_sort(items): + """ Sort a sequence of items with 'before', 'after', and 'id' attributes. + + The sort is topological. If an item does not specify a 'before' or 'after', + it is placed after the preceding item. + + If a cycle is found in the dependencies, a warning is logged and the order + of the items is undefined. + """ + # Handle a degenerate case for which the logic below will fail (because + # prev_item will not be set). + if len(items) < 2: + return items + + # Build a set of pairs representing the graph. + item_map = dict((item.id, item) for item in items if item.id) + pairs = [] + prev_item = None + for item in items: + # Attempt to use 'before' and 'after' to make pairs. + new_pairs = [] + if hasattr(item, "before") and item.before: + parent, child = item, item_map.get(item.before) + if child: + new_pairs.append((parent, child)) + if hasattr(item, "after") and item.after: + parent, child = item_map.get(item.after), item + if parent: + new_pairs.append((parent, child)) + + # If we have any pairs, use them. Otherwise, use the previous unmatched + # item as a parent, if possible. + if new_pairs: + pairs.extend(new_pairs) + else: + if prev_item: + pairs.append((prev_item, item)) + prev_item = item + + # Now perform the actual sort. + result, has_cycle = topological_sort(pairs) + if has_cycle: + logger.warning("Cycle in before/after sort for items %r", items) + return result + + +def topological_sort(pairs): + """ Topologically sort a list of (parent, child) pairs. + + Returns a tuple containing the list of elements sorted in dependency order + (parent to child order), if possible, and a boolean indicating whether the + graph contains cycles. + + A simple algorithm due to Kahn, in which vertices are chosen from the graph + in the same order as the eventual topological sort, is used. + + Note that this implementation is stable in the following sense: if we have + the input list [..., (parent, child1), ..., (parent, child2), ...], then + child1 will be before child2 in the output list (if there there is no + additional dependency forcing another ordering). + """ + # Represent the graph in dictionary form. + graph = OrderedDict() + num_parents = defaultdict(int) + for parent, child in pairs: + graph.setdefault(parent, []).append(child) + num_parents[child] += 1 + + # Begin with the parent-less items. + result = [item for item in graph if num_parents[item] == 0] + + # Descend through graph, removing parents as we go. + for parent in result: + if parent in graph: + for child in graph[parent]: + num_parents[child] -= 1 + if num_parents[child] == 0: + result.append(child) + del graph[parent] + + # If there's a cycle, just throw in whatever is left over. + has_cycle = bool(graph) + if has_cycle: + result.extend(list(graph.keys())) + return result, has_cycle diff -Nru python-pyface-6.1.2/pyface/action/status_bar_manager.py python-pyface-7.4.0/pyface/action/status_bar_manager.py --- python-pyface-6.1.2/pyface/action/status_bar_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/status_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A status bar manager realizes itself in a status bar control. """ +# Thanks for using Enthought open source! + +""" A toolkit-specific status bar manager that realizes itself in a status bar +control. + +- :attr:`~.StatusBarManager` +""" # Import the toolkit specific version. from pyface.toolkit import toolkit_object -StatusBarManager = toolkit_object('action.status_bar_manager:StatusBarManager') -### EOF ####################################################################### +StatusBarManager = toolkit_object("action.status_bar_manager:StatusBarManager") diff -Nru python-pyface-6.1.2/pyface/action/tests/test_action_controller.py python-pyface-7.4.0/pyface/action/tests/test_action_controller.py --- python-pyface-6.1.2/pyface/action/tests/test_action_controller.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_action_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -8,16 +17,15 @@ class TestActionController(unittest.TestCase): - def setUp(self): # test whether function is called by updating list # XXX should really use mock self.memo = [] def perform(): - self.memo.append('called') + self.memo.append("called") - self.action = Action(name='Test', on_perform=perform) + self.action = Action(name="Test", on_perform=perform) self.action_controller = ActionController() def test_perform(self): @@ -25,10 +33,10 @@ # XXX should really use mock event = ActionEvent() self.action_controller.perform(self.action, event) - self.assertEqual(self.memo, ['called']) + self.assertEqual(self.memo, ["called"]) def test_perform_none(self): - action = Action(name='Test') + action = Action(name="Test") event = ActionEvent() # does nothing, but shouldn't error self.action_controller.perform(action, event) diff -Nru python-pyface-6.1.2/pyface/action/tests/test_action_event.py python-pyface-7.4.0/pyface/action/tests/test_action_event.py --- python-pyface-6.1.2/pyface/action/tests/test_action_event.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_action_event.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import time import unittest @@ -7,7 +16,6 @@ class TestActionEvent(unittest.TestCase): - def test_init(self): t0 = time.time() event = ActionEvent() diff -Nru python-pyface-6.1.2/pyface/action/tests/test_action_item.py python-pyface-7.4.0/pyface/action/tests/test_action_item.py --- python-pyface-6.1.2/pyface/action/tests/test_action_item.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_action_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,12 +1,20 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from pyface.image_cache import ImageCache from pyface.toolkit import toolkit_object -from pyface.widget import Widget from pyface.window import Window from ..action import Action from ..action_controller import ActionController @@ -17,7 +25,6 @@ class FalseActionController(ActionController): - def can_add_to_menu(self, action): """ Returns True if the action can be added to a menu/menubar. """ @@ -30,23 +37,24 @@ class TestActionItem(unittest.TestCase, UnittestTools): - def setUp(self): # test whether function is called by updating list # XXX should really use mock self.memo = [] def perform(): - self.memo.append('called') + self.memo.append("called") - self.action = Action(name='Test', on_perform=perform) + self.action = Action(name="Test", on_perform=perform) def control_factory(self, parent, action): - if toolkit_object.toolkit == 'wx': + if toolkit_object.toolkit == "wx": import wx + control = wx.Control(parent) - elif toolkit_object.toolkit == 'qt4': + elif toolkit_object.toolkit == "qt4": from pyface.qt import QtGui + control = QtGui.QWidget(parent) else: control = None @@ -54,25 +62,25 @@ def test_default_id(self): action_item = ActionItem(action=self.action) - self.assertEqual(action_item.id, 'Test') + self.assertEqual(action_item.id, "Test") def test_enabled_changed(self): # XXX these are only one-way changes, which seems wrong. action_item = ActionItem(action=self.action) - with self.assertTraitChanges(self.action, 'enabled', count=1): + with self.assertTraitChanges(self.action, "enabled", count=1): action_item.enabled = False self.assertFalse(self.action.enabled) - with self.assertTraitChanges(self.action, 'enabled', count=1): + with self.assertTraitChanges(self.action, "enabled", count=1): action_item.enabled = True self.assertTrue(self.action.enabled) def test_visible_changed(self): # XXX these are only one-way changes, which seems wrong. action_item = ActionItem(action=self.action) - with self.assertTraitChanges(self.action, 'visible', count=1): + with self.assertTraitChanges(self.action, "visible", count=1): action_item.visible = False self.assertFalse(self.action.visible) - with self.assertTraitChanges(self.action, 'visible', count=1): + with self.assertTraitChanges(self.action, "visible", count=1): action_item.visible = True self.assertTrue(self.action.visible) @@ -86,7 +94,7 @@ window.open() action_item = ActionItem(action=self.action) menu_bar_manager = MenuBarManager() - menu_manager = MenuManager(name='Test') + menu_manager = MenuManager(name="Test") menu_bar = menu_bar_manager.create_menu_bar(window.control) menu = menu_manager.create_menu(menu_bar) action_item.add_to_menu(window.control, menu, None) @@ -97,7 +105,7 @@ window.open() action_item = ActionItem(action=self.action) menu_bar_manager = MenuBarManager() - menu_manager = MenuManager(name='Test') + menu_manager = MenuManager(name="Test") menu_bar = menu_bar_manager.create_menu_bar(window.control) menu = menu_manager.create_menu(menu_bar) controller = ActionController() @@ -109,7 +117,7 @@ window.open() action_item = ActionItem(action=self.action) menu_bar_manager = MenuBarManager() - menu_manager = MenuManager(name='Test') + menu_manager = MenuManager(name="Test") menu_bar = menu_bar_manager.create_menu_bar(window.control) menu = menu_manager.create_menu(menu_bar) controller = FalseActionController() @@ -120,44 +128,50 @@ window = Window() window.open() action_item = ActionItem(action=self.action) - toolbar_manager = ToolBarManager(name='Test') + toolbar_manager = ToolBarManager(name="Test") image_cache = ImageCache(height=32, width=32) menu = toolbar_manager.create_tool_bar(window.control) - action_item.add_to_toolbar(window.control, menu, image_cache, None, True) + action_item.add_to_toolbar( + window.control, menu, image_cache, None, True + ) window.close() def test_add_to_toolbar_no_label(self): window = Window() window.open() action_item = ActionItem(action=self.action) - toolbar_manager = ToolBarManager(name='Test') + toolbar_manager = ToolBarManager(name="Test") image_cache = ImageCache(height=32, width=32) menu = toolbar_manager.create_tool_bar(window.control) - action_item.add_to_toolbar(window.control, menu, image_cache, None, False) + action_item.add_to_toolbar( + window.control, menu, image_cache, None, False + ) window.close() def test_add_to_toolbar_controller(self): window = Window() window.open() action_item = ActionItem(action=self.action) - toolbar_manager = ToolBarManager(name='Test') + toolbar_manager = ToolBarManager(name="Test") image_cache = ImageCache(height=32, width=32) menu = toolbar_manager.create_tool_bar(window.control) controller = ActionController() - action_item.add_to_toolbar(window.control, menu, image_cache, - controller, True) + action_item.add_to_toolbar( + window.control, menu, image_cache, controller, True + ) window.close() def test_add_to_toolbar_controller_false(self): window = Window() window.open() action_item = ActionItem(action=self.action) - toolbar_manager = ToolBarManager(name='Test') + toolbar_manager = ToolBarManager(name="Test") image_cache = ImageCache(height=32, width=32) menu = toolbar_manager.create_tool_bar(window.control) controller = FalseActionController() - action_item.add_to_toolbar(window.control, menu, image_cache, - controller, True) + action_item.add_to_toolbar( + window.control, menu, image_cache, controller, True + ) window.close() def test_add_to_toolbar_widget(self): @@ -167,11 +181,13 @@ window = Window() window.open() action_item = ActionItem(action=self.action) - toolbar_manager = ToolBarManager(name='Test') + toolbar_manager = ToolBarManager(name="Test") image_cache = ImageCache(height=32, width=32) menu = toolbar_manager.create_tool_bar(window.control) try: - action_item.add_to_toolbar(window.control, menu, image_cache, None, True) + action_item.add_to_toolbar( + window.control, menu, image_cache, None, True + ) finally: window.close() diff -Nru python-pyface-6.1.2/pyface/action/tests/test_action_manager.py python-pyface-7.4.0/pyface/action/tests/test_action_manager.py --- python-pyface-6.1.2/pyface/action/tests/test_action_manager.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_action_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from ..action import Action from ..action_item import ActionItem @@ -11,20 +20,19 @@ class TestActionItem(unittest.TestCase, UnittestTools): - def setUp(self): # test whether function is called by updating list # XXX should really use mock self.memo = [] def perform(): - self.memo.append('called') + self.memo.append("called") self.perform = perform - self.action = Action(name='Test', on_perform=perform) + self.action = Action(name="Test", on_perform=perform) self.action_item = ActionItem(action=self.action) - self.group = Group(id='test') + self.group = Group(id="test") def test_init_group(self): action_manager = ActionManager(self.group) @@ -79,7 +87,9 @@ group2 = Group() action_manager.append(group2) default_group = action_manager._get_default_group() - self.assertEqual(action_manager.groups, [self.group, default_group, group2]) + self.assertEqual( + action_manager.groups, [self.group, default_group, group2] + ) self.assertEqual(group2.parent, action_manager) def test_append_string(self): @@ -135,7 +145,9 @@ group2 = Group() action_manager.insert(1, group2) default_group = action_manager._get_default_group() - self.assertEqual(action_manager.groups, [self.group, group2, default_group]) + self.assertEqual( + action_manager.groups, [self.group, group2, default_group] + ) self.assertEqual(group2.parent, action_manager) def test_insert_string(self): @@ -183,24 +195,28 @@ def test_find_item_hierarchy(self): action_manager = ActionManager(self.group) - action_manager_2 = ActionManager(self.action_item, id='test2') + action_manager_2 = ActionManager(self.action_item, id="test2") self.group.append(action_manager_2) item = action_manager.find_item("test2/Test") self.assertEqual(item, self.action_item) def test_walk_hierarchy(self): action_manager = ActionManager(self.group) - action_manager_2 = ActionManager(self.action_item, id='test2') + action_manager_2 = ActionManager(self.action_item, id="test2") self.group.append(action_manager_2) result = [] action_manager.walk(result.append) - self.assertEqual(result, - [action_manager, - self.group, - action_manager_2, - action_manager_2._get_default_group(), - self.action_item, - action_manager._get_default_group()]) + self.assertEqual( + result, + [ + action_manager, + self.group, + action_manager_2, + action_manager_2._get_default_group(), + self.action_item, + action_manager._get_default_group(), + ], + ) def test_enabled_changed(self): self.group.append(self.action_item) @@ -225,5 +241,5 @@ action_manager.visible = True self.assertTrue(self.group.visible) # XXX group doesn't make items visible - #self.assertTrue(self.action_item.enabled) - #self.assertTrue(self.action.enabled) + # self.assertTrue(self.action_item.enabled) + # self.assertTrue(self.action.enabled) diff -Nru python-pyface-6.1.2/pyface/action/tests/test_action.py python-pyface-7.4.0/pyface/action/tests/test_action.py --- python-pyface-6.1.2/pyface/action/tests/test_action.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -7,14 +16,13 @@ class TestAction(unittest.TestCase): - def test_default_id(self): - action = Action(name='Test') - self.assertEqual(action.id, 'Test') + action = Action(name="Test") + self.assertEqual(action.id, "Test") def test_id(self): - action = Action(name='Test', id='test') - self.assertEqual(action.id, 'test') + action = Action(name="Test", id="test") + self.assertEqual(action.id, "test") def test_perform(self): # test whether function is called by updating list @@ -22,21 +30,21 @@ memo = [] def perform(): - memo.append('called') + memo.append("called") - action = Action(name='Test', on_perform=perform) + action = Action(name="Test", on_perform=perform) event = ActionEvent() action.perform(event) - self.assertEqual(memo, ['called']) + self.assertEqual(memo, ["called"]) def test_perform_none(self): - action = Action(name='Test') + action = Action(name="Test") event = ActionEvent() # does nothing, but shouldn't error action.perform(event) def test_destroy(self): - action = Action(name='Test') + action = Action(name="Test") # does nothing, but shouldn't error action.destroy() @@ -49,9 +57,7 @@ memo.append((parent, action)) action = Action( - name="Dummy", - style='widget', - control_factory=control_factory + name="Dummy", style="widget", control_factory=control_factory ) parent = None action.create_control(parent) diff -Nru python-pyface-6.1.2/pyface/action/tests/test_field_action.py python-pyface-7.4.0/pyface/action/tests/test_field_action.py --- python-pyface-6.1.2/pyface/action/tests/test_field_action.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_field_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,13 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import unittest @@ -20,7 +18,6 @@ class TestFieldAction(unittest.TestCase): - def setUp(self): self.gui = GUI() @@ -44,9 +41,9 @@ name="Dummy", field_type=ComboField, field_defaults={ - 'values': ['a', 'b', 'c'], - 'value': 'a', - 'tooltip': 'Dummy', + "values": ["a", "b", "c"], + "value": "a", + "tooltip": "Dummy", }, on_perform=perform, ) @@ -54,10 +51,10 @@ try: self.gui.process_events() - control._field.value = 'b' + control._field.value = "b" self.gui.process_events() - self.assertEqual(memo, ['b']) + self.assertEqual(memo, ["b"]) finally: control._field.destroy() @@ -72,10 +69,7 @@ action = FieldAction( name="Dummy", field_type=TextField, - field_defaults={ - 'value': 'a', - 'tooltip': 'Dummy', - }, + field_defaults={"value": "a", "tooltip": "Dummy"}, on_perform=perform, ) control = action.create_control(self.parent.control) @@ -83,10 +77,10 @@ try: self.gui.process_events() - control._field.value = 'b' + control._field.value = "b" self.gui.process_events() - self.assertEqual(memo, ['b']) + self.assertEqual(memo, ["b"]) finally: control._field.destroy() @@ -102,9 +96,9 @@ name="Dummy", field_type=SpinField, field_defaults={ - 'value': 1, - 'bounds': (0, 100), - 'tooltip': 'Dummy', + "value": 1, + "bounds": (0, 100), + "tooltip": "Dummy", }, on_perform=perform, ) diff -Nru python-pyface-6.1.2/pyface/action/tests/test_group.py python-pyface-7.4.0/pyface/action/tests/test_group.py --- python-pyface-6.1.2/pyface/action/tests/test_group.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_group.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from ..action import Action from ..action_item import ActionItem @@ -10,18 +19,17 @@ class TestActionItem(unittest.TestCase, UnittestTools): - def setUp(self): # test whether function is called by updating list # XXX should really use mock self.memo = [] def perform(): - self.memo.append('called') + self.memo.append("called") self.perform = perform - self.action = Action(name='Test', on_perform=perform) + self.action = Action(name="Test", on_perform=perform) self.action_item = ActionItem(action=self.action) def test_init_action_item(self): @@ -48,7 +56,7 @@ def test_append(self): group = Group(self.action_item) - action_item2 = ActionItem(action=Action(name='Action 2')) + action_item2 = ActionItem(action=Action(name="Action 2")) # XXX items doesn't fire a change event. Should it? group.append(action_item2) self.assertEqual(group.items, [self.action_item, action_item2]) @@ -56,7 +64,7 @@ def test_append_action(self): group = Group(self.action_item) - action2 = Action(name='Action 2') + action2 = Action(name="Action 2") # XXX items doesn't fire a change event. Should it? group.append(action2) self.assertEqual(len(group.items), 2) @@ -66,7 +74,6 @@ def test_append_callable(self): group = Group(self.action_item) - action2 = Action(name='Action 2') # XXX items doesn't fire a change event. Should it? group.append(self.perform) self.assertEqual(len(group.items), 2) @@ -92,7 +99,7 @@ def test_insert(self): group = Group(self.action_item) - action_item2 = ActionItem(action=Action(name='Action 2')) + action_item2 = ActionItem(action=Action(name="Action 2")) # XXX items doesn't fire a change event. Should it? group.insert(1, action_item2) self.assertEqual(group.items, [self.action_item, action_item2]) @@ -100,7 +107,7 @@ def test_insert_action(self): group = Group(self.action_item) - action2 = Action(name='Action 2') + action2 = Action(name="Action 2") # XXX items doesn't fire a change event. Should it? group.insert(1, action2) self.assertEqual(len(group.items), 2) @@ -110,7 +117,6 @@ def test_insert_callable(self): group = Group(self.action_item) - action2 = Action(name='Action 2') # XXX items doesn't fire a change event. Should it? group.insert(1, self.perform) self.assertEqual(len(group.items), 2) @@ -121,7 +127,7 @@ def test_insert_at_start(self): group = Group(self.action_item) - action_item2 = ActionItem(action=Action(name='Action 2')) + action_item2 = ActionItem(action=Action(name="Action 2")) # XXX items doesn't fire a change event. Should it? group.insert(0, action_item2) self.assertEqual(group.items, [action_item2, self.action_item]) @@ -141,7 +147,7 @@ def test_insert_before(self): group = Group(self.action_item) - action_item2 = ActionItem(action=Action(name='Action 2')) + action_item2 = ActionItem(action=Action(name="Action 2")) # XXX items doesn't fire a change event. Should it? group.insert_before(self.action_item, action_item2) self.assertEqual(group.items, [action_item2, self.action_item]) @@ -149,7 +155,7 @@ def test_insert_after(self): group = Group(self.action_item) - action_item2 = ActionItem(action=Action(name='Action 2')) + action_item2 = ActionItem(action=Action(name="Action 2")) # XXX items doesn't fire a change event. Should it? group.insert_after(self.action_item, action_item2) self.assertEqual(group.items, [self.action_item, action_item2]) @@ -157,12 +163,12 @@ def test_find(self): group = Group(self.action_item) - item = group.find('Test') + item = group.find("Test") self.assertEqual(item, self.action_item) def test_find_missing(self): group = Group(self.action_item) - item = group.find('Not here') + item = group.find("Not here") self.assertIsNone(item) def test_enabled_changed(self): diff -Nru python-pyface-6.1.2/pyface/action/tests/test_gui_application_action.py python-pyface-7.4.0/pyface/action/tests/test_gui_application_action.py --- python-pyface-6.1.2/pyface/action/tests/test_gui_application_action.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_gui_application_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from pyface.gui_application import GUIApplication from ..gui_application_action import GUIApplicationAction @@ -40,12 +49,12 @@ self.assertIsNone(action.object) - with self.assertTraitChanges(action, 'object', 1): + with self.assertTraitChanges(action, "object", 1): action.application = self.application self.assertEqual(action.object, self.application) - with self.assertTraitChanges(action, 'object', 1): + with self.assertTraitChanges(action, "object", 1): action.application = None self.assertIsNone(action.object) @@ -55,4 +64,4 @@ action.destroy() - self.assertEqual(action.object, None) \ No newline at end of file + self.assertEqual(action.object, None) diff -Nru python-pyface-6.1.2/pyface/action/tests/test_listening_action.py python-pyface-7.4.0/pyface/action/tests/test_listening_action.py --- python-pyface-6.1.2/pyface/action/tests/test_listening_action.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_listening_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,9 +1,18 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest from traits.api import Any, Bool, HasTraits -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from ..listening_action import ListeningAction from ..action_event import ActionEvent @@ -24,10 +33,10 @@ is_also_visible = Bool(True) #: Flag that is set when method called - was_called = Bool + was_called = Bool() #: Child object to test dotted lookup - child = Any + child = Any() def callback(self): self.was_called = True @@ -43,7 +52,7 @@ memo = [] def perform(): - memo.append('called') + memo.append("called") action.on_perform = perform event = ActionEvent() @@ -67,7 +76,7 @@ memo = self.perform_with_callback(action) - self.assertEqual(memo, ['called']) + self.assertEqual(memo, ["called"]) def test_perform_no_method(self): action = ListeningAction(object=self.object) @@ -75,10 +84,10 @@ memo = self.perform_with_callback(action) self.assertFalse(self.object.was_called) - self.assertEqual(memo, ['called']) + self.assertEqual(memo, ["called"]) def test_perform_method(self): - action = ListeningAction(object=self.object, method='callback') + action = ListeningAction(object=self.object, method="callback") memo = self.perform_with_callback(action) @@ -86,7 +95,7 @@ self.assertEqual(memo, []) def test_perform_method_missing(self): - action = ListeningAction(object=self.object, method='fallback') + action = ListeningAction(object=self.object, method="fallback") # does nothing, but shouldn't error memo = self.perform_with_callback(action) @@ -96,7 +105,7 @@ def test_perform_child_method(self): self.object.child = WatchedObject() - action = ListeningAction(object=self.object, method='child.callback') + action = ListeningAction(object=self.object, method="child.callback") memo = self.perform_with_callback(action) @@ -105,7 +114,7 @@ self.assertEqual(memo, []) def test_perform_missing_child_method(self): - action = ListeningAction(object=self.object, method='child.callback') + action = ListeningAction(object=self.object, method="child.callback") # does nothing, but shouldn't error memo = self.perform_with_callback(action) @@ -114,16 +123,16 @@ self.assertEqual(memo, []) def test_enabled(self): - action = ListeningAction(object=self.object, enabled_name='is_enabled') + action = ListeningAction(object=self.object, enabled_name="is_enabled") self.assertTrue(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): + with self.assertTraitChanges(action, "enabled", 1): self.object.is_enabled = False self.assertFalse(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): + with self.assertTraitChanges(action, "enabled", 1): self.object.is_enabled = True self.assertTrue(action.enabled) @@ -131,60 +140,60 @@ def test_enabled_child(self): self.object.child = WatchedObject() action = ListeningAction( - object=self.object, enabled_name='child.is_enabled' + object=self.object, enabled_name="child.is_enabled" ) self.assertTrue(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): + with self.assertTraitChanges(action, "enabled", 1): self.object.child.is_enabled = False self.assertFalse(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): + with self.assertTraitChanges(action, "enabled", 1): self.object.child.is_enabled = True self.assertTrue(action.enabled) def test_enabled_missing_child(self): action = ListeningAction( - object=self.object, enabled_name='child.is_enabled' + object=self.object, enabled_name="child.is_enabled" ) self.assertFalse(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): + with self.assertTraitChanges(action, "enabled", 1): self.object.child = WatchedObject() self.assertTrue(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): + with self.assertTraitChanges(action, "enabled", 1): self.object.child = None self.assertFalse(action.enabled) def test_enabled_name_change(self): self.object.is_also_enabled = False - action = ListeningAction(object=self.object, enabled_name='is_enabled') + action = ListeningAction(object=self.object, enabled_name="is_enabled") self.assertTrue(action.enabled) - with self.assertTraitChanges(action, 'enabled', 1): - action.enabled_name = 'is_also_enabled' + with self.assertTraitChanges(action, "enabled", 1): + action.enabled_name = "is_also_enabled" self.assertFalse(action.enabled) def test_visible(self): - action = ListeningAction(object=self.object, visible_name='is_visible') + action = ListeningAction(object=self.object, visible_name="is_visible") self.assertTrue(action.visible) - with self.assertTraitChanges(action, 'visible', 1): + with self.assertTraitChanges(action, "visible", 1): self.object.is_visible = False self.assertFalse(action.visible) - with self.assertTraitChanges(action, 'visible', 1): + with self.assertTraitChanges(action, "visible", 1): self.object.is_visible = True self.assertTrue(action.visible) @@ -192,50 +201,50 @@ def test_visible_child(self): self.object.child = WatchedObject() action = ListeningAction( - object=self.object, visible_name='child.is_visible' + object=self.object, visible_name="child.is_visible" ) self.assertTrue(action.visible) - with self.assertTraitChanges(action, 'visible', 1): + with self.assertTraitChanges(action, "visible", 1): self.object.child.is_visible = False self.assertFalse(action.visible) - with self.assertTraitChanges(action, 'visible', 1): + with self.assertTraitChanges(action, "visible", 1): self.object.child.is_visible = True self.assertTrue(action.visible) def test_visible_missing_child(self): action = ListeningAction( - object=self.object, visible_name='child.is_visible' + object=self.object, visible_name="child.is_visible" ) self.assertFalse(action.visible) - with self.assertTraitChanges(action, 'visible', 1): + with self.assertTraitChanges(action, "visible", 1): self.object.child = WatchedObject() self.assertTrue(action.visible) - with self.assertTraitChanges(action, 'visible', 1): + with self.assertTraitChanges(action, "visible", 1): self.object.child = None self.assertFalse(action.visible) def test_visible_name_change(self): self.object.is_also_visible = False - action = ListeningAction(object=self.object, visible_name='is_visible') + action = ListeningAction(object=self.object, visible_name="is_visible") self.assertTrue(action.visible) - with self.assertTraitChanges(action, 'visible', 1): - action.visible_name = 'is_also_visible' + with self.assertTraitChanges(action, "visible", 1): + action.visible_name = "is_also_visible" self.assertFalse(action.visible) def test_destroy(self): action = ListeningAction(object=self.object) - action.destroy() \ No newline at end of file + action.destroy() diff -Nru python-pyface-6.1.2/pyface/action/tests/test_traitsui_widget_action.py python-pyface-7.4.0/pyface/action/tests/test_traitsui_widget_action.py --- python-pyface-6.1.2/pyface/action/tests/test_traitsui_widget_action.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tests/test_traitsui_widget_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,18 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import unittest from traits.api import Enum, HasTraits -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from pyface.gui import GUI from pyface.toolkit import toolkit @@ -24,8 +22,8 @@ @unittest.skipIf(not has_traitsui(), "TraitsUI not installed") +@unittest.skipIf(toolkit.toolkit == "wx", "wxPython not supported") class TestTraitsUIWidgetAction(unittest.TestCase, UnittestTools): - def setUp(self): self.gui = GUI() self.parent = Window() @@ -43,8 +41,8 @@ from traitsui.api import View, Item class SimpleEnum(HasTraits): - value = Enum('a', 'b', 'c') - view = View(Item('value')) + value = Enum("a", "b", "c") + view = View(Item("value")) return SimpleEnum() @@ -52,109 +50,111 @@ from traitsui.api import View, Item class SimpleEnumAction(TraitsUIWidgetAction): - value = Enum('a', 'b', 'c') - view = View(Item('value')) + value = Enum("a", "b", "c") + view = View(Item("value")) action = SimpleEnumAction(name="Simple") control = action.create_control(self.parent.control) self.gui.process_events() - editor = control._ui.get_editors('value')[0] + editor = control._ui.get_editors("value")[0] - with self.assertTraitChanges(action, 'value', count=1): - if toolkit.toolkit in {'qt', 'qt4'}: + with self.assertTraitChanges(action, "value", count=1): + if toolkit.toolkit in {"qt", "qt4"}: editor.control.setCurrentIndex(1) editor.control.activated.emit(1) - elif toolkit.toolkit == 'wx': + elif toolkit.toolkit == "wx": import wx - event = wx.CommandEvent(wx.EVT_CHOICE.typeId, - editor.control.GetId()) - event.SetString('b') + + event = wx.CommandEvent( + wx.EVT_CHOICE.typeId, editor.control.GetId() + ) + event.SetString("b") wx.PostEvent(editor.control.GetEventHandler(), event) else: self.skipTest("Unknown toolkit") self.gui.process_events() - self.assertEqual(action.value, 'b') + self.assertEqual(action.value, "b") def test_traitsui_widget_action_model(self): from traitsui.api import View, Item class SimpleEnumAction(TraitsUIWidgetAction): - view = View(Item('value')) + view = View(Item("value")) model = self.create_model() action = SimpleEnumAction(name="Simple", model=model) control = action.create_control(self.parent.control) self.gui.process_events() - editor = control._ui.get_editors('value')[0] + editor = control._ui.get_editors("value")[0] - with self.assertTraitChanges(model, 'value', count=1): - if toolkit.toolkit in {'qt', 'qt4'}: + with self.assertTraitChanges(model, "value", count=1): + if toolkit.toolkit in {"qt", "qt4"}: editor.control.setCurrentIndex(1) editor.control.activated.emit(1) - elif toolkit.toolkit == 'wx': + elif toolkit.toolkit == "wx": import wx - event = wx.CommandEvent(wx.EVT_CHOICE.typeId, - editor.control.GetId()) - event.SetString('b') + + event = wx.CommandEvent( + wx.EVT_CHOICE.typeId, editor.control.GetId() + ) + event.SetString("b") wx.PostEvent(editor.control.GetEventHandler(), event) else: self.skipTest("Unknown toolkit") self.gui.process_events() - self.assertEqual(model.value, 'b') + self.assertEqual(model.value, "b") def test_traitsui_widget_action_model_view(self): from traitsui.api import HGroup, View, Item class ComplexEnumAction(TraitsUIWidgetAction): - value = Enum('a', 'b', 'c') + value = Enum("a", "b", "c") - view = View( - HGroup( - Item('value'), - Item('action.value'), - ) - ) + view = View(HGroup(Item("value"), Item("action.value"))) model = self.create_model() action = ComplexEnumAction(name="Simple", model=model) control = action.create_control(self.parent.control) self.gui.process_events() - editor = control._ui.get_editors('value')[0] + editor = control._ui.get_editors("value")[0] - with self.assertTraitChanges(model, 'value', count=1): - if toolkit.toolkit in {'qt', 'qt4'}: + with self.assertTraitChanges(model, "value", count=1): + if toolkit.toolkit in {"qt", "qt4"}: editor.control.setCurrentIndex(1) editor.control.activated.emit(1) - elif toolkit.toolkit == 'wx': + elif toolkit.toolkit == "wx": import wx - event = wx.CommandEvent(wx.EVT_CHOICE.typeId, - editor.control.GetId()) - event.SetString('b') + + event = wx.CommandEvent( + wx.EVT_CHOICE.typeId, editor.control.GetId() + ) + event.SetString("b") wx.PostEvent(editor.control.GetEventHandler(), event) else: self.skipTest("Unknown toolkit") self.gui.process_events() - self.assertEqual(model.value, 'b') + self.assertEqual(model.value, "b") - editor = control._ui.get_editors('value')[1] + editor = control._ui.get_editors("value")[1] - with self.assertTraitChanges(action, 'value', count=1): - if toolkit.toolkit in {'qt', 'qt4'}: + with self.assertTraitChanges(action, "value", count=1): + if toolkit.toolkit in {"qt", "qt4"}: editor.control.setCurrentIndex(2) editor.control.activated.emit(2) - elif toolkit.toolkit == 'wx': - event = wx.CommandEvent(wx.EVT_CHOICE.typeId, - editor.control.GetId()) - event.SetString('c') + elif toolkit.toolkit == "wx": + event = wx.CommandEvent( + wx.EVT_CHOICE.typeId, editor.control.GetId() + ) + event.SetString("c") wx.PostEvent(editor.control.GetEventHandler(), event) else: self.skipTest("Unknown toolkit") self.gui.process_events() - self.assertEqual(action.value, 'c') + self.assertEqual(action.value, "c") diff -Nru python-pyface-6.1.2/pyface/action/tool_bar_manager.py python-pyface-7.4.0/pyface/action/tool_bar_manager.py --- python-pyface-6.1.2/pyface/action/tool_bar_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tool_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A tool bar manager realizes itself in a tool bar control. """ +# Thanks for using Enthought open source! + +""" A toolkit-specific tool bar manager that realizes itself in a tool bar +control. + +- :attr:`~.ToolBarManager` +""" # Import the toolkit specific version. from pyface.toolkit import toolkit_object -ToolBarManager = toolkit_object('action.tool_bar_manager:ToolBarManager') -### EOF ####################################################################### +ToolBarManager = toolkit_object("action.tool_bar_manager:ToolBarManager") diff -Nru python-pyface-6.1.2/pyface/action/tool_palette_manager.py python-pyface-7.4.0/pyface/action/tool_palette_manager.py --- python-pyface-6.1.2/pyface/action/tool_palette_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/tool_palette_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A tool bar manager realizes itself in a tool palette control. """ +# Thanks for using Enthought open source! + +""" A toolkit-specific tool bar manager that realizes itself in a tool palette +control. + +- :attr:`~.ToolPaletteManager` +""" # Import the toolkit specific version. from pyface.toolkit import toolkit_object -ToolPaletteManager = toolkit_object('action.tool_palette_manager:ToolPaletteManager') -### EOF ####################################################################### +ToolPaletteManager = toolkit_object( + "action.tool_palette_manager:ToolPaletteManager" +) diff -Nru python-pyface-6.1.2/pyface/action/traitsui_widget_action.py python-pyface-7.4.0/pyface/action/traitsui_widget_action.py --- python-pyface-6.1.2/pyface/action/traitsui_widget_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/traitsui_widget_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! from traits.api import Constant, HasTraits, Instance @@ -53,7 +54,7 @@ control : toolkit control A toolkit control or None. """ - ui = self.edit_traits(kind='subpanel', parent=parent) + ui = self.edit_traits(kind="subpanel", parent=parent) control = ui.control control._ui = ui return control @@ -66,6 +67,6 @@ """ Use the model object for the Traits UI context, if appropriate. """ if self.model is not None: - context = {'object': self.model, 'action': self} + context = {"object": self.model, "action": self} return context - return super(TraitsUIWidgetAction, self).trait_context() + return super().trait_context() diff -Nru python-pyface-6.1.2/pyface/action/window_action.py python-pyface-7.4.0/pyface/action/window_action.py --- python-pyface-6.1.2/pyface/action/window_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/action/window_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,22 @@ -# Copyright (c) 2005-2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: """ Abstract base class for all window actions. """ -# Enthought library imports. + from pyface.window import Window from traits.api import Instance, Property -# Local imports. + from pyface.action.listening_action import ListeningAction @@ -24,7 +25,7 @@ # 'ListeningAction' interface -------------------------------------------- - object = Property(depends_on='window') + object = Property(observe="window") # 'WindowAction' interface ----------------------------------------------- @@ -41,11 +42,12 @@ def destroy(self): # Disconnect listeners to window and dependent properties. self.window = None - super(WindowAction, self).destroy() + super().destroy() class CloseWindowAction(WindowAction): """ Close the specified window """ - name = u'Close' - accelerator = 'Ctrl+W' - method = 'close' \ No newline at end of file + + name = "Close" + accelerator = "Ctrl+W" + method = "close" diff -Nru python-pyface-6.1.2/pyface/api.py python-pyface-7.4.0/pyface/api.py --- python-pyface-6.1.2/pyface/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,106 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + +""" + +API for the ``pyface`` package. + +- :class:`~.Application` +- :class:`~.ApplicationWindow` +- :attr:`~.clipboard` +- :class:`~.Clipboard` +- :class:`~.GUI` +- :class:`~.GUIApplication` +- :class:`~.ImageResource` +- :class:`~.KeyPressedEvent` +- :class:`~.SplashScreen` +- :class:`~.SplitApplicationWindow` +- :class:`~.SplitPanel` +- :class:`~.SystemMetrics` +- :class:`~.Window` +- :class:`~.Widget` + +Dialogs +------- + +- :class:`~.AboutDialog` +- :class:`~.confirm` +- :class:`~.ConfirmationDialog` +- :class:`~.Dialog` +- :class:`~.DirectoryDialog` +- :class:`~.FileDialog` +- :class:`~.error` +- :class:`~.information` +- :class:`~.warning` +- :class:`~.MessageDialog` +- :class:`~.ProgressDialog` +- :class:`~.choose_one` +- :class:`~.SingleChoiceDialog` +- :class:`~.SplitDialog` + +Constants +--------- + +- :class:`~.OK` +- :class:`~.CANCEL` +- :class:`~.YES` +- :class:`~.NO` + +UI Traits +--------- + +- :attr:`~.Alignment` +- :class:`~.Border` +- :class:`~.HasBorder` +- :class:`~.HasMargin` +- :class:`~.Image` +- :class:`~.Margin` + +Miscellaneous +------------- + +- :class:`~.ArrayImage` +- :class:`~.BaseDropHandler` +- :class:`~.beep` +- :class:`~.FileDropHandler` +- :class:`~.Filter` +- :class:`~.HeadingText` +- :class:`~.ImageCache` +- :class:`~.LayeredPanel` +- :class:`~.PILImage` +- :class:`~.PythonEditor` +- :class:`~.PythonShell` +- :class:`~.Sorter` + +Note that the :class:`~.ArrayImage` is only available if the ``numpy`` +package is available in the Python environment. + +Note that the :class:`~.PILImage` is only available if the ``pillow`` +package is available in the Python environment. + +Note that the :class:`~.PythonEditor` and :class:`~.PythonShell` classes are +only available if the ``pygments`` package is available in the Python +environment. + +Wx-specific classes +------------------- + +- :class:`~.ExpandablePanel` +- :class:`~.ImageWidget` +- :class:`~.MDIApplicationWindow` +- :class:`~.MDIWindowMenu` +- :class:`~.MultiToolbarWindow` + +""" -from __future__ import absolute_import +import logging as _logging from .about_dialog import AboutDialog from .application import Application @@ -23,6 +111,7 @@ from .constant import OK, CANCEL, YES, NO from .dialog import Dialog from .directory_dialog import DirectoryDialog +from .drop_handler import BaseDropHandler, FileDropHandler from .file_dialog import FileDialog from .filter import Filter from .gui import GUI @@ -31,10 +120,47 @@ from .image_cache import ImageCache from .image_resource import ImageResource from .key_pressed_event import KeyPressedEvent +from .layered_panel import LayeredPanel from .message_dialog import error, information, warning, MessageDialog from .progress_dialog import ProgressDialog -from .python_editor import PythonEditor -from .python_shell import PythonShell + +from .util._optional_dependencies import optional_import as _optional_import + +# Excuse numpy dependency, otherwise re-raise +with _optional_import( + "numpy", + msg="ArrayImage not available due to missing numpy.", + logger=_logging.getLogger(__name__)): + + # We need to manually try importing numpy because the ``ArrayImage`` + # import will end up raising a ``TraitError`` exception instead of an + # ``ImportError``, which isnt caught by ``_optional_import``. + import numpy + + from .array_image import ArrayImage + + del numpy + +# Excuse pillow dependency, otherwise re-raise +with _optional_import( + "pillow", + msg="PILImage not available due to missing pillow.", + logger=_logging.getLogger(__name__)): + from .pil_image import PILImage + +# Excuse pygments dependency (for Qt), otherwise re-raise +with _optional_import( + "pygments", + msg="PythonEditor not available due to missing pygments.", + logger=_logging.getLogger(__name__)): + from .python_editor import PythonEditor + +with _optional_import( + "pygments", + msg="PythonShell not available due to missing pygments.", + logger=_logging.getLogger(__name__)): + from .python_shell import PythonShell + from .sorter import Sorter from .single_choice_dialog import choose_one, SingleChoiceDialog from .splash_screen import SplashScreen @@ -55,14 +181,14 @@ from .expandable_panel import ExpandablePanel from .image_widget import ImageWidget -from .layered_panel import LayeredPanel from .mdi_application_window import MDIApplicationWindow from .mdi_window_menu import MDIWindowMenu from .multi_toolbar_window import MultiToolbarWindow # This code isn't toolkit widget code, but is wx-specific from traits.etsconfig.api import ETSConfig -if ETSConfig.toolkit == 'wx': + +if ETSConfig.toolkit == "wx": # Fix for broken Pycrust introspect module. # XXX move this somewhere better? - CJW 2017 diff -Nru python-pyface-6.1.2/pyface/application.py python-pyface-7.4.0/pyface/application.py --- python-pyface-6.1.2/pyface/application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-2018 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ This module defines the :py:class:`Application` class for Pyface, Tasks @@ -13,31 +14,35 @@ dependency on GUI code, and can be used for CLI or server applications. Usual usage is to subclass :py:class:`Application`, overriding at least the -:py:method:`Application._run` method, but usually the -:py:method:`Application.start` and :py:method:`Application.stop` +:py:meth:`Application._run` method, but usually the +:py:meth:`Application.start` and :py:meth:`Application.stop` methods as well. However the class can be used as-is by listening to the :py:attr:`Application.application_initialized` event and performing appropriate work there:: - def do_work(): + def do_work(event): print("Hello world") app = Application() - app.on_trait_change(do_work, 'application_initialized') + app.observe(do_work, 'application_initialized') """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) + import logging import os from traits.api import ( - Directory, Event, HasStrictTraits, Instance, ReadOnly, Unicode, Vetoable, - VetoableEvent + Directory, + Event, + HasStrictTraits, + Instance, + ReadOnly, + Str, + Vetoable, + VetoableEvent, ) logger = logging.getLogger(__name__) @@ -45,6 +50,7 @@ class ApplicationException(Exception): """ Exception subclass for Application-centric exceptions """ + pass @@ -54,6 +60,7 @@ If no arguments, then assumed to be a normal exit, otherwise the arguments give information about the problem. """ + pass @@ -80,24 +87,24 @@ # Branding ---------------------------------------------------------------- #: Human-readable application name - name = Unicode('Pyface Application') + name = Str("Pyface Application") #: Human-readable company name - company = Unicode + company = Str() #: Human-readable description of the application - description = Unicode + description = Str() # Infrastructure --------------------------------------------------------- #: The application's globally unique identifier. - id = Unicode + id = Str() #: Application home directory (for preferences, logging, etc.) - home = Directory + home = Directory() #: User data directory (for user files, projects, etc) - user_data = Directory + user_data = Directory() # Application lifecycle -------------------------------------------------- @@ -113,7 +120,7 @@ #: Fired when the application is starting. Called immediately before the #: stop method is run. - exiting = VetoableEvent + exiting = VetoableEvent() #: Fired when the application is starting. Called immediately before the #: stop method is run. @@ -155,13 +162,13 @@ run = stopped = False # Start up the application. - logger.info('---- Application starting ----') - self._fire_application_event('starting') + logger.info("---- Application starting ----") + self._fire_application_event("starting") started = self.start() if started: - logger.info('---- Application started ----') - self._fire_application_event('started') + logger.info("---- Application started ----") + self._fire_application_event("started") try: run = self._run() @@ -170,15 +177,15 @@ logger.info("---- ApplicationExit raised ----") else: logger.exception("---- ApplicationExit raised ----") - run = (exc.args == ()) + run = exc.args == () finally: # Try to shut the application down. - logger.info('---- Application stopping ----') - self._fire_application_event('stopping') + logger.info("---- Application stopping ----") + self._fire_application_event("stopping") stopped = self.stop() if stopped: - self._fire_application_event('stopped') - logger.info('---- Application stopped ----') + self._fire_application_event("stopped") + logger.info("---- Application stopped ----") return started and run and stopped @@ -202,17 +209,17 @@ ApplicationExit Some subclasses may trigger the exit by raising ApplicationExit. """ - logger.info('---- Application exit started ----') + logger.info("---- Application exit started ----") if force or self._can_exit(): try: self._prepare_exit() except Exception: logger.exception("Error preparing for application exit") finally: - logger.info('---- Application exit ----') + logger.info("---- Application exit ----") self._exit() else: - logger.info('---- Application exit vetoed ----') + logger.info("---- Application exit vetoed ----") # Initialization utilities ----------------------------------------------- @@ -223,7 +230,7 @@ stored. """ if not os.path.exists(self.home): - logger.info('Application home directory does not exist, creating') + logger.info("Application home directory does not exist, creating") os.makedirs(self.home) # ------------------------------------------------------------------------- @@ -244,7 +251,7 @@ # event loop (eg. a GUI, Tornado web app, etc.) then this should be # fired _after_ the event loop starts using an appropriate callback # (eg. gui.set_trait_later). - self._fire_application_event('application_initialized') + self._fire_application_event("application_initialized") return True # Utilities --------------------------------------------------------------- @@ -289,26 +296,31 @@ def _id_default(self): """ Use the application's directory as the id """ - from traits.etsconfig.etsconfig import ETSConfig + from traits.etsconfig.api import ETSConfig + return ETSConfig._get_application_dirname() def _home_default(self): """ Default home comes from ETSConfig. """ - from traits.etsconfig.etsconfig import ETSConfig + from traits.etsconfig.api import ETSConfig + return os.path.join(ETSConfig.application_data, self.id) def _user_data_default(self): """ Default user_data comes from ETSConfig. """ - from traits.etsconfig.etsconfig import ETSConfig + from traits.etsconfig.api import ETSConfig + return ETSConfig.user_data def _company_default(self): """ Default company comes from ETSConfig. """ - from traits.etsconfig.etsconfig import ETSConfig + from traits.etsconfig.api import ETSConfig + return ETSConfig.company def _description_default(self): """ Default description is the docstring of the application class. """ from inspect import getdoc + text = getdoc(self) return text diff -Nru python-pyface-6.1.2/pyface/application_window.py python-pyface-7.4.0/pyface/application_window.py --- python-pyface-6.1.2/pyface/application_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a top-level application window. """ -from __future__ import absolute_import # Import the toolkit specific version. from .toolkit import toolkit_object -ApplicationWindow = toolkit_object('application_window:ApplicationWindow') -#### EOF ###################################################################### +ApplicationWindow = toolkit_object("application_window:ApplicationWindow") diff -Nru python-pyface-6.1.2/pyface/array_image.py python-pyface-7.4.0/pyface/array_image.py --- python-pyface-6.1.2/pyface/array_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/array_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,95 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Array, HasStrictTraits, provides + +from pyface.i_image import IImage +from pyface.util.image_helpers import ( + array_to_image, image_to_bitmap, bitmap_to_icon, resize_image +) + +#: Trait type for image arrays. +ImageArray = Array(shape=(None, None, (3, 4)), dtype='uint8') + + +@provides(IImage) +class ArrayImage(HasStrictTraits): + """ An IImage stored in an RGB(A) numpy array. + """ + + # 'ArrayImage' interface ------------------------------------------------ + + #: The bytes of the image. + data = ImageArray() + + # ------------------------------------------------------------------------ + # 'IImage' interface. + # ------------------------------------------------------------------------ + + def create_image(self, size=None): + """ Creates a toolkit-specific image for this array. + + Parameters + ---------- + size : (int, int) or None + The desired size as a width, height tuple, or None if wanting + default image size. + + Returns + ------- + image : toolkit image + The toolkit image corresponding to the image and the specified + size. + """ + image = array_to_image(self.data) + if size is not None: + image = resize_image(image, size) + return image + + def create_bitmap(self, size=None): + """ Creates a toolkit-specific bitmap image for this array. + + Parameters + ---------- + size : (int, int) or None + The desired size as a width, height tuple, or None if wanting + default image size. + + Returns + ------- + image : toolkit bitmap + The toolkit bitmap corresponding to the image and the specified + size. + """ + return image_to_bitmap(self.create_image(size)) + + def create_icon(self, size=None): + """ Creates a toolkit-specific icon for this array. + + Parameters + ---------- + size : (int, int) or None + The desired size as a width, height tuple, or None if wanting + default icon size. + + Returns + ------- + image : toolkit icon + The toolkit image corresponding to the image and the specified + size as an icon. + """ + return bitmap_to_icon(self.create_bitmap(size)) + + # ------------------------------------------------------------------------ + # 'object' interface. + # ------------------------------------------------------------------------ + + def __init__(self, data, **traits): + super().__init__(data=data, **traits) diff -Nru python-pyface-6.1.2/pyface/base_toolkit.py python-pyface-7.4.0/pyface/base_toolkit.py --- python-pyface-6.1.2/pyface/base_toolkit.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/base_toolkit.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Common toolkit loading utilities and classes This module provides common code for ETS packages that need to do GUI toolkit @@ -14,8 +23,8 @@ package, but if this is not explicitly set by an application at startup or via environment variables, there needs to be a way of discovering and loading any available working toolkit implementations. The default mechanism is via the -now-standard :py:mod:`pkg_resources` and :py:mod:`setuptools` "entry point" -system. +now-standard :py:mod:`importlib_metadata` and :py:mod:`setuptools` +"entry point" system. This module provides three things: @@ -39,7 +48,7 @@ Default toolkit loading logic ----------------------------- -The :py_func:`find_toolkit` function uses the following logic when attempting +The :py:func:`find_toolkit` function uses the following logic when attempting to load toolkits: - if ETSConfig.toolkit is set, try to load a plugin with a matching name. @@ -56,46 +65,23 @@ import logging import os -import pkg_resources import sys -from traits.api import HasTraits, List, ReadOnly, Str, TraitError -from traits.etsconfig.api import ETSConfig - - try: - provisional_toolkit = ETSConfig.provisional_toolkit -except AttributeError: - from contextlib import contextmanager - - # for backward compatibility - @contextmanager - def provisional_toolkit(toolkit_name): - """ Perform an operation with toolkit provisionally set - - This sets the toolkit attribute of the ETSConfig object set to the - provided value. If the operation fails with an exception, the toolkit - is reset to nothing. - """ - if ETSConfig.toolkit: - raise AttributeError("ETSConfig toolkit is already set") - ETSConfig.toolkit = toolkit_name - try: - yield - except: - # reset the toolkit state - ETSConfig._toolkit = '' - raise + # Starting Python 3.8, importlib.metadata is available in the Python + # standard library and starting Python 3.10, the "select" interface is + # available on EntryPoints. + import importlib.metadata as importlib_metadata +except ImportError: + import importlib_metadata +from traits.api import HasTraits, List, ReadOnly, Str +from traits.etsconfig.api import ETSConfig logger = logging.getLogger(__name__) -TOOLKIT_PRIORITIES = { - 'qt4': -2, - 'wx': -1, - 'null': float('inf') -} +TOOLKIT_PRIORITIES = {"qt4": -2, "wx": -1, "null": float("inf")} default_priorities = lambda plugin: TOOLKIT_PRIORITIES.get(plugin.name, 0) @@ -117,11 +103,8 @@ packages = List(Str) def __init__(self, package, toolkit, *packages, **traits): - super(Toolkit, self).__init__( - package=package, - toolkit=toolkit, - packages=list(packages), - **traits + super().__init__( + package=package, toolkit=toolkit, packages=list(packages), **traits ) def __call__(self, name): @@ -135,29 +118,33 @@ """ from importlib import import_module - mname, oname = name.split(':') - if not mname.startswith('.'): - mname = '.' + mname + mname, oname = name.split(":") + if not mname.startswith("."): + mname = "." + mname for package in self.packages: try: module = import_module(mname, package) except ImportError as exc: # is the error while trying to import package mname or not? - if all(part not in exc.args[0] for part in mname.split('.') - if part): + if all( + part not in exc.args[0] + for part in mname.split(".") + if part + ): # something else went wrong - let the exception be raised raise # Ignore *ANY* errors unless a debug ENV variable is set. - if 'ETS_DEBUG' in os.environ: + if "ETS_DEBUG" in os.environ: # Attempt to only skip errors in importing the backend modules. # The idea here is that this only happens when the last entry in # the traceback's stack frame mentions the toolkit in question. import traceback - frames = traceback.extract_tb(sys.exc_traceback) + + frames = traceback.extract_tb(sys.exc_info()[2]) filename, lineno, function, text = frames[-1] - if not package in filename: + if package not in filename: raise else: obj = getattr(module, oname, None) @@ -180,7 +167,7 @@ return Unimplemented -def import_toolkit(toolkit_name, entry_point='pyface.toolkits'): +def import_toolkit(toolkit_name, entry_point="pyface.toolkits"): """ Attempt to import an toolkit specified by an entry point. Parameters @@ -201,16 +188,30 @@ If no toolkit is found, or if the toolkit cannot be loaded for some reason. """ - plugins = list(pkg_resources.iter_entry_points(entry_point, toolkit_name)) + + # This compatibility layer can be removed when we drop support for + # Python < 3.10. Ref https://github.com/enthought/pyface/issues/999. + all_entry_points = importlib_metadata.entry_points() + if hasattr(all_entry_points, "select"): + entry_point_group = all_entry_points.select(group=entry_point) + else: + entry_point_group = all_entry_points[entry_point] + + plugins = [ + plugin for plugin in entry_point_group if plugin.name == toolkit_name + ] if len(plugins) == 0: - msg = 'No {} plugin found for toolkit {}' + msg = "No {} plugin found for toolkit {}" msg = msg.format(entry_point, toolkit_name) logger.debug(msg) raise RuntimeError(msg) elif len(plugins) > 1: - msg = ("multiple %r plugins found for toolkit %r: %s") - modules = ', '.join(plugin.module_name for plugin in plugins) - logger.warning(msg, entry_point, toolkit_name, modules) + msg = "multiple %r plugins found for toolkit %r: %s" + module_names = [] + for plugin in plugins: + module_names.append(plugin.value.split(":")[0]) + module_names = ", ".join(module_names) + logger.warning(msg, entry_point, toolkit_name, module_names) for plugin in plugins: try: @@ -218,10 +219,11 @@ return toolkit_object except (ImportError, AttributeError) as exc: msg = "Could not load plugin %r from %r" - logger.info(msg, plugin.name, plugin.module_name) + module_name = plugin.value.split(":")[0] + logger.info(msg, plugin.name, module_name) logger.debug(exc, exc_info=True) - msg = 'No {} plugin could be loaded for {}' + msg = "No {} plugin could be loaded for {}" msg = msg.format(entry_point, toolkit_name) logger.info(msg) raise RuntimeError(msg) @@ -253,8 +255,6 @@ Raises ------ - TraitError - If no working toolkit is found. RuntimeError If no ETSConfig.toolkit is set but the toolkit cannot be loaded for some reason. @@ -262,10 +262,20 @@ if ETSConfig.toolkit: return import_toolkit(ETSConfig.toolkit, entry_point) - entry_points = [ - plugin for plugin in pkg_resources.iter_entry_points(entry_point) - if toolkits is None or plugin.name in toolkits - ] + # This compatibility layer can be removed when we drop support for + # Python < 3.10. Ref https://github.com/enthought/pyface/issues/999. + all_entry_points = importlib_metadata.entry_points() + if hasattr(all_entry_points, "select"): + entry_points = [ + plugin for plugin in all_entry_points.select(group=entry_point) + if toolkits is None or plugin.name in toolkits + ] + else: + entry_points = [ + plugin for plugin in all_entry_points[entry_point] + if toolkits is None or plugin.name in toolkits + ] + for plugin in sorted(entry_points, key=priorities): try: with ETSConfig.provisional_toolkit(plugin.name): @@ -273,11 +283,10 @@ return toolkit except (ImportError, AttributeError, RuntimeError) as exc: msg = "Could not load %s plugin %r from %r" - logger.info(msg, entry_point, plugin.name, plugin.module_name) + module_name = plugin.value.split(":")[0] + logger.info(msg, entry_point, plugin.name, module_name) logger.debug(exc, exc_info=True) # if all else fails, try to import the null toolkit. - with ETSConfig.provisional_toolkit('null'): - return import_toolkit('null', entry_point) - - raise TraitError("Could not import any {} toolkit.".format(entry_point)) + with ETSConfig.provisional_toolkit("null"): + return import_toolkit("null", entry_point) diff -Nru python-pyface-6.1.2/pyface/beep.py python-pyface-7.4.0/pyface/beep.py --- python-pyface-6.1.2/pyface/beep.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/beep.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,9 +1,19 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # Copyright 2012 Philip Chimento """Sound the system bell.""" # Import the toolkit-specific version -from __future__ import absolute_import + from .toolkit import toolkit_object -beep = toolkit_object('beep:beep') + +beep = toolkit_object("beep:beep") diff -Nru python-pyface-6.1.2/pyface/clipboard.py python-pyface-7.4.0/pyface/clipboard.py --- python-pyface-6.1.2/pyface/clipboard.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/clipboard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2009, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Evan Patterson # Date: 06/26/09 -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ """ The interface for manipulating the toolkit clipboard. """ # Import the toolkit specific version -from __future__ import absolute_import + from .toolkit import toolkit_object -Clipboard = toolkit_object('clipboard:Clipboard') + +Clipboard = toolkit_object("clipboard:Clipboard") # Create a singleton clipboard object for convenience clipboard = Clipboard() diff -Nru python-pyface-6.1.2/pyface/color_dialog.py python-pyface-7.4.0/pyface/color_dialog.py --- python-pyface-6.1.2/pyface/color_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/color_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,44 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The implementation of a dialog that allows the user to select a color. +""" + +from .constant import OK +from .toolkit import toolkit_object + + +ColorDialog = toolkit_object("color_dialog:ColorDialog") + + +def get_color(parent, color, show_alpha=False): + """ Convenience function that displays a color dialog. + + Parameters + ---------- + parent : toolkit control + The parent toolkit control for the modal dialog. + color : Color or color description + The initial Color object, rgb(a) tuple or a string holding a valid + color description. + show_alpha : bool + Whether or not to show alpha channel information. + + Returns + ------- + color : Color or None + The selected color, or None if the user made no selection. + """ + dialog = ColorDialog(parent=parent, color=color, show_alpha=show_alpha) + result = dialog.open() + if result == OK: + return dialog.color + else: + return None diff -Nru python-pyface-6.1.2/pyface/color.py python-pyface-7.4.0/pyface/color.py --- python-pyface-6.1.2/pyface/color.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/color.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,263 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Color classes and corresponding trait types for Pyface. + +The base Color class holds red, green, blue and alpha channel values as +a tuple of normalized values from 0.0 to 1.0. Various property traits +pull out the individual channel values and supply values for the HSV +and HSL colour spaces (with and without alpha). + +The ``from_toolkit`` and ``to_toolkit`` methods allow conversion to and +from native toolkit color objects. +""" + +import colorsys + +from traits.api import ( + Bool, HasStrictTraits, Property, Range, Tuple, cached_property +) + +from pyface.util.color_helpers import channels_to_ints, is_dark +from pyface.util.color_helpers import ints_to_channels # noqa: F401 +from pyface.util.color_parser import parse_text + + +#: A trait holding a single channel value. +Channel = Range(0.0, 1.0, value=1.0, channel=True) + +#: A trait holding three channel values. +ChannelTuple = Tuple(Channel, Channel, Channel) + +#: A trait holding four channel values. +AlphaChannelTuple = Tuple(Channel, Channel, Channel, Channel) + + +class Color(HasStrictTraits): + """ A mutable specification of a color with alpha. + + This is a class designed to be used by user interface elements which + need to color some or all of the interface element. Each color has a + number of different representations as channel tuples, each channel + holding a value between 0.0 and 1.0, inclusive. The standard red, + green, blue and alpha channels are also provided as convenience + properties. + + Methods are provided to convert to and from toolkit-specific color + objects. + + Colors implement equality testing, but are not hashable as they are + mutable, and so are not suitable for use as dictionary keys. If you + need a dictionary key, use an appropriate channel tuple from the + object. + """ + + #: A tuple holding the red, green, blue, and alpha channels. + rgba = AlphaChannelTuple() + + #: A tuple holding the red, green, and blue channels. + rgb = Property(ChannelTuple(), observe='rgba') + + #: The red channel. + red = Property(Channel, observe='rgba') + + #: The green channel. + green = Property(Channel, observe='rgba') + + #: The blue channel. + blue = Property(Channel, observe='rgba') + + #: The alpha channel. + alpha = Property(Channel, observe='rgba') + + #: A tuple holding the hue, saturation, value, and alpha channels. + hsva = Property(AlphaChannelTuple, observe='rgba') + + #: A tuple holding the hue, saturation, and value channels. + hsv = Property(ChannelTuple, observe='rgb') + + #: A tuple holding the hue, lightness, saturation, and alpha channels. + hlsa = Property(AlphaChannelTuple, observe='rgba') + + #: A tuple holding the hue, lightness, and saturation channels. + hls = Property(ChannelTuple, observe='rgb') + + #: Whether the color is dark for contrast purposes. + is_dark = Property(Bool, observe='rgba') + + @classmethod + def from_str(cls, text, **traits): + """ Create a new Color object from a string. + + Parameters + ---------- + text : str + A string holding the representation of the color. This can be: + + - a color name, including all CSS color names, plus any additional + names found in pyface.color.color_table. The names are + normalized to lower case and stripped of whitespace, hyphens and + underscores. + + - a hex representation of the color in the form '#RGB', '#RGBA', + '#RRGGBB', '#RRGGBBAA', '#RRRRGGGGBBBB', or '#RRRRGGGGBBBBAAAA'. + + **traits + Any additional trait values to be passed as keyword arguments. + + Raises + ------ + ColorParseError + If the string cannot be converted to a valid color. + """ + space, channels = parse_text(text) + if space in traits: + raise TypeError( + "from_str() got multiple values for keyword argument " + + repr(space) + ) + traits[space] = channels + return cls(**traits) + + @classmethod + def from_toolkit(cls, toolkit_color, **traits): + """ Create a new Color object from a toolkit color object. + + Parameters + ---------- + toolkit_color : toolkit object + A toolkit color object, such as a Qt QColor or a Wx wx.Colour. + **traits + Any additional trait values to be passed as keyword arguments. + """ + from pyface.toolkit import toolkit_object + toolkit_color_to_rgba = toolkit_object('color:toolkit_color_to_rgba') + rgba = toolkit_color_to_rgba(toolkit_color) + return cls(rgba=rgba, **traits) + + def to_toolkit(self): + """ Create a new toolkit color object from a Color object. + + Returns + ------- + toolkit_color : toolkit object + A toolkit color object, such as a Qt QColor or a Wx wx.Colour. + """ + from pyface.toolkit import toolkit_object + rgba_to_toolkit_color = toolkit_object('color:rgba_to_toolkit_color') + return rgba_to_toolkit_color(self.rgba) + + def hex(self): + """ Provide a hex representation of the Color object. + + Note that because the hex value is restricted to 0-255 integer values + for each channel, the representation is not exact. + + Returns + ------- + hex : str + A hex string in standard ``#RRGGBBAA`` format that represents + the color. + """ + values = channels_to_ints(self.rgba) + return "#{:02X}{:02X}{:02X}{:02X}".format(*values) + + def __eq__(self, other): + if isinstance(other, Color): + return self.rgba == other.rgba + return NotImplemented + + def __str__(self): + return "({:0.5}, {:0.5}, {:0.5}, {:0.5})".format(*self.rgba) + + def __repr__(self): + return "{}(rgba={!r})".format(self.__class__.__name__, self.rgba) + + def _get_red(self): + return self.rgba[0] + + def _set_red(self, value): + r, g, b, a = self.rgba + self.rgba = (value, g, b, a) + + def _get_green(self): + return self.rgba[1] + + def _set_green(self, value): + r, g, b, a = self.rgba + self.rgba = (r, value, b, a) + + def _get_blue(self): + return self.rgba[2] + + def _set_blue(self, value): + r, g, b, a = self.rgba + self.rgba = (r, g, value, a) + + def _get_alpha(self): + return self.rgba[3] + + def _set_alpha(self, value): + r, g, b, a = self.rgba + self.rgba = (r, g, b, value) + + @cached_property + def _get_rgb(self): + return self.rgba[:-1] + + def _set_rgb(self, value): + r, g, b = value + self.rgba = (r, g, b, self.rgba[3]) + + @cached_property + def _get_hsva(self): + r, g, b, a = self.rgba + h, s, v = colorsys.rgb_to_hsv(r, g, b) + return (h, s, v, a) + + def _set_hsva(self, value): + h, s, v, a = value + r, g, b = colorsys.hsv_to_rgb(h, s, v) + self.rgba = (r, g, b, a) + + @cached_property + def _get_hsv(self): + r, g, b = self.rgb + return colorsys.rgb_to_hsv(r, g, b) + + def _set_hsv(self, value): + h, s, v = value + r, g, b = colorsys.hsv_to_rgb(h, s, v) + self.rgb = (r, g, b) + + @cached_property + def _get_hlsa(self): + r, g, b, a = self.rgba + h, l, s = colorsys.rgb_to_hls(r, g, b) + return (h, l, s, a) + + def _set_hlsa(self, value): + h, l, s, a = value + r, g, b = colorsys.hls_to_rgb(h, l, s) + self.rgba = (r, g, b, a) + + @cached_property + def _get_hls(self): + r, g, b = self.rgb + return colorsys.rgb_to_hls(r, g, b) + + def _set_hls(self, value): + h, l, s = value + r, g, b = colorsys.hls_to_rgb(h, l, s) + self.rgb = (r, g, b) + + @cached_property + def _get_is_dark(self): + return is_dark(self.rgb) diff -Nru python-pyface-6.1.2/pyface/confirmation_dialog.py python-pyface-7.4.0/pyface/confirmation_dialog.py --- python-pyface-6.1.2/pyface/confirmation_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/confirmation_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a dialog that prompts the user for confirmation. """ -from __future__ import absolute_import -# Local imports. from .constant import NO +from .toolkit import toolkit_object + + +ConfirmationDialog = toolkit_object("confirmation_dialog:ConfirmationDialog") def confirm(parent, message, title=None, cancel=False, default=NO): @@ -39,18 +38,11 @@ title = "Confirmation" dialog = ConfirmationDialog( - parent = parent, - message = message, - cancel = cancel, - default = default, - title = title + parent=parent, + message=message, + cancel=cancel, + default=default, + title=title, ) return dialog.open() - - -# Import the toolkit specific version. -from .toolkit import toolkit_object -ConfirmationDialog = toolkit_object('confirmation_dialog:ConfirmationDialog') - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/constant.py python-pyface-7.4.0/pyface/constant.py --- python-pyface-6.1.2/pyface/constant.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/constant.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Common constants. """ # Standard results for Ok/Cancel, Yes/No operations etc. -OK = 10 -CANCEL = 20 -YES = 30 -NO = 40 - -#### EOF ###################################################################### +OK = 10 +CANCEL = 20 +YES = 30 +NO = 40 diff -Nru python-pyface-6.1.2/pyface/data_view/abstract_data_exporter.py python-pyface-7.4.0/pyface/data_view/abstract_data_exporter.py --- python-pyface-6.1.2/pyface/data_view/abstract_data_exporter.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/abstract_data_exporter.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,91 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from abc import abstractmethod + +from traits.api import ABCHasStrictTraits, Bool, Instance + +from pyface.data_view.data_view_errors import DataViewGetError +from pyface.data_view.i_data_wrapper import DataFormat + + +class AbstractDataExporter(ABCHasStrictTraits): + """ ABC for classes that export data from a data view. + + Concrete classes should implement the ``get_data`` method so that + it produces a value that can be serialized using the provided + ``format``. Some convenience methods are provided to get + text values, as that is a common use-case. + """ + + #: The DataFormat used to serialize the exported data. + format = Instance(DataFormat) + + #: Whether to get item data from the text channel, if available. + is_text = Bool() + + def add_data(self, data_wrapper, model, indices): + """ Add data to the data wrapper from the model and indices. + + Parameters + ---------- + data_wrapper : DataWrapper instance + The data wrapper that will be used to export data. + model : AbstractDataModel instance + The data model holding the data. + indices : list of (row, column) index pairs + The indices where the data is to be stored. + """ + try: + data = self.get_data(model, indices) + data_wrapper.set_format(self.format, data) + except DataViewGetError: + pass + + @abstractmethod + def get_data(self, model, indices): + """ Get the data to be exported from the model and indices. + + Parameters + ---------- + model : AbstractDataModel instance + The data model holding the data. + indices : list of (row, column) index pairs + The indices where the data is to be stored. + + Returns + ------- + data : any + The data, of a type that can be serialized by the format. + """ + raise NotImplementedError() + + def get_value(self, model, row, column): + """ Utility method to extract a value at a given index. + + If ``is_text`` is True, it will use the ``get_text()`` method + to extract the value, otherwise it will try to use the + editor value if it exists, and failing that the raw value + returned from the model. + """ + value_type = model.get_value_type(row, column) + if self.is_text: + if value_type.has_text(model, row, column): + value = value_type.get_text(model, row, column) + else: + value = "" + elif value_type.has_editor_value(model, row, column): + value = value_type.get_editor_value(model, row, column) + else: + value = model.get_value(row, column) + return value + + def _is_text_default(self): + return self.format.mimetype.startswith('text/') diff -Nru python-pyface-6.1.2/pyface/data_view/abstract_data_model.py python-pyface-7.4.0/pyface/data_view/abstract_data_model.py --- python-pyface-6.1.2/pyface/data_view/abstract_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/abstract_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,325 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Provides an AbstractDataModel ABC for Pyface data models. + +This module provides an ABC for all data view data models. This specifies +the API that the data view widgets expect, and which the underlying +data is adapted to by the concrete implementations. Data models are intended +to be toolkit-independent, and be able to adapt any approximately tabular or +nested data structure to what the data view system expects. +""" +from abc import abstractmethod + +from traits.api import ABCHasStrictTraits, Event, Instance + +from .data_view_errors import DataViewSetError +from .index_manager import AbstractIndexManager + + +class AbstractDataModel(ABCHasStrictTraits): + """ Abstract base class for Pyface data models. + + The data model API is intended to provide a common API for hierarchical + and tabular data. This class is concerned with the structure, type and + values provided by the data, but not with how the data is presented. + + Row and column indices are represented by sequences (usually lists) of + integers, specifying the index at each level of the hierarchy. The root + row and column are represented by empty lists. + + Subclasses need to implement the ``get_column_count``, + ``can_have_children`` and ``get_row_count`` methods to return the number + of columns in a particular row, as well as the hierarchical structure of + the rows. Appropriate observers should be set up on the underlaying data + so that the ``structure_changed`` event is fired when the values returned + by these methods would change. + + Subclasses also have to implement the ``get_value`` and ``get_value_type`` + methods. These expect a row and column index, with root values treated + specially: the root row corresponds to the values which will be displayed + in the column headers of the view, and the root column corresponds to the + values which will be displayed in the row headers of the view. + The ``get_value`` returns an arbitrary Python object corresponding to the + cell being viewed, and the ``get_value_type`` should return an instance of + an ``AbstractValueType`` that adapts the raw value to the data channels + that the data view expects (eg. text, color, icons, editable value, etc.). + Implementations should ensure that the ``values_changed`` event fires + whenever the data, or the way the data is presented, is updated. + + If the data is to be editable then the subclass should override the + ``set_value`` method. It should attempt to change the underlying data as a + side-effect or raise DataViewSetError on failure (for example, + setting an invalid value). If the underlying data structure cannot be + listened to internally (such as a numpy array or Pandas data frame), + ``set_value`` should also fire the ``values_changed`` event with + appropriate values. + + In the cases where the underlying data structure cannot be observed by + the usual traits mechanisms, the end-user of the code may be responsible + for ensuring that the ``structure_changed`` and ``values_changed`` events + are fired appropriately. + """ + + #: The index manager that helps convert toolkit indices to data view + #: indices. This should be an IntIndexManager for non-hierarchical data + #: or a TupleIndexManager for hierarchical data. + index_manager = Instance(AbstractIndexManager) + + #: Event fired when the structure of the data changes. + structure_changed = Event() + + #: Event fired when value changes without changes to structure. This + #: should be set to a 4-tuple of (start_row_index, start_column_index, + #: end_row_index, end_column_index) indicated the subset of data which + #: changed. These end values are inclusive, unlike standard Python + #: slicing notation. + values_changed = Event() + + # Data structure methods + + @abstractmethod + def get_column_count(self): + """ How many columns in the data view model. + + Returns + ------- + column_count : non-negative int + The number of columns that the data view provides. This count + should not include the row header. + """ + raise NotImplementedError() + + @abstractmethod + def can_have_children(self, row): + """ Whether or not a row can have child rows. + + The root row should always return True. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + can_have_children : bool + Whether or not the row can ever have child rows. + """ + raise NotImplementedError() + + @abstractmethod + def get_row_count(self, row): + """ How many child rows the row currently has. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + row_count : non-negative int + The number of child rows that the row has. + """ + raise NotImplementedError() + + # Data value methods + + @abstractmethod + def get_value(self, row, column): + """ Return the Python value for the row and column. + + The values for column headers are returned by calling this method with + row equal to (). The values for row headers are returned by calling + this method with column equal to (). + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + value : any + The value represented by the given row and column. + + Raises + ------- + DataViewGetError + If the value cannot be accessed in an expected way. If this is + raised then the error will be ignored and not logged by the + data view infrastructure. + """ + raise NotImplementedError() + + def can_set_value(self, row, column): + """ Whether the value in the indicated row and column can be set. + + The default method assumes the data is read-only and always + returns False. + + Whether or a column header can be set is returned by calling this + method with row equal to (). Whether or a row header can be set + is returned by calling this method with column equal to (). + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + return False + + def set_value(self, row, column, value): + """ Set the Python value for the row and column. + + The default method assumes the data is read-only and always + returns False. + + The values for column headers can be set by calling this method with + row equal to (). The values for row headers can be set by calling + this method with column equal to (). + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + value : any + The new value for the given row and column. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + raise DataViewSetError() + + @abstractmethod + def get_value_type(self, row, column): + """ Return the value type of the given row and column. + + The value type for column headers are returned by calling this method + with row equal to (). The value types for row headers are returned + by calling this method with column equal to (). + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + value_type : AbstractValueType or None + The value type of the given row and column, or None if no value + should be displayed. + """ + raise NotImplementedError() + + # Convenience methods + + def is_row_valid(self, row): + """ Return whether or not the given row index refers to a valid row. + + A row index is valid if every value in the tuple is between 0 and the + number of child rows of the parent. + + Parameters + ---------- + row : sequence of int + The row to check as indices of the row from root to leaf. + + Returns + ------- + valid : bool + Whether or not the row index is valid. + """ + for i, index in enumerate(row): + parent = row[:i] + if not self.can_have_children(parent): + return False + if not 0 <= index < self.get_row_count(parent): + return False + return True + + def is_column_valid(self, column): + """ Return whether or not the given column index refers to a valid column. + + A column index is valid if it is the root, or the value is between 0 and + the number of columns in the model. + + Parameters + ---------- + column : sequence of int + The column to check. + + Returns + ------- + valid : bool + Whether or not the column index is valid. + """ + if len(column) == 1: + return 0 <= column[0] < self.get_column_count() + + return len(column) == 0 + + def iter_rows(self, start_row=()): + """ Iterator that yields rows in preorder. + + Parameters + ---------- + start_row : sequence of int + The row to start at. The iterator will yeild the row and all + descendant rows. + + Yields + ------ + row_index : sequence of int + The current row index. + """ + start_row = tuple(start_row) + yield start_row + if self.can_have_children(start_row): + for row in range(self.get_row_count(start_row)): + yield from self.iter_rows(start_row + (row,)) + + def iter_items(self, start_row=()): + """ Iterator that yields rows and columns in preorder. + + This yields pairs of row, column for all rows in preorder + and and all column indices for all rows, including (). + Columns are iterated in order. + + Parameters + ---------- + start_row : sequence of int + The row to start iteration from. + + Yields + ------ + row_index, column_index + The current row and column indices. + """ + for row in self.iter_rows(start_row): + yield row, () + for column in range(self.get_column_count()): + yield row, (column,) diff -Nru python-pyface-6.1.2/pyface/data_view/abstract_value_type.py python-pyface-7.4.0/pyface/data_view/abstract_value_type.py --- python-pyface-6.1.2/pyface/data_view/abstract_value_type.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/abstract_value_type.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,385 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Provides an AbstractValueType ABC for Pyface data models. + +This module provides an ABC for data view value types, which are responsible +for adapting raw data values as used by the data model's ``get_value`` and +``set_value`` methods to the data channels that the data view expects, such +as text, color, icons, etc. + +It is up to the data view to take this standardized data and determine what +and how to actually display it. +""" + +from enum import IntEnum + +from traits.api import ABCHasStrictTraits, Event, observe + +from pyface.color import Color +from .data_view_errors import DataViewSetError + + +class CheckState(IntEnum): + "Possible checkbox states" + # XXX in the future this may need a "partial" state, see Pyface #695 + UNCHECKED = 0 + CHECKED = 1 + + +class AbstractValueType(ABCHasStrictTraits): + """ A value type converts raw data into data channels. + + The data channels are editor value, text, color, image, and description. + The data channels are used by other parts of the code to produce the actual + display. + + Subclasses should mark traits that potentially affect the display of values + with ``update_value_type=True`` metdadata, or alternatively fire the + ``updated`` event when the state of the value type changes. + + Each data channel is set up to have a method which returns whether there + is a value for the channel, a second method which returns the value, + and an optional third method which sets the channel value. These methods + should not raise an Exception, eveen when called inappropriately (eg. + calling a "get" method after a "has" method has returned False). + """ + + #: Fired when a change occurs that requires updating values. + updated = Event + + def has_editor_value(self, model, row, column): + """ Return whether or not the value can be edited. + + The default implementation is that cells that can be set are + editable. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_editor_value : bool + Whether or not the value is editable. + """ + return model.can_set_value(row, column) + + def get_editor_value(self, model, row, column): + """ Return a value suitable for editing. + + The default implementation is to return the underlying data value + directly from the data model. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + value : any + The value to edit. + """ + return model.get_value(row, column) + + def set_editor_value(self, model, row, column, value): + """ Set a value that is returned from editing. + + The default implementation is to set the value directly from the + data model. Returns True if successful, False if it fails. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + value : any + The value to set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + model.set_value(row, column, value) + + def has_text(self, model, row, column): + """ Whether or not the value has a textual representation. + + The default implementation returns True if ``get_text`` + returns a non-empty value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_text : bool + Whether or not the value has a textual representation. + """ + return self.get_text(model, row, column) != "" + + def get_text(self, model, row, column): + """ The textual representation of the underlying value. + + The default implementation calls str() on the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + The textual representation of the underlying value. + """ + return str(model.get_value(row, column)) + + def set_text(self, model, row, column, text): + """ Set the text of the underlying value. + + This is provided primarily for backends which may not permit + non-text editing of values, in which case this provides an + alternative route to setting the value. The default implementation + does not allow setting the text. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + text : str + The text to set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + raise DataViewSetError("Cannot set value.") + + def has_color(self, model, row, column): + """ Whether or not the value has color data. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_color : bool + Whether or not the value has data-associated color + values. + """ + return False + + def get_color(self, model, row, column): + """ Get data-associated colour values for the given item. + + The default implementation returns white. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + color : Color instance + The color associated with the cell. + """ + return Color(rgba=(1.0, 1.0, 1.0, 1.0)) + + def has_image(self, model, row, column): + """ Whether or not the value has an image associated with it. + + The default implementation returns True if ``get_image`` + returns a non-None value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_image : bool + Whether or not the value has an image associated with it. + """ + return False + + def get_image(self, model, row, column): + """ An image associated with the underlying value. + + The default implementation returns None. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + image : IImage + The image associated with the underlying value. + """ + from pyface.image_resource import ImageResource + return ImageResource("image_not_found") + + def has_check_state(self, model, row, column): + """ Whether or not the value has checked state. + + The default implementation returns False. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_check_state : bool + Whether or not the value has a checked state. + """ + return False + + def get_check_state(self, model, row, column): + """ The state of the item check box. + + The default implementation returns "checked" if the value is + truthy, or "unchecked" if the value is falsey. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + check_state : CheckState + The current checked state. + """ + return ( + CheckState.CHECKED + if model.get_value(row, column) + else CheckState.UNCHECKED + ) + + def set_check_state(self, model, row, column, check_state): + """ Set the checked state of the underlying value. + + The default implementation does not allow setting the checked state. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + check_state : CheckState + The check state value to set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + raise DataViewSetError("Cannot set check state.") + + def has_tooltip(self, model, row, column): + """ Whether or not the value has a tooltip. + + The default implementation returns True if ``get_tooltip`` + returns a non-empty value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_tooltip : bool + Whether or not the value has a textual representation. + """ + return self.get_tooltip(model, row, column) != "" + + def get_tooltip(self, model, row, column): + """ The tooltip for the underlying value. + + The default implementation returns an empty string. + + tooltip : str + The textual representation of the underlying value. + """ + return "" + + @observe('+update_value_type') + def update_value_type(self, event=None): + """ Fire update event when marked traits change. """ + self.updated = True diff -Nru python-pyface-6.1.2/pyface/data_view/api.py python-pyface-7.4.0/pyface/data_view/api.py --- python-pyface-6.1.2/pyface/data_view/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,88 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.data_view`` subpackage. +Note that this public-facing API is provisional and may change in future +minor releases until Pyface 8. + +- :class:`~.AbstractDataExporter` +- :class:`~.AbstractDataModel` +- :class:`~.AbstractValueType` +- :class:`~.DataViewWidget` +- :class:`~.DataWrapper` + +Data Formats +------------ + +- :class:`~.DataFormat` +- :func:`~.text_format` +- :attr:`~.csv_format` +- :attr:`~.csv_column_format` +- :attr:`~.csv_row_format` +- :attr:`~.html_format` +- :attr:`~.npy_format` +- :attr:`~.standard_text_format` +- :attr:`~.table_format` +- :attr:`~.text_column_format` +- :attr:`~.text_row_format` +- :func:`~.from_csv` +- :func:`~.from_csv_column` +- :func:`~.from_csv_row` +- :func:`~.from_json` +- :func:`~.from_npy` +- :func:`~.to_csv` +- :func:`~.to_csv_column` +- :func:`~.to_csv_row` +- :func:`~.to_json` +- :func:`~.to_npy` + +Index Managers +-------------- + +- :class:`~.AbstractIndexManager` +- :class:`~.IntIndexManager` +- :class:`~.TupleIndexManager` + +Exceptions +---------- +- :class:`~.DataViewError` +- :class:`~.DataViewGetError` +- :class:`~.DataViewSetError` + +Interfaces +---------- +- :class:`~.IDataViewWidget` +- :class:`~.IDataWrapper` + +""" + +from pyface.data_view.abstract_data_exporter import AbstractDataExporter # noqa: 401 +from pyface.data_view.abstract_data_model import AbstractDataModel # noqa: 401 +from pyface.data_view.abstract_value_type import AbstractValueType # noqa: 401 +from pyface.data_view.data_formats import ( # noqa: 401 + csv_column_format, csv_format, csv_row_format, from_csv, from_csv_column, + from_csv_row, from_json, from_npy, html_format, npy_format, + standard_text_format, to_csv, to_csv_column, to_csv_row, to_json, to_npy, + table_format, text_column_format, text_row_format +) +from pyface.data_view.data_view_errors import ( # noqa: 401 + DataViewError, DataViewGetError, DataViewSetError +) +from pyface.data_view.data_view_widget import DataViewWidget # noqa: 401 +from pyface.data_view.data_wrapper import DataWrapper # noqa: 401 +from pyface.data_view.i_data_view_widget import IDataViewWidget # noqa: 401 +from pyface.data_view.i_data_wrapper import ( # noqa: 401 + DataFormat, IDataWrapper, text_format +) +from pyface.data_view.index_manager import ( # noqa: 401 + AbstractIndexManager, IntIndexManager, TupleIndexManager +) diff -Nru python-pyface-6.1.2/pyface/data_view/data_formats.py python-pyface-7.4.0/pyface/data_view/data_formats.py --- python-pyface-6.1.2/pyface/data_view/data_formats.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_formats.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,290 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +import csv +from functools import partial +from io import BytesIO, StringIO +import os +import json + +from pyface.data_view.i_data_wrapper import DataFormat, text_format + + +# Scalar formats + +def to_json(data, default=None): + """ Serialize an object to a JSON bytestring. + + Parameters + ---------- + data : any + The data to be serialized. + default : callable or None + Callable that takes a Python object and returns a JSON-serializable + data structure. + + Returns + ------- + raw_data : bytes + The serialized data as a bytestring. + """ + str_data = json.dumps(data, default=default, separators=(',', ':')) + return str_data.encode('utf-8') + + +def from_json(raw_data, object_hook=None): + """ Deserialize a JSON bytestring. + + Parameters + ---------- + raw_data : bytes + The serialized JSON data as a byte string. + object_hook : callable + Callable that takes a dictionary and returns an corresponding + Python object. + + Returns + ------- + data : any + The data extracted. + """ + return json.loads(raw_data.decode('utf-8'), object_hook=object_hook) + + +#: The text/plain format with utf-8 encoding. +standard_text_format = text_format() + +#: The text/html format with utf-8 encoding. +html_format = text_format(mimetype='text/html') + +#: A generic JSON format. +json_format = DataFormat('application/json', to_json, from_json) + + +# 1D formats + +def to_csv_row(data, delimiter=',', encoding='utf-8', **kwargs): + """ Serialize a list to a single-row CSV bytestring. + + Parameters + ---------- + data : list + The list to be serialized. Any elements which are not strings will + be converted to strings by calling ``str()``. + delimiter : str + The CSV delimiter. + encoding : str + The encoding of the bytes + **kwargs + Additional arguments to csv.writer. + + Returns + ------- + raw_data : bytes + The serialized data as a bytestring. + """ + fp = StringIO() + writer = csv.writer(fp, delimiter=delimiter, **kwargs) + writer.writerow(data) + return fp.getvalue().encode(encoding) + + +def from_csv_row(raw_data, delimiter=',', encoding='utf-8', **kwargs): + """ Deserialize the first row of a CSV bytestring as a list. + + Any rows beyond the first are ignored. + + Parameters + ---------- + raw_data : bytes + The serialized CSV data as a byte string. + delimiter : str + The CSV delimiter. + encoding : str + The encoding of the bytes + **kwargs + Additional arguments to csv.reader. + + Returns + ------- + data : list of str + The data extracted as a list of strings. + """ + fp = StringIO(raw_data.decode(encoding)) + reader = csv.reader(fp, delimiter=delimiter, **kwargs) + return next(reader) + + +def to_csv_column(data, delimiter=',', encoding='utf-8', **kwargs): + """ Serialize a list to a single-column CSV bytestring. + + Parameters + ---------- + data : list + The list to be serialized. Any elements which are not strings will + be converted to strings by calling ``str()``. + delimiter : str + The CSV delimiter. + encoding : str + The encoding of the bytes + **kwargs + Additional arguments to csv.writer. + + Returns + ------- + raw_data : bytes + The serialized data as a bytestring. + """ + fp = StringIO() + writer = csv.writer(fp, delimiter=delimiter, **kwargs) + for row in data: + writer.writerow([row]) + return fp.getvalue().encode(encoding) + + +def from_csv_column(raw_data, delimiter=',', encoding='utf-8', **kwargs): + """ Deserialize the first column of a CSV bytestring as a list. + + Any columns beyond the first are ignored. + + Parameters + ---------- + raw_data : bytes + The serialized CSV data as a byte string. + delimiter : str + The CSV delimiter. + encoding : str + The encoding of the bytes + **kwargs + Additional arguments to csv.reader. + + Returns + ------- + data : list of str + The data extracted as a list of strings. + """ + fp = StringIO(raw_data.decode(encoding)) + reader = csv.reader(fp, delimiter=delimiter, **kwargs) + return [row[0] for row in reader] + + +text_row_format = DataFormat( + 'text/plain', + partial(to_csv_row, delimiter='\t', lineterminator=os.linesep), + partial(from_csv_row, delimiter='\t'), +) +csv_row_format = DataFormat('text/csv', to_csv_row, from_csv_row) +text_column_format = DataFormat( + 'text/plain', + partial(to_csv_column, delimiter='\t', lineterminator=os.linesep), + partial(from_csv_column, delimiter='\t'), +) +csv_column_format = DataFormat('text/csv', to_csv_column, from_csv_column) + + +# 2D formats + +def to_csv(data, delimiter=',', encoding='utf-8', **kwargs): + """ Serialize a list of lists to a CSV bytestring. + + Parameters + ---------- + data : list of lists + The data to be serialized. Any elements which are not strings will + be converted to strings by calling ``str()``. + delimiter : str + The CSV delimiter. + encoding : str + The encoding of the bytes + **kwargs + Additional arguments to csv.writer. + + Returns + ------- + raw_data : bytes + The serialized data as a bytestring. + """ + fp = StringIO() + writer = csv.writer(fp, delimiter=delimiter, **kwargs) + for row in data: + writer.writerow(row) + return fp.getvalue().encode(encoding) + + +def from_csv(raw_data, delimiter=',', encoding='utf-8', **kwargs): + """ Deserialize a CSV bytestring. + + Parameters + ---------- + raw_data : bytes + The serialized CSV data as a byte string. + delimiter : str + The CSV delimiter. + encoding : str + The encoding of the bytes + **kwargs + Additional arguments to csv.reader. + + Returns + ------- + data : list of list of str + The data extracted as a list of lists of strings. + """ + fp = StringIO(raw_data.decode(encoding)) + reader = csv.reader(fp, delimiter=delimiter, **kwargs) + return list(reader) + + +def to_npy(data): + """ Serialize an array to a bytestring using .npy format. + + Parameters + ---------- + data : array-like + The array to be serialized. + + Returns + ------- + raw_data : bytes + The serialized data as a bytestring. + """ + import numpy as np + + data = np.atleast_2d(data) + fp = BytesIO() + np.save(fp, data, allow_pickle=False) + return fp.getvalue() + + +def from_npy(raw_data): + """ Deserialize a .npy-format bytestring. + + Parameters + ---------- + raw_data : bytes + The serialized CSV data as a byte string. + + Returns + ------- + data : list of list of str + The data extracted as a list of lists of strings. + """ + import numpy as np + + fp = BytesIO(raw_data) + return np.load(fp, allow_pickle=False) + + +table_format = DataFormat( + 'text/plain', + partial(to_csv, delimiter='\t', lineterminator=os.linesep), + partial(from_csv, delimiter='\t'), +) +csv_format = DataFormat('text/csv', to_csv, from_csv) +npy_format = DataFormat('application/x-npy', to_npy, from_npy) diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/api.py python-pyface-7.4.0/pyface/data_view/data_models/api.py --- python-pyface-6.1.2/pyface/data_view/data_models/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,44 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.data_view.data_models`` subpackage. + +Data Accessors +-------------- + +- :class:`~.AbstractDataAccessor` +- :class:`~.AttributeDataAccessor` +- :class:`~.ConstantDataAccessor` +- :class:`~.IndexDataAccessor` +- :class:`~.KeyDataAccessor` + +Data Models +----------- + +- :class:`~.RowTableDataModel` +- :class:`~.ArrayDataModel`. Note that this data model is only available if + ``numpy`` is available in the environment. + +""" +try: + import numpy # noqa: F401 +except ImportError: + pass +else: + del numpy + from .array_data_model import ArrayDataModel # noqa: F401 + +from .data_accessors import ( # noqa: F401 + AbstractDataAccessor, AttributeDataAccessor, ConstantDataAccessor, + IndexDataAccessor, KeyDataAccessor +) +from .row_table_data_model import RowTableDataModel # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/array_data_model.py python-pyface-7.4.0/pyface/data_view/data_models/array_data_model.py --- python-pyface-6.1.2/pyface/data_view/data_models/array_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/array_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,293 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Provides an N-dimensional array data model implementation. + +This module provides a concrete implementation of a data model for an +n-dim numpy array. +""" +from traits.api import Array, HasRequiredTraits, Instance, observe + +from pyface.data_view.abstract_data_model import AbstractDataModel +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.abstract_value_type import AbstractValueType +from pyface.data_view.value_types.api import ( + ConstantValue, IntValue, no_value +) +from pyface.data_view.index_manager import TupleIndexManager + + +class _AtLeastTwoDArray(Array): + """ Trait type that holds an array that at least two dimensional. + """ + + def validate(self, object, name, value): + value = super().validate(object, name, value) + if value.ndim == 0: + value = value.reshape((0, 0)) + elif value.ndim == 1: + value = value.reshape((-1, 1)) + return value + + +class ArrayDataModel(AbstractDataModel, HasRequiredTraits): + """ A data model for an n-dim array. + + This data model presents the data from a multidimensional array + hierarchically by dimension. The underlying array must be at least 2 + dimensional. + + Values are adapted by the ``value_type`` trait. This provides sensible + default values for integer, float and text dtypes, but other dtypes may + need the user of the class to supply an appropriate value type class to + adapt values. + + There are additional value types which provide data sources for row + headers, column headers, and the label of the row header column. The + defaults are likely suitable for most cases, but can be overriden if + required. + """ + + #: The array being displayed. This must have dimension at least 2. + data = _AtLeastTwoDArray() + + #: The index manager that helps convert toolkit indices to data view + #: indices. + index_manager = Instance(TupleIndexManager, args=()) + + #: The value type of the row index column header. + label_header_type = Instance( + AbstractValueType, + factory=ConstantValue, + kw={'text': "Index"}, + allow_none=False, + ) + + #: The value type of the column titles. + column_header_type = Instance( + AbstractValueType, + factory=IntValue, + kw={'is_editable': False}, + allow_none=False, + ) + + #: The value type of the row titles. + row_header_type = Instance( + AbstractValueType, + factory=IntValue, + kw={'is_editable': False}, + allow_none=False, + ) + + #: The type of value being displayed in the data model. + value_type = Instance(AbstractValueType, allow_none=False, required=True) + + # Data structure methods + + def get_column_count(self): + """ How many columns in the data view model. + + The number of columns is the size of the last dimension of the array. + + Returns + ------- + column_count : non-negative int + The number of columns in the data view model, which is the size of + the last dimension of the array. + """ + return self.data.shape[-1] + + def can_have_children(self, row): + """ Whether or not a row can have child rows. + + A row is a leaf row if the length of the index is one less than + the dimension of the array: the final coordinate for the value will + be supplied by the column index. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + can_have_children : bool + Whether or not the row can ever have child rows. + """ + if len(row) < self.data.ndim - 1: + return True + return False + + def get_row_count(self, row): + """ Whether or not the row currently has any child rows. + + The number of rows in a non-leaf row is equal to the size of the + next dimension. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + has_children : bool + Whether or not the row currently has child rows. + """ + if len(row) < self.data.ndim - 1: + return self.data.shape[len(row)] + return 0 + + # Data value methods + + def get_value(self, row, column): + """ Return the Python value for the row and column. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + row_count : non-negative int + The number of child rows that the row has. + """ + if len(row) == 0: + if len(column) == 0: + return None + return column[0] + elif len(column) == 0: + return row[-1] + else: + index = tuple(row + column) + if len(index) != self.data.ndim: + return None + return self.data[index] + + def can_set_value(self, row, column): + """ Whether the value in the indicated row and column can be set. + + This returns False for row and column headers, but True for all + array values. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + # can only set values when we have the full index + index = tuple(row + column) + return len(index) == self.data.ndim + + def set_value(self, row, column, value): + """ Return the Python value for the row and column. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 1. + + Returns + ------- + value : any + The value represented by the given row and column. + """ + if self.can_set_value(row, column): + index = tuple(row + column) + self.data[index] = value + self.values_changed = (row, column, row, column) + else: + raise DataViewSetError() + + def get_value_type(self, row, column): + """ Return the value type of the given row and column. + + This method returns the value of ``column_header_type`` for column + headers, the value of ``row_header_type`` for row headers, the value + of ``label_header_type`` for the top-left corner value, the value of + ``value_type`` for all array values, and ``no_value`` for everything + else. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + value_type : AbstractValueType + The value type of the given row and column. + """ + if len(row) == 0: + if len(column) == 0: + return self.label_header_type + return self.column_header_type + elif len(column) == 0: + return self.row_header_type + elif len(row) < self.data.ndim - 1: + return no_value + else: + return self.value_type + + # data update methods + + @observe('data') + def data_updated(self, event): + """ Handle the array being replaced with a new array. """ + if event.new.shape == event.old.shape: + if self.data.size > 0: + self.values_changed = ( + (0,), (0,), + (event.old.shape[0] - 1,), (event.old.shape[-1] - 1,) + ) + else: + self.structure_changed = True + + @observe('value_type.updated') + def value_type_updated(self, event): + """ Handle the value type being updated. """ + if self.data.size > 0: + self.values_changed = ( + (0,), (0,), (self.data.shape[0] - 1,), (self.data.shape[-1] - 1,) + ) + + @observe('column_header_type.updated') + def column_header_type_updated(self, event): + """ Handle the column header type being updated. """ + if self.data.shape[-1] > 0: + self.values_changed = ((), (0,), (), (self.data.shape[-1] - 1,)) + + @observe('row_header_type.updated') + def value_header_type_updated(self, event): + """ Handle the value header type being updated. """ + if self.data.shape[0] > 0: + self.values_changed = ((0,), (), (self.data.shape[0] - 1,), ()) + + @observe('label_header_type.updated') + def label_header_type_updated(self, event): + """ Handle the label header type being updated. """ + self.values_changed = ((), (), (), ()) + + # default array value + + def _data_default(self): + from numpy import zeros + return zeros(shape=(0, 0)) diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/data_accessors.py python-pyface-7.4.0/pyface/data_view/data_models/data_accessors.py --- python-pyface-6.1.2/pyface/data_view/data_models/data_accessors.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/data_accessors.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,332 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Classes for extracting data from objects + +This module provides helper classes for the row table data model and +related classes for extracting data from an object in a consistent way. +""" + +from abc import abstractmethod +from collections.abc import Hashable, MutableMapping, MutableSequence + +from traits.api import ( + ABCHasStrictTraits, Any, Event, Instance, Int, Str, observe +) +from traits.trait_base import xgetattr, xsetattr + +from pyface.data_view.abstract_data_model import DataViewSetError +from pyface.data_view.abstract_value_type import AbstractValueType +from pyface.data_view.value_types.api import TextValue + + +class AbstractDataAccessor(ABCHasStrictTraits): + """ Accessor that gets and sets data on an object. + """ + + #: A human-readable label for the accessor. + title = Str() + + #: The value type of the title of this accessor, suitable for use in a + #: header. + title_type = Instance(AbstractValueType, factory=TextValue) + + #: The value type of the data accessed. + value_type = Instance(AbstractValueType) + + #: An event fired when accessor is updated update. The payload is + #: a tuple of the accessor info and whether the title or value changed + #: (or both). + updated = Event + + @abstractmethod + def get_value(self, obj): + """ Return a value for the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + + Returns + ------- + value : any + The data value contained in the object. + """ + raise NotImplementedError() + + def can_set_value(self, obj): + """ Return whether the value can be set on the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + return False + + def set_value(self, obj, value): + """ Set the value on the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + value : any + The data value to set. + + Raises + ------- + DataViewSetError + If setting the value fails. + """ + raise DataViewSetError( + "Cannot set {!r} column of {!r}.".format(self.title, obj) + ) + + # trait observers + + @observe('title,title_type.updated') + def _title_updated(self, event): + self.updated = (self, 'title') + + @observe('value_type.updated') + def _value_type_updated(self, event): + self.updated = (self, 'value') + + +class ConstantDataAccessor(AbstractDataAccessor): + """ DataAccessor that returns a constant value. + """ + + #: The value to return. + value = Any() + + def get_value(self, obj): + """ Return the value ignoring the provided object. + + Parameters + ---------- + obj : any + An object. + + Returns + ------- + value : any + The data value contained in this class' value trait. + """ + return self.value + + @observe('value') + def _value_updated(self, event): + self.updated = (self, 'value') + + +class AttributeDataAccessor(AbstractDataAccessor): + """ DataAccessor that presents an extended attribute on an object. + + This is suitable for use with Python objects, including HasTraits + classes. + """ + + #: The extended attribute name of the trait holding the value. + attr = Str() + + def get_value(self, obj): + """ Return the attribute value for the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + + Returns + ------- + value : any + The data value contained in the object's attribute. + """ + return xgetattr(obj, self.attr) + + def can_set_value(self, obj): + """ Return whether the value can be set on the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + return bool(self.attr) + + def set_value(self, obj, value): + if not self.can_set_value(obj): + raise DataViewSetError( + "Attribute is not specified for {!r}".format(self) + ) + xsetattr(obj, self.attr, value) + + @observe('attr') + def _attr_updated(self, event): + self.updated = (self, 'value') + + def _title_default(self): + # create human-friendly version of extended attribute + attr = self.attr.split('.')[-1] + title = attr.replace('_', ' ').title() + return title + + +class IndexDataAccessor(AbstractDataAccessor): + """ DataAccessor that presents an index on a sequence object. + + This is suitable for use with a sequence. + """ + + #: The index in a sequence which holds the value. + index = Int() + + def get_value(self, obj): + """ Return the indexed value for the provided object. + + Parameters + ---------- + obj : sequence + The object that contains the data. + + Returns + ------- + value : any + The data value contained in the object at the index. + """ + return obj[self.index] + + def can_set_value(self, obj): + """ Return whether the value can be set on the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + return isinstance(obj, MutableSequence) and 0 <= self.index < len(obj) + + def set_value(self, obj, value): + """ Set the value on the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + value : any + The data value to set. + + Raises + ------- + DataViewSetError + If setting the value fails. + """ + if not self.can_set_value(obj): + raise DataViewSetError( + "Cannot set {!r} index of {!r}.".format(self.index, obj) + ) + obj[self.index] = value + + @observe('index') + def _index_updated(self, event): + self.updated = (self, 'value') + + def _title_default(self): + title = str(self.index) + return title + + +class KeyDataAccessor(AbstractDataAccessor): + """ DataAccessor that presents an item on a mapping object. + + This is suitable for use with a mapping, such as a dictionary. + """ + + #: The key in the mapping holding the value. + key = Instance(Hashable) + + def get_value(self, obj): + """ Return the key's value for the provided object. + + Parameters + ---------- + obj : mapping + The object that contains the data. + + Returns + ------- + value : any + The data value contained in the given key of the object. + """ + return obj[self.key] + + def can_set_value(self, obj): + """ Set the value on the provided object. + + Parameters + ---------- + obj : mapping + The object that contains the data. + value : any + The data value to set. + + Raises + ------- + DataViewSetError + If setting the value fails. + """ + return isinstance(obj, MutableMapping) + + def set_value(self, obj, value): + """ Set the value on the provided object. + + Parameters + ---------- + obj : any + The object that contains the data. + value : any + The data value to set. + + Raises + ------- + DataViewSetError + If setting the value fails. + """ + if not self.can_set_value(obj): + raise DataViewSetError( + "Cannot set {!r} key of {!r}.".format(self.key, obj) + ) + obj[self.key] = value + + @observe('key') + def _key_updated(self, event): + self.updated = (self, 'value') + + def _title_default(self): + title = str(self.key).title() + return title diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/row_table_data_model.py python-pyface-7.4.0/pyface/data_view/data_models/row_table_data_model.py --- python-pyface-6.1.2/pyface/data_view/data_models/row_table_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/row_table_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,260 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" A row-oriented data model implementation. + +This module provides a concrete implementation of a data model for the +case of non-hierarchical, row-oriented data. +""" +from collections.abc import Sequence + +from traits.api import Instance, List, observe +from traits.observation.api import trait + +from pyface.data_view.abstract_data_model import ( + AbstractDataModel, DataViewSetError +) +from pyface.data_view.index_manager import IntIndexManager +from pyface.data_view.data_models.data_accessors import AbstractDataAccessor + + +class RowTableDataModel(AbstractDataModel): + """ A data model that presents a sequence of objects as rows. + + The data is expected to be a sequence of row objects, each object + providing values for the columns via an AbstractDataAccessor subclass. + Concrete implementations can be found in the data_accessors module that + get data from attributes, indices of sequences, and keys of mappings, + but for more complex situations, custom accessors can be defined. + """ + + #: A sequence of objects to display as rows. + data = Instance(Sequence, allow_none=False) + + #: An object which describes how to map data for the row headers. + row_header_data = Instance(AbstractDataAccessor, allow_none=False) + + #: An object which describes how to map data for each column. + column_data = List(Instance(AbstractDataAccessor, allow_none=False)) + + #: The index manager that helps convert toolkit indices to data view + #: indices. + index_manager = Instance(IntIndexManager, args=(), allow_none=False) + + # Data structure methods + + def get_column_count(self): + """ How many columns in the data view model. + + Returns + ------- + column_count : non-negative int + The number of columns that the data view provides. + """ + return len(self.column_data) + + def can_have_children(self, row): + """ Whether or not a row can have child rows. + + Only the root has children. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + can_have_children : bool + Whether or not the row can ever have child rows. + """ + return len(row) == 0 + + def get_row_count(self, row): + """ How many child rows the row currently has. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + + Returns + ------- + row_count : non-negative int + The number of child rows that the row has. + """ + if len(row) == 0: + return len(self.data) + else: + return 0 + + # Data value methods + + def get_value(self, row, column): + """ Return the Python value for the row and column. + + This uses the row_header_data and column_data accessors to extract + values for the row and column. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + value : any + The value represented by the given row and column. + """ + if len(column) == 0: + column_data = self.row_header_data + else: + column_data = self.column_data[column[0]] + if len(row) == 0: + return column_data.title + obj = self.data[row[0]] + return column_data.get_value(obj) + + def can_set_value(self, row, column): + """ Whether the value in the indicated row and column can be set. + + This uses the row_header_data and column_data accessors to determine + if the value may be changed. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + can_set_value : bool + Whether or not the value can be set. + """ + if len(row) == 0: + return False + if len(column) == 0: + column_data = self.row_header_data + else: + column_data = self.column_data[column[0]] + obj = self.data[row[0]] + return column_data.can_set_value(obj) + + def set_value(self, row, column, value): + """ Set the Python value for the row and column. + + This uses the row_header_data and column_data accessors to set + the value. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + value : any + The new value for the given row and column. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + if len(row) == 0: + raise DataViewSetError("Can't set column titles.") + if len(column) == 0: + column_data = self.row_header_data + else: + column_data = self.column_data[column[0]] + obj = self.data[row[0]] + column_data.set_value(obj, value) + self.values_changed = (row, column, row, column) + + def get_value_type(self, row, column): + """ Return the value type of the given row and column. + + This uses the row_header_data and column_data accessors to get + the value type. + + Parameters + ---------- + row : sequence of int + The indices of the row as a sequence from root to leaf. + column : sequence of int + The indices of the column as a sequence of length 0 or 1. + + Returns + ------- + value_type : AbstractValueType or None + The value type of the given row and column, or None if no value + should be displayed. + """ + if len(column) == 0: + column_data = self.row_header_data + else: + column_data = self.column_data[column[0]] + if len(row) == 0: + return column_data.title_type + return column_data.value_type + + # data update methods + + @observe("data") + def _update_data(self, event): + self.structure_changed = True + + @observe(trait("data", notify=False).list_items(optional=True)) + def _update_data_items(self, event): + if len(event.added) != len(event.removed): + # number of rows has changed + self.structure_changed = True + else: + if isinstance(event.index, int): + start = event.index + stop = min(event.index + len(event.added), len(self.data)) - 1 + else: + start = event.index.start + stop = min(event.index.stop, len(self.data)) - 1 + self.values_changed = ((start,), (), (stop,), ()) + + @observe('row_header_data') + def _update_row_header_data(self, event): + self.values_changed = ((), (), (), ()) + + @observe('row_header_data:updated') + def _update_row_header_data_event(self, event): + if event.new[1] == 'value': + if len(self.data) > 0: + self.values_changed = ((0,), (), (len(self.data) - 1,), ()) + else: + self.values_changed = ((), (), (), ()) + + @observe('column_data.items') + def _update_all_column_data_items(self, event): + self.structure_changed = True + + @observe('column_data:items:updated') + def _update_column_data(self, event): + index = self.column_data.index(event.new[0]) + if event.new[1] == 'value': + if len(self.data) > 0: + self.values_changed = ( + (0,), (index,), (len(self.data) - 1,), (index,) + ) + else: + self.values_changed = ((), (index,), (), (index,)) + + # default data value + + def _data_default(self): + return [] diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/tests/test_api.py python-pyface-7.4.0/pyface/data_view/data_models/tests/test_api.py --- python-pyface-6.1.2/pyface/data_view/data_models/tests/test_api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/tests/test_api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,57 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Test the api module. """ + +import unittest + + +class TestApi(unittest.TestCase): + def test_all_imports_exclude_numpy_dependencies(self): + # These objects do not depend on NumPy + from pyface.data_view.data_models.api import ( # noqa: F401 + AbstractDataAccessor, + AttributeDataAccessor, + ConstantDataAccessor, + IndexDataAccessor, + KeyDataAccessor, + RowTableDataModel, + ) + + def test_import_with_numpy_dependency(self): + # These objects require NumPy. + try: + import numpy # noqa: F401 + except ImportError: + self.skipTest("NumPy not available.") + + from pyface.data_view.data_models.api import ( # noqa: F401 + ArrayDataModel, + ) + + def test_api_items_count(self): + # This test helps developer to keep the above list + # up-to-date. Bump the number when the API content changes. + from pyface.data_view.data_models import api + + expected_count = 6 + try: + import numpy # noqa: F401 + except ImportError: + pass + else: + expected_count += 1 + + items_in_api = { + name + for name in dir(api) + if not name.startswith("_") + } + self.assertEqual(len(items_in_api), expected_count) diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/tests/test_array_data_model.py python-pyface-7.4.0/pyface/data_view/data_models/tests/test_array_data_model.py --- python-pyface-6.1.2/pyface/data_view/data_models/tests/test_array_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/tests/test_array_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,414 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from traits.testing.api import UnittestTools +from traits.testing.optional_dependencies import numpy as np, requires_numpy + +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.abstract_value_type import AbstractValueType +from pyface.data_view.value_types.api import ( + FloatValue, IntValue, no_value +) +# This import results in an error without numpy installed +# see enthought/pyface#742 +if np is not None: + from pyface.data_view.data_models.api import ArrayDataModel + + +@requires_numpy +class TestArrayDataModel(UnittestTools, TestCase): + + def setUp(self): + super().setUp() + self.array = np.arange(30.0).reshape(5, 2, 3) + self.model = ArrayDataModel(data=self.array, value_type=FloatValue()) + self.values_changed_event = None + self.structure_changed_event = None + self.model.observe(self.model_values_changed, 'values_changed') + self.model.observe(self.model_structure_changed, 'structure_changed') + + def tearDown(self): + self.model.observe( + self.model_values_changed, 'values_changed', remove=True) + self.model.observe( + self.model_structure_changed, 'structure_changed', remove=True) + self.values_changed_event = None + self.structure_changed_event = None + super().tearDown() + + def model_values_changed(self, event): + self.values_changed_event = event + + def model_structure_changed(self, event): + self.structure_changed_event = event + + def test_no_data(self): + model = ArrayDataModel(value_type=FloatValue()) + self.assertEqual(model.data.ndim, 2) + self.assertEqual(model.data.shape, (0, 0)) + self.assertEqual(model.data.dtype, float) + self.assertEqual(model.get_column_count(), 0) + self.assertTrue(model.can_have_children(())) + self.assertEqual(model.get_row_count(()), 0) + + def test_data_1d(self): + array = np.arange(30.0) + model = ArrayDataModel(data=array, value_type=FloatValue()) + self.assertEqual(model.data.ndim, 2) + self.assertEqual(model.data.shape, (30, 1)) + + def test_data_list(self): + data = list(range(30)) + model = ArrayDataModel(data=data, value_type=FloatValue()) + self.assertEqual(model.data.ndim, 2) + self.assertEqual(model.data.shape, (30, 1)) + + def test_set_data_1d(self): + with self.assertTraitChanges(self.model, 'structure_changed'): + self.model.data = np.arange(30.0) + self.assertEqual(self.model.data.ndim, 2) + self.assertEqual(self.model.data.shape, (30, 1)) + + def test_set_data_list(self): + with self.assertTraitChanges(self.model, 'structure_changed'): + self.model.data = list(range(30)) + self.assertEqual(self.model.data.ndim, 2) + self.assertEqual(self.model.data.shape, (30, 1)) + + def test_get_column_count(self): + result = self.model.get_column_count() + self.assertEqual(result, 3) + + def test_can_have_children(self): + for row in self.model.iter_rows(): + with self.subTest(row=row): + result = self.model.can_have_children(row) + if len(row) <= 1: + self.assertEqual(result, True) + else: + self.assertEqual(result, False) + + def test_get_row_count(self): + for row in self.model.iter_rows(): + with self.subTest(row=row): + result = self.model.get_row_count(row) + if len(row) == 0: + self.assertEqual(result, 5) + elif len(row) == 1: + self.assertEqual(result, 2) + else: + self.assertEqual(result, 0) + + def test_get_value(self): + for row, column in self.model.iter_items(): + with self.subTest(row=row, column=column): + result = self.model.get_value(row, column) + if len(row) == 0 and len(column) == 0: + self.assertIsNone(result) + elif len(row) == 0: + self.assertEqual(result, column[0]) + elif len(column) == 0: + self.assertEqual(result, row[-1]) + elif len(row) == 1: + self.assertIsNone(result) + else: + self.assertEqual( + result, + self.array[row[0], row[1], column[0]] + ) + + def test_set_value(self): + for row, column in self.model.iter_items(): + with self.subTest(row=row, column=column): + if len(row) == 0 and len(column) == 0: + with self.assertRaises(DataViewSetError): + self.model.set_value(row, column, 0) + elif len(row) == 0: + with self.assertRaises(DataViewSetError): + self.model.set_value(row, column, column[0] + 1) + elif len(column) == 0: + with self.assertRaises(DataViewSetError): + self.model.set_value(row, column, row[-1] + 1) + elif len(row) == 1: + value = 6.0 * row[-1] + 2 * column[0] + with self.assertTraitDoesNotChange( + self.model, "values_changed"): + with self.assertRaises(DataViewSetError): + self.model.set_value(row, column, value) + else: + value = 6.0 * row[-1] + 2 * column[0] + with self.assertTraitChanges(self.model, "values_changed"): + self.model.set_value(row, column, value) + self.assertEqual( + self.array[row[0], row[1], column[0]], + value, + ) + self.assertEqual( + self.values_changed_event.new, + (row, column, row, column) + ) + + def test_get_value_type(self): + for row, column in self.model.iter_items(): + with self.subTest(row=row, column=column): + result = self.model.get_value_type(row, column) + if len(row) == 0 and len(column) == 0: + self.assertIsInstance(result, AbstractValueType) + self.assertIs(result, self.model.label_header_type) + elif len(row) == 0: + self.assertIsInstance(result, AbstractValueType) + self.assertIs(result, self.model.column_header_type) + elif len(column) == 0: + self.assertIsInstance(result, AbstractValueType) + self.assertIs(result, self.model.row_header_type) + elif len(row) == 1: + self.assertIs(result, no_value) + else: + self.assertIsInstance(result, AbstractValueType) + self.assertIs(result, self.model.value_type) + + def test_data_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.data = 2 * self.array + self.assertEqual( + self.values_changed_event.new, + ((0,), (0,), (4,), (2,)) + ) + + def test_data_updated_new_shape(self): + with self.assertTraitChanges(self.model, "structure_changed"): + self.model.data = 2 * self.array.T + self.assertTrue(self.structure_changed_event.new) + + def test_type_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.value_type = IntValue() + self.assertEqual( + self.values_changed_event.new, + ((0,), (0,), (4,), (2,)) + ) + + def test_type_updated_empty(self): + self.model.data = np.empty((0, 0, 0), dtype='int') + with self.assertTraitDoesNotChange(self.model, "values_changed"): + self.model.value_type = IntValue() + + def test_type_attribute_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.value_type.is_editable = False + self.assertEqual( + self.values_changed_event.new, + ((0,), (0,), (4,), (2,)) + ) + + def test_type_attribute_updated_empty(self): + self.model.data = np.empty((0, 0, 0), dtype='int') + with self.assertTraitDoesNotChange(self.model, "values_changed"): + self.model.value_type.is_editable = False + + def test_row_header_type_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.row_header_type = no_value + self.assertEqual( + self.values_changed_event.new, + ((0,), (), (4,), ()) + ) + + def test_row_header_type_updated_empty(self): + self.model.data = np.empty((0, 4, 2), dtype='int') + with self.assertTraitDoesNotChange(self.model, "values_changed"): + self.model.row_header_type = no_value + + def test_row_header_attribute_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.row_header_type.format = str + self.assertEqual( + self.values_changed_event.new, + ((0,), (), (4,), ()) + ) + + def test_row_header_attribute_updated_empty(self): + self.model.data = np.empty((0, 4, 2), dtype='int') + with self.assertTraitDoesNotChange(self.model, "values_changed"): + self.model.row_header_type.format = str + + def test_column_header_type_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.column_header_type = no_value + self.assertEqual( + self.values_changed_event.new, + ((), (0,), (), (2,)) + ) + + def test_column_header_type_updated_empty(self): + self.model.data = np.empty((2, 4, 0), dtype='int') + with self.assertTraitDoesNotChange(self.model, "values_changed"): + self.model.column_header_type = no_value + + def test_column_header_type_attribute_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.column_header_type.format = str + self.assertEqual( + self.values_changed_event.new, + ((), (0,), (), (2,)) + ) + + def test_column_header_attribute_updated_empty(self): + self.model.data = np.empty((2, 4, 0), dtype='int') + with self.assertTraitDoesNotChange(self.model, "values_changed"): + self.model.column_header_type.format = str + + def test_label_header_type_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.label_header_type = no_value + self.assertEqual( + self.values_changed_event.new, + ((), (), (), ()) + ) + + def test_label_header_type_attribute_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.label_header_type.text = "My Table" + self.assertEqual( + self.values_changed_event.new, + ((), (), (), ()) + ) + + def test_is_row_valid(self): + # valid rows are valid + for row in self.model.iter_rows(): + with self.subTest(row=row): + result = self.model.is_row_valid(row) + self.assertTrue(result) + + def test_is_row_valid_big(self): + result = self.model.is_row_valid((5,)) + self.assertFalse(result) + + def test_is_row_valid_long(self): + result = self.model.is_row_valid((1, 1, 1)) + self.assertFalse(result) + + def test_is_column_valid(self): + # valid columns are valid + columns = [()] + [(i,) for i in range(3)] + for column in columns: + with self.subTest(column=column): + result = self.model.is_column_valid(column) + self.assertTrue(result) + + def test_is_column_valid_big(self): + result = self.model.is_column_valid((3,)) + self.assertFalse(result) + + def test_is_column_valid_long(self): + result = self.model.is_column_valid((1, 1)) + self.assertFalse(result) + + def test_iter_rows(self): + result = list(self.model.iter_rows()) + self.assertEqual( + result, + [ + (), + (0,), + (0, 0), + (0, 1), + (1,), + (1, 0), + (1, 1), + (2,), + (2, 0), + (2, 1), + (3,), + (3, 0), + (3, 1), + (4,), + (4, 0), + (4, 1), + ] + ) + + def test_iter_rows_start(self): + result = list(self.model.iter_rows((2,))) + self.assertEqual( + result, + [(2,), (2, 0), (2, 1)] + ) + + def test_iter_rows_leaf(self): + result = list(self.model.iter_rows([2, 0])) + self.assertEqual(result, [(2, 0)]) + + def test_iter_items(self): + result = list(self.model.iter_items()) + self.assertEqual( + result, + [ + ((), ()), + ((), (0,)), ((), (1,)), ((), (2,)), + ((0,), ()), + ((0,), (0,)), ((0,), (1,)), ((0,), (2,)), + ((0, 0), ()), + ((0, 0), (0,)), ((0, 0), (1,)), ((0, 0), (2,)), + ((0, 1), ()), + ((0, 1), (0,)), ((0, 1), (1,)), ((0, 1), (2,)), + ((1,), ()), + ((1,), (0,)), ((1,), (1,)), ((1,), (2,)), + ((1, 0), ()), + ((1, 0), (0,)), ((1, 0), (1,)), ((1, 0), (2,)), + ((1, 1), ()), + ((1, 1), (0,)), ((1, 1), (1,)), ((1, 1), (2,)), + ((2,), ()), + ((2,), (0,)), ((2,), (1,)), ((2,), (2,)), + ((2, 0), ()), + ((2, 0), (0,)), ((2, 0), (1,)), ((2, 0), (2,)), + ((2, 1), ()), + ((2, 1), (0,)), ((2, 1), (1,)), ((2, 1), (2,)), + ((3,), ()), + ((3,), (0,)), ((3,), (1,)), ((3,), (2,)), + ((3, 0), ()), + ((3, 0), (0,)), ((3, 0), (1,)), ((3, 0), (2,)), + ((3, 1), ()), + ((3, 1), (0,)), ((3, 1), (1,)), ((3, 1), (2,)), + ((4,), ()), + ((4,), (0,)), ((4,), (1,)), ((4,), (2,)), + ((4, 0), ()), + ((4, 0), (0,)), ((4, 0), (1,)), ((4, 0), (2,)), + ((4, 1), ()), + ((4, 1), (0,)), ((4, 1), (1,)), ((4, 1), (2,)), + ] + ) + + def test_iter_items_start(self): + result = list(self.model.iter_items((2,))) + self.assertEqual( + result, + [ + ((2,), ()), + ((2,), (0,)), ((2,), (1,)), ((2,), (2,)), + ((2, 0), ()), + ((2, 0), (0,)), ((2, 0), (1,)), ((2, 0), (2,)), + ((2, 1), ()), + ((2, 1), (0,)), ((2, 1), (1,)), ((2, 1), (2,)), + ] + ) + + def test_iter_items_leaf(self): + result = list(self.model.iter_items((2, 0))) + self.assertEqual( + result, + [ + ((2, 0), ()), + ((2, 0), (0,)), ((2, 0), (1,)), ((2, 0), (2,)), + ] + ) diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/tests/test_data_accessors.py python-pyface-7.4.0/pyface/data_view/data_models/tests/test_data_accessors.py --- python-pyface-6.1.2/pyface/data_view/data_models/tests/test_data_accessors.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/tests/test_data_accessors.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,424 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from types import MappingProxyType +import unittest + +from traits.api import TraitError +from traits.testing.api import UnittestTools + +from pyface.data_view.abstract_data_model import DataViewSetError +from pyface.data_view.value_types.api import TextValue +from pyface.data_view.data_models.data_accessors import ( + AttributeDataAccessor, + ConstantDataAccessor, + IndexDataAccessor, + KeyDataAccessor, +) + + +class AttributeDummy: + + def __init__(self, attr_value): + self.attr_value = attr_value + + +class DataAccessorMixin(UnittestTools): + + def accessor_observer(self, event): + self.accessor_event = event + + def test_title_type_changed(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.title_type = TextValue() + + self.assertEqual(self.accessor_event.new, (accessor, 'title')) + + def test_title_type_updated(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.title_type.updated = True + + self.assertEqual(self.accessor_event.new, (accessor, 'title')) + + def test_value_type_changed(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.value_type = TextValue() + + self.assertEqual(self.accessor_event.new, (accessor, 'value')) + + def test_value_type_updated(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.value_type.updated = True + + self.assertEqual(self.accessor_event.new, (accessor, 'value')) + + +class TestConstantDataAccessor(unittest.TestCase, DataAccessorMixin): + + def create_accessor(self): + return ConstantDataAccessor( + title='Test', + value='test', + value_type=TextValue(), + ) + + def test_defaults(self): + accessor = ConstantDataAccessor() + + self.assertEqual(accessor.value, None) + self.assertIsNone(accessor.value_type) + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, '') + + def test_typical_defaults(self): + accessor = self.create_accessor() + + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, 'Test') + + def test_get_value(self): + accessor = self.create_accessor() + obj = object() + + value = accessor.get_value(obj) + + self.assertEqual(value, 'test') + + def test_can_set_value(self): + accessor = self.create_accessor() + obj = object() + + can_set = accessor.can_set_value(obj) + + self.assertFalse(can_set) + + def test_set_value_error(self): + accessor = self.create_accessor() + obj = object() + + with self.assertRaises(DataViewSetError): + accessor.set_value(obj, 'new_value') + + def test_value_updated(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.value = 'other_value' + + self.assertEqual(self.accessor_event.new, (accessor, 'value')) + + +class TestAttributeDataAccessor(unittest.TestCase, DataAccessorMixin): + + def create_accessor(self): + return AttributeDataAccessor( + attr='attr_value', + value_type=TextValue(), + ) + + def test_defaults(self): + accessor = AttributeDataAccessor() + + self.assertEqual(accessor.attr, '') + self.assertIsNone(accessor.value_type) + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, '') + + def test_typical_defaults(self): + accessor = self.create_accessor() + + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, 'Attr Value') + + def test_get_value(self): + accessor = self.create_accessor() + obj = AttributeDummy('test_value') + + value = accessor.get_value(obj) + + self.assertEqual(value, 'test_value') + + def test_get_value_extended(self): + accessor = self.create_accessor() + accessor.attr = 'attr_value.attr_value' + obj = AttributeDummy(AttributeDummy('test_value')) + + value = accessor.get_value(obj) + + self.assertEqual(value, 'test_value') + + def test_get_value_missing(self): + accessor = self.create_accessor() + accessor.attr = '' + obj = AttributeDummy('test_value') + + with self.assertRaises(AttributeError): + accessor.get_value(obj) + + def test_get_value_error(self): + accessor = self.create_accessor() + accessor.attr = 'other_attr' + obj = AttributeDummy('test_value') + + with self.assertRaises(AttributeError): + accessor.get_value(obj) + + def test_can_set_value(self): + accessor = self.create_accessor() + obj = AttributeDummy('test_value') + + can_set = accessor.can_set_value(obj) + + self.assertTrue(can_set) + + def test_can_set_value_false(self): + accessor = AttributeDataAccessor() + obj = AttributeDummy('test_value') + + can_set = accessor.can_set_value(obj) + + self.assertFalse(can_set) + + def test_set_value(self): + accessor = self.create_accessor() + obj = AttributeDummy('test_value') + + accessor.set_value(obj, 'new_value') + + self.assertEqual(obj.attr_value, 'new_value') + + def test_set_value_extended(self): + accessor = self.create_accessor() + accessor.attr = 'attr_value.attr_value' + obj = AttributeDummy(AttributeDummy('test_value')) + + accessor.set_value(obj, 'new_value') + + self.assertEqual(obj.attr_value.attr_value, 'new_value') + + def test_set_value_error(self): + accessor = AttributeDataAccessor() + obj = AttributeDummy('test_value') + + with self.assertRaises(DataViewSetError): + accessor.set_value(obj, 'new_value') + + def test_attr_updated(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.attr = 'other_attr' + + self.assertEqual(self.accessor_event.new, (accessor, 'value')) + + +class TestIndexDataAccessor(unittest.TestCase, DataAccessorMixin): + + def create_accessor(self): + return IndexDataAccessor( + index=1, + value_type=TextValue(), + ) + + def test_defaults(self): + accessor = IndexDataAccessor() + + self.assertEqual(accessor.index, 0) + self.assertIsNone(accessor.value_type) + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, '0') + + def test_typical_defaults(self): + accessor = self.create_accessor() + + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, '1') + + def test_get_value(self): + accessor = self.create_accessor() + obj = ['zero', 'one', 'two', 'three'] + + value = accessor.get_value(obj) + + self.assertEqual(value, 'one') + + def test_get_value_out_of_bounds(self): + accessor = self.create_accessor() + accessor.index = 10 + obj = ['zero', 'one', 'two', 'three'] + + with self.assertRaises(IndexError): + accessor.get_value(obj) + + def test_can_set_value(self): + accessor = self.create_accessor() + obj = ['zero', 'one', 'two', 'three'] + + can_set = accessor.can_set_value(obj) + + self.assertTrue(can_set) + + def test_can_set_value_false(self): + accessor = self.create_accessor() + obj = ['zero'] + + can_set = accessor.can_set_value(obj) + + self.assertFalse(can_set) + + def test_can_set_value_immuatble(self): + accessor = self.create_accessor() + obj = ('zero', 'one', 'two', 'three') + + can_set = accessor.can_set_value(obj) + + self.assertFalse(can_set) + + def test_set_value(self): + accessor = self.create_accessor() + obj = ['zero', 'one', 'two', 'three'] + + accessor.set_value(obj, 'new_value') + + self.assertEqual(obj[1], 'new_value') + + def test_set_value_error(self): + accessor = self.create_accessor() + obj = ['zero'] + + with self.assertRaises(DataViewSetError): + accessor.set_value(obj, 'new_value') + + def test_index_updated(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.index = 2 + + self.assertEqual(self.accessor_event.new, (accessor, 'value')) + + +class TestKeyDataAccessor(unittest.TestCase, DataAccessorMixin): + + def create_accessor(self): + return KeyDataAccessor( + key='one', + value_type=TextValue(), + ) + + def test_defaults(self): + accessor = KeyDataAccessor() + + self.assertIsNone(accessor.key) + self.assertIsNone(accessor.value_type) + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, 'None') + + def test_typical_defaults(self): + accessor = self.create_accessor() + + self.assertIsInstance(accessor.title_type, TextValue) + self.assertEqual(accessor.title, 'One') + + def test_unhashable_error(self): + accessor = self.create_accessor() + with self.assertRaises(TraitError): + accessor.key = [] + + def test_get_value(self): + accessor = self.create_accessor() + obj = {'one': 'a', 'two': 'b'} + + value = accessor.get_value(obj) + + self.assertEqual(value, 'a') + + def test_get_value_missing(self): + accessor = self.create_accessor() + accessor.key = 'three' + obj = {'one': 'a', 'two': 'b'} + + with self.assertRaises(KeyError): + accessor.get_value(obj) + + def test_can_set_value(self): + accessor = self.create_accessor() + obj = {'one': 'a', 'two': 'b'} + + can_set = accessor.can_set_value(obj) + + self.assertTrue(can_set) + + def test_can_set_value_new(self): + accessor = self.create_accessor() + accessor.key = 'three' + obj = {'one': 'a', 'two': 'b'} + + can_set = accessor.can_set_value(obj) + + self.assertTrue(can_set) + + def test_can_set_value_immutable(self): + accessor = self.create_accessor() + # TODO: eventually replace with frozenmap in 3.9 + obj = MappingProxyType({'one': 'a', 'two': 'b'}) + + can_set = accessor.can_set_value(obj) + + self.assertFalse(can_set) + + def test_set_value(self): + accessor = self.create_accessor() + obj = {'one': 'a', 'two': 'b'} + + accessor.set_value(obj, 'new_value') + + self.assertEqual(obj['one'], 'new_value') + + def test_set_value_new(self): + accessor = self.create_accessor() + accessor.key = 'three' + obj = {'one': 'a', 'two': 'b'} + + accessor.set_value(obj, 'new_value') + + self.assertEqual(obj['three'], 'new_value') + + def test_set_value_error(self): + accessor = KeyDataAccessor() + accessor.key = 'one' + obj = MappingProxyType({'one': 'a', 'two': 'b'}) + + with self.assertRaises(DataViewSetError): + accessor.set_value(obj, 'new_value') + + def test_key_updated(self): + accessor = self.create_accessor() + accessor.observe(self.accessor_observer, 'updated') + + with self.assertTraitChanges(accessor, 'updated', count=1): + accessor.key = 2 + + self.assertEqual(self.accessor_event.new, (accessor, 'value')) diff -Nru python-pyface-6.1.2/pyface/data_view/data_models/tests/test_row_table_data_model.py python-pyface-7.4.0/pyface/data_view/data_models/tests/test_row_table_data_model.py --- python-pyface-6.1.2/pyface/data_view/data_models/tests/test_row_table_data_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_models/tests/test_row_table_data_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,426 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + +from traits.trait_list_object import TraitList +from traits.testing.api import UnittestTools + +from pyface.data_view.abstract_data_model import DataViewSetError +from pyface.data_view.abstract_value_type import AbstractValueType +from pyface.data_view.value_types.api import IntValue, TextValue +from pyface.data_view.data_models.data_accessors import ( + AttributeDataAccessor, IndexDataAccessor, KeyDataAccessor +) +from pyface.data_view.data_models.row_table_data_model import RowTableDataModel + + +class DataItem: + + def __init__(self, a, b, c): + self.a = a + self.b = b + self.c = c + + +class TestRowTableDataModel(UnittestTools, unittest.TestCase): + + def setUp(self): + super().setUp() + self.data = [ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ] + self.model = RowTableDataModel( + data=self.data, + row_header_data=AttributeDataAccessor( + attr='a', + value_type=IntValue(), + ), + column_data=[ + AttributeDataAccessor( + attr='b', + value_type=IntValue(), + ), + AttributeDataAccessor( + attr='c', + value_type=TextValue(), + ) + ] + ) + self.values_changed_event = None + self.structure_changed_event = None + self.model.observe(self.model_values_changed, 'values_changed') + self.model.observe(self.model_structure_changed, 'structure_changed') + + def tearDown(self): + self.model.observe( + self.model_values_changed, 'values_changed', remove=True) + self.model.observe( + self.model_structure_changed, 'structure_changed', remove=True) + self.values_changed_event = None + self.structure_changed_event = None + super().tearDown() + + def model_values_changed(self, event): + self.values_changed_event = event + + def model_structure_changed(self, event): + self.structure_changed_event = event + + def test_no_data(self): + model = RowTableDataModel() + self.assertEqual(model.get_column_count(), 0) + self.assertTrue(model.can_have_children(())) + self.assertEqual(model.get_row_count(()), 0) + + def test_get_column_count(self): + result = self.model.get_column_count() + self.assertEqual(result, 2) + + def test_can_have_children(self): + for row in self.model.iter_rows(): + with self.subTest(row=row): + result = self.model.can_have_children(row) + if len(row) == 0: + self.assertEqual(result, True) + else: + self.assertEqual(result, False) + + def test_get_row_count(self): + for row in self.model.iter_rows(): + with self.subTest(row=row): + result = self.model.get_row_count(row) + if len(row) == 0: + self.assertEqual(result, 10) + else: + self.assertEqual(result, 0) + + def test_get_value(self): + for row, column in self.model.iter_items(): + with self.subTest(row=row, column=column): + result = self.model.get_value(row, column) + if len(row) == 0 and len(column) == 0: + self.assertEqual(result, 'A') + elif len(row) == 0: + attr = self.model.column_data[column[0]].attr + self.assertEqual(result, attr.title()) + elif len(column) == 0: + self.assertEqual(result, row[0]) + else: + attr = self.model.column_data[column[0]].attr + self.assertEqual( + result, + getattr(self.data[row[0]], attr) + ) + + def test_set_value(self): + for row, column in self.model.iter_items(): + with self.subTest(row=row, column=column): + if len(row) == 0 and len(column) == 0: + with self.assertRaises(DataViewSetError): + self.model.set_value(row, column, 0) + elif len(row) == 0: + with self.assertRaises(DataViewSetError): + self.model.set_value(row, column, 0) + elif len(column) == 0: + value = 6.0 * row[0] + with self.assertTraitChanges(self.model, "values_changed"): + self.model.set_value(row, column, value) + self.assertEqual(self.data[row[0]].a, value) + self.assertEqual( + self.values_changed_event.new, + (row, column, row, column) + ) + else: + value = 6.0 * row[-1] + 2 * column[0] + with self.assertTraitChanges(self.model, "values_changed"): + self.model.set_value(row, column, value) + attr = self.model.column_data[column[0]].attr + self.assertEqual( + getattr(self.data[row[0]], attr), + value, + ) + self.assertEqual( + self.values_changed_event.new, + (row, column, row, column) + ) + + def test_get_value_type(self): + for row, column in self.model.iter_items(): + with self.subTest(row=row, column=column): + result = self.model.get_value_type(row, column) + if len(row) == 0 and len(column) == 0: + self.assertIsInstance(result, AbstractValueType) + self.assertIs( + result, + self.model.row_header_data.title_type, + ) + elif len(row) == 0: + self.assertIsInstance(result, AbstractValueType) + self.assertIs( + result, + self.model.column_data[column[0]].title_type, + ) + elif len(column) == 0: + self.assertIsInstance(result, AbstractValueType) + self.assertIs( + result, + self.model.row_header_data.value_type, + ) + else: + self.assertIsInstance(result, AbstractValueType) + self.assertIs( + result, + self.model.column_data[column[0]].value_type, + ) + + def test_data_updated(self): + with self.assertTraitChanges(self.model, "structure_changed"): + self.model.data = [ + DataItem(a=i+1, b=20*(i+1), c=str(i)) for i in range(10) + ] + self.assertTrue(self.structure_changed_event.new) + + def test_data_items_updated_item_added(self): + self.model.data = TraitList([ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ]) + with self.assertTraitChanges(self.model, "structure_changed"): + self.model.data += [DataItem(a=100, b=200, c="a string")] + self.assertTrue(self.structure_changed_event.new) + + def test_data_items_updated_item_replaced(self): + self.model.data = TraitList([ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ]) + with self.assertTraitChanges(self.model, "values_changed"): + self.model.data[1] = DataItem(a=100, b=200, c="a string") + self.assertEqual(self.values_changed_event.new, ((1,), (), (1,), ())) + + def test_data_items_updated_item_replaced_negative(self): + self.model.data = TraitList([ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ]) + with self.assertTraitChanges(self.model, "values_changed"): + self.model.data[-2] = DataItem(a=100, b=200, c="a string") + self.assertEqual(self.values_changed_event.new, ((8,), (), (8,), ())) + + def test_data_items_updated_items_replaced(self): + self.model.data = TraitList([ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ]) + with self.assertTraitChanges(self.model, "values_changed"): + self.model.data[1:3] = [ + DataItem(a=100, b=200, c="a string"), + DataItem(a=200, b=300, c="another string"), + ] + self.assertEqual(self.values_changed_event.new, ((1,), (), (2,), ())) + + def test_data_items_updated_slice_replaced(self): + self.model.data = TraitList([ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ]) + with self.assertTraitChanges(self.model, "values_changed"): + self.model.data[1:4:2] = [ + DataItem(a=100, b=200, c="a string"), + DataItem(a=200, b=300, c="another string"), + ] + self.assertEqual(self.values_changed_event.new, ((1,), (), (3,), ())) + + def test_data_items_updated_reverse_slice_replaced(self): + self.model.data = TraitList([ + DataItem(a=i, b=10*i, c=str(i)) for i in range(10) + ]) + with self.assertTraitChanges(self.model, "values_changed"): + self.model.data[3:1:-1] = [ + DataItem(a=100, b=200, c="a string"), + DataItem(a=200, b=300, c="another string"), + ] + self.assertEqual(self.values_changed_event.new, ((2,), (), (3,), ())) + + def test_row_header_data_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.row_header_data = AttributeDataAccessor(attr='b') + self.assertEqual( + self.values_changed_event.new, + ((), (), (), ()) + ) + + def test_row_header_data_values_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.row_header_data.updated = (self.model.row_header_data, 'value') + self.assertEqual( + self.values_changed_event.new, + ((0,), (), (9,), ()) + ) + + def test_row_header_data_title_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.row_header_data.updated = (self.model.row_header_data, 'title') + self.assertEqual( + self.values_changed_event.new, + ((), (), (), ()) + ) + + def test_no_data_row_header_data_update(self): + model = RowTableDataModel( + row_header_data=AttributeDataAccessor( + attr='a', + value_type=IntValue(), + ), + column_data=[ + AttributeDataAccessor( + attr='b', + value_type=IntValue(), + ), + AttributeDataAccessor( + attr='c', + value_type=TextValue(), + ) + ] + ) + + # check that updating accessors is safe with empty data + with self.assertTraitDoesNotChange(model, 'values_changed'): + model.row_header_data.attr = 'b' + + def test_column_data_updated(self): + with self.assertTraitChanges(self.model, "structure_changed"): + self.model.column_data = [ + AttributeDataAccessor( + attr='c', + value_type=TextValue(), + ), + AttributeDataAccessor( + attr='b', + value_type=IntValue(), + ), + ] + self.assertTrue(self.structure_changed_event.new) + + def test_column_data_items_updated(self): + with self.assertTraitChanges(self.model, "structure_changed"): + self.model.column_data.pop() + self.assertTrue(self.structure_changed_event.new) + + def test_column_data_value_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.column_data[0].updated = (self.model.column_data[0], 'value') + self.assertEqual( + self.values_changed_event.new, + ((0,), (0,), (9,), (0,)) + ) + + def test_no_data_column_data_update(self): + model = RowTableDataModel( + row_header_data=AttributeDataAccessor( + attr='a', + value_type=IntValue(), + ), + column_data=[ + AttributeDataAccessor( + attr='b', + value_type=IntValue(), + ), + AttributeDataAccessor( + attr='c', + value_type=TextValue(), + ) + ] + ) + + with self.assertTraitDoesNotChange(model, 'values_changed'): + model.column_data[0].attr = 'a' + + def test_column_data_title_updated(self): + with self.assertTraitChanges(self.model, "values_changed"): + self.model.column_data[0].updated = (self.model.column_data[0], 'title') + self.assertEqual( + self.values_changed_event.new, + ((), (0,), (), (0,)) + ) + + def test_list_tuple_data(self): + data = [ + (i, 10*i, str(i)) for i in range(10) + ] + model = RowTableDataModel( + data=data, + row_header_data=IndexDataAccessor( + index=0, + value_type=IntValue(), + ), + column_data=[ + IndexDataAccessor( + index=1, + value_type=IntValue(), + ), + IndexDataAccessor( + index=2, + value_type=TextValue(), + ) + ] + ) + + for row, column in model.iter_items(): + with self.subTest(row=row, column=column): + result = model.get_value(row, column) + if len(row) == 0 and len(column) == 0: + self.assertEqual(result, '0') + elif len(row) == 0: + index = model.column_data[column[0]].index + self.assertEqual(result, str(index)) + elif len(column) == 0: + self.assertEqual(result, row[0]) + else: + index = model.column_data[column[0]].index + self.assertEqual( + result, + data[row[0]][index] + ) + + def test_list_dict_data(self): + data = [ + {'a': i, 'b': 10*i, 'c': str(i)} for i in range(10) + ] + model = RowTableDataModel( + data=data, + row_header_data=KeyDataAccessor( + key='a', + value_type=IntValue(), + ), + column_data=[ + KeyDataAccessor( + key='b', + value_type=IntValue(), + ), + KeyDataAccessor( + key='c', + value_type=TextValue(), + ) + ] + ) + + for row, column in model.iter_items(): + with self.subTest(row=row, column=column): + result = model.get_value(row, column) + if len(row) == 0 and len(column) == 0: + self.assertEqual(result, 'A') + elif len(row) == 0: + key = model.column_data[column[0]].key + self.assertEqual(result, str(key).title()) + elif len(column) == 0: + self.assertEqual(result, data[row[0]]['a']) + else: + key = model.column_data[column[0]].key + self.assertEqual( + result, + data[row[0]][key] + ) diff -Nru python-pyface-6.1.2/pyface/data_view/data_view_errors.py python-pyface-7.4.0/pyface/data_view/data_view_errors.py --- python-pyface-6.1.2/pyface/data_view/data_view_errors.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_view_errors.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,27 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Provide an Exception classes for the the DataView code. +""" + + +class DataViewError(ValueError): + """ The base exception class for DataView errors. """ + pass + + +class DataViewGetError(DataViewError): + """ An exception raised when getting a value fails. """ + pass + + +class DataViewSetError(DataViewError): + """ An exception raised when setting a value fails. """ + pass diff -Nru python-pyface-6.1.2/pyface/data_view/data_view_widget.py python-pyface-7.4.0/pyface/data_view/data_view_widget.py --- python-pyface-6.1.2/pyface/data_view/data_view_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_view_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,13 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.toolkit import toolkit_object + +DataViewWidget = toolkit_object('data_view.data_view_widget:DataViewWidget') diff -Nru python-pyface-6.1.2/pyface/data_view/data_wrapper.py python-pyface-7.4.0/pyface/data_view/data_wrapper.py --- python-pyface-6.1.2/pyface/data_view/data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,13 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.toolkit import toolkit_object + +DataWrapper = toolkit_object('data_view.data_wrapper:DataWrapper') diff -Nru python-pyface-6.1.2/pyface/data_view/exporters/api.py python-pyface-7.4.0/pyface/data_view/exporters/api.py --- python-pyface-6.1.2/pyface/data_view/exporters/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/exporters/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,24 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.data_view.exporters`` subpackage. + +Exporters +--------- + +- :class:`~.ItemExporter` +- :class:`~.RowExporter` + +""" + +from .item_exporter import ItemExporter +from .row_exporter import RowExporter diff -Nru python-pyface-6.1.2/pyface/data_view/exporters/item_exporter.py python-pyface-7.4.0/pyface/data_view/exporters/item_exporter.py --- python-pyface-6.1.2/pyface/data_view/exporters/item_exporter.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/exporters/item_exporter.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,58 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.data_view.abstract_data_exporter import AbstractDataExporter +from pyface.data_view.data_view_errors import DataViewGetError + + +class ItemExporter(AbstractDataExporter): + """ Export a single item from a data view. + + This is suitable for drag and drop or copying of the content of a single + item in a data view. If passed an multiple items it will fail by + raising ``DataViewGetError``; drag and drop support will then ignore this + as an exporter to use. + """ + + def add_data(self, data_wrapper, model, indices): + """ Add data to the data wrapper from the model and indices. + + Parameters + ---------- + data_wrapper : DataWrapper instance + The data wrapper that will be used to export data. + model : AbstractDataModel instance + The data model holding the data. + indices : list of (row, column) index pairs + The indices where the data is to be stored. + """ + # only export single item values + if len(indices) == 1: + super().add_data(data_wrapper, model, indices) + + def get_data(self, model, indices): + """ Get the data to be exported from the model and indices. + + Parameters + ---------- + model : AbstractDataModel instance + The data model holding the data. + indices : list of (row, column) index pairs + The indices where the data is to be stored. + + Returns + ------- + data : any + The data, of a type that can be serialized by the format. + """ + if len(indices) != 1: + raise DataViewGetError("ItemExporter can only export single values") + row, column = indices[0] + return self.get_value(model, row, column) diff -Nru python-pyface-6.1.2/pyface/data_view/exporters/row_exporter.py python-pyface-7.4.0/pyface/data_view/exporters/row_exporter.py --- python-pyface-6.1.2/pyface/data_view/exporters/row_exporter.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/exporters/row_exporter.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,69 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Bool + +from pyface.data_view.abstract_data_exporter import AbstractDataExporter + + +class RowExporter(AbstractDataExporter): + """ Export a collection of rows from a data view as a list of lists. + + This is suitable for drag and drop or copying of the content of multiple + selected rows. + + This exports a list of data associated with each row in the + indices. Each row item is itself a list of values extracted + from the model. + + If the format mimetype is a text mimetype, it will use the + ``get_text()`` method to extract the values, otherwise it will + try to use the editor value if it exists, and failing that + the raw value returned from the model. + """ + + #: Whether or not to include row headers. + row_headers = Bool() + + #: Whether or not to include column headers. + column_headers = Bool() + + def get_data(self, model, indices): + """ Get the data to be exported from the model and indices. + + This exports a list of data associated with each row in the + indices. Each row item is itself a list of values extracted + from the model. + + Parameters + ---------- + model : AbstractDataModel instance + The data model holding the data. + indices : list of (row, column) index pairs + The indices where the data is to be stored. + + Returns + ------- + data : any + The data, of a type that can be serialized by the format. + """ + rows = sorted({row for row, column in indices}) + n_columns = model.get_column_count() + columns = [(column,) for column in range(n_columns)] + + if self.column_headers: + rows = [()] + rows + if self.row_headers: + columns = [()] + columns + + return [ + [self.get_value(model, row, column,) for column in columns] + for row in rows + ] diff -Nru python-pyface-6.1.2/pyface/data_view/exporters/tests/test_api.py python-pyface-7.4.0/pyface/data_view/exporters/tests/test_api.py --- python-pyface-6.1.2/pyface/data_view/exporters/tests/test_api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/exporters/tests/test_api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,32 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Test the api module. """ + +import unittest + + +class TestApi(unittest.TestCase): + def test_all_imports(self): + from pyface.data_view.exporters.api import ( # noqa: F401 + ItemExporter, + RowExporter, + ) + + def test_api_items_count(self): + # This test helps developer to keep the above list + # up-to-date. Bump the number when the API content changes. + from pyface.data_view.exporters import api + items_in_api = { + name + for name in dir(api) + if not name.startswith("_") + } + self.assertEqual(len(items_in_api), 2) diff -Nru python-pyface-6.1.2/pyface/data_view/exporters/tests/test_item_exporter.py python-pyface-7.4.0/pyface/data_view/exporters/tests/test_item_exporter.py --- python-pyface-6.1.2/pyface/data_view/exporters/tests/test_item_exporter.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/exporters/tests/test_item_exporter.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,69 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.exporters.item_exporter import ItemExporter +from pyface.data_view.data_wrapper import DataWrapper +from pyface.data_view.i_data_wrapper import DataFormat + + +trivial_format = DataFormat( + 'null/null', + lambda x: str(x).encode('utf-8'), + lambda x: x, +) + + +class TestItemExporter(TestCase): + + def setUp(self): + self.value_type = Mock() + self.value_type.has_text = Mock(return_value=True) + self.value_type.get_text = Mock(return_value='text') + self.value_type.has_editor_value = Mock(return_value=True) + self.value_type.get_editor_value = Mock(return_value=1) + + self.model = Mock() + self.model.get_value = Mock(return_value=0.0) + self.model.get_value_type = Mock(return_value=self.value_type) + + def test_get_data_(self): + exporter = ItemExporter(format=trivial_format) + + result = exporter.get_data(self.model, [((0,), (0,))]) + + self.assertEqual(result, 1) + + def test_add_data_(self): + exporter = ItemExporter(format=trivial_format) + data_wrapper = DataWrapper() + + exporter.add_data(data_wrapper, self.model, [((0,), (0,))]) + + self.assertTrue(data_wrapper.has_format(trivial_format)) + self.assertEqual(data_wrapper.get_mimedata('null/null'), b'1') + + def test_add_data_length_0(self): + exporter = ItemExporter(format=trivial_format) + data_wrapper = DataWrapper() + + exporter.add_data(data_wrapper, self.model, []) + + self.assertFalse(data_wrapper.has_format(trivial_format)) + + def test_add_data_length_2(self): + exporter = ItemExporter(format=trivial_format) + data_wrapper = DataWrapper() + + exporter.add_data(data_wrapper, self.model, [((), ()), ((0,), (0,))]) + + self.assertFalse(data_wrapper.has_format(trivial_format)) diff -Nru python-pyface-6.1.2/pyface/data_view/exporters/tests/test_row_exporter.py python-pyface-7.4.0/pyface/data_view/exporters/tests/test_row_exporter.py --- python-pyface-6.1.2/pyface/data_view/exporters/tests/test_row_exporter.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/exporters/tests/test_row_exporter.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,74 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from itertools import count +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.exporters.row_exporter import RowExporter +from pyface.data_view.i_data_wrapper import DataFormat + + +trivial_format = DataFormat( + 'null/null', + lambda x: str(x).encode('utf-8'), + lambda x: x, +) + + +class TestRowExporter(TestCase): + + def setUp(self): + self.value_type = Mock() + self.value_type.has_text = Mock(return_value=False) + self.value_type.has_editor_value = Mock(return_value=True) + self.value_type.get_editor_value = Mock( + return_value=1, + side_effect=count(), + ) + + self.model = Mock() + self.model.get_column_count = Mock(return_value=3) + self.model.get_value = Mock(return_value=0) + self.model.get_value_type = Mock(return_value=self.value_type) + + def test_get_data(self): + exporter = RowExporter(format=trivial_format) + + result = exporter.get_data(self.model, [((0,), (0,)), ((1,), ())]) + + self.assertEqual(result, [[0, 1, 2], [3, 4, 5]]) + + def test_get_data_deduplicate(self): + exporter = RowExporter(format=trivial_format) + + result = exporter.get_data( + self.model, + [ + ((0,), (0,)), + ((0,), (2,)), + ((1,), ()), + ]) + + self.assertEqual(result, [[0, 1, 2], [3, 4, 5]]) + + def test_get_data_row_headers(self): + exporter = RowExporter(format=trivial_format, row_headers=True) + + result = exporter.get_data(self.model, [((0,), (0,)), ((1,), ())]) + + self.assertEqual(result, [[0, 1, 2, 3], [4, 5, 6, 7]]) + + def test_get_data_column_headers(self): + exporter = RowExporter(format=trivial_format, column_headers=True) + + result = exporter.get_data(self.model, [((0,), (0,)), ((1,), ())]) + + self.assertEqual(result, [[0, 1, 2], [3, 4, 5], [6, 7, 8]]) diff -Nru python-pyface-6.1.2/pyface/data_view/i_data_view_widget.py python-pyface-7.4.0/pyface/data_view/i_data_view_widget.py --- python-pyface-6.1.2/pyface/data_view/i_data_view_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/i_data_view_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,371 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from contextlib import contextmanager +import logging + +from traits.api import ( + Bool, Enum, HasTraits, Instance, List, Property, + TraitError, Tuple, cached_property, +) + +from pyface.data_view.abstract_data_model import AbstractDataModel +from pyface.data_view.abstract_data_exporter import AbstractDataExporter +from pyface.i_drop_handler import IDropHandler +from pyface.i_layout_widget import ILayoutWidget + + +logger = logging.getLogger(__name__) + + +class IDataViewWidget(ILayoutWidget): + """ Interface for data view widgets. """ + + #: The data model for the data view. + data_model = Instance(AbstractDataModel, allow_none=False) + + #: Whether or not the column headers are visible. + header_visible = Bool(True) + + #: The global drop handlers for the data view. These are intended to + #: handle drop actions which either affect the whole data view, or where + #: the data handler can work out how to change the underlying data without + #: additional input. + drop_handlers = List(Instance(IDropHandler, allow_none=False)) + + #: What can be selected. Implementations may optionally allow "column" + #: and "item" selection types. + selection_type = Enum("row",) + + #: How selections are modified. Implementations may optionally allow + #: "none" for no selection, or possibly other multiple selection modes + #: as supported by the toolkit. + selection_mode = Enum("extended", "single") + + #: The selected indices in the view. + selection = List(Tuple) + + #: Exporters available for the DataViewWidget. + exporters = List(Instance(AbstractDataExporter)) + + +class MDataViewWidget(HasTraits): + """ Mixin class for data view widgets. """ + + # IDataViewWidget Interface traits -------------------------------------- + + #: The data model for the data view. + data_model = Instance(AbstractDataModel, allow_none=False) + + #: Whether or not the column headers are visible. + header_visible = Bool(True) + + #: The global drop handlers for the data view. These are intended to + #: handle drop actions which either affect the whole data view, or where + #: the data handler can work out how to change the underlying data without + #: additional input. + drop_handlers = List(Instance(IDropHandler, allow_none=False)) + + #: The selected indices in the view. This should never be mutated, any + #: changes should be by replacement of the entire list. + selection = Property(observe='_selection.items') + + #: Exporters available for the DataViewWidget. + exporters = List(Instance(AbstractDataExporter)) + + # Private traits -------------------------------------------------------- + + #: Whether the selection is currently being updated. + _selection_updating_flag = Bool() + + #: The selected indices in the view. This should never be mutated, any + #: changes should be by replacement of the entire list. + _selection = List(Tuple) + + # ------------------------------------------------------------------------ + # MDataViewWidget Interface + # ------------------------------------------------------------------------ + + def _header_visible_updated(self, event): + """ Observer for header_visible trait. """ + if self.control is not None: + self._set_control_header_visible(event.new) + + def _get_control_header_visible(self): + """ Toolkit specific method to get the visibility of the header. + + Returns + ------- + control_header_visible : bool + Whether or not the control's header is visible. + """ + raise NotImplementedError() + + def _set_control_header_visible(self, control_header_visible): + """ Toolkit specific method to set the visibility of the header. + + Parameters + ---------- + control_header_visible : bool + Whether or not the control's header is visible. + """ + raise NotImplementedError() + + def _selection_type_updated(self, event): + """ Observer for selection_type trait. """ + if self.control is not None: + self._set_control_selection_type(event.new) + self.selection = [] + + def _get_control_selection_type(self): + """ Toolkit specific method to get the selection type. + + Returns + ------- + selection_type : str + The type of selection the control is using. + """ + raise NotImplementedError() + + def _set_control_selection_type(self, selection_type): + """ Toolkit specific method to change the selection type. + + Parameters + ---------- + selection_type : str + The type of selection the control is using. + """ + raise NotImplementedError() + + def _selection_mode_updated(self, event): + """ Observer for selection_mode trait. """ + if self.control is not None: + self._set_control_selection_mode(event.new) + self.selection = [] + + def _get_control_selection_mode(self): + """ Toolkit specific method to get the selection mode. + + Returns + ------- + selection_mode : str + The selection mode the control is using (eg. single vs. extended + selection). + """ + raise NotImplementedError() + + def _set_control_selection_mode(self, selection_mode): + """ Toolkit specific method to change the selection mode. + + Parameters + ---------- + selection_mode : str + The selection mode the control is using (eg. single vs. extended + selection). + """ + raise NotImplementedError() + + def _selection_updated(self, event): + """ Observer for selection trait. """ + if self.control is not None and not self._selection_updating_flag: + with self._selection_updating(): + self._set_control_selection(self.selection) + + def _get_control_selection(self): + """ Toolkit specific method to get the selection. + + Returns + ------- + selection : list of pairs of row and column indices + The selected elements of the control. + """ + raise NotImplementedError() + + def _set_control_selection(self, selection): + """ Toolkit specific method to change the selection. + + Parameters + ---------- + selection : list of pairs of row and column indices + The selected elements of the control. + """ + raise NotImplementedError() + + def _observe_control_selection(self, remove=False): + """ Toolkit specific method to watch for changes in the selection. + + The _update_selection method is available as a toolkit-independent + callback when the selection changes, but particular toolkits may + choose to implement their own callbacks with similar functionality + if appropriate. + """ + raise NotImplementedError() + + def _update_selection(self, *args, **kwargs): + """ Handle a toolkit even that changes the selection. + + This is designed to be usable as a callback for a toolkit event + or signal handler, so it accepts any arguments. + """ + if not self._selection_updating_flag: + with self._selection_updating(): + self._selection = self._get_control_selection() + + # ------------------------------------------------------------------------ + # Widget Interface + # ------------------------------------------------------------------------ + + def _create(self): + """ Creates the toolkit specific control. + + This method should create the control and assign it to the + :py:attr:``control`` trait. + """ + super()._create() + + self.show(self.visible) + self.enable(self.enabled) + + def _initialize_control(self): + """ Initializes the toolkit specific control. + """ + logger.debug('Initializing DataViewWidget') + super()._initialize_control() + self._set_control_header_visible(self.header_visible) + self._set_control_selection_mode(self.selection_mode) + self._set_control_selection_type(self.selection_type) + self._set_control_selection(self.selection) + + def _add_event_listeners(self): + logger.debug('Adding DataViewWidget listeners') + super()._add_event_listeners() + self.observe( + self._header_visible_updated, + 'header_visible', + dispatch='ui', + ) + self.observe( + self._selection_type_updated, + 'selection_type', + dispatch='ui', + ) + self.observe( + self._selection_mode_updated, + 'selection_mode', + dispatch='ui', + ) + self.observe( + self._selection_updated, + '_selection.items', + dispatch='ui', + ) + if self.control is not None: + self._observe_control_selection() + + def _remove_event_listeners(self): + logger.debug('Removing DataViewWidget listeners') + if self.control is not None: + self._observe_control_selection(remove=True) + self.observe( + self._header_visible_updated, + 'header_visible', + dispatch='ui', + remove=True, + ) + self.observe( + self._selection_type_updated, + 'selection_type', + dispatch='ui', + remove=True, + ) + self.observe( + self._selection_mode_updated, + 'selection_mode', + dispatch='ui', + remove=True, + ) + self.observe( + self._selection_updated, + '_selection.items', + dispatch='ui', + remove=True, + ) + super()._remove_event_listeners() + + # ------------------------------------------------------------------------ + # Private methods + # ------------------------------------------------------------------------ + + @contextmanager + def _selection_updating(self): + """ Context manager to prevent loopback when updating selections. """ + if self._selection_updating_flag: + yield + else: + self._selection_updating_flag = True + try: + yield + finally: + self._selection_updating_flag = False + + # Trait property handlers + + @cached_property + def _get_selection(self): + return self._selection + + def _set_selection(self, selection): + if self.selection_mode == 'none' and len(selection) != 0: + raise TraitError( + "Selection must be empty when selection_mode is 'none', " + "got {!r}".format(selection) + ) + elif self.selection_mode == 'single' and len(selection) > 1: + raise TraitError( + "Selection must have at most one element when selection_mode " + "is 'single', got {!r}".format(selection) + ) + + if self.selection_type == 'row': + for row, column in selection: + if column != (): + raise TraitError( + "Column values must be () when selection_type is " + "'row', got {!r}".format(column) + ) + if not self.data_model.is_row_valid(row): + raise TraitError( + "Invalid row index {!r}".format(row) + ) + elif self.selection_type == 'column': + for row, column in selection: + if not (self.data_model.is_row_valid(row) + and self.data_model.can_have_children(row) + and self.data_model.get_row_count(row) > 0): + raise TraitError( + "Row values must have children when selection_type " + "is 'column', got {!r}".format(column) + ) + if not self.data_model.is_column_valid(column): + raise TraitError( + "Invalid column index {!r}".format(column) + ) + else: + for row, column in selection: + if not self.data_model.is_row_valid(row): + raise TraitError( + "Invalid row index {!r}".format(row) + ) + if not self.data_model.is_column_valid(column): + raise TraitError( + "Invalid column index {!r}".format(column) + ) + + self._selection = selection diff -Nru python-pyface-6.1.2/pyface/data_view/i_data_wrapper.py python-pyface-7.4.0/pyface/data_view/i_data_wrapper.py --- python-pyface-6.1.2/pyface/data_view/i_data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/i_data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,189 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from codecs import decode, encode +from functools import partial +from typing import Any as TAny, Callable as TCallable, NamedTuple + +from traits.api import Any, HasStrictTraits, Interface + + +class DataFormat(NamedTuple): + """ Information about a mimetype and serializers. + + Simple namedtuple-based class that stores the mimetype, serializer and + deserializer together. + """ + + #: The mimetype of the data. + mimetype: str + + #: A callable that serializes this format. It should take an python + #: object of a supported type, and return a bytestring. + serialize: TCallable[[TAny], bytes] + + #: A callable that deserializes this format. It should take a + #: bytestring and return the extracted object. + deserialize: TCallable[[bytes], TAny] + + +def text_format(encoding='utf-8', mimetype='text/plain'): + """ DataFormat factory for text mimetypes. + """ + return DataFormat( + mimetype=mimetype, + serialize=partial(encode, encoding=encoding), + deserialize=partial(decode, encoding=encoding), + ) + + +class IDataWrapper(Interface): + """ Wrapper around polymorphic toolkit data object containing mimedata. + + To support clipboard and drag and drop operations, toolkits need a way + of generically representing data in multiple formats. This is a wrapper + class that provides a toolkit independent intreface to these classes + which allow the exchange of data, with types specified by MIME types. + """ + + #: The toolkit data object. + toolkit_data = Any() + + def mimetypes(self): + """ Return a set of mimetypes holding data. + + Returns + ------- + mimetypes : set of str + The set of mimetypes currently storing data in the toolkit data + object. + """ + pass + + def has_format(self, format): + """ Whether or not a particular format has available data. + + Parameters + ---------- + format : DataFormat + A data format object. + + Returns + ------- + has_format : bool + Whether or not there is data associated with that format in the + underlying toolkit object. + """ + raise NotImplementedError() + + def get_format(self, format): + """ The decoded data associted with the format. + + Parameters + ---------- + format : DataFormat + A data format object. + + Returns + ------- + data : any + The data decoded for the given format. + """ + raise NotImplementedError() + + def set_format(self, format, data): + """ Encode and set data for the format. + + Parameters + ---------- + format : DataFormat + A data format object. + data : any + The data to be encoded and stored. + """ + raise NotImplementedError() + + def get_mimedata(self, mimetype): + """ Get raw data for the given media type. + + Parameters + ---------- + mimetype : str + The mime media type to be extracted. + + Returns + ------- + mimedata : bytes + The mime media data as bytes. + """ + raise NotImplementedError() + + def set_mimedata(self, mimetype, mimedata): + """ Set raw data for the given media type. + + Parameters + ---------- + mimetype : str + The mime media type to be extracted. + mimedata : bytes + The mime media data encoded as bytes.. + """ + raise NotImplementedError() + + +class MDataWrapper(HasStrictTraits): + """ Mixin class for DataWrappers. + + This provides standard methods for using DataFormat objects, but not the + low-level communication with the underlying toolkit. + """ + + def has_format(self, format): + """ Whether or not a particular format has available data. + + Parameters + ---------- + format : DataFormat + A data format object. + + Returns + ------- + has_format : bool + Whether or not there is data associated with that format in the + underlying toolkit object. + """ + return format.mimetype in self.mimetypes() + + def get_format(self, format): + """ The decoded data associted with the format. + + Parameters + ---------- + format : DataFormat + A data format object. + + Returns + ------- + data : any + The data decoded for the given format. + """ + return format.deserialize(self.get_mimedata(format.mimetype)) + + def set_format(self, format, data): + """ Encode and set data for the format. + + Parameters + ---------- + format : DataFormat + A data format object. + data : any + The data to be encoded and stored. + """ + self.set_mimedata(format.mimetype, format.serialize(data)) diff -Nru python-pyface-6.1.2/pyface/data_view/index_manager.py python-pyface-7.4.0/pyface/data_view/index_manager.py --- python-pyface-6.1.2/pyface/data_view/index_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/index_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,424 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" +Index Managers +============== + +This module provides a number of classes for efficiently managing the +mapping between different ways of representing indices. To do so, each +index manager provides an intermediate, opaque index object that is +suitable for use in these situations and is guaranteed to have a long +enough life that it will not change or be garbage collected while a C++ +object has a reference to it. + +The wx DataView classes expect to be given an integer id value that is +stable and can be used to return the parent reference id. + +And Qt's ModelView system expects to be given a pointer to an object +that is long-lived (in particular, it will not be garbage-collected +during the lifetime of a QModelIndex) and which can be used to find +the parent object of the current object. + +The default representation of an index from the point of view of the +data view infrastructure is a sequence of integers, giving the index at +each level of the hierarchy. DataViewModel classes can then use these +indices to identify objects in the underlying data model. + +There are three main classes defined in the module: AbstractIndexManager, +IntIndexManager, and TupleIndexManager. + +AbstractIndexManager + An ABC that defines the API + +IntIndexManager + An efficient index manager for non-hierarchical data, such as + lists, tables and 2D arrays. + +TupleIndexManager + An index manager that handles non-hierarchical data while trying + to be fast and memory efficient. + +The two concrete subclasses should be sufficient for most cases, but advanced +users may create their own if for some reason the provided managers do not +work well for a particular situation. Developers who implement this API +need to be mindful of the requirements on the lifetime and identity +constraints required by the various toolkit APIs. + +""" + +from abc import abstractmethod + +from traits.api import ABCHasStrictTraits, Dict, Int, Tuple + + +#: The singular root object for all index managers. +Root = () + + +class AbstractIndexManager(ABCHasStrictTraits): + """ Abstract base class for index managers. + """ + + @abstractmethod + def create_index(self, parent, row): + """ Given a parent index and a row number, create an index. + + The internal structure of the index should not matter to + consuming code. However obejcts returned from this method + should persist until the reset method is called. + + Parameters + ---------- + parent : index object + The parent index object. + row : int + The position of the resuling index in the parent's children. + + Returns + ------- + index : index object + The resulting opaque index object. + + Raises + ------ + IndexError + Negative row values raise an IndexError exception. + RuntimeError + If asked to create a persistent index for a parent and row + where that is not possible, a RuntimeError will be raised. + """ + raise NotImplementedError() + + @abstractmethod + def get_parent_and_row(self, index): + """ Given an index object, return the parent index and row. + + Parameters + ---------- + index : index object + The opaque index object. + + Returns + ------- + parent : index object + The parent index object. + row : int + The position of the resuling index in the parent's children. + + Raises + ------ + IndexError + If the Root object is passed as the index, this method will + raise an IndexError, as it has no parent. + """ + raise NotImplementedError() + + def from_sequence(self, indices): + """ Given a sequence of indices, return the index object. + + The default implementation starts at the root and repeatedly calls + create_index() to find the index at each level, returning the final + value. + + Parameters + ---------- + indices : sequence of int + The row location at each level of the hierarchy. + + Returns + ------- + index : index object + The persistent index object associated with this sequence. + + Raises + ------ + RuntimeError + If asked to create a persistent index for a sequence of indices + where that is not possible, a RuntimeError will be raised. + """ + index = Root + for row in indices: + index = self.create_index(index, row) + return index + + def to_sequence(self, index): + """ Given an index, return the corresponding sequence of row values. + + The default implementation repeatedly calls get_parent_and_row() + to walk up the hierarchy and push the row values into the start + of the sequence. + + Parameters + ---------- + index : index object + The opaque index object. + + Returns + ------- + sequence : tuple of int + The row location at each level of the hierarchy. + """ + result = () + while index != Root: + index, row = self.get_parent_and_row(index) + result = (row,) + result + return result + + @abstractmethod + def from_id(self, id): + """ Given an integer id, return the corresponding index. + + Parameters + ---------- + id : int + An integer object id value. + + Returns + ------- + index : index object + The persistent index object associated with this id. + """ + raise NotImplementedError() + + @abstractmethod + def id(self, index): + """ Given an index, return the corresponding id. + + Parameters + ---------- + index : index object + The persistent index object. + + Returns + ------- + id : int + The associated integer object id value. + """ + raise NotImplementedError() + + def reset(self): + """ Reset any caches and other state. + + Resettable traits in subclasses are indicated by having + ``can_reset=True`` metadata. This is provided to allow + toolkit code to clear caches to prevent memory leaks when + working with very large tables. + + Care should be taken when calling this method, as Qt may + crash if a QModelIndex is referencing an index that no + longer has a reference in a cache. + + For some IndexManagers, particularly for those which are flat + or static, reset() may do nothing. + """ + resettable_traits = self.trait_names(can_reset=True) + self.reset_traits(resettable_traits) + + +class IntIndexManager(AbstractIndexManager): + """ Efficient IndexManager for non-hierarchical indexes. + + This is a simple index manager for flat data structures. The + index values returned are either the Root, or simple integers + that indicate the position of the index as a child of the root. + + While it cannot handle nested data, this index manager can + operate without having to perform any caching, and so is very + efficient. + """ + + def create_index(self, parent, row): + """ Given a parent index and a row number, create an index. + + This should only ever be called with Root as the parent. + + Parameters + ---------- + parent : index object + The parent index object. + row : non-negative int + The position of the resulting index in the parent's children. + + Returns + ------- + index : index object + The resulting opaque index object. + + Raises + ------ + IndexError + Negative row values raise an IndexError exception. + RuntimeError + If the parent is not the Root, a RuntimeError will be raised + """ + if row < 0: + raise IndexError("Row must be non-negative. Got {}".format(row)) + if parent != Root: + raise RuntimeError( + "{} cannot create persistent index value for {}.".format( + self.__class__.__name__, + (parent, row) + ) + ) + return row + + def get_parent_and_row(self, index): + """ Given an index object, return the parent index and row. + + Parameters + ---------- + index : index object + The opaque index object. + + Returns + ------- + parent : index object + The parent index object. + row : int + The position of the resuling index in the parent's children. + + Raises + ------ + IndexError + If the Root object is passed as the index, this method will + raise an IndexError, as it has no parent. + """ + if index == Root: + raise IndexError("Root index has no parent.") + return Root, int(index) + + def from_id(self, id): + """ Given an integer id, return the corresponding index. + + Parameters + ---------- + id : int + An integer object id value. + + Returns + ------- + index : index object + The persistent index object associated with this id. + """ + if id == 0: + return Root + return id - 1 + + def id(self, index): + """ Given an index, return the corresponding id. + + Parameters + ---------- + index : index object + The persistent index object. + + Returns + ------- + id : int + The associated integer object id value. + """ + if index == Root: + return 0 + return index + 1 + + +class TupleIndexManager(AbstractIndexManager): + + #: A dictionary that maps tuples to the canonical version of the tuple. + _cache = Dict(Tuple, Tuple, {Root: Root}, can_reset=True) + + #: A dictionary that maps ids to the canonical version of the tuple. + _id_cache = Dict(Int, Tuple, {0: Root}, can_reset=True) + + def create_index(self, parent, row): + """ Given a parent index and a row number, create an index. + + Parameters + ---------- + parent : index object + The parent index object. + row : non-negative int + The position of the resulting index in the parent's children. + + Returns + ------- + index : index object + The resulting opaque index object. + + Raises + ------ + IndexError + Negative row values raise an IndexError exception. + """ + if row < 0: + raise IndexError("Row must be non-negative. Got {}".format(row)) + + index = (parent, row) + canonical_index = self._cache.setdefault(index, index) + self._id_cache[self.id(canonical_index)] = canonical_index + return canonical_index + + def get_parent_and_row(self, index): + """ Given an index object, return the parent index and row. + + Parameters + ---------- + index : index object + The opaque index object. + + Returns + ------- + parent : index object + The parent index object. + row : int + The position of the resuling index in the parent's children. + + Raises + ------ + IndexError + If the Root object is passed as the index, this method will + raise an IndexError, as it has no parent. + """ + if index == Root: + raise IndexError("Root index has no parent.") + return index + + def from_id(self, id): + """ Given an integer id, return the corresponding index. + + Parameters + ---------- + id : int + An integer object id value. + + Returns + ------- + index : index object + The persistent index object associated with this id. + """ + return self._id_cache[id] + + def id(self, index): + """ Given an index, return the corresponding id. + + Parameters + ---------- + index : index object + The persistent index object. + + Returns + ------- + id : int + The associated integer object id value. + """ + if index == Root: + return 0 + canonical_index = self._cache.setdefault(index, index) + return id(canonical_index) diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_abstract_data_exporter.py python-pyface-7.4.0/pyface/data_view/tests/test_abstract_data_exporter.py --- python-pyface-6.1.2/pyface/data_view/tests/test_abstract_data_exporter.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_abstract_data_exporter.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,119 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.abstract_data_exporter import AbstractDataExporter +from pyface.data_view.data_view_errors import DataViewGetError +from pyface.data_view.data_wrapper import DataWrapper +from pyface.data_view.i_data_wrapper import DataFormat + + +trivial_format = DataFormat( + 'null/null', + lambda x: x, + lambda x: x, +) + +trivial_text_format = DataFormat( + 'text/null', + lambda x: x, + lambda x: x, +) + + +class TrivialExporter(AbstractDataExporter): + + def get_data(self, model, indices): + if len(indices) == 0: + raise DataViewGetError('bad data') + return b'data' + + +class TestAbstractDataExporter(TestCase): + + def setUp(self): + self.value_type = Mock() + self.value_type.has_text = Mock(return_value=True) + self.value_type.get_text = Mock(return_value='text') + self.value_type.has_editor_value = Mock(return_value=True) + self.value_type.get_editor_value = Mock(return_value=1) + + self.model = Mock() + self.model.get_value = Mock(return_value=0.0) + self.model.get_value_type = Mock(return_value=self.value_type) + + def test_is_text_default_false(self): + exporter = TrivialExporter(format=trivial_format) + self.assertFalse(exporter.is_text) + + def test_is_text_default_true(self): + exporter = TrivialExporter(format=trivial_text_format) + self.assertTrue(exporter.is_text) + + def test_add_data(self): + exporter = TrivialExporter(format=trivial_format) + data_wrapper = DataWrapper() + + exporter.add_data(data_wrapper, self.model, [((0,), (0,))]) + + self.assertTrue(data_wrapper.has_format(trivial_format)) + self.assertEqual(data_wrapper.get_mimedata('null/null'), b'data') + + def test_add_data_fail(self): + exporter = TrivialExporter(format=trivial_format) + data_wrapper = DataWrapper() + + exporter.add_data(data_wrapper, self.model, []) + + self.assertFalse(data_wrapper.has_format(trivial_format)) + + def test_get_value_is_text(self): + exporter = TrivialExporter( + format=trivial_format, + is_text=True, + ) + + value = exporter.get_value(self.model, (0,), (0,)) + + self.assertEqual(value, 'text') + + def test_get_value_is_text_not_has_text(self): + self.value_type.has_text = Mock(return_value=False) + exporter = TrivialExporter( + format=trivial_format, + is_text=True, + ) + + value = exporter.get_value(self.model, (0,), (0,)) + + self.assertEqual(value, '') + + def test_get_value_is_not_text(self): + exporter = TrivialExporter( + format=trivial_format, + is_text=False, + ) + + value = exporter.get_value(self.model, (0,), (0,)) + + self.assertEqual(value, 1.0) + + def test_get_value_is_not_text_not_editor_value(self): + self.value_type.has_editor_value = Mock(return_value=False) + exporter = TrivialExporter( + format=trivial_format, + is_text=False, + ) + + value = exporter.get_value(self.model, (0,), (0,)) + + self.assertEqual(value, 0.0) diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_abstract_value_type.py python-pyface-7.4.0/pyface/data_view/tests/test_abstract_value_type.py --- python-pyface-6.1.2/pyface/data_view/tests/test_abstract_value_type.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_abstract_value_type.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,117 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from traits.api import Str +from traits.testing.api import UnittestTools + +from pyface.color import Color +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.abstract_value_type import AbstractValueType, CheckState + + +class ValueType(AbstractValueType): + + #: a parameter which should fire the update trait + sample_parameter = Str(update_value_type=True) + + +class TestAbstractValueType(UnittestTools, TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock(return_value=1.0) + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock() + + def test_has_editor_value(self): + value_type = ValueType() + result = value_type.has_editor_value(self.model, [0], [0]) + self.assertTrue(result) + + def test_has_editor_value_can_set_value_false(self): + self.model.can_set_value = Mock(return_value=False) + value_type = ValueType() + result = value_type.has_editor_value(self.model, [0], [0]) + self.assertFalse(result) + + def test_get_editor_value(self): + value_type = ValueType() + result = value_type.get_editor_value(self.model, [0], [0]) + self.assertEqual(result, 1.0) + + def test_set_editor_value(self): + value_type = ValueType() + value_type.set_editor_value(self.model, [0], [0], 2.0) + self.model.set_value.assert_called_once_with([0], [0], 2.0) + + def test_set_editor_value_set_value_raises(self): + self.model.set_value = Mock(side_effect=DataViewSetError) + value_type = ValueType() + with self.assertRaises(DataViewSetError): + value_type.set_editor_value(self.model, [0], [0], 2.0) + self.model.set_value.assert_called_once_with([0], [0], 2.0) + + def test_has_text(self): + value_type = ValueType() + result = value_type.has_text(self.model, [0], [0]) + self.assertTrue(result) + + def test_get_text(self): + value_type = ValueType() + result = value_type.get_text(self.model, [0], [0]) + self.assertEqual(result, "1.0") + + def test_set_text(self): + value_type = ValueType() + with self.assertRaises(DataViewSetError): + value_type.set_text(self.model, [0], [0], "2.0") + + def test_has_color(self): + value_type = ValueType() + result = value_type.has_color(self.model, [0], [0]) + self.assertFalse(result) + + def test_get_color(self): + value_type = ValueType() + result = value_type.get_color(self.model, [0], [0]) + self.assertEqual(result, Color(rgb=(1.0, 1.0, 1.0))) + + def test_has_image(self): + value_type = ValueType() + result = value_type.has_image(self.model, [0], [0]) + self.assertFalse(result) + + def test_get_image(self): + value_type = ValueType() + result = value_type.get_image(self.model, [0], [0]) + self.assertEqual(result.name, "image_not_found") + + def test_has_check_state(self): + value_type = ValueType() + result = value_type.has_check_state(self.model, [0], [0]) + self.assertFalse(result) + + def test_get_check_state(self): + value_type = ValueType() + result = value_type.get_check_state(self.model, [0], [0]) + self.assertEqual(result, CheckState.CHECKED) + + def test_set_check_state(self): + value_type = ValueType() + with self.assertRaises(DataViewSetError): + value_type.set_check_state(self.model, [0], [0], CheckState.CHECKED) + + def test_parameter_update(self): + value_type = ValueType() + with self.assertTraitChanges(value_type, 'updated', count=1): + value_type.sample_parameter = "new value" diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_api.py python-pyface-7.4.0/pyface/data_view/tests/test_api.py --- python-pyface-6.1.2/pyface/data_view/tests/test_api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,64 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Test the api module. """ + +import unittest + + +class TestApi(unittest.TestCase): + def test_all_imports(self): + from pyface.data_view.api import ( # noqa: F401 + AbstractDataExporter, + AbstractDataModel, + AbstractValueType, + csv_column_format, + csv_format, + csv_row_format, + from_csv, + from_csv_column, + from_csv_row, + from_json, + from_npy, + html_format, + npy_format, + standard_text_format, + to_csv, + to_csv_column, + to_csv_row, + to_json, + to_npy, + table_format, + text_column_format, + text_format, + text_row_format, + DataViewError, + DataViewGetError, + DataViewSetError, + DataViewWidget, + DataWrapper, + IDataViewWidget, + IDataWrapper, + AbstractIndexManager, + IntIndexManager, + TupleIndexManager, + DataFormat, + ) + + def test_api_items_count(self): + # This test helps developer to keep the above list + # up-to-date. Bump the number when the API content changes. + from pyface.data_view import api + items_in_api = { + name + for name in dir(api) + if not name.startswith("_") + } + self.assertEqual(len(items_in_api), 34) diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_data_formats.py python-pyface-7.4.0/pyface/data_view/tests/test_data_formats.py --- python-pyface-6.1.2/pyface/data_view/tests/test_data_formats.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_data_formats.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,236 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from traits.api import HasTraits, Int, Str +from traits.testing.optional_dependencies import numpy as np, requires_numpy +from traits.version import version + +from pyface.data_view.data_formats import ( + from_csv, from_csv_column, from_csv_row, from_npy, from_json, + to_csv, to_csv_column, to_csv_row, to_npy, to_json, +) + + +def default_extractor(obj): + return obj.__getstate__() + + +def object_hook(data): + obj = ExampleObject() + obj.__setstate__(data) + return obj + + +class ExampleObject(HasTraits): + a = Int(1) + b = Str("two") + c = Str("three", transient=True) + + +class TestJSON(TestCase): + + def test_to_json(self): + test_data = {'a': 1, 'b': 'two'} + + raw_data = to_json(test_data) + + self.assertEqual(raw_data, b'{"a":1,"b":"two"}') + + def test_to_json_default(self): + test_data = ExampleObject() + + raw_data = to_json(test_data, default=default_extractor) + + self.assertEqual( + raw_data, + b'{"a":1,"b":"two","__traits_version__":"' + + version.encode('utf-8') + + b'"}' + ) + + def test_from_json(self): + raw_data = b'{"a":1,"b":"two"}' + + data = from_json(raw_data) + + self.assertEqual(data, {'a': 1, 'b': 'two'}) + + def test_from_json_object_hook(self): + raw_data = ( + b'{"a":2,"b":"four","__traits_version__":"' + + version.encode('utf-8') + + b'"}' + ) + + data = from_json(raw_data, object_hook=object_hook) + + self.assertIsInstance(data, ExampleObject) + self.assertEqual(data.a, 2) + self.assertEqual(data.b, 'four') + self.assertEqual(data.c, 'three') + + +class TestCSV(TestCase): + + def test_to_csv(self): + test_data = [['one', 2], ['three,four', 5]] + + raw_data = to_csv(test_data) + + self.assertEqual(raw_data, b'one,2\r\n"three,four",5\r\n') + + def test_to_csv_delimiter(self): + test_data = [['one', 2], ['three,four', 5]] + + raw_data = to_csv(test_data, delimiter='\t') + + self.assertEqual(raw_data, b'one\t2\r\nthree,four\t5\r\n') + + def test_to_csv_encoding(self): + test_data = [['øne', 2], ['three,four', 5]] + + raw_data = to_csv(test_data, encoding='latin-1') + + self.assertEqual(raw_data, b'\xf8ne,2\r\n"three,four",5\r\n') + + def test_from_csv(self): + raw_data = b'one,2\r\n"three,four",5\r\n' + + data = from_csv(raw_data) + + self.assertEqual(data, [['one', '2'], ['three,four', '5']]) + + def test_from_csv_delimiter(self): + raw_data = b'one\t2\r\nthree,four\t5\r\n' + + data = from_csv(raw_data, delimiter='\t') + + self.assertEqual(data, [['one', '2'], ['three,four', '5']]) + + def test_from_csv_encoding(self): + raw_data = b'\xf8ne,2\r\n"three,four",5\r\n' + + data = from_csv(raw_data, encoding='latin-1') + + self.assertEqual(data, [['øne', '2'], ['three,four', '5']]) + + def test_to_csv_column(self): + test_data = ['one', 2, 'three,four'] + + raw_data = to_csv_column(test_data) + + self.assertEqual(raw_data, b'one\r\n2\r\n"three,four"\r\n') + + def test_to_csv_column_delimiter(self): + test_data = ['one', 2, 'three,four'] + + raw_data = to_csv_column(test_data, delimiter='\t') + + self.assertEqual(raw_data, b'one\r\n2\r\nthree,four\r\n') + + def test_to_csv_column_encoding(self): + test_data = ['øne', 2, 'three,four'] + + raw_data = to_csv_column(test_data, encoding='latin-1') + + self.assertEqual(raw_data, b'\xf8ne\r\n2\r\n"three,four"\r\n') + + def test_from_csv_column(self): + raw_data = b'one\r\n2\r\n"three,four"\r\n' + + data = from_csv_column(raw_data) + + self.assertEqual(data, ['one', '2', 'three,four']) + + def test_from_csv_column_delimiter(self): + raw_data = b'one\r\n2\r\nthree,four\r\n' + + data = from_csv_column(raw_data, delimiter='\t') + + self.assertEqual(data, ['one', '2', 'three,four']) + + def test_from_csv_column_encoding(self): + raw_data = b'\xf8ne\r\n2\r\n"three,four"\r\n' + + data = from_csv_column(raw_data, encoding='latin-1') + + self.assertEqual(data, ['øne', '2', 'three,four']) + + def test_to_csv_row(self): + test_data = ['one', 2, 'three,four'] + + raw_data = to_csv_row(test_data) + + self.assertEqual(raw_data, b'one,2,"three,four"\r\n') + + def test_to_csv_row_delimiter(self): + test_data = ['one', 2, 'three,four'] + + raw_data = to_csv_row(test_data, delimiter='\t') + + self.assertEqual(raw_data, b'one\t2\tthree,four\r\n') + + def test_to_csv_row_encoding(self): + test_data = ['øne', 2, 'three,four'] + + raw_data = to_csv_row(test_data, encoding='latin-1') + + self.assertEqual(raw_data, b'\xf8ne,2,"three,four"\r\n') + + def test_from_csv_row(self): + raw_data = b'one,2,"three,four"\r\n' + + data = from_csv_row(raw_data) + + self.assertEqual(data, ['one', '2', 'three,four']) + + def test_from_csv_row_delimiter(self): + raw_data = b'one\t2\tthree,four\r\n' + + data = from_csv_row(raw_data, delimiter='\t') + + self.assertEqual(data, ['one', '2', 'three,four']) + + def test_from_csv_row_encoding(self): + raw_data = b'\xf8ne,2,"three,four"\r\n' + + data = from_csv_row(raw_data, encoding='latin-1') + + self.assertEqual(data, ['øne', '2', 'three,four']) + + +@requires_numpy +class TestNpy(TestCase): + + def test_to_npy(self): + data = np.array([[1, 2, 3], [4, 5, 6]], dtype='uint8') + + raw_data = to_npy(data) + + self.assertEqual( + raw_data, + b"\x93NUMPY\x01\x00v\x00{'descr': '|u1', 'fortran_order': False, 'shape': (2, 3), } \n" # noqa: E501 + + b"\x01\x02\x03\x04\x05\x06" + ) + + def test_from_npy(self): + raw_data = ( + b"\x93NUMPY\x01\x00v\x00{'descr': '|u1', 'fortran_order': False, 'shape': (2, 3), } \n" # noqa: E501 + + b"\x01\x02\x03\x04\x05\x06" + ) + + data = from_npy(raw_data) + + np.testing.assert_array_equal( + data, + np.array([[1, 2, 3], [4, 5, 6]], dtype='uint8') + ) diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_data_view_widget.py python-pyface-7.4.0/pyface/data_view/tests/test_data_view_widget.py --- python-pyface-6.1.2/pyface/data_view/tests/test_data_view_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_data_view_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,356 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import platform +import unittest + +from traits.api import TraitError +from traits.testing.optional_dependencies import numpy as np, requires_numpy + +from pyface.toolkit import toolkit +from pyface.testing.layout_widget_mixin import LayoutWidgetMixin + +# This import results in an error without numpy installed +# see enthought/pyface#742 +if np is not None: + from pyface.data_view.data_models.api import ArrayDataModel +from pyface.data_view.data_view_widget import DataViewWidget +from pyface.data_view.value_types.api import FloatValue + + +is_wx = (toolkit.toolkit == "wx") +is_linux = (platform.system() == "Linux") + + +# The available selection modes and types for the toolkit +selection_modes = DataViewWidget().trait("selection_mode").trait_type.values +selection_types = DataViewWidget().trait("selection_type").trait_type.values + + +@requires_numpy +class TestWidget(LayoutWidgetMixin, unittest.TestCase): + + def _create_widget(self): + self.data = np.arange(120.0).reshape(4, 5, 6) + self.model = ArrayDataModel(data=self.data, value_type=FloatValue()) + return DataViewWidget( + parent=self.parent.control, + data_model=self.model + ) + + def test_defaults(self): + self.assertTrue(self.widget.header_visible) + + def test_lifecycle(self): + self._create_widget_control() + + def test_header_visible(self): + self._create_widget_control() + + self.assertTrue(self.widget._get_control_header_visible()) + + self.widget.header_visible = False + self.gui.process_events() + + self.assertFalse(self.widget._get_control_header_visible()) + + def test_header_visible_before_control(self): + self.widget.header_visible = False + + self._create_widget_control() + self.assertFalse(self.widget._get_control_header_visible()) + + def test_init_selection(self): + self.widget.selection = [((1, ), ())] + self._create_widget_control() + + self.assertEqual( + self.widget._get_control_selection(), [((1, ), ())] + ) + + def test_selection_mode_change(self): + self._create_widget_control() + self.widget.selection = [((1, 4), ()), ((2, 0), ())] + + self.widget.selection_mode = "single" + + self.assertEqual(self.widget._get_control_selection_mode(), "single") + self.assertEqual(self.widget.selection, []) + + self.widget.selection = [((1, 4), ())] + if "none" in selection_modes: + self.widget.selection_mode = "none" + + self.assertEqual(self.widget._get_control_selection_mode(), "none") + self.assertEqual(self.widget.selection, []) + + self.widget.selection_mode = "extended" + + self.assertEqual(self.widget._get_control_selection_mode(), "extended") + self.assertEqual(self.widget.selection, []) + + @unittest.skipIf( + len(selection_types) <= 1, + "Changing selection types not supported", + ) + def test_selection_type_change(self): + self._create_widget_control() + + if "column" in selection_types: + self.widget.selection_type = "column" + self.assertEqual( + self.widget._get_control_selection_type(), + "column", + ) + self.assertEqual(self.widget.selection, []) + + if "item" in selection_types: + self.widget.selection_type = "item" + self.assertEqual(self.widget._get_control_selection_type(), "item") + self.assertEqual(self.widget.selection, []) + + if "row" in selection_types: + self.widget.selection_type = "row" + + self.assertEqual(self.widget._get_control_selection_type(), "row") + self.assertEqual(self.widget.selection, []) + + @unittest.skipIf( + "none" not in selection_modes, + "Selection mode 'none' not supported", + ) + def test_selection_mode_none(self): + self.widget.selection_mode = "none" + self._create_widget_control() + + self.assertEqual(self.widget._get_control_selection_mode(), "none") + + self.widget.selection = [] + self.gui.process_events() + + self.assertEqual(self.widget.selection, []) + self.assertEqual(self.widget._get_control_selection(), []) + + @unittest.skipIf( + "none" not in selection_modes, + "Selection mode 'none' not supported", + ) + def test_selection_mode_none_invalid(self): + self.widget.selection_mode = "none" + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 4), ()), (2, 1), ()] + + def test_selection_mode_single(self): + self.widget.selection_mode = "single" + self._create_widget_control() + + self.assertEqual(self.widget._get_control_selection_mode(), "single") + + self.widget.selection = [((1, 4), ())] + self.gui.process_events() + + self.assertEqual(self.widget.selection, [((1, 4), ())]) + self.assertEqual(self.widget._get_control_selection(), [((1, 4), ())]) + + def test_selection_mode_single_invalid(self): + self.widget.selection_mode = "single" + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 4), ()), (2, 1), ()] + + @unittest.skipIf( + is_wx and is_linux, + "Selection mode 'extended' not working on Linux", + ) + def test_selection_mode_extended(self): + self._create_widget_control() + + self.assertEqual(self.widget._get_control_selection_mode(), "extended") + + self.widget.selection = [((1, 4), ()), ((2, 0), ())] + self.gui.process_events() + + self.assertEqual(self.widget.selection, [((1, 4), ()), ((2, 0), ())]) + self.assertEqual( + self.widget._get_control_selection(), + [((1, 4), ()), ((2, 0), ())], + ) + + @unittest.skipIf( + 'column' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_column(self): + self.widget.selection_type = "column" + self._create_widget_control() + + self.assertEqual(self.widget._get_control_selection_type(), "column") + + self.widget.selection = [((0,), (2,)), ((1,), (4,))] + + self.gui.process_events() + + self.assertEqual(self.widget.selection, [((0,), (2,)), ((1,), (4,))]) + self.assertEqual( + self.widget._get_control_selection(), + [((0,), (2,)), ((1,), (4,))] + ) + + @unittest.skipIf( + 'item' not in selection_types, + "Selection type 'item' not supported", + ) + def test_selection_type_item(self): + self.widget.selection_type = "item" + self._create_widget_control() + + self.assertEqual(self.widget._get_control_selection_type(), "item") + + self.widget.selection = [((1, 4), (2,)), ((2, 0), (4,))] + self.gui.process_events() + + self.assertEqual( + self.widget.selection, + [((1, 4), (2,)), ((2, 0), (4,))] + ) + self.assertEqual( + self.widget._get_control_selection(), + [((1, 4), (2,)), ((2, 0), (4,))], + ) + + def test_selection_type_row_invalid_row_big(self): + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((10,), ())] + + def test_selection_type_row_invalid_row_long(self): + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 1, 1), ())] + + def test_selection_type_row_invalid_column(self): + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 2), (2,))] + + @unittest.skipIf( + 'item' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_item_invalid_row_too_big(self): + self.widget.selection_type = 'item' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 10), (2,))] + + @unittest.skipIf( + 'item' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_item_invalid_row_too_long(self): + self.widget.selection_type = 'item' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 4, 5, 6), (2,))] + + @unittest.skipIf( + 'item' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_item_invalid_column(self): + self.widget.selection_type = 'item' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 2), (10,))] + + @unittest.skipIf( + 'column' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_column_invalid_row_too_long(self): + self.widget.selection_type = 'column' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 4, 5, 6), (2,))] + + @unittest.skipIf( + 'column' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_column_invalid_row_too_big(self): + self.widget.selection_type = 'column' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((10,), (2,))] + + @unittest.skipIf( + 'column' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_column_invalid_row_not_parent(self): + self.widget.selection_type = 'column' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((1, 2), (2,))] + + @unittest.skipIf( + 'column' not in selection_types, + "Selection type 'column' not supported", + ) + def test_selection_type_column_invalid_column(self): + self.widget.selection_type = 'column' + self._create_widget_control() + + with self.assertRaises(TraitError): + self.widget.selection = [((), (10,))] + + def test_selection_updated(self): + self._create_widget_control() + + with self.assertTraitChanges(self.widget, 'selection'): + self.widget._set_control_selection([((1, 4), ())]) + self.gui.process_events() + + self.assertEqual(self.widget.selection, [((1, 4), ())]) + self.assertEqual( + self.widget._get_control_selection(), + [((1, 4), ())], + ) + + def test_selection_updating_context_manager(self): + self.assertFalse(self.widget._selection_updating_flag) + + with self.widget._selection_updating(): + self.assertTrue(self.widget._selection_updating_flag) + with self.widget._selection_updating(): + self.assertTrue(self.widget._selection_updating_flag) + self.assertTrue(self.widget._selection_updating_flag) + + self.assertFalse(self.widget._selection_updating_flag) + + def test_selection_updating_context_manager_exception(self): + with self.assertRaises(ZeroDivisionError): + with self.widget._selection_updating(): + with self.widget._selection_updating(): + 1/0 + + self.assertFalse(self.widget._selection_updating_flag) diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_data_wrapper.py python-pyface-7.4.0/pyface/data_view/tests/test_data_wrapper.py --- python-pyface-6.1.2/pyface/data_view/tests/test_data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,48 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from pyface.data_view.i_data_wrapper import text_format +from pyface.data_view.data_wrapper import DataWrapper + + +class TestDataWrapper(TestCase): + + def test_instantiate(self): + data_wrapper = DataWrapper() + self.assertEqual(data_wrapper.mimetypes(), set()) + + def test_mimedata_roundtrip(self): + data_wrapper = DataWrapper() + data_wrapper.set_mimedata('text/plain', b'hello world') + result = data_wrapper.get_mimedata('text/plain') + + self.assertEqual(data_wrapper.mimetypes(), {'text/plain'}) + self.assertEqual(result, b'hello world') + + def test_mimedata_overwrite(self): + data_wrapper = DataWrapper() + data_wrapper.set_mimedata('text/plain', b'hello world') + data_wrapper.set_mimedata('text/plain', b'hello mars') + result = data_wrapper.get_mimedata('text/plain') + + self.assertEqual(data_wrapper.mimetypes(), {'text/plain'}) + self.assertEqual(result, b'hello mars') + + def test_set_format(self): + data_wrapper = DataWrapper() + format = text_format() + + data_wrapper.set_format(format, 'hëllø wørld') + result = data_wrapper.get_mimedata('text/plain') + + self.assertEqual(data_wrapper.mimetypes(), {'text/plain'}) + self.assertEqual(result, 'hëllø wørld'.encode('utf-8')) diff -Nru python-pyface-6.1.2/pyface/data_view/tests/test_index_manager.py python-pyface-7.4.0/pyface/data_view/tests/test_index_manager.py --- python-pyface-6.1.2/pyface/data_view/tests/test_index_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/tests/test_index_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,193 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from pyface.data_view.index_manager import ( + IntIndexManager, Root, TupleIndexManager, +) + + +class IndexManagerMixin: + + def test_root_has_no_parent(self): + with self.assertRaises(IndexError): + self.index_manager.get_parent_and_row(Root) + + def test_root_to_sequence(self): + result = self.index_manager.to_sequence(Root) + + self.assertEqual(result, ()) + + def test_root_from_sequence(self): + result = self.index_manager.from_sequence([]) + + self.assertIs(result, Root) + + def test_root_id_round_trip(self): + root_id = self.index_manager.id(Root) + result = self.index_manager.from_id(root_id) + + self.assertIs(result, Root) + + def test_simple_sequence_round_trip(self): + sequence = (5,) + index = self.index_manager.from_sequence(sequence) + result = self.index_manager.to_sequence(index) + + self.assertEqual(result, sequence) + + def test_simple_sequence_invalid(self): + sequence = (-5,) + with self.assertRaises(IndexError): + self.index_manager.from_sequence(sequence) + + def test_simple_sequence_to_parent_row(self): + sequence = (5,) + index = self.index_manager.from_sequence(sequence) + result = self.index_manager.get_parent_and_row(index) + + self.assertEqual(result, (Root, 5)) + + def test_simple_row_round_trip(self): + index = self.index_manager.create_index(Root, 5) + result = self.index_manager.get_parent_and_row(index) + + self.assertEqual(result, (Root, 5)) + + def test_simple_row_invalid(self): + with self.assertRaises(IndexError): + self.index_manager.create_index(Root, -5) + + def test_simple_row_to_sequence(self): + index = self.index_manager.create_index(Root, 5) + result = self.index_manager.to_sequence(index) + + self.assertEqual(result, (5,)) + + def test_simple_id_round_trip(self): + index = self.index_manager.create_index(Root, 5) + id = self.index_manager.id(index) + result = self.index_manager.from_id(id) + + self.assertEqual(result, index) + + +class TestIntIndexManager(IndexManagerMixin, TestCase): + + def setUp(self): + super().setUp() + self.index_manager = IntIndexManager() + + def tearDown(self): + self.index_manager.reset() + + def test_create_index_root(self): + result = self.index_manager.create_index(Root, 5) + self.assertEqual(result, 5) + + def test_create_index_leaf(self): + with self.assertRaises(RuntimeError): + self.index_manager.create_index(5, 1) + + def test_create_index_negative(self): + with self.assertRaises(IndexError): + self.index_manager.create_index(Root, -5) + + +class TestTupleIndexManager(IndexManagerMixin, TestCase): + + def setUp(self): + super().setUp() + self.index_manager = TupleIndexManager() + + def tearDown(self): + self.index_manager.reset() + + def test_complex_sequence_round_trip(self): + sequence = (5, 6, 7, 8, 9, 10) + index = self.index_manager.from_sequence(sequence) + result = self.index_manager.to_sequence(index) + + self.assertEqual(result, sequence) + + def test_complex_sequence_identical_index(self): + sequence = (5, 6, 7, 8, 9, 10) + index_1 = self.index_manager.from_sequence(sequence[:]) + index_2 = self.index_manager.from_sequence(sequence[:]) + + self.assertIs(index_1, index_2) + + def test_complex_sequence_to_parent_row(self): + sequence = (5, 6, 7, 8, 9, 10) + index = self.index_manager.from_sequence(sequence) + + parent, row = self.index_manager.get_parent_and_row(index) + + self.assertEqual(row, 10) + self.assertIs( + parent, + self.index_manager.from_sequence((5, 6, 7, 8, 9)) + ) + + def test_complex_index_round_trip(self): + sequence = (5, 6, 7, 8, 9, 10) + + parent = Root + for depth, row in enumerate(sequence): + with self.subTest(depth=depth): + index = self.index_manager.create_index(parent, row) + result = self.index_manager.get_parent_and_row(index) + self.assertIs(result[0], parent) + self.assertEqual(result[1], row) + parent = index + + def test_complex_index_create_index_identical(self): + sequence = (5, 6, 7, 8, 9, 10) + + parent = Root + for depth, row in enumerate(sequence): + with self.subTest(depth=depth): + index_1 = self.index_manager.create_index(parent, row) + index_2 = self.index_manager.create_index(parent, row) + self.assertIs(index_1, index_2) + parent = index_1 + + def test_complex_index_to_sequence(self): + sequence = (5, 6, 7, 8, 9, 10) + parent = Root + for depth, row in enumerate(sequence): + with self.subTest(depth=depth): + index = self.index_manager.create_index(parent, row) + result = self.index_manager.to_sequence(index) + self.assertEqual(result, sequence[:depth+1]) + parent = index + + def test_complex_index_sequence_round_trip(self): + parent = Root + for depth, row in enumerate([5, 6, 7, 8, 9, 10]): + with self.subTest(depth=depth): + index = self.index_manager.create_index(parent, row) + sequence = self.index_manager.to_sequence(index) + result = self.index_manager.from_sequence(sequence) + self.assertIs(result, index) + parent = index + + def test_complex_index_id_round_trip(self): + sequence = (5, 6, 7, 8, 9, 10) + parent = Root + for depth, row in enumerate(sequence): + with self.subTest(depth=depth): + index = self.index_manager.create_index(parent, row) + id = self.index_manager.id(index) + self.assertIsInstance(id, int) + result = self.index_manager.from_id(id) + self.assertIs(result, index) + parent = index diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/api.py python-pyface-7.4.0/pyface/data_view/value_types/api.py --- python-pyface-6.1.2/pyface/data_view/value_types/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,38 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.data_view.value_types`` subpackage. + +Value Types +----------- + +- :class:`~.BoolValue` +- :class:`~.ColorValue` +- :class:`~.ConstantValue` +- :class:`~.EditableValue` +- :class:`~.NoValue` +- :attr:`~.no_value` +- :class:`~.FloatValue` +- :class:`~.IntValue` +- :class:`~.NumericValue` +- :class:`~.TextValue` + +""" + +from .bool_value import BoolValue # noqa: F401 +from .color_value import ColorValue # noqa: F401 +from .constant_value import ConstantValue # noqa: F401 +from .editable_value import EditableValue # noqa: F401 +from .enum_value import EnumValue # noqa: F401 +from .no_value import NoValue, no_value # noqa: F401 +from .numeric_value import FloatValue, IntValue, NumericValue # noqa: F401 +from .text_value import TextValue # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/bool_value.py python-pyface-7.4.0/pyface/data_view/value_types/bool_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/bool_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/bool_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,108 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Str + +from pyface.data_view.abstract_value_type import AbstractValueType, CheckState + + +class BoolValue(AbstractValueType): + """ Value that presents a boolean value via checked state. + """ + + #: The text to display next to a True value. + true_text = Str() + + #: The text to display next to a False value. + false_text = Str() + + def has_editor_value(self, model, row, column): + """ BoolValues don't use editors, but have always-on checkbox. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_editor_value : bool + Whether or not the value is editable. + """ + return False + + def get_text(self, model, row, column): + """ The textual representation of the underlying value. + + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + The textual representation of the underlying value. + """ + return ( + self.true_text if model.get_value(row, column) else self.false_text + ) + + def has_check_state(self, model, row, column): + """ Whether or not the value has checked state. + + The default implementation returns True. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_check_state : bool + Whether or not the value has a checked state. + """ + return True + + def set_check_state(self, model, row, column, check_state): + """ Set the boolean value from the check state. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being set. + column : sequence of int + The column in the data model being set. + check_state : "checked" or "unchecked" + The check state being set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + value = (check_state == CheckState.CHECKED) + model.set_value(row, column, value) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/color_value.py python-pyface-7.4.0/pyface/data_view/value_types/color_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/color_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/color_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,174 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.color import Color + +from pyface.data_view.abstract_data_model import DataViewSetError +from .editable_value import EditableValue + + +class ColorValue(EditableValue): + """ Editable value that presents a color value. + + This is suitable for use where the value returned by an item in + the model is a Color object. + """ + + def is_valid(self, model, row, column, value): + """ Is the given value valid for this item. + + The default implementation says the value must be a Color. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + value : any + The value to test. + + Returns + ------- + success : bool + Whether or not the value is valid. + """ + return isinstance(value, Color) + + def get_editor_value(self, model, row, column): + """ Get the editable representation of the underlying value. + + The default uses a text hex representation of the color. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + A hex string representation of the colour. + """ + return model.get_value(row, column).hex() + + def set_editor_value(self, model, row, column, value): + """ Set the editable representation of the underlying value. + + The default expects a string that can be parsed to a color value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + value : str + A string that can be parsed to a color value. + + Returns + ------- + success : bool + Whether or not the value was successfully set. + """ + try: + color = Color.from_str(value) + except Exception: + raise DataViewSetError() + return super().set_editor_value(model, row, column, color) + + def get_text(self, model, row, column): + """ Get the textual representation of the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + A hex string representation of the colour. + """ + return model.get_value(row, column).hex() + + def set_text(self, model, row, column, text): + """ Set the textual representation of the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + text : str + The text to set. + + Returns + ------- + success : bool + Whether or not the value was successfully set. + """ + return self.set_editor_value(model, row, column, text) + + def has_color(self, model, row, column): + """ Whether or not the value has color data. + + The default implementation returns False. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_color : bool + Whether or not the value has data-associated color + values. + """ + return True + + def get_color(self, model, row, column): + """ Get data-associated colour values for the given item. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + color : Color instance + The color associated with the cell. + """ + return model.get_value(row, column) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/constant_value.py python-pyface-7.4.0/pyface/data_view/value_types/constant_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/constant_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/constant_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,100 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Str, Union, observe + +from pyface.ui_traits import PyfaceColor +from pyface.data_view.abstract_value_type import AbstractValueType +from pyface.ui_traits import Image + + +class ConstantValue(AbstractValueType): + """ A value type that does not depend on the underlying data model. + + This value type is not editable, but the other data channels it + provides can be modified by changing the appropriate trait on the + value type. + """ + + #: The text value to display. + text = Str(update_value_type=True) + + #: The color value to display or None if no color. + color = Union(None, PyfaceColor) + + #: The image value to display. + image = Image(update_value_type=True) + + #: The tooltip value to display. + tooltip = Str(update_value_type=True) + + def has_editor_value(self, model, row, column): + return False + + def get_text(self, model, row, column): + return self.text + + def has_color(self, model, row, column): + """ Whether or not the value has color data. + + Returns true if the supplied color is not None. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_color : bool + Whether or not the value has data-associated color + values. + """ + return self.color is not None + + def get_color(self, model, row, column): + """ Get data-associated colour values for the given item. + + The default implementation returns white. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + color : Color instance + The color associated with the cell. + """ + return self.color + + def has_image(self, model, row, column): + return self.image is not None + + def get_image(self, model, row, column): + if self.image is not None: + return self.image + return super().get_image(model, row, column) + + def get_tooltip(self, model, row, column): + return self.tooltip + + @observe('color.rgba') + def _color_updated(self, event): + self.updated = True diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/editable_value.py python-pyface-7.4.0/pyface/data_view/value_types/editable_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/editable_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/editable_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,99 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Bool + +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.abstract_value_type import AbstractValueType + + +class EditableValue(AbstractValueType): + """ A base class for editable values. + + This class provides two things beyond the base AbstractValueType: + a trait ``is_editable`` which allows toggling editing state on and + off, and an ``is_valid`` method that is used for validation before + setting a value. + """ + + #: Whether or not the value is editable, assuming the underlying data can + #: be set. + is_editable = Bool(True, update_value_type=True) + + def is_valid(self, model, row, column, value): + """ Whether or not the value is valid for the data item specified. + + The default implementation returns True for all values. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + value : any + The value to validate. + + Returns + ------- + is_valid : bool + Whether or not the value is valid. + """ + return True + + # AbstractValueType Interface -------------------------------------------- + + def has_editor_value(self, model, row, column): + """ Return whether or not the value can be edited. + + A cell is editable if the underlying data can be set, and the + ``is_editable`` flag is set to True + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_editor_value : bool + Whether or not the value is editable. + """ + return model.can_set_value(row, column) and self.is_editable + + def set_editor_value(self, model, row, column, value): + """ Set the edited value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being set. + column : sequence of int + The column in the data model being set. + value : any + The value being set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + if self.is_valid(model, row, column, value): + model.set_value(row, column, value) + else: + raise DataViewSetError("Invalid value set: {!r}".format(value)) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/enum_value.py python-pyface-7.4.0/pyface/data_view/value_types/enum_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/enum_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/enum_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,173 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Callable, List + +from .editable_value import EditableValue + + +class EnumValue(EditableValue): + """ Editable value that takes one of a collection of pre-set values. + + Each value can be associated with text, colors and images by supplying + functions ``format``, ``colors`` and ``images``, respectively. + """ + + #: The list of values which are allowed for the value. + values = List() + + #: A function that converts a value to a string for display. + format = Callable(str, update_value_type=True) + + #: A map from valid values to colors. + colors = Callable(None, update_value_type=True) + + #: A map from valid values to images. + images = Callable(None, update_value_type=True) + + def is_valid(self, model, row, column, value): + """ Whether or not the value is valid for the data item specified. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + value : any + The value to validate. + + Returns + ------- + is_valid : bool + Whether or not the value is valid. + """ + return value in self.values + + def has_text(self, model, row, column): + """ Get the display text from the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + The text to display. + """ + return self.format is not None + + def get_text(self, model, row, column): + """ Get the display text from the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + The text to display. + """ + return self.format(model.get_value(row, column)) + + def has_color(self, model, row, column): + """ Whether or not the value has color data. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_color : bool + Whether or not the value has data-associated color + values. + """ + return self.colors is not None + + def get_color(self, model, row, column): + """ Get data-associated colour values for the given item. + + The default implementation returns white. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + color : Color instance + The color associated with the cell. + """ + return self.colors(model.get_value(row, column)) + + def has_image(self, model, row, column): + """ Whether or not the value has an image associated with it. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_image : bool + Whether or not the value has an image associated with it. + """ + return self.images is not None + + def get_image(self, model, row, column): + """ An image associated with the underlying value. + + The default implementation returns None. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + image : IImage + The image associated with the underlying value. + """ + return self.images(model.get_value(row, column)) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/no_value.py python-pyface-7.4.0/pyface/data_view/value_types/no_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/no_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/no_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,31 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.data_view.abstract_value_type import AbstractValueType + + +class NoValue(AbstractValueType): + """ A ValueType that has no data in any channel. """ + + def has_editor_value(self, model, row, column): + return False + + def has_text(self, model, row, column): + return False + + def has_image(self, model, row, column): + return False + + def has_tooltip(self, model, row, column): + return False + + +#: Standard instance of the NoValue class, since it has no state. +no_value = NoValue() diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/numeric_value.py python-pyface-7.4.0/pyface/data_view/value_types/numeric_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/numeric_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/numeric_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,149 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import locale +from math import inf + +from traits.api import Callable, Float + +from pyface.data_view.data_view_errors import DataViewSetError +from .editable_value import EditableValue + + +def format_locale(value): + return "{:n}".format(value) + + +class NumericValue(EditableValue): + """ Data channels for a numeric value. + """ + + #: The minimum value for the numeric value. + minimum = Float(-inf) + + #: The maximum value for the numeric value. + maximum = Float(inf) + + #: A function that converts to the numeric type. + evaluate = Callable() + + #: A function that converts the required type to a string for display. + format = Callable(format_locale, update_value_type=True) + + #: A function that converts the required type from a display string. + unformat = Callable(locale.delocalize) + + def is_valid(self, model, row, column, value): + """ Whether or not the value within the specified range. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + value : any + The value to validate. + + Returns + ------- + is_valid : bool + Whether or not the value is valid. + """ + try: + return self.minimum <= value <= self.maximum + except Exception: + return False + + def get_editor_value(self, model, row, column): + """ Get the numerical value for the editor to use. + + This uses the evaluate method to convert the underlying value to a + number. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + editor_value : number + Whether or not the value is editable. + """ + # evaluate is needed to convert numpy types to python types so + # Qt recognises them + return self.evaluate(model.get_value(row, column)) + + def get_text(self, model, row, column): + """ Get the display text from the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + The text to display. + """ + return self.format(model.get_value(row, column)) + + def set_text(self, model, row, column, text): + """ Set the text of the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + text : str + The text to set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + try: + value = self.evaluate(self.unformat(text)) + except ValueError: + raise DataViewSetError( + "Can't evaluate value: {!r}".format(text) + ) + self.set_editor_value(model, row, column, value) + + +class IntValue(NumericValue): + """ Data channels for an integer value. + """ + + evaluate = Callable(int) + + +class FloatValue(NumericValue): + """ Data channels for a floating point value. + """ + + evaluate = Callable(float) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_bool_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_bool_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_bool_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_bool_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,85 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.abstract_value_type import CheckState +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.value_types.bool_value import BoolValue + + +class TestBoolValue(TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock(return_value=True) + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock() + + def test_defaults(self): + value = BoolValue() + self.assertEqual(value.true_text, "") + self.assertEqual(value.false_text, "") + + def test_has_text_default(self): + value = BoolValue() + has_text = value.has_text(self.model, [0], [0]) + self.assertFalse(has_text) + + def test_has_text(self): + value = BoolValue(true_text="Yes", false_text="No") + has_text = value.has_text(self.model, [0], [0]) + self.assertTrue(has_text) + + def test_get_text_default(self): + value = BoolValue() + text = value.get_text(self.model, [0], [0]) + self.assertEqual(text, "") + + self.model.get_value = Mock(return_value=False) + text = value.get_text(self.model, [0], [0]) + self.assertEqual(text, "") + + def test_get_text(self): + value = BoolValue(true_text="Yes", false_text="No") + text = value.get_text(self.model, [0], [0]) + self.assertEqual(text, "Yes") + + self.model.get_value = Mock(return_value=False) + text = value.get_text(self.model, [0], [0]) + self.assertEqual(text, "No") + + def test_get_check_state(self): + value = BoolValue() + check_state = value.get_check_state(self.model, [0], [0]) + self.assertEqual(check_state, CheckState.CHECKED) + + def test_get_check_state_false(self): + value = BoolValue() + self.model.get_value = Mock(return_value=False) + check_state = value.get_check_state(self.model, [0], [0]) + self.assertEqual(check_state, CheckState.UNCHECKED) + + def test_set_check_state(self): + value = BoolValue() + value.set_check_state(self.model, [0], [0], CheckState.CHECKED) + self.model.set_value.assert_called_once_with([0], [0], True) + + def test_set_check_state_unchecked(self): + value = BoolValue() + value.set_check_state(self.model, [0], [0], CheckState.UNCHECKED) + self.model.set_value.assert_called_once_with([0], [0], False) + + def test_set_check_state_no_set_value(self): + self.model.can_set_value = Mock(return_value=False) + value = BoolValue() + with self.assertRaises(DataViewSetError): + value.set_text(self.model, [0], [0], CheckState.CHECKED) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_color_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_color_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_color_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_color_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,83 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.color import Color +from pyface.data_view.abstract_data_model import DataViewSetError +from pyface.data_view.value_types.color_value import ColorValue + + +class TestColorValue(TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock( + return_value=Color(rgba=(0.4, 0.2, 0.6, 0.8)), + ) + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock() + + def test_defaults(self): + value = ColorValue() + self.assertTrue(value.is_editable) + + def test_is_valid(self): + value = ColorValue() + self.assertTrue(value.is_valid(None, [0], [0], Color())) + + def test_get_editor_value(self): + value = ColorValue() + editable = value.get_editor_value(self.model, [0], [0]) + self.assertEqual(editable, "#663399CC") + + def test_set_editor_value(self): + value = ColorValue() + value.set_editor_value(self.model, [0], [0], "#3399CC66") + self.model.set_value.assert_called_once_with( + [0], + [0], + Color(rgba=(0.2, 0.6, 0.8, 0.4)), + ) + + def test_get_text(self): + value = ColorValue() + editable = value.get_text(self.model, [0], [0]) + self.assertEqual(editable, "#663399CC") + + def test_set_text(self): + value = ColorValue() + value.set_text(self.model, [0], [0], "red") + self.model.set_value.assert_called_once_with( + [0], + [0], + Color(rgba=(1.0, 0.0, 0.0, 1.0)), + ) + + def test_set_text_error(self): + value = ColorValue() + with self.assertRaises(DataViewSetError): + value.set_text(self.model, [0], [0], "not a real color") + + def test_set_text_no_set_value(self): + self.model.can_set_value = Mock(return_value=False) + value = ColorValue() + value.set_text(self.model, [0], [0], "red") + self.model.set_value.assert_called_once_with( + [0], + [0], + Color(rgba=(1.0, 0.0, 0.0, 1.0)), + ) + + def test_get_color(self): + value = ColorValue() + editable = value.get_color(self.model, [0], [0]) + self.assertEqual(editable, Color(rgba=(0.4, 0.2, 0.6, 0.8))) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_constant_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_constant_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_constant_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_constant_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,140 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from traits.testing.api import UnittestTools + +from pyface.color import Color +from pyface.data_view.value_types.constant_value import ConstantValue +from pyface.image_resource import ImageResource + + +class TestConstantValue(UnittestTools, TestCase): + + def setUp(self): + self.model = Mock() + + def test_defaults(self): + value_type = ConstantValue() + self.assertEqual(value_type.text, "") + self.assertEqual(value_type.tooltip, "") + + def test_has_editor_value(self): + value_type = ConstantValue() + self.assertFalse(value_type.has_editor_value(self.model, [0], [0])) + + def test_has_text(self): + value_type = ConstantValue() + self.assertFalse(value_type.has_text(self.model, [0], [0])) + + def test_has_text_true(self): + value_type = ConstantValue(text="something") + self.assertTrue(value_type.has_text(self.model, [0], [0])) + + def test_get_text(self): + value_type = ConstantValue(text="something") + self.assertEqual( + value_type.get_text(self.model, [0], [0]), + "something" + ) + + def test_text_changed(self): + value_type = ConstantValue() + with self.assertTraitChanges(value_type, 'updated'): + value_type.text = 'something' + self.assertEqual(value_type.text, 'something') + + def test_has_color_default(self): + value_type = ConstantValue() + self.assertFalse(value_type.has_color(self.model, [0], [0])) + + def test_has_color(self): + value_type = ConstantValue(color=Color(rgba=(0.4, 0.2, 0.6, 0.8))) + self.assertTrue(value_type.has_color(self.model, [0], [0])) + + def test_get_color_default(self): + value_type = ConstantValue() + self.assertIsNone(value_type.get_color(self.model, [0], [0])) + + def test_get_color(self): + value_type = ConstantValue(color='rebeccapurple') + self.assertEqual( + value_type.get_color(self.model, [0], [0]), + Color(rgba=(0.4, 0.2, 0.6, 1.0)) + ) + + def test_get_color_changed(self): + value_type = ConstantValue() + with self.assertTraitChanges(value_type, 'updated'): + value_type.color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual( + value_type.get_color(self.model, [0], [0]), + Color(rgba=(0.4, 0.2, 0.6, 0.8)) + ) + + def test_get_color_rgba_changed(self): + value_type = ConstantValue(color=Color()) + with self.assertTraitChanges(value_type, 'updated'): + value_type.color.rgba = (0.4, 0.2, 0.6, 0.8) + self.assertEqual( + value_type.get_color(self.model, [0], [0]), + Color(rgba=(0.4, 0.2, 0.6, 0.8)) + ) + + def test_has_image(self): + value_type = ConstantValue() + self.assertFalse(value_type.has_image(self.model, [0], [0])) + + def test_has_image_true(self): + value_type = ConstantValue(image="question") + self.assertTrue(value_type.has_image(self.model, [0], [0])) + + def test_get_image(self): + image = ImageResource("question") + value_type = ConstantValue(image=image) + self.assertEqual( + value_type.get_image(self.model, [0], [0]), + image + ) + + def test_get_image_none(self): + value_type = ConstantValue() + image = value_type.get_image(self.model, [0], [0]) + self.assertEqual(image.name, "image_not_found") + + def test_image_changed(self): + value_type = ConstantValue() + image = ImageResource("question") + with self.assertTraitChanges(value_type, 'updated'): + value_type.image = image + self.assertEqual(value_type.image, image) + + def test_has_tooltip(self): + value_type = ConstantValue() + self.assertFalse(value_type.has_tooltip(self.model, [0], [0])) + + def test_has_tooltip_true(self): + value_type = ConstantValue(tooltip="something") + self.assertTrue(value_type.has_tooltip(self.model, [0], [0])) + + def test_get_tooltip(self): + value_type = ConstantValue(tooltip="something") + self.assertEqual( + value_type.get_tooltip(self.model, [0], [0]), + "something" + ) + + def test_tooltip_changed(self): + value_type = ConstantValue() + with self.assertTraitChanges(value_type, 'updated'): + value_type.tooltip = 'something' + self.assertEqual(value_type.tooltip, 'something') diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_editable_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_editable_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_editable_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_editable_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,74 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from traits.testing.api import UnittestTools + +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.value_types.editable_value import EditableValue + + +class EditableWithValid(EditableValue): + + def is_valid(self, model, row, column, value): + return value >= 0 + + +class TestEditableValue(UnittestTools, TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock(return_value=1.0) + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock() + + def test_default(self): + value_type = EditableValue() + self.assertTrue(value_type.is_editable) + + def test_is_valid(self): + value_type = EditableValue() + result = value_type.is_valid(self.model, [0], [0], 2.0) + self.assertTrue(result) + + def test_has_editor_value(self): + value_type = EditableValue() + result = value_type.has_editor_value(self.model, [0], [0]) + self.assertTrue(result) + + def test_has_editor_value_not_editable(self): + value_type = EditableValue(is_editable=False) + result = value_type.has_editor_value(self.model, [0], [0]) + self.assertFalse(result) + + def test_set_editor_value(self): + value_type = EditableValue() + value_type.set_editor_value(self.model, [0], [0], 2.0) + self.model.set_value.assert_called_once_with([0], [0], 2.0) + + def test_set_editor_value_set_value_raises(self): + self.model.set_value = Mock(side_effect=DataViewSetError) + value_type = EditableValue(is_editable=False) + with self.assertRaises(DataViewSetError): + value_type.set_editor_value(self.model, [0], [0], 2.0) + self.model.set_value.assert_called_once_with([0], [0], 2.0) + + def test_set_editor_value_not_valid(self): + value_type = EditableWithValid() + with self.assertRaises(DataViewSetError): + value_type.set_editor_value(self.model, [0], [0], -1.0) + self.model.set_value.assert_not_called() + + def test_is_editable_update(self): + value_type = EditableValue() + with self.assertTraitChanges(value_type, 'updated', count=1): + value_type.is_editable = False diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_enum_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_enum_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_enum_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_enum_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,110 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.value_types.enum_value import EnumValue + + +class TestEnumValue(TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock(return_value=1) + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock() + + def test_defaults(self): + value = EnumValue() + self.assertTrue(value.is_editable) + + def test_is_valid(self): + value = EnumValue(values=[1, 2]) + self.assertTrue(value.is_valid(None, [0], [0], 1)) + + def test_is_valid_false(self): + value = EnumValue(values=[2]) + self.assertFalse(value.is_valid(None, [0], [0], 1)) + + def test_get_editor_value(self): + value = EnumValue() + editable = value.get_editor_value(self.model, [0], [0]) + self.assertEqual(editable, 1) + + def test_set_editor_value(self): + value = EnumValue(values=[1, 2]) + value.set_editor_value(self.model, [0], [0], 2) + self.model.set_value.assert_called_once_with([0], [0], 2) + + def test_set_editor_value_bad(self): + value = EnumValue(values=[1]) + with self.assertRaises(DataViewSetError): + value.set_editor_value(self.model, [0], [0], 2) + + def test_has_text(self): + value = EnumValue(values=[1, 2]) + has_text = value.has_text(self.model, [0], [0]) + self.assertTrue(has_text) + + def test_has_text_false(self): + value = EnumValue(values=[1, 2], format=None) + has_text = value.has_text(self.model, [0], [0]) + self.assertFalse(has_text) + + def test_get_text(self): + value = EnumValue(values=[1, 2]) + text = value.get_text(self.model, [0], [0]) + self.assertEqual(text, "1") + + def test_has_color_false(self): + value = EnumValue(values=[1, 2]) + has_color = value.has_color(self.model, [0], [0]) + self.assertFalse(has_color) + + def test_has_color_true(self): + value = EnumValue(values=[1, 2], colors=lambda x: "dummy") + has_color = value.has_color(self.model, [0], [0]) + self.assertTrue(has_color) + + def test_get_color(self): + value = EnumValue(values=[1, 2], colors=lambda x: "dummy") + color = value.get_color(self.model, [0], [0]) + self.assertEqual(color, "dummy") + + def test_color_function_none(self): + value = EnumValue(values=[1, 2], colors=lambda x: None) + has_color = value.has_color(self.model, [0], [0]) + self.assertTrue(has_color) + color = value.get_color(self.model, [0], [0]) + self.assertIsNone(color) + + def test_has_image_false(self): + value = EnumValue(values=[1, 2]) + has_image = value.has_image(self.model, [0], [0]) + self.assertFalse(has_image) + + def test_has_image_true(self): + value = EnumValue(values=[1, 2], images=lambda x: "dummy") + has_image = value.has_image(self.model, [0], [0]) + self.assertTrue(has_image) + + def test_get_image(self): + value = EnumValue(values=[1, 2], images=lambda x: "dummy") + image = value.get_image(self.model, [0], [0]) + self.assertEqual(image, "dummy") + + def test_image_function_none(self): + value = EnumValue(values=[1, 2], images=lambda x: None) + has_image = value.has_image(self.model, [0], [0]) + self.assertTrue(has_image) + image = value.get_image(self.model, [0], [0]) + self.assertIsNone(image) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_no_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_no_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_no_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_no_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,36 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.value_types.no_value import NoValue + + +class TestNoValue(TestCase): + + def setUp(self): + self.model = Mock() + + def test_has_editor_value(self): + value_type = NoValue() + self.assertFalse(value_type.has_editor_value(self.model, [0], [0])) + + def test_has_text(self): + value_type = NoValue() + self.assertFalse(value_type.has_text(self.model, [0], [0])) + + def test_has_image(self): + value_type = NoValue() + self.assertFalse(value_type.has_image(self.model, [0], [0])) + + def test_has_tooltip(self): + value_type = NoValue() + self.assertFalse(value_type.has_tooltip(self.model, [0], [0])) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_numeric_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_numeric_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_numeric_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_numeric_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,101 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.data_view_errors import DataViewSetError +from pyface.data_view.value_types.numeric_value import ( + FloatValue, IntValue, NumericValue, format_locale +) + + +class TestNumericValue(TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock(return_value=1.0) + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock(return_value=True) + + def test_defaults(self): + value = NumericValue() + self.assertIsNone(value.evaluate) + + def test_is_valid(self): + value = NumericValue() + self.assertTrue(value.is_valid(None, [0], [0], 0.0)) + + def test_is_valid_false(self): + value = NumericValue(minimum=0.0, maximum=1.0) + self.assertFalse(value.is_valid(None, [0], [0], -1.0)) + + def test_is_valid_error(self): + value = NumericValue() + self.assertFalse(value.is_valid(None, [0], [0], 'invalid')) + + def test_get_editor_value(self): + value = NumericValue(evaluate=float) + editable = value.get_editor_value(self.model, [0], [0]) + + self.assertEqual(editable, 1.0) + + def test_set_editor_value(self): + value = NumericValue(evaluate=float) + value.set_editor_value(self.model, [0], [0], 1.0) + self.model.set_value.assert_called_once_with([0], [0], 1.0) + + def test_set_editor_value_invalid(self): + value = NumericValue(minimum=0.0, maximum=1.0) + with self.assertRaises(DataViewSetError): + value.set_editor_value(self.model, [0], [0], -1.0) + self.model.set_value.assert_not_called() + + def test_set_editor_value_error(self): + value = NumericValue(minimum=0.0, maximum=1.0) + with self.assertRaises(DataViewSetError): + value.set_editor_value(self.model, [0], [0], 'invalid') + self.model.set_value.assert_not_called() + + def test_get_text(self): + value = NumericValue() + text = value.get_text(self.model, [0], [0]) + self.assertEqual(text, format_locale(1.0)) + + def test_set_text(self): + value = NumericValue(evaluate=float) + value.set_text(self.model, [0], [0], format_locale(1.1)) + self.model.set_value.assert_called_once_with([0], [0], 1.1) + + def test_set_text_invalid(self): + value = NumericValue(evaluate=float, minimum=0.0, maximum=1.0) + with self.assertRaises(DataViewSetError): + value.set_text(self.model, [0], [0], format_locale(1.1)) + self.model.set_value.assert_not_called() + + def test_set_text_error(self): + value = NumericValue(evaluate=float) + with self.assertRaises(DataViewSetError): + value.set_text(self.model, [0], [0], "invalid") + self.model.set_value.assert_not_called() + + +class TestIntValue(TestCase): + + def test_defaults(self): + value = IntValue() + self.assertIs(value.evaluate, int) + + +class TestFloatValue(TestCase): + + def test_defaults(self): + value = FloatValue() + self.assertIs(value.evaluate, float) diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/tests/test_text_value.py python-pyface-7.4.0/pyface/data_view/value_types/tests/test_text_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/tests/test_text_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/tests/test_text_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,57 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase +from unittest.mock import Mock + +from pyface.data_view.value_types.text_value import TextValue + + +class TestTextValue(TestCase): + + def setUp(self): + self.model = Mock() + self.model.get_value = Mock(return_value="test") + self.model.can_set_value = Mock(return_value=True) + self.model.set_value = Mock() + + def test_defaults(self): + value = TextValue() + self.assertTrue(value.is_editable) + + def test_is_valid(self): + value = TextValue() + self.assertTrue(value.is_valid(None, [0], [0], "test")) + + def test_get_editor_value(self): + value = TextValue() + editable = value.get_editor_value(self.model, [0], [0]) + self.assertEqual(editable, "test") + + def test_set_editor_value(self): + value = TextValue() + value.set_editor_value(self.model, [0], [0], "test") + self.model.set_value.assert_called_once_with([0], [0], "test") + + def test_get_text(self): + value = TextValue() + editable = value.get_text(self.model, [0], [0]) + self.assertEqual(editable, "test") + + def test_set_text(self): + value = TextValue() + value.set_text(self.model, [0], [0], "test") + self.model.set_value.assert_called_once_with([0], [0], "test") + + def test_set_text_no_set_value(self): + self.model.can_set_value = Mock(return_value=False) + value = TextValue() + value.set_text(self.model, [0], [0], "test") + self.model.set_value.assert_called_once_with([0], [0], "test") diff -Nru python-pyface-6.1.2/pyface/data_view/value_types/text_value.py python-pyface-7.4.0/pyface/data_view/value_types/text_value.py --- python-pyface-6.1.2/pyface/data_view/value_types/text_value.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/data_view/value_types/text_value.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,65 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Callable + +from .editable_value import EditableValue + + +class TextValue(EditableValue): + """ Editable value that presents a string value. + """ + + #: A function that converts the value to a string for display. + format = Callable(str, update_value_type=True) + + #: A function that converts to a value from a display string. + unformat = Callable(str) + + def get_text(self, model, row, column): + """ Get the display text from the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : str + The text to display. + """ + return self.format(model.get_value(row, column)) + + def set_text(self, model, row, column, text): + """ Set the text of the underlying value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + text : str + The text to set. + + Raises + ------- + DataViewSetError + If the value cannot be set. + """ + value = self.unformat(text) + self.set_editor_value(model, row, column, value) diff -Nru python-pyface-6.1.2/pyface/dialog.py python-pyface-7.4.0/pyface/dialog.py --- python-pyface-6.1.2/pyface/dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The abstract implementation of all pyface dialogs. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -Dialog = toolkit_object('dialog:Dialog') -#### EOF ###################################################################### +Dialog = toolkit_object("dialog:Dialog") diff -Nru python-pyface-6.1.2/pyface/directory_dialog.py python-pyface-7.4.0/pyface/directory_dialog.py --- python-pyface-6.1.2/pyface/directory_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/directory_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a dialog that allows the user to browse for a directory. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -DirectoryDialog = toolkit_object('directory_dialog:DirectoryDialog') -#### EOF ###################################################################### +DirectoryDialog = toolkit_object("directory_dialog:DirectoryDialog") diff -Nru python-pyface-6.1.2/pyface/dock/api.py python-pyface-7.4.0/pyface/dock/api.py --- python-pyface-6.1.2/pyface/dock/api.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,12 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 10/18/2005 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ Pyface 'DockWindow' support. @@ -24,15 +17,22 @@ separate notebook-like tab within the region. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- - from .dock_window import DockWindow, DockWindowHandler -from .dock_sizer import DockSizer, DockSection, DockRegion, DockControl, \ - DockStyle, DOCK_LEFT, DOCK_RIGHT, DOCK_TOP, DOCK_BOTTOM, \ - SetStructureHandler, add_feature, DockGroup +from .dock_sizer import ( + DockSizer, + DockSection, + DockRegion, + DockControl, + DockStyle, + DOCK_LEFT, + DOCK_RIGHT, + DOCK_TOP, + DOCK_BOTTOM, + SetStructureHandler, + add_feature, + DockGroup, +) from .idockable import IDockable diff -Nru python-pyface-6.1.2/pyface/dock/dock_sizer.py python-pyface-7.4.0/pyface/dock/dock_sizer.py --- python-pyface-6.1.2/pyface/dock/dock_sizer.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/dock_sizer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,12 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 10/18/2005 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ Pyface 'DockSizer' support. @@ -22,21 +15,35 @@ and the notebook tabs and dragbars associated with the DockWindow. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- - -from __future__ import print_function -import wx, sys - -from traits.api import HasPrivateTraits, Instance, Str, Int, List, Enum, \ - Tuple, Any, Range, Property, Callable, Constant, Event, Undefined, Bool, \ - cached_property +import sys + +import wx + +from traits.api import ( + HasPrivateTraits, + Instance, + Str, + Int, + List, + Enum, + Tuple, + Any, + Range, + Property, + Callable, + Constant, + Event, + Undefined, + Bool, + cached_property, + observe, +) from traitsui.dock_window_theme import dock_window_theme from traitsui.wx.helper import BufferDC from pyface.api import SystemMetrics from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from pyface.wx.drag_and_drop import PythonDropSource from pyface.timer.api import do_later, do_after from .idockable import IDockable @@ -44,12 +51,11 @@ # Define version dependent values: -wx_26 = (wx.__version__[:3] == '2.6') -is_mac = (sys.platform == 'darwin') +is_mac = sys.platform == "darwin" -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Constants: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Standard font text height: text_dy = 13 @@ -61,79 +67,83 @@ DragBarSize = 14 # Images sizes (in pixels): -CloseTabSize = 10 +CloseTabSize = 10 CloseDragSize = 7 # Tab drawing states: TabInactive = 0 -TabActive = 1 -TabHover = 2 +TabActive = 1 +TabHover = 2 -NormalStates = ( TabInactive, TabActive ) -NotActiveStates = ( TabInactive, TabHover ) +NormalStates = (TabInactive, TabActive) +NotActiveStates = (TabInactive, TabHover) # Feature overlay colors: -FeatureBrushColor = ( 255, 255, 255 ) -FeaturePenColor = ( 92, 92, 92 ) +FeatureBrushColor = (255, 255, 255) +FeaturePenColor = (92, 92, 92) # Color used to update the screen while dragging a splitter bar: -DragColor = ( 96, 96, 96 ) +DragColor = (96, 96, 96) # Color used to update the screen while showing a docking operation in progress: -DockColorBrush = ( 255, 0, 0, 96 ) +DockColorBrush = (255, 0, 0, 96) # Drop Info kinds: -DOCK_TOP = 0 -DOCK_BOTTOM = 1 -DOCK_LEFT = 2 -DOCK_RIGHT = 3 -DOCK_TAB = 4 -DOCK_TABADD = 5 -DOCK_BAR = 6 -DOCK_NONE = 7 +DOCK_TOP = 0 +DOCK_BOTTOM = 1 +DOCK_LEFT = 2 +DOCK_RIGHT = 3 +DOCK_TAB = 4 +DOCK_TABADD = 5 +DOCK_BAR = 6 +DOCK_NONE = 7 DOCK_SPLITTER = 8 -DOCK_EXPORT = 9 +DOCK_EXPORT = 9 # Splitter states: -SPLIT_VLEFT = 0 +SPLIT_VLEFT = 0 SPLIT_VMIDDLE = 1 -SPLIT_VRIGHT = 2 -SPLIT_HTOP = 3 +SPLIT_VRIGHT = 2 +SPLIT_HTOP = 3 SPLIT_HMIDDLE = 4 SPLIT_HBOTTOM = 5 # Empty clipping area: -no_clip = ( 0, 0, 0, 0 ) +no_clip = (0, 0, 0, 0) # Valid sequence types: -SequenceType = ( list, tuple ) +SequenceType = (list, tuple) # Tab scrolling directions: -SCROLL_LEFT = 1 +SCROLL_LEFT = 1 SCROLL_RIGHT = 2 -SCROLL_TO = 3 +SCROLL_TO = 3 # Feature modes: -FEATURE_NONE = -1 # Has no features -FEATURE_NORMAL = 0 # Has normal features -FEATURE_CHANGED = 1 # Has changed or new features -FEATURE_DROP = 2 # Has drag data compatible drop features -FEATURE_DISABLED = 3 # Has feature icon, but is currently disabled -FEATURE_VISIBLE = 4 # Has visible features (mouseover mode) -FEATURE_DROP_VISIBLE = 5 # Has visible drop features (mouseover mode) -FEATURE_PRE_NORMAL = 6 # Has normal features (but has not been drawn yet) -FEATURE_EXTERNAL_DRAG = 256 # A drag started in another DockWindow is active +FEATURE_NONE = -1 # Has no features +FEATURE_NORMAL = 0 # Has normal features +FEATURE_CHANGED = 1 # Has changed or new features +FEATURE_DROP = 2 # Has drag data compatible drop features +FEATURE_DISABLED = 3 # Has feature icon, but is currently disabled +FEATURE_VISIBLE = 4 # Has visible features (mouseover mode) +FEATURE_DROP_VISIBLE = 5 # Has visible drop features (mouseover mode) +FEATURE_PRE_NORMAL = 6 # Has normal features (but has not been drawn yet) +FEATURE_EXTERNAL_DRAG = 256 # A drag started in another DockWindow is active # Feature sets: -NO_FEATURE_ICON = ( FEATURE_NONE, FEATURE_DISABLED, FEATURE_VISIBLE, - FEATURE_DROP_VISIBLE ) -FEATURES_VISIBLE = ( FEATURE_VISIBLE, FEATURE_DROP_VISIBLE ) -FEATURE_END_DROP = ( FEATURE_DROP, FEATURE_VISIBLE, FEATURE_DROP_VISIBLE ) -NORMAL_FEATURES = ( FEATURE_NORMAL, FEATURE_DISABLED ) +NO_FEATURE_ICON = ( + FEATURE_NONE, + FEATURE_DISABLED, + FEATURE_VISIBLE, + FEATURE_DROP_VISIBLE, +) +FEATURES_VISIBLE = (FEATURE_VISIBLE, FEATURE_DROP_VISIBLE) +FEATURE_END_DROP = (FEATURE_DROP, FEATURE_VISIBLE, FEATURE_DROP_VISIBLE) +NORMAL_FEATURES = (FEATURE_NORMAL, FEATURE_DISABLED) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Global data: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Standard font used by the DockWindow: standard_font = None @@ -141,28 +151,29 @@ # The list of available DockWindowFeatures: features = [] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Trait definitions: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Bounds (i.e. x, y, dx, dy): -Bounds = Tuple( Int, Int, Int, Int ) +Bounds = Tuple(Int, Int, Int, Int) # Docking drag bar style: -DockStyle = Enum( 'horizontal', 'vertical', 'tab', 'fixed' ) +DockStyle = Enum("horizontal", "vertical", "tab", "fixed") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Adds a new DockWindowFeature class to the list of available features: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -def add_feature ( feature_class ): +def add_feature(feature_class): """ Adds a new DockWindowFeature class to the list of available features. """ global features - result = (feature_class not in features) + result = feature_class not in features if result: - features.append( feature_class ) + features.append(feature_class) # Mark the feature class as having been installed: if feature_class.state == 0: @@ -170,94 +181,104 @@ return result -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Sets the standard font to use for a specified device context: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -def set_standard_font ( dc ): +def set_standard_font(dc): """ Sets the standard font to use for a specified device context. """ global standard_font if standard_font is None: - standard_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT ) + standard_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) - dc.SetFont( standard_font ) + dc.SetFont(standard_font) return dc -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Clears a window to the standard background color: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -def clear_window ( window ): +def clear_window(window): """ Clears a window to the standard background color. """ bg_color = SystemMetrics().dialog_background_color - bg_color = wx.Colour(bg_color[0]*255, bg_color[1]*255, bg_color[2]*255) + bg_color = wx.Colour( + int(bg_color[0] * 255), int(bg_color[1] * 255), int(bg_color[2] * 255) + ) + + dx, dy = window.GetSize().Get() + dc = wx.PaintDC(window) + dc.SetBrush(wx.Brush(bg_color, wx.SOLID)) + dc.SetPen(wx.TRANSPARENT_PEN) + dc.DrawRectangle(0, 0, dx, dy) - dx, dy = window.GetSizeTuple() - dc = wx.PaintDC( window ) - dc.SetBrush( wx.Brush( bg_color, wx.SOLID ) ) - dc.SetPen( wx.TRANSPARENT_PEN ) - dc.DrawRectangle( 0, 0, dx, dy ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Gets a temporary device context for a specified window to draw in: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -def get_dc ( window ): + +def get_dc(window): """ Gets a temporary device context for a specified window to draw in. """ if is_mac: - dc = wx.ClientDC( window ) - x, y = window.GetPositionTuple() - dx, dy = window.GetSizeTuple() + dc = wx.ClientDC(window) + x, y = window.GetPosition().Get() + dx, dy = window.GetSize().Get() while True: window = window.GetParent() if window is None: break - xw, yw = window.GetPositionTuple() - dxw, dyw = window.GetSizeTuple() - dx, dy = min( dx, dxw - x ), min( dy, dyw - y ) + xw, yw = window.GetPosition().Get() + dxw, dyw = window.GetSize().Get() + dx, dy = min(dx, dxw - x), min(dy, dyw - y) x += xw y += yw - dc.SetClippingRegion( 0, 0, dx, dy ) + dc.SetClippingRegion(0, 0, dx, dy) + + return (dc, 0, 0) - return ( dc, 0, 0 ) + x, y = window.ClientToScreen(0, 0) + return (wx.ScreenDC(), x, y) - x, y = window.ClientToScreenXY( 0, 0 ) - return ( wx.ScreenDC(), x, y ) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'DockImages' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class DockImages ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class DockImages(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Image for closing a tab: - close_tab = Instance( ImageResource, ImageResource( 'close_tab' ) ) + close_tab = Image(ImageResource("close_tab")) # Image for closing a drag bar: - close_drag = Instance( ImageResource, ImageResource( 'close_drag' ) ) + close_drag = Image(ImageResource("close_drag")) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Initalizes the object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def __init__ ( self, **traits ): + def __init__(self, **traits): """ Initializes the object. """ - super( DockImages, self ).__init__( **traits ) + super().__init__(**traits) self._lazy_init_done = False - def init ( self ): + def init(self): """ Initializes the parts of the object that depend on the toolkit selection. """ @@ -268,95 +289,115 @@ self._lazy_init_done = True - self._close_tab = self.close_tab.create_image().ConvertToBitmap() + self._close_tab = self.close_tab.create_image().ConvertToBitmap() self._close_drag = self.close_drag.create_image().ConvertToBitmap() self._splitter_images = [ - ImageResource( name ).create_image().ConvertToBitmap() - for name in [ 'sv_left', 'sv_middle', 'sv_right', - 'sh_top', 'sh_middle', 'sh_bottom' ] + ImageResource(name).create_image().ConvertToBitmap() + for name in [ + "sv_left", + "sv_middle", + "sv_right", + "sh_top", + "sh_middle", + "sh_bottom", + ] ] self._tab_scroller_images = [ - ImageResource( name ).create_image().ConvertToBitmap() - for name in [ 'tab_scroll_l', 'tab_scroll_r', 'tab_scroll_lr' ] + ImageResource(name).create_image().ConvertToBitmap() + for name in ["tab_scroll_l", "tab_scroll_r", "tab_scroll_lr"] ] self._tab_scroller_dx = self._tab_scroller_images[0].GetWidth() self._tab_scroller_dy = self._tab_scroller_images[0].GetHeight() self._feature_images = [ - ImageResource( name ).create_image().ConvertToBitmap() - for name in [ 'tab_feature_normal', 'tab_feature_changed', - 'tab_feature_drop', 'tab_feature_disabled', - 'bar_feature_normal', 'bar_feature_changed', - 'bar_feature_drop', 'bar_feature_disabled' ] + ImageResource(name).create_image().ConvertToBitmap() + for name in [ + "tab_feature_normal", + "tab_feature_changed", + "tab_feature_drop", + "tab_feature_disabled", + "bar_feature_normal", + "bar_feature_changed", + "bar_feature_drop", + "bar_feature_disabled", + ] ] - self._tab_feature_width = self._feature_images[0].GetWidth() + self._tab_feature_width = self._feature_images[0].GetWidth() self._tab_feature_height = self._feature_images[0].GetHeight() - self._bar_feature_width = self._feature_images[3].GetWidth() + self._bar_feature_width = self._feature_images[3].GetWidth() self._bar_feature_height = self._feature_images[3].GetHeight() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the splitter image to use for a specified splitter state: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_splitter_image ( self, state ): + def get_splitter_image(self, state): """ Returns the splitter image to use for a specified splitter state. """ - return self._splitter_images[ state ] + return self._splitter_images[state] - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the feature image to use for a specified feature state: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_feature_image ( self, state, is_tab = True ): + def get_feature_image(self, state, is_tab=True): """ Returns the feature image to use for a specified feature state. """ if is_tab: - return self._feature_images[ state ] + return self._feature_images[state] + + return self._feature_images[state + 3] - return self._feature_images[ state + 3 ] # Creates a singleton instance of the class: DockImages = DockImages() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'DockItem' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class DockItem ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class DockItem(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The parent of this item: - parent = Any + parent = Any() + + # The control associated with this item (used in subclasses): + control = Instance(wx.Control) # The DockWindow that owns this item: - owner = Property( depends_on = 'parent' ) + owner = Property(observe="parent") # Bounds of the item: bounds = Bounds + # The name of this item (used in subclasses): + name = Str() + # Current width of the item: - width = Int( -1 ) + width = Int(-1) # Current height of the item: - height = Int( -1 ) + height = Int(-1) # Bounds of the item's drag bar or tab: drag_bounds = Bounds # The current tab state: - tab_state = Any + tab_state = Any() # The tab displayable version of the control's UI name: - tab_name = Property( depends_on = 'name' ) + tab_name = Property(observe="name") # Width of the item's tab: - tab_width = Property( depends_on = 'control, tab_state, tab_name' ) + tab_width = Property(observe="control, tab_state, tab_name") # The DockWindowTheme for this item's DockWindow: theme = Property @@ -365,60 +406,58 @@ tab_theme = Property # The current feature mode: - feature_mode = Enum( FEATURE_NONE, FEATURE_NORMAL, FEATURE_CHANGED, - FEATURE_DROP, FEATURE_VISIBLE, FEATURE_DROP_VISIBLE, - FEATURE_DISABLED, FEATURE_PRE_NORMAL ) + feature_mode = Enum( + FEATURE_NONE, + FEATURE_NORMAL, + FEATURE_CHANGED, + FEATURE_DROP, + FEATURE_VISIBLE, + FEATURE_DROP_VISIBLE, + FEATURE_DISABLED, + FEATURE_PRE_NORMAL, + ) # The position where the feature popup should appear: feature_popup_position = Property # The list of features for this item: - features = List + features = List() # The list of drag data compatible drop features for this item: - drop_features = List + drop_features = List() # Current active set of features: active_features = Property - # The name of this item (implemented in subclasses): - # name = Str - - # The control associated with this item (implemented in subclasses): - # control = Instance( wx.Control ) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'owner' property: - #--------------------------------------------------------------------------- - - def __init__(self, **kw): - super(DockItem, self).__init__(**kw) + # --------------------------------------------------------------------------- @cached_property - def _get_owner ( self ): + def _get_owner(self): if self.parent is None: return None return self.parent.owner - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'tab_name' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @cached_property - def _get_tab_name ( self ): + def _get_tab_name(self): name = self.name - if len( name ) > MaxTabLength: - name = '%s...%s' % ( name[ : MaxTabLength - 23 ], name[ -20: ] ) + if len(name) > MaxTabLength: + name = "%s...%s" % (name[: MaxTabLength - 23], name[-20:]) return name - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'tab_width' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @cached_property - def _get_tab_width ( self ): + def _get_tab_width(self): if self.control is None: return 0 @@ -426,44 +465,48 @@ # Calculate the size needed by the theme and margins: theme = self.tab_theme - tw = (theme.image_slice.xleft + theme.image_slice.xright + - theme.content.left + theme.content.right) + tw = ( + theme.image_slice.xleft + + theme.image_slice.xright + + theme.content.left + + theme.content.right + ) # Add feature marker width: if self.feature_mode != FEATURE_NONE: tw += DockImages._tab_feature_width + 3 # Add text width: - dc = set_standard_font( wx.ClientDC( self.control ) ) - tw += dc.GetTextExtent( self.tab_name )[0] + dc = set_standard_font(wx.ClientDC(self.control)) + tw += dc.GetTextExtent(self.tab_name)[0] # Add custom image width: image = self.get_image() if image is not None: - tw += (image.GetWidth() + 3) + tw += image.GetWidth() + 3 # Add close button width: if self.closeable: - tw += (CloseTabSize + 6) + tw += CloseTabSize + 6 # Return the computed width: return tw - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'theme' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_theme ( self ): + def _get_theme(self): if self.control is None: return dock_window_theme() return self.control.GetParent().owner.theme - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'tab_theme' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_tab_theme ( self ): + def _get_tab_theme(self): if self.tab_state == TabInactive: return self.theme.tab_inactive @@ -472,151 +515,157 @@ return self.theme.tab_hover - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'active_features' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_active_features ( self ): - if len( self.drop_features ) > 0: + def _get_active_features(self): + if len(self.drop_features) > 0: return self.drop_features return self.features - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'feature_popup_position' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_feature_popup_position ( self ): + def _get_feature_popup_position(self): x, y, dx, dy = self.drag_bounds - return wx.Point( x + 5, y + 3 ) + return wx.Point(x + 5, y + 3) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not the item is at a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def is_at ( self, x, y, bounds = None ): + def is_at(self, x, y, bounds=None): """ Returns whether or not the item is at a specified window position. """ if bounds is None: bounds = self.bounds bx, by, bdx, bdy = bounds - return ((bx <= x < (bx + bdx)) and (by <= y < (by + bdy))) + return (bx <= x < (bx + bdx)) and (by <= y < (by + bdy)) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not an event is within a specified bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def is_in ( self, event, x, y, dx, dy ): + def is_in(self, event, x, y, dx, dy): """ Returns whether or not an event is within a specified bounds. """ - return ((x <= event.GetX() < (x + dx)) and - (y <= event.GetY() < (y + dy))) + return (x <= event.GetX() < (x + dx)) and ( + y <= event.GetY() < (y + dy) + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the control's drag bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_drag_bounds ( self, x, y, dx, dy ): + def set_drag_bounds(self, x, y, dx, dy): """ Sets the control's drag bounds. """ bx, by, bdx, bdy = self.bounds - self.drag_bounds = ( x, y, min( x + dx, bx + bdx ) - x, dy ) + if (bx + bdx - x) > 0: + self.drag_bounds = (x, y, min(x + dx, bx + bdx) - x, dy) + else: + self.drag_bounds = (x, y, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the cursor to use when the mouse is over the item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_cursor ( self, event ): + def get_cursor(self, event): """ Gets the cursor to use when the mouse is over the item. """ - if self._is_tab and (not self._is_in_close( event )): + if self._is_tab and (not self._is_in_close(event)): return wx.CURSOR_ARROW return wx.CURSOR_HAND - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the DockInfo object for a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock_info_at ( self, x, y, tdx, is_control ): + def dock_info_at(self, x, y, tdx, is_control): """ Gets the DockInfo object for a specified window position. """ - if self.is_at( x, y, self.drag_bounds ): + if self.is_at(x, y, self.drag_bounds): x, y, dx, dy = self.drag_bounds - control = self + control = self if self._is_tab: if is_control: - kind = DOCK_TABADD - tab_bounds = ( x, y, dx, dy ) + kind = DOCK_TABADD + tab_bounds = (x, y, dx, dy) else: - kind = DOCK_TAB - tab_bounds = ( x - (tdx / 2), y, tdx, dy ) + kind = DOCK_TAB + tab_bounds = (x - (tdx // 2), y, tdx, dy) else: if is_control: - kind = DOCK_TABADD - tab_bounds = ( x, y, self.tab_width, dy ) + kind = DOCK_TABADD + tab_bounds = (x, y, self.tab_width, dy) else: - kind = DOCK_TAB - control = None - tab_bounds = ( x + self.tab_width, y, tdx, dy ) - - return DockInfo( kind = kind, - tab_bounds = tab_bounds, - region = self.parent, - control = control ) + kind = DOCK_TAB + control = None + tab_bounds = (x + self.tab_width, y, tdx, dy) + + return DockInfo( + kind=kind, + tab_bounds=tab_bounds, + region=self.parent, + control=control, + ) return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Prepares for drawing into a device context: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def begin_draw ( self, dc, ox = 0, oy = 0 ): + def begin_draw(self, dc, ox=0, oy=0): """ Prepares for drawing into a device context. """ - self._save_clip = dc.GetClippingBox() - x, y, dx, dy = self.bounds - dc.SetClippingRegion( x + ox, y + oy, dx, dy ) + self._save_clip = dc.GetClippingRect() + x, y, dx, dy = self.bounds + dc.SetClippingRegion(x + ox, y + oy, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Terminates drawing into a device context: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def end_draw ( self, dc ): + def end_draw(self, dc): """ Terminates drawing into a device context. """ dc.DestroyClippingRegion() if self._save_clip != no_clip: - dc.SetClippingRegion( *self._save_clip ) + dc.SetClippingRegion(*self._save_clip) self._save_clip = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_down ( self, event ): + def mouse_down(self, event): """ Handles the left mouse button being pressed. """ - self._xy = ( event.GetX(), event.GetY() ) - self._closing = self._is_in_close( event ) + self._xy = (event.GetX(), event.GetY()) + self._closing = self._is_in_close(event) self._dragging = False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_up ( self, event ): + def mouse_up(self, event): """ Handles the left mouse button being released. """ # Handle the user closing a control: if self._closing: - if self._is_in_close( event ): + if self._is_in_close(event): self.close() # Handle the completion of a dragging operation: elif self._dragging: window = event.GetEventObject() dock_info, self._dock_info = self._dock_info, None - self.mark_bounds( False ) + self.mark_bounds(False) control = self # Check to see if the user is attempting to drag an entire notebook @@ -624,8 +673,9 @@ if event.AltDown(): control = self.parent # If the parent is not a notebook, then use the parent's parent: - if (isinstance( control, DockRegion ) and - (not control.is_notebook)): + if isinstance(control, DockRegion) and ( + not control.is_notebook + ): control = control.parent # Make sure the target is not contained within the notebook @@ -639,27 +689,29 @@ # Check to see if the user is attempting to copy the control: elif event.ControlDown(): - owner = window.owner + owner = window.owner control = owner.handler.dock_control_for( - *(owner.handler_args + ( window, control )) ) + *(owner.handler_args + (window, control)) + ) # Complete the docking maneuver: - dock_info.dock( control, window ) + dock_info.dock(control, window) # Handle the user clicking on a notebook tab to select it: - elif (self._is_tab and - self.is_at( event.GetX(), event.GetY(), self.drag_bounds )): - self.parent.tab_clicked( self ) + elif self._is_tab and self.is_at( + event.GetX(), event.GetY(), self.drag_bounds + ): + self.parent.tab_clicked(self) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse moving while the left mouse button is pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_move ( self, event ): + def mouse_move(self, event): """ Handles the mouse moving while the left mouse button is pressed. """ # Exit if control is 'fixed' or a 'close' is pending: - if self._closing or self.locked or (self.style == 'fixed'): + if self._closing or self.locked or (self.style == "fixed"): return window = event.GetEventObject() @@ -667,56 +719,65 @@ # Check to see if we are in 'drag mode' yet: if not self._dragging: x, y = self._xy - if (abs( x - event.GetX() ) + abs( y - event.GetY() )) < 3: + if (abs(x - event.GetX()) + abs(y - event.GetY())) < 3: return - self._dragging = True + self._dragging = True self._dock_info = no_dock_info self._dock_size = self.tab_width - self.mark_bounds( True ) - + self.mark_bounds(True) # Get the window and DockInfo object associated with the event: - cur_dock_info = self._dock_info - self._dock_info = dock_info = \ - window.GetSizer().DockInfoAt( event.GetX(), event.GetY(), - self._dock_size, event.ShiftDown() ) + cur_dock_info = self._dock_info + self._dock_info = dock_info = window.GetSizer().DockInfoAt( + event.GetX(), event.GetY(), self._dock_size, event.ShiftDown() + ) # If the DockInfo has not changed, then no update is needed: - if ((cur_dock_info.kind == dock_info.kind) and - (cur_dock_info.region is dock_info.region) and - (cur_dock_info.bounds == dock_info.bounds) and - (cur_dock_info.tab_bounds == dock_info.tab_bounds)): + if ( + (cur_dock_info.kind == dock_info.kind) + and (cur_dock_info.region is dock_info.region) + and (cur_dock_info.bounds == dock_info.bounds) + and (cur_dock_info.tab_bounds == dock_info.tab_bounds) + ): return # Make sure the new DockInfo is legal: region = self.parent - if ((not event.ControlDown()) and - (dock_info.region is region) and - ((len( region.contents ) <= 1) or - (DOCK_TAB <= dock_info.kind <= DOCK_BAR) and - (dock_info.control is self))): + if ( + (not event.ControlDown()) + and (dock_info.region is region) + and ( + (len(region.contents) <= 1) + or (DOCK_TAB <= dock_info.kind <= DOCK_BAR) + and (dock_info.control is self) + ) + ): self._dock_info = no_dock_info - window.owner.set_cursor( wx.CURSOR_SIZING ) + window.owner.set_cursor(wx.CURSOR_SIZING) return # Draw the new region: - dock_info.draw( window, self._drag_bitmap ) + dock_info.draw(window, self._drag_bitmap) # If this is the start of an export (i.e. drag and drop) request: - if ((dock_info.kind == DOCK_EXPORT) and - (self.export != '') and - (self.dockable is not None)): + if ( + (dock_info.kind == DOCK_EXPORT) + and (self.export != "") + and (self.dockable is not None) + ): # Begin the drag and drop operation: - self.mark_bounds( False ) - window.owner.set_cursor( wx.CURSOR_ARROW ) + self.mark_bounds(False) + window.owner.set_cursor(wx.CURSOR_ARROW) window.owner.release_mouse() try: window._dragging = True - if (PythonDropSource( window, self ).result in - ( wx.DragNone, wx.DragCancel )): - window.owner.handler.open_view_for( self ) + if PythonDropSource(window, self).result in ( + wx.DragNone, + wx.DragCancel, + ): + window.owner.handler.open_view_for(self) finally: window._dragging = False else: @@ -724,56 +785,56 @@ cursor = wx.CURSOR_SIZING if dock_info.kind == DOCK_BAR: cursor = wx.CURSOR_HAND - window.owner.set_cursor( cursor ) + window.owner.set_cursor(cursor) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse hovering over the item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def hover_enter ( self, event ): + def hover_enter(self, event): """ Handles the mouse hovering over the item. """ if self._is_tab and (self.tab_state != TabActive): - self._redraw_tab( TabHover ) + self._redraw_tab(TabHover) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse exiting from hovering over the item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def hover_exit ( self, event ): + def hover_exit(self, event): """ Handles the mouse exiting from hovering over the item. """ if self._is_tab and (self.tab_state != TabActive): - self._redraw_tab( TabInactive ) + self._redraw_tab(TabInactive) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Marks/Unmarks the bounds of the bounding DockWindow: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mark_bounds ( self, begin ): + def mark_bounds(self, begin): """ Marks/Unmarks the bounds of the bounding DockWindow. """ window = self.control.GetParent() if begin: - dc, x, y = get_dc( window ) - dx, dy = window.GetSize() - dc2 = wx.MemoryDC() - self._drag_bitmap = wx.EmptyBitmap( dx, dy ) - dc2.SelectObject( self._drag_bitmap ) - dc2.Blit( 0, 0, dx, dy, dc, x, y ) + dc, x, y = get_dc(window) + dx, dy = window.GetSize().Get() + dc2 = wx.MemoryDC() + self._drag_bitmap = wx.Bitmap(dx, dy) + dc2.SelectObject(self._drag_bitmap) + dc2.Blit(0, 0, dx, dy, dc, x, y) try: - dc3 = wx.GCDC( dc2 ) - dc3.SetBrush( wx.Brush( wx.Colour( 158, 166, 255, 64 ) ) ) - dc3.SetPen( wx.TRANSPARENT_PEN ) - dc3.DrawRectangle( 0, 0, dx, dy ) + dc3 = wx.GCDC(dc2) + dc3.SetBrush(wx.Brush(wx.Colour(158, 166, 255, 64))) + dc3.SetPen(wx.TRANSPARENT_PEN) + dc3.DrawRectangle(0, 0, dx, dy) except AttributeError: pass - dc.Blit( x, y, dx, dy, dc2, 0, 0 ) + dc.Blit(x, y, dx, dy, dc2, 0, 0) else: self._drag_bitmap = None if is_mac: - top_level_window_for( window ).Refresh() + top_level_window_for(window).Refresh() else: window.Refresh() @@ -781,26 +842,27 @@ """ Gets the background color """ color = SystemMetrics().dialog_background_color - return wx.Colour( color[0]*255, color[1]*255, color[2]*255 ) + return wx.Colour( + int(color[0] * 255), int(color[1] * 255), int(color[2] * 255) + ) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Fills a specified region with the control's background color: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def fill_bg_color ( self, dc, x, y, dx, dy ): + def fill_bg_color(self, dc, x, y, dx, dy): """ Fills a specified region with the control's background color. """ - dc.SetPen( wx.TRANSPARENT_PEN ) + dc.SetPen(wx.TRANSPARENT_PEN) - dc.SetBrush( wx.Brush( self.get_bg_color() ) ) - dc.DrawRectangle( x, y, dx, dy ) + dc.SetBrush(wx.Brush(self.get_bg_color())) + dc.DrawRectangle(x, y, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws a notebook tab: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw_tab ( self, dc, state ): + def draw_tab(self, dc, state): global text_dy """ Draws a notebook tab. @@ -811,17 +873,17 @@ if state == TabActive: pass elif state == TabInactive: - r,g,b = tab_color.Get() - tab_color.Set(max(0, r-20), max(0, g-20), max(0, b-20)) + r, g, b = tab_color.Get()[0:3] + tab_color.Set(max(0, r - 20), max(0, g - 20), max(0, b - 20)) else: - r,g,b = tab_color.Get() - tab_color.Set(min(255, r+20), min(255, g+20), min(255, b+20)) + r, g, b = tab_color.Get()[0:3] + tab_color.Set(min(255, r + 20), min(255, g + 20), min(255, b + 20)) - self._is_tab = True + self._is_tab = True self.tab_state = state - theme = self.tab_theme - slice = theme.image_slice - bdc = BufferDC( dc, dx, dy ) + theme = self.tab_theme + slice = theme.image_slice + bdc = BufferDC(dc, dx, dy) self.fill_bg_color(bdc, 0, 0, dx, dy) @@ -835,15 +897,17 @@ # Draw the left, top, and right side of a rectange around the tab pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)) bdc.SetPen(pen) - bdc.DrawLine(0,dy,0,0) #up - bdc.DrawLine(0,0,dx,0) #right - bdc.DrawLine(dx-1,0,dx-1,dy) #down - - pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)) + bdc.DrawLine(0, dy, 0, 0) # up + bdc.DrawLine(0, 0, dx, 0) # right + bdc.DrawLine(dx - 1, 0, dx - 1, dy) # down + + pen = wx.Pen( + wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT) + ) bdc.SetPen(pen) - bdc.DrawLine(1,dy,1,1) - bdc.DrawLine(1,1,dx-2,1) - bdc.DrawLine(dx-2,1,dx-2,dy) + bdc.DrawLine(1, dy, 1, 1) + bdc.DrawLine(1, 1, dx - 2, 1) + bdc.DrawLine(dx - 2, 1, dx - 2, dy) else: # fill the tab bg with the desired color @@ -855,167 +919,182 @@ # Draw the left, top, and right side of a rectange around the tab pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)) bdc.SetPen(pen) - bdc.DrawLine(0,dy,0,3) - bdc.DrawLine(0,3,dx-1,3) - bdc.DrawLine(dx-1,3,dx-1,dy) - + bdc.DrawLine(0, dy, 0, 3) + bdc.DrawLine(0, 3, dx - 1, 3) + bdc.DrawLine(dx - 1, 3, dx - 1, dy) # Compute the initial drawing position: - name = self.tab_name - tdx, text_dy = dc.GetTextExtent( name ) - tc = theme.content - ox, oy = theme.label.left, theme.label.top - y = (oy + ((dy + slice.xtop + tc.top - slice.xbottom - tc.bottom - - text_dy) / 2)) + name = self.tab_name + tdx, text_dy = dc.GetTextExtent(name) + tc = theme.content + ox, oy = theme.label.left, theme.label.top + y = oy + ( + (dy + slice.xtop + tc.top - slice.xbottom - tc.bottom - text_dy) + // 2 + ) x = ox + slice.xleft + tc.left mode = self.feature_mode if mode == FEATURE_PRE_NORMAL: - mode = self.set_feature_mode( False ) + mode = self.set_feature_mode(False) # Draw the feature 'trigger' icon (if necessary): if mode != FEATURE_NONE: if mode not in FEATURES_VISIBLE: - bdc.DrawBitmap( DockImages.get_feature_image( mode ), x, y, - True ) - x += (DockImages._tab_feature_width + 3) + bdc.DrawBitmap(DockImages.get_feature_image(mode), x, y, True) + x += DockImages._tab_feature_width + 3 # Draw the image (if necessary): image = self.get_image() if image is not None: - bdc.DrawBitmap( image, x, y, True ) - x += (image.GetWidth() + 3) + bdc.DrawBitmap(image, x, y, True) + x += image.GetWidth() + 3 # Draw the text label: - bdc.DrawText( name, x, y + 1 ) + bdc.DrawText(name, x, y + 1) # Draw the close button (if necessary): if self.closeable: - bdc.DrawBitmap( DockImages._close_tab, x + tdx + 5, y + 2, True ) + bdc.DrawBitmap(DockImages._close_tab, x + tdx + 5, y + 2, True) # Copy the buffer to the display: - bdc.copy( x0, y0 ) + bdc.copy(x0, y0) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws a fixed drag bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw_fixed ( self, dc ): + def draw_fixed(self, dc): """ Draws a fixed drag bar. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws a horizontal drag bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw_horizontal ( self, dc ): + def draw_horizontal(self, dc): """ Draws a horizontal drag bar. """ self._is_tab = False x, y, dx, dy = self.drag_bounds - self.fill_bg_color( dc, x, y, dx, dy ) + self.fill_bg_color(dc, x, y, dx, dy) pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHILIGHT)) dc.SetPen(pen) - dc.DrawLine(x, y, x+dx, y) - dc.DrawLine(x, y+2, x+dx, y+2) + dc.DrawLine(x, y, x + dx, y) + dc.DrawLine(x, y + 2, x + dx, y + 2) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws a vertical drag bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw_vertical ( self, dc ): + def draw_vertical(self, dc): """ Draws a vertical drag bar. """ self._is_tab = False x, y, dx, dy = self.drag_bounds - self.fill_bg_color( dc, x, y, dx, dy ) + self.fill_bg_color(dc, x, y, dx, dy) pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHILIGHT)) dc.SetPen(pen) - dc.DrawLine(x, y, x, y+dy) - dc.DrawLine(x+2, y, x+2, y+dy) + dc.DrawLine(x, y, x, y + dy) + dc.DrawLine(x + 2, y, x + 2, y + dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Redraws the control's tab: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _redraw_tab ( self, state = None ): + def _redraw_tab(self, state=None): if state is None: state = self.tab_state region = self.parent if region is not None: - dc = set_standard_font( wx.ClientDC( self.control.GetParent() ) ) + dc = set_standard_font(wx.ClientDC(self.control.GetParent())) if region.is_notebook: - dc.SetClippingRegion( *region._tab_clip_bounds ) - self.draw_tab( dc, state ) + dc.SetClippingRegion(*region._tab_clip_bounds) + self.draw_tab(dc, state) dc.DestroyClippingRegion() else: - self.draw_tab( dc, state ) + self.draw_tab(dc, state) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Redraws the control's drag bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _redraw_bar ( self ): - dc = wx.ClientDC( self.control ) - getattr( self, 'draw_' + self.style )( dc ) + def _redraw_bar(self): + dc = wx.ClientDC(self.control) + getattr(self, "draw_" + self.style)(dc) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Redraws the control's tab or bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _redraw_control ( self ): + def _redraw_control(self): if self._is_tab: self._redraw_tab() else: self._redraw_bar() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the bounds of the close button (if any): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _close_bounds ( self ): + def _close_bounds(self): global text_dy if self.closeable and self._is_tab: x, y, dx, dy = self.drag_bounds - theme = self.tab_theme - slice = theme.image_slice - tc = theme.content + theme = self.tab_theme + slice = theme.image_slice + tc = theme.content ox, oy = theme.label.left, theme.label.top # fixme: x calculation seems to be off by -1... - return ( x + dx + ox - slice.xright - tc.right - CloseTabSize, - y + oy + ((dy + slice.xtop + tc.top - slice.xbottom - - tc.bottom - text_dy) / 2) + 3, - CloseTabSize, CloseTabSize ) + return ( + x + dx + ox - slice.xright - tc.right - CloseTabSize, + y + + oy + + ( + ( + dy + + slice.xtop + + tc.top + - slice.xbottom + - tc.bottom + - text_dy + ) + // 2 + ) + + 3, + CloseTabSize, + CloseTabSize, + ) - return ( 0, 0, 0, 0 ) + return (0, 0, 0, 0) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether a specified window position is over the close button: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _is_in_close ( self, event ): - return self.is_in( event, *self._close_bounds() ) + def _is_in_close(self, event): + return self.is_in(event, *self._close_bounds()) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets/Returns the 'normal' feature mode for the control based on the # number of currently active features: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_feature_mode ( self, changed = True ): + def set_feature_mode(self, changed=True): if (not changed) or (self.feature_mode != FEATURE_PRE_NORMAL): mode = FEATURE_DROP features = self.drop_features - if len( features ) == 0: - mode = FEATURE_NORMAL + if len(features) == 0: + mode = FEATURE_NORMAL features = self.features for feature in features: @@ -1030,12 +1109,12 @@ return self.feature_mode - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not a specified window position is over the feature # 'trigger' icon, and if so, triggers display of the feature icons: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_activate ( self, event, drag_object = Undefined ): + def feature_activate(self, event, drag_object=Undefined): global text_dy if (self.feature_mode in NO_FEATURE_ICON) or (not self._is_tab): @@ -1043,47 +1122,49 @@ # In 'drag' mode, we may get the same coordinate over and over again. # We don't want to restart the timer, so exit now: - exy = ( event.GetX(), event.GetY() ) + exy = (event.GetX(), event.GetY()) if self._feature_popup_xy == exy: return True x, y, dx, dy = self.drag_bounds - idx = DockImages._tab_feature_width - idy = DockImages._tab_feature_height - theme = self.tab_theme - slice = theme.image_slice - tc = theme.content + idx = DockImages._tab_feature_width + idy = DockImages._tab_feature_height + theme = self.tab_theme + slice = theme.image_slice + tc = theme.content ox, oy = theme.label.left, theme.label.top - y += (oy + ((dy + slice.xtop + tc.top - slice.xbottom - tc.bottom - - text_dy) / 2)) - x += ox + slice.xleft + tc.left - result = self.is_in( event, x, y, idx, idy ) + y += oy + ( + (dy + slice.xtop + tc.top - slice.xbottom - tc.bottom - text_dy) + // 2 + ) + x += ox + slice.xleft + tc.left + result = self.is_in(event, x, y, idx, idy) # If the pointer is over the feature 'trigger' icon, save the event for # the popup processing: if result: # If this is part of a drag operation, prepare for drag mode: if drag_object is not Undefined: - self.pre_drag( drag_object, FEATURE_EXTERNAL_DRAG ) + self.pre_drag(drag_object, FEATURE_EXTERNAL_DRAG) # Schedule the popup for later: self._feature_popup_xy = exy - do_after( 100, self._feature_popup ) + do_after(100, self._feature_popup) return result - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Resets any pending feature popup: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def reset_feature_popup ( self ): + def reset_feature_popup(self): self._feature_popup_xy = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Pops up the current features if a feature popup is still pending: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _feature_popup ( self ): + def _feature_popup(self): if self._feature_popup_xy is not None: # Set the new feature mode: if self.feature_mode == FEATURE_DROP: @@ -1091,69 +1172,73 @@ else: self.feature_mode = FEATURE_VISIBLE - self.owner.feature_bar_popup( self ) + self.owner.feature_bar_popup(self) self._feature_popup_xy = None else: - self.post_drag( FEATURE_EXTERNAL_DRAG ) + self.post_drag(FEATURE_EXTERNAL_DRAG) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Finishes the processing of a feature popup: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_bar_closed ( self ): + def feature_bar_closed(self): if self.feature_mode == FEATURE_DROP_VISIBLE: self.feature_mode = FEATURE_DROP else: self.feature_mode = FEATURE_NORMAL - do_later( self._redraw_control ) + do_later(self._redraw_control) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles all pre-processing before a feature is dragged: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def pre_drag_all ( self, object ): + def pre_drag_all(self, object): """ Prepare all DockControls in the associated DockWindow for being dragged over. """ for control in self.dock_controls: - control.pre_drag( object ) + control.pre_drag(object) - self.pre_drag( object ) + self.pre_drag(object) - def pre_drag ( self, object, tag = 0 ): + def pre_drag(self, object, tag=0): """ Prepare this DockControl for being dragged over. """ - if (self.visible and - (self.feature_mode != FEATURE_NONE) and - (self._feature_mode is None)): - - if isinstance( object, IFeatureTool ): - if (object.feature_can_drop_on( self.object ) or - object.feature_can_drop_on_dock_control( self )): - from feature_tool import FeatureTool - - self.drop_features = [ - FeatureTool( dock_control = self ) ] - else: - self.drop_features = [ f for f in self.features - if f.can_drop( object ) and - (f.bitmap is not None) ] + if ( + self.visible + and (self.feature_mode != FEATURE_NONE) + and (self._feature_mode is None) + ): + + if isinstance(object, IFeatureTool): + if object.feature_can_drop_on( + self.object + ) or object.feature_can_drop_on_dock_control(self): + from .feature_tool import FeatureTool + + self.drop_features = [FeatureTool(dock_control=self)] + else: + self.drop_features = [ + f + for f in self.features + if f.can_drop(object) and (f.bitmap is not None) + ] self._feature_mode = self.feature_mode + tag - if len( self.drop_features ) > 0: + if len(self.drop_features) > 0: self.feature_mode = FEATURE_DROP else: self.feature_mode = FEATURE_DISABLED self._redraw_control() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles all post-processing after a feature has been dragged: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def post_drag_all ( self ): + def post_drag_all(self): """ Restore all DockControls in the associated DockWindow after a drag operation is completed. """ @@ -1161,52 +1246,57 @@ control.post_drag() self.post_drag() - def post_drag ( self, tag = 0 ): + def post_drag(self, tag=0): """ Restore this DockControl after a drag operation is completed. """ - if ((self._feature_mode is None) or (tag == 0) or - ((self._feature_mode & tag) != 0)): + if ( + (self._feature_mode is None) + or (tag == 0) + or ((self._feature_mode & tag) != 0) + ): self.drop_features = [] if self.feature_mode != FEATURE_NONE: if self._feature_mode is not None: - self.feature_mode = self._feature_mode & (~tag) + self.feature_mode = self._feature_mode & (~tag) self._feature_mode = None else: - self.set_feature_mode( False ) + self.set_feature_mode(False) self._redraw_control() -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockSplitter' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class DockSplitter ( DockItem ): - #--------------------------------------------------------------------------- +class DockSplitter(DockItem): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Style of the splitter bar: - style = Enum( 'horizontal', 'vertical' ) + style = Enum("horizontal", "vertical") # Index of the splitter within its parent: - index = Int + index = Int() # Current state of the splitter (i.e. its position relative to the things # it splits): state = Property - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Override the definition of the inherited 'theme' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_theme ( self ): + def _get_theme(self): return self.parent.control.GetParent().owner.theme - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the contents of the splitter: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw ( self, dc ): + def draw(self, dc): """ Draws the contents of the splitter. """ if (self._live_drag is False) and (self._first_bounds is not None): @@ -1214,19 +1304,19 @@ else: x, y, dx, dy = self.bounds - image = DockImages.get_splitter_image( self.state ) + image = DockImages.get_splitter_image(self.state) idx, idy = image.GetWidth(), image.GetHeight() - self.fill_bg_color( dc, x, y, dx, dy ) + self.fill_bg_color(dc, x, y, dx, dy) - if self.style == 'horizontal': + if self.style == "horizontal": # Draw a line the same color as the system button shadow, which # should be a darkish color in the users color scheme pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)) dc.SetPen(pen) - dc.DrawLine(x+idx+1,y+dy/2,x+dx-2,y+dy/2) + dc.DrawLine(x + idx + 1, y + dy / 2, x + dx - 2, y + dy / 2) - iy = y+2 + iy = y + 2 ix = x # sets the hittable area for changing the cursor to be the height of @@ -1237,7 +1327,7 @@ # should be a darkish color in the users color scheme pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)) dc.SetPen(pen) - dc.DrawLine(x+dx/2,y+idy+1,x+dx/2,y+dy-2) + dc.DrawLine(x + dx / 2, y + idy + 1, x + dx / 2, y + dy - 2) iy = y ix = x + 2 @@ -1246,63 +1336,64 @@ # the image dy = idy - dc.DrawBitmap( image, ix, iy, True ) - self._hot_spot = ( x, y, dx, dy ) + dc.DrawBitmap(image, ix, iy, True) + self._hot_spot = (x, y, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the cursor to use when the mouse is over the splitter bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_cursor ( self, event ): + def get_cursor(self, event): """ Gets the cursor to use when the mouse is over the splitter bar. """ - if (self._hot_spot is None) or self.is_in( event, *self._hot_spot ): + if (self._hot_spot is None) or self.is_in(event, *self._hot_spot): return wx.CURSOR_ARROW - if self.style == 'horizontal': + if self.style == "horizontal": return wx.CURSOR_SIZENS return wx.CURSOR_SIZEWE - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a copy of the splitter 'structure', minus the actual content: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_structure ( self ): + def get_structure(self): """ Returns a copy of the splitter 'structure', minus the actual content. """ - return self.clone_traits( [ '_last_bounds' ] ) + return self.clone_traits(["_last_bounds"]) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_down ( self, event ): + def mouse_down(self, event): """ Handles the left mouse button being pressed. """ - self._live_drag = event.ControlDown() - self._click_pending = ((self._hot_spot is not None) and - self.is_in( event, *self._hot_spot )) + self._live_drag = event.ControlDown() + self._click_pending = (self._hot_spot is not None) and self.is_in( + event, *self._hot_spot + ) if not self._click_pending: - self._xy = ( event.GetX(), event.GetY() ) - self._max_bounds = self.parent.get_splitter_bounds( self ) + self._xy = (event.GetX(), event.GetY()) + self._max_bounds = self.parent.get_splitter_bounds(self) self._first_bounds = self.bounds if not self._live_drag: - self._draw_bounds( event, self.bounds ) + self._draw_bounds(event, self.bounds) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_up ( self, event ): + def mouse_up(self, event): """ Handles the left mouse button being released. """ if self._click_pending: hx, hy, hdx, hdy = self._hot_spot - if not self.is_in( event, hx, hy, hdx, hdy ): + if not self.is_in(event, hx, hy, hdx, hdy): return - if self.style == 'horizontal': + if self.style == "horizontal": if event.GetX() < (hx + (hdx / 2)): self.collapse(True) else: @@ -1315,41 +1406,42 @@ else: self._last_bounds, self._first_bounds = self._first_bounds, None if not self._live_drag: - self._draw_bounds( event ) + self._draw_bounds(event) - self.parent.update_splitter( self, event.GetEventObject() ) + self.parent.update_splitter(self, event.GetEventObject()) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse moving while the left mouse button is pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_move ( self, event ): + def mouse_move(self, event): """ Handles the mouse moving while the left mouse button is pressed. """ if not self._click_pending: - x, y, dx, dy = self._first_bounds + if self._first_bounds is not None: + x, y, dx, dy = self._first_bounds mx, my, mdx, mdy = self._max_bounds - if self.style == 'horizontal': + if self.style == "horizontal": y = y + event.GetY() - self._xy[1] - y = min( max( y, my ), my + mdy - dy ) + y = min(max(y, my), my + mdy - dy) else: x = x + event.GetX() - self._xy[0] - x = min( max( x, mx ), mx + mdx - dx ) + x = min(max(x, mx), mx + mdx - dx) - bounds = ( x, y, dx, dy ) + bounds = (x, y, dx, dy) if bounds != self.bounds: self.bounds = bounds if self._live_drag: - self.parent.update_splitter( self, event.GetEventObject() ) + self.parent.update_splitter(self, event.GetEventObject()) else: - self._draw_bounds( event, bounds ) + self._draw_bounds(event, bounds) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Collapse/expands a splitter - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def collapse ( self, forward ): + def collapse(self, forward): """ Move the splitter has far as possible in one direction. 'forward' is a boolean: True=right/down, False=left/up. @@ -1357,23 +1449,25 @@ position. """ - is_horizontal = (self.style == 'horizontal') - x, y, dx, dy = self.bounds + is_horizontal = self.style == "horizontal" + x, y, dx, dy = self.bounds if self._last_bounds is not None: if is_horizontal: y = self._last_bounds[1] else: x = self._last_bounds[0] - state = self.state - contents = self.parent.visible_contents - ix1, iy1, idx1, idy1 = contents[ self.index ].bounds - ix2, iy2, idx2, idy2 = contents[ self.index + 1 ].bounds + state = self.state + contents = self.parent.visible_contents + ix1, iy1, idx1, idy1 = contents[self.index].bounds + ix2, iy2, idx2, idy2 = contents[self.index + 1].bounds if is_horizontal: if state != SPLIT_HMIDDLE: - if ((y == self.bounds[1]) or - (y < iy1) or - ((y + dy) > (iy2 + idy2))): - y = (iy1 + iy2 + idy2 - dy) / 2 + if ( + (y == self.bounds[1]) + or (y < iy1) + or ((y + dy) > (iy2 + idy2)) + ): + y = (iy1 + iy2 + idy2 - dy) // 2 else: self._last_bounds = self.bounds if forward: @@ -1381,51 +1475,49 @@ else: y = iy2 + idy2 - dy elif state != SPLIT_VMIDDLE: - if ((x == self.bounds[0]) or - (x < ix1) or - ((x + dx) > (ix2 + idx2))): - x = (ix1 + ix2 + idx2 - dx) / 2 + if (x == self.bounds[0]) or (x < ix1) or ((x + dx) > (ix2 + idx2)): + x = (ix1 + ix2 + idx2 - dx) // 2 else: self._last_bounds = self.bounds if forward: x = ix2 + idx2 - dx else: x = ix1 - self.bounds = ( x, y, dx, dy ) + self.bounds = (x, y, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse hovering over the item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def hover_enter ( self, event ): + def hover_enter(self, event): """ Handles the mouse hovering over the item. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse exiting from hovering over the item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def hover_exit ( self, event ): + def hover_exit(self, event): """ Handles the mouse exiting from hovering over the item. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the splitter bar in a new position while it is being dragged: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _draw_bounds ( self, event, bounds = None ): + def _draw_bounds(self, event, bounds=None): """ Draws the splitter bar in a new position while it is being dragged. """ # Set up the drawing environment: - window = event.GetEventObject() - dc, x0, y0 = get_dc( window ) - dc.SetLogicalFunction( wx.XOR ) - dc.SetPen( wx.TRANSPARENT_PEN ) - dc.SetBrush( wx.Brush( wx.Colour( *DragColor ), wx.SOLID ) ) + window = event.GetEventObject() + dc, x0, y0 = get_dc(window) + dc.SetLogicalFunction(wx.XOR) + dc.SetPen(wx.TRANSPARENT_PEN) + dc.SetBrush(wx.Brush(wx.Colour(*DragColor), wx.SOLID)) - is_horizontal = (self.style == 'horizontal') + is_horizontal = self.style == "horizontal" nx = ox = None # Draw the new bounds (if any): @@ -1433,13 +1525,13 @@ ax = ay = adx = ady = 0 nx, ny, ndx, ndy = bounds if is_horizontal: - ady = (ndy - 6) - ay = ady / 2 + ady = ndy - 6 + ay = ady // 2 else: - adx = (ndx - 6) - ax = adx / 2 - nx += ax - ny += ay + adx = ndx - 6 + ax = adx // 2 + nx += ax + ny += ay ndx -= adx ndy -= ady @@ -1447,13 +1539,13 @@ ax = ay = adx = ady = 0 ox, oy, odx, ody = self._bounds if is_horizontal: - ady = (ody - 6) - ay = ady / 2 + ady = ody - 6 + ay = ady // 2 else: - adx = (odx - 6) - ax = adx / 2 - ox += ax - oy += ay + adx = odx - 6 + ax = adx // 2 + ox += ax + oy += ay odx -= adx ody -= ady @@ -1465,17 +1557,17 @@ if 0 <= yoy < tdy: tdy = yoy elif -ody < yoy <= 0: - ty = oy + ody + ty = oy + ody tdy = tdy - ody - yoy else: xox = ox - tx if 0 <= xox < tdx: tdx = xox elif -odx < xox <= 0: - tx = ox + odx + tx = ox + odx tdx = tdx - odx - xox - dc.DrawRectangle( tx + x0, ty + y0, tdx, tdy ) + dc.DrawRectangle(tx + x0, ty + y0, tdx, tdy) # Erase the old bounds (if any): if ox is not None: @@ -1485,17 +1577,17 @@ if 0 <= yoy < ody: ody = yoy elif -ndy < yoy <= 0: - oy = ny + ndy + oy = ny + ndy ody = ody - ndy - yoy else: xox = nx - ox if 0 <= xox < odx: odx = xox elif -ndx < xox <= 0: - ox = nx + ndx + ox = nx + ndx odx = odx - ndx - xox - dc.DrawRectangle( ox + x0, oy + y0, odx, ody ) + dc.DrawRectangle(ox + x0, oy + y0, odx, ody) if is_mac: window.Refresh(rect=wx.Rect(ox + x0, oy + y0, odx, ody)) @@ -1503,16 +1595,16 @@ # Save the new bounds for the next call: self._bounds = bounds - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'state' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_state ( self ): - contents = self.parent.contents - x, y, dx, dy = self.bounds - ix1, iy1, idx1, idy1 = contents[ self.index ].bounds - ix2, iy2, idx2, idy2 = contents[ self.index + 1 ].bounds - if self.style == 'horizontal': + def _get_state(self): + contents = self.parent.contents + x, y, dx, dy = self.bounds + ix1, iy1, idx1, idy1 = contents[self.index].bounds + ix2, iy2, idx2, idy2 = contents[self.index + 1].bounds + if self.style == "horizontal": if y == iy1: return SPLIT_HTOP if (y + dy) == (iy2 + idy2): @@ -1525,146 +1617,147 @@ return SPLIT_VRIGHT return SPLIT_VMIDDLE -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockControl' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockControl ( DockItem ): +class DockControl(DockItem): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The control this object describes: - control = Instance( wx.Window, allow_none = True ) + control = Instance(wx.Window, allow_none=True) # The number of global DockWindowFeature's that were available the last # the time the feature set was checked: - num_features = Int + num_features = Int() # A feature associated with the DockControl has been changed: - feature_changed = Event + feature_changed = Event() # The image to display for this control: - image = Instance( ImageResource, allow_none = True ) + image = Image() # The UI name of this control: - name = Str + name = Str() # Has the user set the name of the control? - user_name = Bool( False ) + user_name = Bool(False) # The object (if any) associated with this control: object = Property # The id of this control: - id = Str + id = Str() # Style of drag bar/tab: style = DockStyle # Has the user set the style for this control: - user_style = Bool( False ) + user_style = Bool(False) # Category of control when it is dragged out of the DockWindow: - export = Str + export = Str() # Is the control visible? - visible = Bool( True ) + visible = Bool(True) # Is the control's drag bar locked? - locked = Bool( False ) + locked = Bool(False) # Can the control be resized? - resizable = Bool( True ) + resizable = Bool(True) # Can the control be closed? - closeable = Bool( False ) + closeable = Bool(False) # Function to call when a DockControl is requesting to be closed: on_close = Callable # (Optional) object that allows the control to be docked with a different # DockWindow: - dockable = Instance( IDockable, allow_none = True ) + dockable = Instance(IDockable, allow_none=True) # List of all other DockControl's in the same DockWindow: dock_controls = Property # Event fired when the control's notebook tab is activated by the user: - activated = Event + activated = Event() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Calculates the minimum size of the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def calc_min ( self, use_size = False ): + def calc_min(self, use_size=False): """ Calculates the minimum size of the control. """ self.check_features() dx, dy = self.width, self.height if self.control is not None: - if wx_26: - size = self.control.GetBestFittingSize() - else: - size = self.control.GetEffectiveMinSize() + size = self.control.GetEffectiveMinSize() dx = size.GetWidth() dy = size.GetHeight() if self.width < 0: self.width, self.height = dx, dy if use_size and (self.width >= 0): - return ( self.width, self.height ) + return (self.width, self.height) - return ( dx, dy ) + return (dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Layout the contents of the control based on the specified bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def recalc_sizes ( self, x, y, dx, dy ): + def recalc_sizes(self, x, y, dx, dy): """ Layout the contents of the region based on the specified bounds. """ - self.width = dx = max( 0, dx ) - self.height = dy = max( 0, dy ) - self.bounds = ( x, y, dx, dy ) + self.width = dx = max(0, dx) + self.height = dy = max(0, dy) + self.bounds = (x, y, dx, dy) - # Note: All we really want to do is the 'SetDimensions' call, but the + # Note: All we really want to do is the 'SetSize' call, but the # other code is needed for Linux/GTK which will not correctly process - # the SetDimensions call if the min size is larger than the specified + # the SetSize call if the min size is larger than the specified # size. So we temporarily set its min size to (0,0), do the - # SetDimensions, then restore the original min size. The restore is + # SetSize, then restore the original min size. The restore is # necessary so that DockWindow itself will correctly draw the 'drag' # box when performing a docking maneuver... - control = self.control + control = self.control min_size = control.GetMinSize() - control.SetMinSize( wx.Size( 0, 0 ) ) - control.SetDimensions( x, y, dx, dy ) - control.SetMinSize( min_size ) + control.SetMinSize(wx.Size(0, 0)) + control.SetSize(x, y, dx, dy) + control.SetMinSize(min_size) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Checks to make sure that all applicable DockWindowFeatures have been # applied: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def check_features ( self ): + def check_features(self): """ Checks to make sure that all applicable DockWindowFeatures have been applied. """ global features mode = self.feature_mode - n = len( features ) - if ((self.num_features < n) and - (self.control is not None) and - isinstance( self.control.GetParent().GetSizer(), DockSizer )): - for i in range( self.num_features, n ): + n = len(features) + if ( + (self.num_features < n) + and (self.control is not None) + and isinstance(self.control.GetParent().GetSizer(), DockSizer) + ): + for i in range(self.num_features, n): feature_class = features[i] - feature = feature_class.new_feature_for( self ) + feature = feature_class.new_feature_for(self) if feature is not None: - if not isinstance( feature, SequenceType ): - feature = [ feature ] - self.features.extend( list( feature ) ) + if not isinstance(feature, SequenceType): + feature = [feature] + self.features.extend(list(feature)) if mode == FEATURE_NONE: self.feature_mode = FEATURE_PRE_NORMAL if feature_class.state != 1: @@ -1676,33 +1769,33 @@ self.set_feature_mode() self.num_features = n - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the visibility of the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_visibility ( self, visible ): + def set_visibility(self, visible): """ Sets the visibility of the control. """ if self.control is not None: - self.control.Show( visible ) + self.control.Show(visible) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns all DockControl objects contained in the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_controls ( self, visible_only = True ): + def get_controls(self, visible_only=True): """ Returns all DockControl objects contained in the control. """ if visible_only and (not self.visible): return [] - return [ self ] + return [self] - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the image (if any) associated with the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_image ( self ): + def get_image(self): """ Gets the image (if any) associated with the control. """ if self._image is None: @@ -1711,34 +1804,34 @@ return self._image - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Hides or shows the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def show ( self, visible = True, layout = True ): + def show(self, visible=True, layout=True): """ Hides or shows the control. """ if visible != self.visible: self.visible = visible - self._layout( layout ) + self._layout(layout) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Activates a control (i.e. makes it the active page within its containing # notebook): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def activate ( self, layout = True ): + def activate(self, layout=True): """ Activates a control (i.e. makes it the active page within its containing notebook). """ if self.parent is not None: - self.parent.activate( self, layout ) + self.parent.activate(self, layout) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Closes the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def close ( self, layout = True, force = False ): + def close(self, layout=True, force=False): """ Closes the control. """ control = self.control @@ -1747,13 +1840,13 @@ if self.on_close is not None: # Ask the handler if it is OK to close the control: - if self.on_close( self, force ) is False: + if self.on_close(self, force) is False: # If not OK to close it, we're done: return elif self.dockable is not None: # Ask the IDockable handler if it is OK to close the control: - if self.dockable.dockable_close( self, force ) is False: + if self.dockable.dockable_close(self, force) is False: # If not OK to close it, we're done: return @@ -1765,7 +1858,7 @@ self.reset_features() # Remove the DockControl from the sizer: - self.parent.remove( self ) + self.parent.remove(self) # Mark the DockControl as closed (i.e. has no associated widget or # parent): @@ -1776,132 +1869,163 @@ window.Layout() window.Refresh() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object at a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def object_at ( self, x, y ): + def object_at(self, x, y): """ Returns the object at a specified window position. """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a copy of the control 'structure', minus the actual content: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_structure ( self ): + def get_structure(self): """ Returns a copy of the control 'structure', minus the actual content. """ - return self.clone_traits( [ - 'id', 'name', 'user_name', 'style', 'user_style', 'visible', - 'locked', 'closeable', 'resizable', 'width', 'height' - ] ) + return self.clone_traits( + [ + "id", + "name", + "user_name", + "style", + "user_style", + "visible", + "locked", + "closeable", + "resizable", + "width", + "height", + ] + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Toggles the 'lock' status of the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def toggle_lock ( self ): + def toggle_lock(self): """ Toggles the 'lock' status of the control. """ self.locked = not self.locked - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Prints the contents of the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dump ( self, indent ): + def dump(self, indent): """ Prints the contents of the control. """ - print(('%sControl( %08X, name = %s, id = %s,\n%s' - 'style = %s, locked = %s,\n%s' - 'closeable = %s, resizable = %s, visible = %s\n%s' - 'width = %d, height = %d )' % ( - ' ' * indent, id( self ), self.name, self.id, - ' ' * (indent + 9), self.style, self.locked, - ' ' * (indent + 9), self.closeable, self.resizable, self.visible, - ' ' * (indent + 9), self.width, self.height ))) + print( + ( + "%sControl( %08X, name = %s, id = %s,\n%s" + "style = %s, locked = %s,\n%s" + "closeable = %s, resizable = %s, visible = %s\n%s" + "width = %d, height = %d )" + % ( + " " * indent, + id(self), + self.name, + self.id, + " " * (indent + 9), + self.style, + self.locked, + " " * (indent + 9), + self.closeable, + self.resizable, + self.visible, + " " * (indent + 9), + self.width, + self.height, + ) + ) + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the contents of the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw ( self, dc ): + def draw(self, dc): """ Draws the contents of the control. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets a new name for the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_name ( self, name, layout = True ): + def set_name(self, name, layout=True): """ Sets a new name for the control. """ if name != self.name: self.name = name - self._layout( layout ) + self._layout(layout) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Resets the state of the tab: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def reset_tab ( self ): + def reset_tab(self): """ Resets the state of the tab. """ self.reset_features() self._layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Resets all currently defined features: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def reset_features ( self ): + def reset_features(self): """ Resets all currently defined features. """ for feature in self.features: feature.dispose() - self.features = [] + self.features = [] self.num_features = 0 - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Forces the containing DockWindow to be laid out: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _layout ( self, layout = True ): + def _layout(self, layout=True): """ Forces the containing DockWindow to be laid out. """ if layout and (self.control is not None): - do_later( self.control.GetParent().owner.update_layout ) + do_later(self.control.GetParent().owner.update_layout) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'activated' event being fired: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _activated_fired(self): + @observe('activated') + def _activate_dockable_tab(self, event): """ Notifies the active dockable that the control's tab is being activated. """ if self.dockable is not None: self.dockable.dockable_tab_activated(self, True) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'feature_changed' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _feature_changed ( self ): + @observe("feature_changed") + def _feature_changed_updated(self, event): """ Handles the 'feature_changed' trait being changed """ self.set_feature_mode() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'control' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _control_changed ( self, old, new ): + @observe("control") + def _control_updated(self, event): """ Handles the 'control' trait being changed. """ + old, new = event.old, event.new self._tab_width = None if old is not None: @@ -1911,90 +2035,102 @@ new._dock_control = self self.reset_tab() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'name' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _name_changed ( self ): + @observe("name") + def _name_updated(self, event): """ Handles the 'name' trait being changed. """ self._tab_width = self._tab_name = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'style' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _style_changed ( self ): + @observe("style") + def _style_updated(self, event): """ Handles the 'style' trait being changed. """ if self.parent is not None: self.parent._is_notebook = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'image' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _image_changed ( self ): + @observe("image") + def _image_updated(self, event): """ Handles the 'image' trait being changed. """ self._image = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'visible' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _visible_changed ( self ): + @observe("visible") + def _visible_updated(self, event): """ Handles the 'visible' trait being changed. """ if self.parent is not None: - self.parent.show_hide( self ) + self.parent.show_hide(self) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'dockable' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _dockable_changed ( self, dockable ): + @observe("dockable") + def _dockable_updated(self, event): """ Handles the 'dockable' trait being changed. """ + dockable = event.new if dockable is not None: - dockable.dockable_bind( self ) + dockable.dockable_bind(self) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'object' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_object ( self ): - return getattr( self.control, '_object', None ) + def _get_object(self): + return getattr(self.control, "_object", None) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the DockControl's property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_dock_controls ( self ): + def _get_dock_controls(self): # Get all of the DockControls in the parent DockSizer: - controls = self.control.GetParent().GetSizer().GetContents( - ).get_controls( False ) + controls = ( + self.control.GetParent() + .GetSizer() + .GetContents() + .get_controls(False) + ) # Remove ourself from the list: try: - controls.remove( self ) + controls.remove(self) except: pass return controls -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockGroup' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockGroup ( DockItem ): +class DockGroup(DockItem): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The contents of the group: - contents = List + contents = List() # The UI name of this group: name = Property @@ -2006,7 +2142,7 @@ resizable = Property # Category of control when it is dragged out of the DockWindow: - export = Constant( '' ) + export = Constant("") # Is the group visible? visible = Property @@ -2024,70 +2160,70 @@ locked = Property # Has the initial layout been performed? - initialized = Bool( False ) + initialized = Bool(False) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'name' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_name ( self ): + def _get_name(self): controls = self.get_controls() - n = len( controls ) + n = len(controls) if n == 0: - return '' + return "" if n == 1: return controls[0].name - return '%s [%d]' % ( controls[0].name, n ) + return "%s [%d]" % (controls[0].name, n) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'visible' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_visible ( self ): + def _get_visible(self): for item in self.contents: if item.visible: return True return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'visible_contents' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_visible_contents ( self ): - return [ item for item in self.contents if item.visible ] + def _get_visible_contents(self): + return [item for item in self.contents if item.visible] - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'closeable' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_closeable ( self ): + def _get_closeable(self): for item in self.contents: if not item.closeable: return False return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'style' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_style ( self ): + def _get_style(self): # Make sure there is at least one item in the group: - if len( self.contents ) > 0: + if len(self.contents) > 0: # Return the first item's style: return self.contents[0].style # Otherwise, return a default style for an empty group: - return 'horizontal' + return "horizontal" - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'resizable' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_resizable ( self ): + def _get_resizable(self): if self._resizable is None: self._resizable = False for control in self.get_controls(): @@ -2097,73 +2233,74 @@ return self._resizable - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'control' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_control ( self ): - if len( self.contents ) == 0: + def _get_control(self): + if len(self.contents) == 0: return None return self.contents[0].control - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'locked' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_locked ( self ): + def _get_locked(self): return self.contents[0].locked - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles 'initialized' being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _initialized_changed( self ): + @observe("initialized") + def _initialized_updated(self, event): """ Handles 'initialized' being changed. """ for item in self.contents: - if isinstance( item, DockGroup ): + if isinstance(item, DockGroup): item.initialized = self.initialized - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Hides or shows the contents of the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def show ( self, visible = True, layout = True ): + def show(self, visible=True, layout=True): """ Hides or shows the contents of the group. """ for item in self.contents: - item.show( visible, False ) + item.show(visible, False) if layout: window = self.control.GetParent() window.Layout() window.Refresh() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Replaces a specified DockControl by another: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def replace_control ( self, old, new ): + def replace_control(self, old, new): """ Replaces a specified DockControl by another. """ - for i, item in enumerate( self.contents ): - if isinstance( item, DockControl ): + for i, item in enumerate(self.contents): + if isinstance(item, DockControl): if item is old: self.contents[i] = new new.parent = self return True - elif item.replace_control( old, new ): + elif item.replace_control(old, new): return True return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns all DockControl objects contained in the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_controls ( self, visible_only = True ): + def get_controls(self, visible_only=True): """ Returns all DockControl objects contained in the group. """ if visible_only: @@ -2173,159 +2310,167 @@ result = [] for item in contents: - result.extend( item.get_controls( visible_only ) ) + result.extend(item.get_controls(visible_only)) return result - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the image (if any) associated with the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_image ( self ): + def get_image(self): """ Gets the image (if any) associated with the group. """ - if len( self.contents ) == 0: + if len(self.contents) == 0: return None return self.contents[0].get_image() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the cursor to use when the mouse is over the item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_cursor ( self, event ): + def get_cursor(self, event): """ Gets the cursor to use when the mouse is over the item. """ return wx.CURSOR_ARROW - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Toggles the 'lock' status of every control in the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def toggle_lock ( self ): + def toggle_lock(self): """ Toggles the 'lock' status of every control in the group. """ for item in self.contents: item.toggle_lock() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Closes the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def close ( self, layout = True, force = False ): + def close(self, layout=True, force=False): """ Closes the control. """ window = self.control.control.GetParent() for item in self.contents[:]: - item.close( False, force = force ) + item.close(False, force=force) if layout: window.Layout() window.Refresh() -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockRegion' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockRegion ( DockGroup ): +class DockRegion(DockGroup): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Index of the currently active 'contents' DockControl: - active = Int + active = Int() # Is the region drawn as a notebook or not: is_notebook = Property # Index of the tab scroll image to use (-1 = No tab scroll): - tab_scroll_index = Int( -1 ) + tab_scroll_index = Int(-1) # The index of the current leftmost visible tab: - left_tab = Int + left_tab = Int() # The current maximum value for 'left_tab': - max_tab = Int + max_tab = Int() # Contents have been modified property: modified = Property - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Calculates the minimum size of the region: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def calc_min ( self, use_size = False ): + def calc_min(self, use_size=False): """ Calculates the minimum size of the region. """ - tab_dx = tdx = tdy = 0 + tab_dx = tdx = tdy = 0 contents = self.visible_contents - theme = self.theme + theme = self.theme if self.is_notebook: for item in contents: - dx, dy = item.calc_min( use_size ) - tdx = max( tdx, dx ) - tdy = max( tdy, dy ) + dx, dy = item.calc_min(use_size) + tdx = max(tdx, dx) + tdy = max(tdy, dy) tab_dx += item.tab_width - tis = theme.tab.image_slice - tc = theme.tab.content - tdx = max( tdx, tab_dx ) + (tis.xleft + tis.xright + - tc.left + tc.right) - tdy += (theme.tab_active.image_slice.dy + - tis.xtop + tis.xbottom + tc.top + tc.bottom) - elif len( contents ) > 0: - item = contents[0] - tdx, tdy = item.calc_min( use_size ) + tis = theme.tab.image_slice + tc = theme.tab.content + tdx = max(tdx, tab_dx) + ( + tis.xleft + tis.xright + tc.left + tc.right + ) + tdy += ( + theme.tab_active.image_slice.dy + + tis.xtop + + tis.xbottom + + tc.top + + tc.bottom + ) + elif len(contents) > 0: + item = contents[0] + tdx, tdy = item.calc_min(use_size) if not item.locked: - if item.style == 'horizontal': + if item.style == "horizontal": tdy += theme.horizontal_drag.image_slice.dy - elif item.style == 'vertical': + elif item.style == "vertical": tdx += theme.vertical_drag.image_slice.dx if self.width < 0: - self.width = tdx + self.width = tdx self.height = tdy - return ( tdx, tdy ) + return (tdx, tdy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Layout the contents of the region based on the specified bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def recalc_sizes ( self, x, y, dx, dy ): + def recalc_sizes(self, x, y, dx, dy): """ Layout the contents of the region based on the specified bounds. """ - self.width = dx = max( 0, dx ) - self.height = dy = max( 0, dy ) - self.bounds = ( x, y, dx, dy ) + self.width = dx = max(0, dx) + self.height = dy = max(0, dy) + self.bounds = (x, y, dx, dy) - theme = self.theme + theme = self.theme contents = self.visible_contents if self.is_notebook: tis = theme.tab.image_slice - tc = theme.tab.content - th = theme.tab_active.image_slice.dy + tc = theme.tab.content + th = theme.tab_active.image_slice.dy # Layout the region out as a notebook: - x += tis.xleft + tc.left + x += tis.xleft + tc.left tx0 = tx = x + theme.tab.label.left - dx -= (tis.xleft + tis.xright + tc.left + tc.right) + dx -= tis.xleft + tis.xright + tc.left + tc.right ady = dy - th - dy = ady - tis.xtop - tis.xbottom - tc.top - tc.bottom - iy = y + tis.xtop + tc.top + dy = ady - tis.xtop - tis.xbottom - tc.top - tc.bottom + iy = y + tis.xtop + tc.top if theme.tabs_at_top: iy += th else: y += ady for item in contents: - item.recalc_sizes( x, iy, dx, dy ) + item.recalc_sizes(x, iy, dx, dy) tdx = item.tab_width - item.set_drag_bounds( tx, y, tdx, th ) + item.set_drag_bounds(tx, y, tdx, th) tx += tdx # Calculate the default tab clipping bounds: cdx = dx + tc.left + tc.right - self._tab_clip_bounds = ( tx0, y, cdx, th ) + self._tab_clip_bounds = (tx0, y, cdx, th) # Do we need to enable tab scrolling? xr = tx0 + cdx @@ -2333,148 +2478,154 @@ # Scrolling needed, calculate maximum tab index for scrolling: self.max_tab = 1 - n = len( contents ) - 1 - xr -= DockImages._tab_scroller_dx - for i in range( n, -1, -1 ): + n = len(contents) - 1 + xr -= DockImages._tab_scroller_dx + for i in range(n, -1, -1): xr -= contents[i].tab_width if xr < tx0: - self.max_tab = min( i + 1, n ) + self.max_tab = min(i + 1, n) break # Set the new leftmost tab index: - self.left_tab = min( self.left_tab, self.max_tab ) + self.left_tab = min(self.left_tab, self.max_tab) # Determine which tab scroll image to use: - self.tab_scroll_index = ((self.left_tab < self.max_tab) + - (2 * (self.left_tab > 0))) - 1 + self.tab_scroll_index = ( + (self.left_tab < self.max_tab) + (2 * (self.left_tab > 0)) + ) - 1 # Now adjust each tab's bounds accordingly: if self.left_tab > 0: - adx = contents[ self.left_tab ].drag_bounds[0] - tx0 + adx = contents[self.left_tab].drag_bounds[0] - tx0 for item in contents: dbx, dby, dbdx, dbdy = item.drag_bounds - item.set_drag_bounds( dbx - adx, dby, item.tab_width, - dbdy ) + item.set_drag_bounds( + dbx - adx, dby, item.tab_width, dbdy + ) # Exclude the scroll buttons from the tab clipping region: - self._tab_clip_bounds = ( tx0, y, cdx - - DockImages._tab_scroller_dx, th ) + self._tab_clip_bounds = ( + tx0, + y, + cdx - DockImages._tab_scroller_dx, + th, + ) else: self.tab_scroll_index = -1 self.left_tab = 0 else: # Lay the region out as a drag bar: - item = contents[0] - drag_bounds = ( 0, 0, 0, 0 ) + item = contents[0] + drag_bounds = (0, 0, 0, 0) if not item.locked: - if item.style == 'horizontal': + if item.style == "horizontal": db_dy = theme.horizontal_drag.image_slice.dy - drag_bounds = ( x, y, dx, db_dy ) - y += db_dy + drag_bounds = (x, y, dx, db_dy) + y += db_dy dy -= db_dy - elif item.style == 'vertical': + elif item.style == "vertical": db_dx = theme.vertical_drag.image_slice.dx - drag_bounds = ( x, y, db_dx, dy ) - x += db_dx + drag_bounds = (x, y, db_dx, dy) + x += db_dx dx -= db_dx - item.recalc_sizes( x, y, dx, dy ) - item.set_drag_bounds( *drag_bounds ) + item.recalc_sizes(x, y, dx, dy) + item.set_drag_bounds(*drag_bounds) # Make sure all of the contained controls have the right visiblity: self._set_visibility() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a new control before or after a specified control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add ( self, control, before = None, after = None, activate = True ): + def add(self, control, before=None, after=None, activate=True): """ Adds a new control before a specified control. """ contents = self.contents if control.parent is self: - contents.remove( control ) + contents.remove(control) if before is None: if after is None: - i = len( contents ) + i = len(contents) else: - i = contents.index( after ) + 1 + i = contents.index(after) + 1 else: - i = contents.index( before ) - contents.insert( i, control ) + i = contents.index(before) + contents.insert(i, control) if activate: self.active = i - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Removes a specified item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def remove ( self, item ): + def remove(self, item): """ Removes a specified item. """ contents = self.contents - i = contents.index( item ) + i = contents.index(item) - if isinstance( item, DockGroup ) and (len( item.contents ) == 1): + if isinstance(item, DockGroup) and (len(item.contents) == 1): item = item.contents[0] - if isinstance( item, DockRegion ): - contents[ i: i + 1 ] = item.contents[:] + if isinstance(item, DockRegion): + contents[i:i + 1] = item.contents[:] else: - contents[ i ] = item + contents[i] = item else: - del contents[ i ] + del contents[i] # Change the active selection only if 'item' is in closing mode, # or was dragged to a new location. # If this entire dock region is being closed, then all contained # dock items will be removed and we do not want to change 'active' # selection. if item._closing or item._dragging: - if (self.active > i) or (self.active >= len( contents )): + if (self.active > i) or (self.active >= len(contents)): self.active -= 1 # If the active item was removed, then 'active' stays # unchanged, but it reflects the index of the next page in # the dock region. Since _active_changed won't be fired now, # we fire the 'activated' event on the next page. - elif (i == self.active): - control = self.contents[ i ] - if isinstance( control, DockControl ): + elif i == self.active: + control = self.contents[i] + if isinstance(control, DockControl): control.activated = True if self.parent is not None: - if len( contents ) == 0: - self.parent.remove( self ) - elif ((len( contents ) == 1) and - isinstance( self.parent, DockRegion )): - self.parent.remove( self ) + if len(contents) == 0: + self.parent.remove(self) + elif (len(contents) == 1) and isinstance(self.parent, DockRegion): + self.parent.remove(self) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a copy of the region 'structure', minus the actual content: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_structure ( self ): + def get_structure(self): """ Returns a copy of the region 'structure', minus the actual content. """ - return self.clone_traits( [ 'active', 'width', 'height' ] ).trait_set( - contents = [ item.get_structure() for item in self.contents ] ) + return self.clone_traits(["active", "width", "height"]).trait_set( + contents=[item.get_structure() for item in self.contents] + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Toggles the 'lock' status of every control in the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def toggle_lock ( self ): + def toggle_lock(self): """ Toggles the 'lock' status of every control in the group. """ - super( DockRegion, self ).toggle_lock() + super().toggle_lock() self._is_notebook = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the contents of the region: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw ( self, dc ): + def draw(self, dc): """ Draws the contents of the region. """ if self._visible is not False: - self.begin_draw( dc ) + self.begin_draw(dc) if self.is_notebook: # fixme: There seems to be a case where 'draw' is called before @@ -2484,40 +2635,43 @@ # ( 0, 0, 0, 0 ), so there is nothing to draw anyways. The # question is why 'recalc_sizes' is not being called first. if self._tab_clip_bounds is None: - self.end_draw( dc ) + self.end_draw(dc) return - self.fill_bg_color( dc, *self.bounds ) + self.fill_bg_color(dc, *self.bounds) if self.active >= len(self.contents): # on some platforms, if the active tab was destroyed # the new active tab may not have been set yet self.active = len(self.contents) - 1 - - self._draw_notebook( dc ) + self._draw_notebook(dc) active = self.active # Draw the scroll buttons (if necessary): x, y, dx, dy = self._tab_clip_bounds index = self.tab_scroll_index if index >= 0: - dc.DrawBitmap( DockImages._tab_scroller_images[ index ], - x + dx, y + 2, True ) + dc.DrawBitmap( + DockImages._tab_scroller_images[index], + x + dx, + y + 2, + True, + ) # Draw all the inactive tabs first: - dc.SetClippingRegion( x, y, dx, dy ) + dc.SetClippingRegion(x, y, dx, dy) last_inactive = -1 - for i, item in enumerate( self.contents ): + for i, item in enumerate(self.contents): if (i != active) and item.visible: last_inactive = i - state = item.tab_state + state = item.tab_state if state not in NotActiveStates: state = TabInactive - item.draw_tab( dc, state ) + item.draw_tab(dc, state) # Draw the active tab last: - self.contents[ active ].draw_tab( dc, TabActive ) + self.contents[active].draw_tab(dc, TabActive) # If the last inactive tab drawn is also the rightmost tab and # the theme has a 'tab right edge' image, draw the image just @@ -2529,65 +2683,72 @@ bitmap = self.theme.tab_hover_edge_bitmap if bitmap is not None: x, y, dx, dy = item.drag_bounds - dc.DrawBitmap( bitmap, x + dx, y, True ) + dc.DrawBitmap(bitmap, x + dx, y, True) else: item = self.visible_contents[0] if not item.locked: - getattr( item, 'draw_' + item.style )( dc ) + getattr(item, "draw_" + item.style)(dc) - self.end_draw( dc ) + self.end_draw(dc) # Draw each of the items contained in the region: for item in self.contents: if item.visible: - item.draw( dc ) + item.draw(dc) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object at a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def object_at ( self, x, y ): + def object_at(self, x, y): """ Returns the object at a specified window position. """ - if (self._visible is not False) and self.is_at( x, y ): + if (self._visible is not False) and self.is_at(x, y): if self.is_notebook and (self.tab_scroll_index >= 0): cx, cy, cdx, cdy = self._tab_clip_bounds - if self.is_at( x, y, ( cx + cdx, cy + 2, - DockImages._tab_scroller_dx, - DockImages._tab_scroller_dy ) ): + if self.is_at( + x, + y, + ( + cx + cdx, + cy + 2, + DockImages._tab_scroller_dx, + DockImages._tab_scroller_dy, + ), + ): return self for item in self.visible_contents: - if item.is_at( x, y, item.drag_bounds ): + if item.is_at(x, y, item.drag_bounds): return item - object = item.object_at( x, y ) + object = item.object_at(x, y) if object is not None: return object return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the DockInfo object for a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock_info_at ( self, x, y, tdx, is_control ): + def dock_info_at(self, x, y, tdx, is_control): """ Gets the DockInfo object for a specified window position. """ # Check to see if the point is in our drag bar: - info = super( DockRegion, self ).dock_info_at( x, y, tdx, is_control ) + info = super().dock_info_at(x, y, tdx, is_control) if info is not None: return info # If we are not visible, or the point is not contained in us, give up: - if (self._visible is False) or (not self.is_at( x, y )): + if (self._visible is False) or (not self.is_at(x, y)): return None # Check to see if the point is in the drag bars of any controls: contents = self.visible_contents for item in contents: - object = item.dock_info_at( x, y, tdx, is_control ) + object = item.dock_info_at(x, y, tdx, is_control) if object is not None: return object @@ -2595,390 +2756,413 @@ # empty region outside of any tabs: lx, ty, dx, dy = self.bounds if self.is_notebook: - item = contents[-1] + item = contents[-1] ix, iy, idx, idy = item.drag_bounds if (x > (ix + idx)) and (iy <= y < (iy + idy)): - return DockInfo( kind = DOCK_TAB, - tab_bounds = ( ix + idx, iy, tdx, idy ), - region = self ) + return DockInfo( + kind=DOCK_TAB, + tab_bounds=(ix + idx, iy, tdx, idy), + region=self, + ) # Otherwise, figure out which edge the point is closest to, and # return a DockInfo object describing that edge: - left = x - lx - right = lx + dx - 1 - x - top = y - ty + left = x - lx + right = lx + dx - 1 - x + top = y - ty bottom = ty + dy - 1 - y - choice = min( left, right, top, bottom ) - mdx = dx / 3 - mdy = dy / 3 + choice = min(left, right, top, bottom) + mdx = dx // 3 + mdy = dy // 3 if choice == left: - return DockInfo( kind = DOCK_LEFT, - bounds = ( lx, ty, mdx, dy ), - region = self ) + return DockInfo( + kind=DOCK_LEFT, bounds=(lx, ty, mdx, dy), region=self + ) if choice == right: - return DockInfo( kind = DOCK_RIGHT, - bounds = ( lx + dx - mdx, ty, mdx, dy ), - region = self ) + return DockInfo( + kind=DOCK_RIGHT, + bounds=(lx + dx - mdx, ty, mdx, dy), + region=self, + ) if choice == top: - return DockInfo( kind = DOCK_TOP, - bounds = ( lx, ty, dx, mdy ), - region = self ) - - return DockInfo( kind = DOCK_BOTTOM, - bounds = ( lx, ty + dy - mdy, dx, mdy ), - region = self ) + return DockInfo( + kind=DOCK_TOP, bounds=(lx, ty, dx, mdy), region=self + ) + + return DockInfo( + kind=DOCK_BOTTOM, bounds=(lx, ty + dy - mdy, dx, mdy), region=self + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a contained notebook tab being clicked: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def tab_clicked ( self, control ): + def tab_clicked(self, control): """ Handles a contained notebook tab being clicked. """ # Find the page that was clicked and mark it as active: - i = self.contents.index( control ) + i = self.contents.index(control) if i != self.active: self.active = i # Recalculate the tab layout: - self.recalc_sizes( *self.bounds ) + self.recalc_sizes(*self.bounds) # Force the notebook to be redrawn: - control.control.GetParent().RefreshRect( wx.Rect( *self.bounds ) ) + control.control.GetParent().RefreshRect(wx.Rect(*self.bounds)) # Fire the 'activated' event on the control: - if isinstance( control, DockControl ): + if isinstance(control, DockControl): control.activated = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user clicking an active scroll button: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def scroll ( self, type, left_tab = 0 ): + def scroll(self, type, left_tab=0): """ Handles the user clicking an active scroll button. """ if type == SCROLL_LEFT: - left_tab = min( self.left_tab + 1, self.max_tab ) + left_tab = min(self.left_tab + 1, self.max_tab) elif type == SCROLL_RIGHT: - left_tab = max( self.left_tab - 1, 0 ) + left_tab = max(self.left_tab - 1, 0) if left_tab != self.left_tab: # Calculate the amount we need to adjust each tab by: contents = self.visible_contents - adx = (contents[ left_tab ].drag_bounds[0] - - contents[ self.left_tab ].drag_bounds[0]) + adx = ( + contents[left_tab].drag_bounds[0] + - contents[self.left_tab].drag_bounds[0] + ) # Set the new leftmost tab index: self.left_tab = left_tab # Determine which tab scroll image to use: - self.tab_scroll_index = ((left_tab < self.max_tab) + - (2 * (left_tab > 0))) - 1 + self.tab_scroll_index = ( + (left_tab < self.max_tab) + (2 * (left_tab > 0)) + ) - 1 # Now adjust each tab's bounds accordingly: for item in contents: dbx, dby, dbdx, dbdy = item.drag_bounds - item.set_drag_bounds( dbx - adx, dby, item.tab_width, dbdy ) + item.set_drag_bounds(dbx - adx, dby, item.tab_width, dbdy) # Finally, force a redraw of the affected part of the window: x, y, dx, dy = self._tab_clip_bounds item.control.GetParent().RefreshRect( - wx.Rect( x, y, dx + DockImages._tab_scroller_dx, dy ) ) + wx.Rect(x, y, dx + DockImages._tab_scroller_dx, dy) + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_down ( self, event ): + def mouse_down(self, event): """ Handles the left mouse button being pressed. """ - self._scroll = self._get_scroll_button( event ) + self._scroll = self._get_scroll_button(event) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_up ( self, event ): + def mouse_up(self, event): """ Handles the left mouse button being released. """ - if ((self._scroll is not None) and - (self._scroll == self._get_scroll_button( event ))): - self.scroll( self._scroll ) + if (self._scroll is not None) and ( + self._scroll == self._get_scroll_button(event) + ): + self.scroll(self._scroll) else: - super( DockRegion, self ).mouse_up( event ) + super().mouse_up(event) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse moving while the left mouse button is pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def mouse_move ( self, event ): + def mouse_move(self, event): """ Handles the mouse moving while the left mouse button is pressed. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the visibility of the region: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_visibility ( self, visible ): + def set_visibility(self, visible): """ Sets the visibility of the region. """ self._visible = visible - active = self.active - for i, item in enumerate( self.contents ): - item.set_visibility( visible and (i == active) ) + active = self.active + for i, item in enumerate(self.contents): + item.set_visibility(visible and (i == active)) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Activates a specified control (i.e. makes it the current notebook tab): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def activate ( self, control, layout = True ): + def activate(self, control, layout=True): """ Activates a specified control (i.e. makes it the current notebook tab). """ if control.visible and self.is_notebook: - active = self.contents.index( control ) + active = self.contents.index(control) if active != self.active: self.active = active self.make_active_tab_visible() window = control.control.GetParent() if layout: - do_later( window.owner.update_layout ) + do_later(window.owner.update_layout) else: - window.RefreshRect( wx.Rect( *self.bounds ) ) + window.RefreshRect(wx.Rect(*self.bounds)) else: # Fire the activated event for the control. - if isinstance( control, DockControl ): + if isinstance(control, DockControl): control.activated = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Makes sure the active control's tab is completely visible (if possible): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def make_active_tab_visible ( self ): + def make_active_tab_visible(self): """ Makes sure the active control's tab is completely visible (if possible). """ active = self.active if active < self.left_tab: - self.scroll( SCROLL_TO, active ) + self.scroll(SCROLL_TO, active) else: - x, y, dx, dy = self.contents[ active ].drag_bounds - if not self.is_at( x + dx - 1, y + dy - 1, self._tab_clip_bounds ): - self.scroll( SCROLL_TO, min( active, self.max_tab ) ) + x, y, dx, dy = self.contents[active].drag_bounds + if not self.is_at(x + dx - 1, y + dy - 1, self._tab_clip_bounds): + self.scroll(SCROLL_TO, min(active, self.max_tab)) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a contained DockControl item being hidden or shown: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def show_hide ( self, control ): + def show_hide(self, control): """ Handles a contained DockControl item being hidden or shown. """ - i = self.contents.index( control ) + i = self.contents.index(control) if i == self.active: self._update_active() elif (self.active < 0) and control.visible: self.active = i self._is_notebook = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Prints the contents of the region: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dump ( self, indent ): + def dump(self, indent): """ Prints the contents of the region. """ - print('%sRegion( %08X, active = %s, width = %d, height = %d )' % ( - ' ' * indent, id( self ), self.active, self.width, self.height )) + print( + "%sRegion( %08X, active = %s, width = %d, height = %d )" + % (" " * indent, id(self), self.active, self.width, self.height) + ) for item in self.contents: - item.dump( indent + 3 ) + item.dump(indent + 3) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns which scroll button (if any) the pointer is currently over: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_scroll_button ( self, event ): + def _get_scroll_button(self, event): """ Returns which scroll button (if any) the pointer is currently over. """ x, y, dx, dy = self._tab_clip_bounds - if self.is_in( event, x + dx, y + 2, DockImages._tab_scroller_dx, - DockImages._tab_scroller_dy ): - if (event.GetX() - (x + dx)) < (DockImages._tab_scroller_dx / 2): + if self.is_in( + event, + x + dx, + y + 2, + DockImages._tab_scroller_dx, + DockImages._tab_scroller_dy, + ): + if (event.GetX() - (x + dx)) < (DockImages._tab_scroller_dx // 2): return SCROLL_LEFT return SCROLL_RIGHT return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Updates the currently active page after a change: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _update_active ( self, active = None ): + def _update_active(self, active=None): """ Updates the currently active page after a change. """ if active is None: active = self.active contents = self.contents - for i in (list(range(active, len(contents))) + - list(range(active - 1, -1, -1))): - if contents[ i ].visible: + for i in list(range(active, len(contents))) + list( + range(active - 1, -1, -1) + ): + if contents[i].visible: self.active = i return self.active = -1 - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'active' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _active_changed ( self, old, new ): + @observe("active") + def _active_updated(self, event): + old, new = event.old, event.new self._set_visibility() # Set the correct tab state for each tab: - for i, item in enumerate( self.contents ): - item.tab_state = NormalStates[ i == new ] + for i, item in enumerate(self.contents): + item.tab_state = NormalStates[i == new] - n = len( self.contents ) + n = len(self.contents) if 0 <= old < n: # Notify the previously active dockable that the control's tab is # being deactivated: - control = self.contents[ old ] - if (isinstance( control, DockControl ) and - (control.dockable is not None)): - control.dockable.dockable_tab_activated( control, False ) + control = self.contents[old] + if isinstance(control, DockControl) and ( + control.dockable is not None + ): + control.dockable.dockable_tab_activated(control, False) if 0 <= new < n: # Notify the new dockable that the control's tab is being # activated: - control = self.contents[ new ] - if isinstance( control, DockControl ): + control = self.contents[new] + if isinstance(control, DockControl): control.activated = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'contents' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _contents_changed ( self ): + @observe("contents") + def _contents_updated(self, event): """ Handles the 'contents' trait being changed. """ self._is_notebook = None for item in self.contents: item.parent = self - self.calc_min( True ) + self.calc_min(True) self.modified = True - def _contents_items_changed ( self, event ): + @observe("contents:items") + def _contents_items_updated(self, event): """ Handles the 'contents' trait being changed. """ self._is_notebook = None for item in event.added: item.parent = self - self.calc_min( True ) + self.calc_min(True) self.modified = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Set the proper visiblity for all contained controls: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_visibility ( self ): + def _set_visibility(self): """ Set the proper visiblity for all contained controls. """ active = self.active - for i, item in enumerate( self.contents ): - item.set_visibility( i == active ) + for i, item in enumerate(self.contents): + item.set_visibility(i == active) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'modified' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_modified ( self, value ): + def _set_modified(self, value): if self.parent is not None: self.parent.modified = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'is_notebook' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_is_notebook ( self ): + def _get_is_notebook(self): if self._is_notebook is None: contents = self.visible_contents - n = len( contents ) - self._is_notebook = (n > 1) + n = len(contents) + self._is_notebook = n > 1 if n == 1: - self._is_notebook = (contents[0].style == 'tab') + self._is_notebook = contents[0].style == "tab" return self._is_notebook - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the notebook body: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _draw_notebook ( self, dc ): + def _draw_notebook(self, dc): """ Draws the notebook body. """ - theme = self.theme - tab_height = theme.tab_active.image_slice.dy + theme = self.theme + tab_height = theme.tab_active.image_slice.dy x, y, dx, dy = self.bounds - self.fill_bg_color( dc, x, y, dx, dy ) + self.fill_bg_color(dc, x, y, dx, dy) # Draws a box around the frame containing the tab contents, starting # below the tab pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)) dc.SetPen(pen) - dc.DrawRectangle(x, y+tab_height, dx, dy-tab_height) + dc.DrawRectangle(x, y + tab_height, dx, dy - tab_height) # draw highlight pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)) dc.SetPen(pen) - dc.DrawLine(x+1, y+tab_height+1, x+dx-1, y+tab_height+1) + dc.DrawLine(x + 1, y + tab_height + 1, x + dx - 1, y + tab_height + 1) # Erases the line under the active tab x0 = x + self.tab_theme.label.left x1 = x0 - for i in range(self.active+1): + for i in range(self.active + 1): x0 = x1 + 1 x1 += self.contents[i].tab_width dc.SetPen(wx.Pen(self.get_bg_color())) - dc.DrawLine(x0, y+tab_height, x1, y+tab_height) - dc.DrawLine(x0, y+tab_height+1, x1, y+tab_height+1) + dc.DrawLine(x0, y + tab_height, x1, y + tab_height) + dc.DrawLine(x0, y + tab_height + 1, x1, y + tab_height + 1) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'DockSection' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockSection ( DockGroup ): +class DockSection(DockGroup): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Is this a row (or a column)? - is_row = Bool( True ) + is_row = Bool(True) # Bounds of any splitter bars associated with the region: - splitters = List( DockSplitter ) + splitters = List(DockSplitter) # The DockWindow that owns this section (set on top level section only): - dock_window = Instance( 'pyface.dock.dock_window.DockWindow' ) + dock_window = Instance("pyface.dock.dock_window.DockWindow") # Contents of the section have been modified property: modified = Property - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Re-implementation of the 'owner' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @cached_property - def _get_owner ( self ): + def _get_owner(self): if self.dock_window is not None: return self.dock_window @@ -2987,30 +3171,30 @@ return self.parent.owner - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Calculates the minimum size of the section: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def calc_min ( self, use_size = False ): + def calc_min(self, use_size=False): """ Calculates the minimum size of the section. """ - tdx = tdy = 0 + tdx = tdy = 0 contents = self.visible_contents - n = len( contents ) + n = len(contents) if self.is_row: # allow 10 pixels for the splitter sdx = 10 for item in contents: - dx, dy = item.calc_min( use_size ) - tdx += dx - tdy = max( tdy, dy ) + dx, dy = item.calc_min(use_size) + tdx += dx + tdy = max(tdy, dy) if self.resizable: - tdx += ((n - 1) * sdx) + tdx += (n - 1) * sdx else: - tdx += ((n + 1) * 3) + tdx += (n + 1) * 3 tdy += 6 else: @@ -3018,40 +3202,40 @@ sdy = 10 for item in contents: - dx, dy = item.calc_min( use_size ) - tdx = max( tdx, dx ) - tdy += dy + dx, dy = item.calc_min(use_size) + tdx = max(tdx, dx) + tdy += dy if self.resizable: - tdy += ((n - 1) * sdy) + tdy += (n - 1) * sdy else: tdx += 6 - tdy += ((n + 1) * 3) + tdy += (n + 1) * 3 if self.width < 0: - self.width = tdx + self.width = tdx self.height = tdy - return ( tdx, tdy ) + return (tdx, tdy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Perform initial layout of the section based on the specified bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def initial_recalc_sizes ( self, x, y, dx, dy ): + def initial_recalc_sizes(self, x, y, dx, dy): """ Layout the contents of the section based on the specified bounds. """ - self.width = dx = max( 0, dx ) - self.height = dy = max( 0, dy ) - self.bounds = ( x, y, dx, dy ) + self.width = dx = max(0, dx) + self.height = dy = max(0, dy) + self.bounds = (x, y, dx, dy) # If none of the contents are resizable, use the fixed layout method if not self.resizable: - self.recalc_sizes_fixed( x, y, dx, dy ) + self.recalc_sizes_fixed(x, y, dx, dy) return - contents = self.visible_contents - n = len( contents ) - 1 + contents = self.visible_contents + n = len(contents) - 1 splitters = [] # Find out how much space is available. @@ -3069,25 +3253,25 @@ dockable = dock_control.dockable if dockable is not None and dockable.element is not None: if self.is_row: - size = max( size, dockable.element.width ) + size = max(size, dockable.element.width) else: - size = max( size, dockable.element.height ) - sizes.append( size ) + size = max(size, dockable.element.height) + sizes.append(size) # Allocate requested space. avail = total remain = 0 - for i, sz in enumerate( sizes ): + for i, sz in enumerate(sizes): if avail <= 0: break if sz >= 0: if sz >= 1: - sz = min( sz, avail ) + sz = min(sz, avail) else: sz *= total - sz = int( sz ) + sz = int(sz) sizes[i] = sz avail -= sz else: @@ -3095,9 +3279,9 @@ # Allocate the remainder to those parts that didn't request a width. if remain > 0: - remain = int( avail / remain ) + remain = int(avail / remain) - for i, sz in enumerate( sizes ): + for i, sz in enumerate(sizes): if sz < 0: sizes[i] = remain @@ -3107,34 +3291,40 @@ # Resize contents and add splitters if self.is_row: - for i, item in enumerate( contents ): - idx = int( sizes[i] ) - item.recalc_sizes( x, y, idx, dy ) + for i, item in enumerate(contents): + idx = int(sizes[i]) + item.recalc_sizes(x, y, idx, dy) x += idx if i < n: splitters.append( - DockSplitter( bounds = ( x, y, splitter_size, dy ), - style = 'vertical', - parent = self, - index = i ) ) + DockSplitter( + bounds=(x, y, splitter_size, dy), + style="vertical", + parent=self, + index=i, + ) + ) x += splitter_size else: - for i, item in enumerate( contents ): - idy = int( sizes[i] ) - item.recalc_sizes( x, y, dx, idy ) + for i, item in enumerate(contents): + idy = int(sizes[i]) + item.recalc_sizes(x, y, dx, idy) y += idy if i < n: splitters.append( - DockSplitter( bounds = ( x, y, dx, splitter_size ), - style = 'horizontal', - parent = self, - index = i ) ) + DockSplitter( + bounds=(x, y, dx, splitter_size), + style="horizontal", + parent=self, + index=i, + ) + ) y += splitter_size # Preserve the current internal '_last_bounds' for all splitters if # possible: cur_splitters = self.splitters - for i in range( min( len( splitters ), len( cur_splitters ) ) ): + for i in range(min(len(splitters), len(cur_splitters))): splitters[i]._last_bounds = cur_splitters[i]._last_bounds # Save the new set of splitter bars: @@ -3143,30 +3333,30 @@ # Set the visibility for all contained items: self._set_visibility() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Layout the contents of the section based on the specified bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def recalc_sizes ( self, x, y, dx, dy ): + def recalc_sizes(self, x, y, dx, dy): """ Layout the contents of the section based on the specified bounds. """ # Check if we need to perform initial layout if not self.initialized: - self.initial_recalc_sizes( x, y, dx, dy ) + self.initial_recalc_sizes(x, y, dx, dy) self.initialized = True return - self.width = dx = max( 0, dx ) - self.height = dy = max( 0, dy ) - self.bounds = ( x, y, dx, dy ) + self.width = dx = max(0, dx) + self.height = dy = max(0, dy) + self.bounds = (x, y, dx, dy) # If none of the contents are resizable, use the fixed layout method: if not self.resizable: - self.recalc_sizes_fixed( x, y, dx, dy ) + self.recalc_sizes_fixed(x, y, dx, dy) return - contents = self.visible_contents - n = len( contents ) - 1 + contents = self.visible_contents + n = len(contents) - 1 splitters = [] # Perform a horizontal layout: @@ -3174,36 +3364,39 @@ # allow 10 pixels for the splitter sdx = 10 - dx -= (n * sdx) + dx -= n * sdx cdx = 0 # Calculate the current and minimum width: for item in contents: cdx += item.width - cdx = max( 1, cdx ) + cdx = max(1, cdx) # Calculate the delta between the current and new width: delta = remaining = dx - cdx # Allocate the change (plus or minus) proportionally based on each # item's current size: - for i, item in enumerate( contents ): + for i, item in enumerate(contents): if i < n: - idx = int( round( float( item.width * delta ) / cdx ) ) + idx = int(round(float(item.width * delta) / cdx)) else: idx = remaining remaining -= idx - idx += item.width - item.recalc_sizes( x, y, idx, dy ) + idx += item.width + item.recalc_sizes(x, y, idx, dy) x += idx # Define the splitter bar between adjacent items: if i < n: splitters.append( - DockSplitter( bounds = ( x, y, sdx, dy ), - style = 'vertical', - parent = self, - index = i ) ) + DockSplitter( + bounds=(x, y, sdx, dy), + style="vertical", + parent=self, + index=i, + ) + ) x += sdx # Perform a vertical layout: @@ -3211,42 +3404,45 @@ # allow 10 pixels for the splitter sdy = 10 - dy -= (n * sdy) + dy -= n * sdy cdy = 0 # Calculate the current and minimum height: for item in contents: cdy += item.height - cdy = max( 1, cdy ) + cdy = max(1, cdy) # Calculate the delta between the current and new height: - delta = remaining = dy - cdy + delta = remaining = dy - cdy # Allocate the change (plus or minus) proportionally based on each # item's current size: - for i, item in enumerate( contents ): + for i, item in enumerate(contents): if i < n: - idy = int( round( float( item.height * delta ) / cdy ) ) + idy = int(round(float(item.height * delta) / cdy)) else: idy = remaining remaining -= idy - idy += item.height - item.recalc_sizes( x, y, dx, idy ) + idy += item.height + item.recalc_sizes(x, y, dx, idy) y += idy # Define the splitter bar between adjacent items: if i < n: splitters.append( - DockSplitter( bounds = ( x, y, dx, sdy ), - style = 'horizontal', - parent = self, - index = i ) ) + DockSplitter( + bounds=(x, y, dx, sdy), + style="horizontal", + parent=self, + index=i, + ) + ) y += sdy # Preserve the current internal '_last_bounds' for all splitters if # possible: cur_splitters = self.splitters - for i in range( min( len( splitters ), len( cur_splitters ) ) ): + for i in range(min(len(splitters), len(cur_splitters))): splitters[i]._last_bounds = cur_splitters[i]._last_bounds # Save the new set of splitter bars: @@ -3255,12 +3451,12 @@ # Set the visibility for all contained items: self._set_visibility() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Layout the contents of the section based on the specified bounds using # the minimum requested size for each item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def recalc_sizes_fixed ( self, x, y, dx, dy ): + def recalc_sizes_fixed(self, x, y, dx, dy): """ Layout the contents of the section based on the specified bounds using the minimum requested size for each item. """ @@ -3268,8 +3464,8 @@ x += 3 y += 3 - dx = max( 0, dx - 3 ) - dy = max( 0, dy - 3 ) + dx = max(0, dx - 3) + dy = max(0, dy - 3) # Perform a horizontal layout: if self.is_row: @@ -3277,10 +3473,10 @@ # the space runs out: for item in self.visible_contents: idx, idy = item.calc_min() - idx = min( dx, idx ) - idy = min( dy, idy ) - dx = max( 0, dx - idx - 3 ) - item.recalc_sizes( x, y, idx, idy ) + idx = min(dx, idx) + idy = min(dy, idy) + dx = max(0, dx - idx - 3) + item.recalc_sizes(x, y, idx, idy) x += idx + 3 # Perform a vertical layout: @@ -3289,66 +3485,66 @@ # the space runs out: for item in self.visible_contents: idx, idy = item.calc_min() - idx = min( dx, idx ) - idy = min( dy, idy ) - dy = max( 0, dy - idy - 3 ) - item.recalc_sizes( x, y, idx, idy ) + idx = min(dx, idx) + idy = min(dy, idy) + dy = max(0, dy - idy - 3) + item.recalc_sizes(x, y, idx, idy) y += idy + 3 # Set the visibility for all contained items: self._set_visibility() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the contents of the section: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw ( self, dc ): + def draw(self, dc): """ Draws the contents of the section. """ if self._visible is not False: contents = self.visible_contents x, y, dx, dy = self.bounds - self.fill_bg_color( dc, x, y, dx, dy ) + self.fill_bg_color(dc, x, y, dx, dy) for item in contents: - item.draw( dc ) + item.draw(dc) - self.begin_draw( dc ) + self.begin_draw(dc) for item in self.splitters: - item.draw( dc ) - self.end_draw( dc ) + item.draw(dc) + self.end_draw(dc) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object at a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def object_at ( self, x, y, force = False ): + def object_at(self, x, y, force=False): """ Returns the object at a specified window position. """ if self._visible is not False: for item in self.splitters: - if item.is_at( x, y ): + if item.is_at(x, y): return item for item in self.visible_contents: - object = item.object_at( x, y ) + object = item.object_at(x, y) if object is not None: return object - if force and self.is_at( x, y ): + if force and self.is_at(x, y): return self return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the DockInfo object for a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock_info_at ( self, x, y, tdx, is_control, force = False ): + def dock_info_at(self, x, y, tdx, is_control, force=False): """ Gets the DockInfo object for a specified window position. """ # Check to see if the point is in our drag bar: - info = super( DockSection, self ).dock_info_at( x, y, tdx, is_control ) + info = super().dock_info_at(x, y, tdx, is_control) if info is not None: return info @@ -3356,11 +3552,11 @@ return None for item in self.splitters: - if item.is_at( x, y ): - return DockInfo( kind = DOCK_SPLITTER ) + if item.is_at(x, y): + return DockInfo(kind=DOCK_SPLITTER) for item in self.visible_contents: - object = item.dock_info_at( x, y, tdx, is_control ) + object = item.dock_info_at(x, y, tdx, is_control) if object is not None: return object @@ -3371,259 +3567,274 @@ # Otherwise, figure out which edge the point is closest to, and # return a DockInfo object describing that edge: lx, ty, dx, dy = self.bounds - left = lx - x - right = x - lx - dx + 1 - top = ty - y + left = lx - x + right = x - lx - dx + 1 + top = ty - y bottom = y - ty - dy + 1 # If the point is way outside of the section, mark it is a drag and # drop candidate: - if max( left, right, top, bottom ) > 20: - return DockInfo( kind = DOCK_EXPORT ) + if max(left, right, top, bottom) > 20: + return DockInfo(kind=DOCK_EXPORT) - left = abs( left ) - right = abs( right ) - top = abs( top ) - bottom = abs( bottom ) - choice = min( left, right, top, bottom ) - mdx = dx / 3 - mdy = dy / 3 + left = abs(left) + right = abs(right) + top = abs(top) + bottom = abs(bottom) + choice = min(left, right, top, bottom) + mdx = dx // 3 + mdy = dy // 3 if choice == left: - return DockInfo( kind = DOCK_LEFT, - bounds = ( lx, ty, mdx, dy ) ) + return DockInfo(kind=DOCK_LEFT, bounds=(lx, ty, mdx, dy)) if choice == right: - return DockInfo( kind = DOCK_RIGHT, - bounds = ( lx + dx - mdx, ty, mdx, dy ) ) + return DockInfo( + kind=DOCK_RIGHT, bounds=(lx + dx - mdx, ty, mdx, dy) + ) if choice == top: - return DockInfo( kind = DOCK_TOP, - bounds = ( lx, ty, dx, mdy ) ) + return DockInfo(kind=DOCK_TOP, bounds=(lx, ty, dx, mdy)) - return DockInfo( kind = DOCK_BOTTOM, - bounds = ( lx, ty + dy - mdy, dx, mdy ) ) + return DockInfo(kind=DOCK_BOTTOM, bounds=(lx, ty + dy - mdy, dx, mdy)) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a control to the section at the edge of the region specified: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add ( self, control, region, kind ): + def add(self, control, region, kind): """ Adds a control to the section at the edge of the region specified. """ - contents = self.contents + contents = self.contents new_region = control - if not isinstance( control, DockRegion ): - new_region = DockRegion( contents = [ control ] ) - i = contents.index( region ) + if not isinstance(control, DockRegion): + new_region = DockRegion(contents=[control]) + i = contents.index(region) if self.is_row: if (kind == DOCK_TOP) or (kind == DOCK_BOTTOM): if kind == DOCK_TOP: - new_contents = [ new_region, region ] + new_contents = [new_region, region] else: - new_contents = [ region, new_region ] - contents[ i ] = DockSection( is_row = False ).trait_set( - contents = new_contents ) + new_contents = [region, new_region] + contents[i] = DockSection(is_row=False).trait_set( + contents=new_contents + ) else: if new_region.parent is self: - contents.remove( new_region ) - i = contents.index( region ) + contents.remove(new_region) + i = contents.index(region) if kind == DOCK_RIGHT: i += 1 - contents.insert( i, new_region ) + contents.insert(i, new_region) else: if (kind == DOCK_LEFT) or (kind == DOCK_RIGHT): if kind == DOCK_LEFT: - new_contents = [ new_region, region ] + new_contents = [new_region, region] else: - new_contents = [ region, new_region ] - contents[ i ] = DockSection( is_row = True ).trait_set( - contents = new_contents ) + new_contents = [region, new_region] + contents[i] = DockSection(is_row=True).trait_set( + contents=new_contents + ) else: if new_region.parent is self: - contents.remove( new_region ) - i = contents.index( region ) + contents.remove(new_region) + i = contents.index(region) if kind == DOCK_BOTTOM: i += 1 - contents.insert( i, new_region ) + contents.insert(i, new_region) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Removes a specified region or section from the section: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def remove ( self, item ): + def remove(self, item): """ Removes a specified region or section from the section. """ contents = self.contents - if isinstance( item, DockGroup ) and (len( item.contents ) == 1): - contents[ contents.index( item ) ] = item.contents[0] + if isinstance(item, DockGroup) and (len(item.contents) == 1): + contents[contents.index(item)] = item.contents[0] else: - contents.remove( item ) + contents.remove(item) if self.parent is not None: - if len( contents ) <= 1: - self.parent.remove( self ) - elif (len( contents ) == 0) and (self.dock_window is not None): + if len(contents) <= 1: + self.parent.remove(self) + elif (len(contents) == 0) and (self.dock_window is not None): self.dock_window.dock_window_empty() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the visibility of the group: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_visibility ( self, visible ): + def set_visibility(self, visible): """ Sets the visibility of the group. """ self._visible = visible for item in self.contents: - item.set_visibility( visible ) + item.set_visibility(visible) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a copy of the section 'structure', minus the actual content: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_structure ( self ): + def get_structure(self): """ Returns a copy of the section 'structure', minus the actual content. """ - return self.clone_traits( [ 'is_row', 'width', 'height' ] ).trait_set( - contents = [ item.get_structure() for item in self.contents ], - splitters = [ item.get_structure() for item in self.splitters ] ) + return self.clone_traits(["is_row", "width", "height"]).trait_set( + contents=[item.get_structure() for item in self.contents], + splitters=[item.get_structure() for item in self.splitters], + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the maximum bounds that a splitter bar is allowed to be dragged: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_splitter_bounds ( self, splitter ): + def get_splitter_bounds(self, splitter): """ Gets the maximum bounds that a splitter bar is allowed to be dragged. """ - x, y, dx, dy = splitter.bounds - i = self.splitters.index( splitter ) - contents = self.visible_contents - item1 = contents[ i ] - item2 = contents[ i + 1 ] + x, y, dx, dy = splitter.bounds + i = self.splitters.index(splitter) + contents = self.visible_contents + item1 = contents[i] + item2 = contents[i + 1] bx, by, bdx, bdy = item2.bounds if self.is_row: - x = item1.bounds[0] + x = item1.bounds[0] dx = bx + bdx - x else: - y = item1.bounds[1] + y = item1.bounds[1] dy = by + bdy - y - return ( x, y, dx, dy ) + return (x, y, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Updates the affected regions when a splitter bar is released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def update_splitter ( self, splitter, window ): + def update_splitter(self, splitter, window): """ Updates the affected regions when a splitter bar is released. """ - x, y, dx, dy = splitter.bounds - i = self.splitters.index( splitter ) - contents = self.visible_contents - item1 = contents[ i ] - item2 = contents[ i + 1 ] + x, y, dx, dy = splitter.bounds + i = self.splitters.index(splitter) + contents = self.visible_contents + item1 = contents[i] + item2 = contents[i + 1] ix1, iy1, idx1, idy1 = item1.bounds ix2, iy2, idx2, idy2 = item2.bounds window.Freeze() if self.is_row: - item1.recalc_sizes( ix1, iy1, x - ix1, idy1 ) - item2.recalc_sizes( x + dx, iy2, ix2 + idx2 - x - dx, idy2 ) + item1.recalc_sizes(ix1, iy1, x - ix1, idy1) + item2.recalc_sizes(x + dx, iy2, ix2 + idx2 - x - dx, idy2) else: - item1.recalc_sizes( ix1, iy1, idx1, y - iy1 ) - item2.recalc_sizes( ix2, y + dy, idx2, iy2 + idy2 - y - dy ) + item1.recalc_sizes(ix1, iy1, idx1, y - iy1) + item2.recalc_sizes(ix2, y + dy, idx2, iy2 + idy2 - y - dy) window.Thaw() - if splitter.style == 'horizontal': + if splitter.style == "horizontal": dx = 0 else: dy = 0 - window.RefreshRect( wx.Rect( ix1 - dx, iy1 - dy, - ix2 + idx2 - ix1 + 2 * dx, iy2 + idy2 - iy1 + 2 * dy ) ) + window.RefreshRect( + wx.Rect( + ix1 - dx, + iy1 - dy, + ix2 + idx2 - ix1 + 2 * dx, + iy2 + idy2 - iy1 + 2 * dy, + ) + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Prints the contents of the section: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dump ( self, indent = 0 ): + def dump(self, indent=0): """ Prints the contents of the section. """ - print('%sSection( %08X, is_row = %s, width = %d, height = %d )' % ( - ' ' * indent, id( self ), self.is_row, self.width, self.height )) + print( + "%sSection( %08X, is_row = %s, width = %d, height = %d )" + % (" " * indent, id(self), self.is_row, self.width, self.height) + ) for item in self.contents: - item.dump( indent + 3 ) + item.dump(indent + 3) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the correct visiblity for all contained items: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_visibility ( self ): + def _set_visibility(self): """ Sets the correct visiblity for all contained items. """ for item in self.contents: - item.set_visibility( item.visible ) + item.set_visibility(item.visible) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'contents' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _contents_changed ( self ): + @observe("contents") + def _contents_updated(self, event): """ Handles the 'contents' trait being changed. """ for item in self.contents: item.parent = self - self.calc_min( True ) + self.calc_min(True) self.modified = True - def _contents_items_changed ( self, event ): + @observe("contents:items") + def _contents_items_updated(self, event): """ Handles the 'contents' trait being changed. """ for item in event.added: item.parent = self - self.calc_min( True ) + self.calc_min(True) self.modified = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'splitters' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _splitters_changed ( self ): + @observe("splitters") + def _splitters_updated(self, event): """ Handles the 'splitters' trait being changed. """ for item in self.splitters: item.parent = self - def _splitters_items_changed ( self, event ): + @observe("splitters:items") + def _splitters_items_updated(self, event): """ Handles the 'splitters' trait being changed. """ for item in event.added: item.parent = self - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'modified' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_modified ( self, value ): + def _set_modified(self, value): self._resizable = None if self.parent is not None: self.parent.modified = True -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockInfo' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class DockInfo ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class DockInfo(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Dock kind: - kind = Range( DOCK_TOP, DOCK_EXPORT ) + kind = Range(DOCK_TOP, DOCK_EXPORT) # Dock bounds: bounds = Bounds @@ -3632,19 +3843,16 @@ tab_bounds = Bounds # Dock Region: - region = Instance( DockRegion ) + region = Instance(DockRegion) # Dock Control: - control = Instance( DockItem ) - - def __init__(self, **kw): - super(DockInfo, self).__init__(**kw) + control = Instance(DockItem) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the DockInfo on the display: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def draw ( self, window, bitmap = None ): + def draw(self, window, bitmap=None): """ Draws the DockInfo on the display. """ if DOCK_TOP <= self.kind <= DOCK_TABADD: @@ -3655,137 +3863,146 @@ else: self._bitmap = bitmap - sdc, bx, by = get_dc( window ) - bdc = wx.MemoryDC() - bdc2 = wx.MemoryDC() - bdx, bdy = bitmap.GetWidth(), bitmap.GetHeight() - bitmap2 = wx.EmptyBitmap( bdx, bdy ) - bdc.SelectObject( bitmap ) - bdc2.SelectObject( bitmap2 ) - bdc2.Blit( 0, 0, bdx, bdy, bdc, 0, 0 ) + sdc, bx, by = get_dc(window) + bdc = wx.MemoryDC() + bdc2 = wx.MemoryDC() + bdx, bdy = bitmap.GetWidth(), bitmap.GetHeight() + bitmap2 = wx.Bitmap(bdx, bdy) + bdc.SelectObject(bitmap) + bdc2.SelectObject(bitmap2) + bdc2.Blit(0, 0, bdx, bdy, bdc, 0, 0) try: - bdc3 = wx.GCDC( bdc2 ) - bdc3.SetPen( wx.TRANSPARENT_PEN ) - bdc3.SetBrush( wx.Brush( wx.Colour( *DockColorBrush ) ) ) + bdc3 = wx.GCDC(bdc2) + bdc3.SetPen(wx.TRANSPARENT_PEN) + bdc3.SetBrush(wx.Brush(wx.Colour(*DockColorBrush))) x, y, dx, dy = self.bounds if DOCK_TAB <= self.kind <= DOCK_TABADD: tx, ty, tdx, tdy = self.tab_bounds - bdc3.DrawRoundedRectangle( tx, ty, tdx, tdy, 4 ) + bdc3.DrawRoundedRectangle(tx, ty, tdx, tdy, 4) else: - bdc3.DrawRoundedRectangle( x, y, dx, dy, 8 ) + bdc3.DrawRoundedRectangle(x, y, dx, dy, 8) except Exception: pass - sdc.Blit( bx, by, bdx, bdy, bdc2, 0, 0 ) + sdc.Blit(bx, by, bdx, bdy, bdc2, 0, 0) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Docks the specified control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock ( self, control, window ): + def dock(self, control, window): """ Docks the specified control. """ the_control = control - kind = self.kind + kind = self.kind if kind < DOCK_NONE: the_parent = control.parent - region = self.region + region = self.region if (kind == DOCK_TAB) or (kind == DOCK_BAR): - region.add( control, self.control ) + region.add(control, self.control) elif kind == DOCK_TABADD: item = self.control - if isinstance( item, DockControl ): - if isinstance( control, DockControl ): - control = DockRegion( contents = [ control ] ) - i = region.contents.index( item ) - region.contents[ i ] = item = DockSection( - contents = [ DockRegion( contents = [ item ] ), - control ], - is_row = True ) - elif isinstance( item, DockSection ): - if (isinstance( control, DockSection ) and - (item.is_row == control.is_row)): - item.contents.extend( control.contents ) + if isinstance(item, DockControl): + if isinstance(control, DockControl): + control = DockRegion(contents=[control]) + i = region.contents.index(item) + region.contents[i] = item = DockSection( + contents=[DockRegion(contents=[item]), control], + is_row=True, + ) + elif isinstance(item, DockSection): + if isinstance(control, DockSection) and ( + item.is_row == control.is_row + ): + item.contents.extend(control.contents) else: - if isinstance( control, DockControl ): - control = DockRegion( contents = [ control ] ) - item.contents.append( control ) + if isinstance(control, DockControl): + control = DockRegion(contents=[control]) + item.contents.append(control) else: - item.contents.append( control ) - region.active = region.contents.index( item ) + item.contents.append(control) + region.active = region.contents.index(item) elif region is not None: - region.parent.add( control, region, kind ) + region.parent.add(control, region, kind) else: - sizer = window.GetSizer() + sizer = window.GetSizer() section = sizer._contents - if ((section.is_row and - ((kind == DOCK_TOP) or (kind == DOCK_BOTTOM))) or - ((not section.is_row) and - ((kind == DOCK_LEFT) or (kind == DOCK_RIGHT)))): - if len( section.contents ) > 0: + if ( + section.is_row + and ((kind == DOCK_TOP) or (kind == DOCK_BOTTOM)) + ) or ( + (not section.is_row) + and ((kind == DOCK_LEFT) or (kind == DOCK_RIGHT)) + ): + if len(section.contents) > 0: sizer._contents = section = DockSection( - is_row = not section.is_row ).trait_set( - contents = [ section ] ) - if len( section.contents ) > 0: + is_row=not section.is_row + ).trait_set(contents=[section]) + if len(section.contents) > 0: i = 0 if (kind == DOCK_RIGHT) or (kind == DOCK_BOTTOM): i = -1 - section.add( control, section.contents[ i ], kind ) + section.add(control, section.contents[i], kind) else: - section.is_row = not section.is_row - section.contents = [ DockRegion( contents = [ control ] ) ] - section = None - - if ((the_parent is not None) and - (the_parent is not the_control.parent)): - the_parent.remove( the_control ) + section.is_row = not section.is_row + section.contents = [DockRegion(contents=[control])] + section = None + + if (the_parent is not None) and ( + the_parent is not the_control.parent + ): + the_parent.remove(the_control) # Force the main window to be laid out and redrawn: window.Layout() window.Refresh() + # Create a reusable DockInfo indicating no information available: -no_dock_info = DockInfo( kind = DOCK_NONE ) +no_dock_info = DockInfo(kind=DOCK_NONE) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'SetStructureHandler' class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class SetStructureHandler ( object ): +class SetStructureHandler(object): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Resolves an unresolved DockControl id: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def resolve_id ( self, id ): + def resolve_id(self, id): """ Resolves an unresolved DockControl id. """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Resolves extra, unused DockControls not referenced by the structure: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def resolve_extras ( self, structure, extras ): + def resolve_extras(self, structure, extras): """ Resolves extra, unused DockControls not referenced by the structure. """ for dock_control in extras: if dock_control.control is not None: - dock_control.control.Show( False ) + dock_control.control.Show(False) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockSizer' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockSizer ( wx.PySizer ): +class DockSizer(wx.Sizer): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Initializes the object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def __init__ ( self, contents = None ): - super( DockSizer, self ).__init__() + def __init__(self, contents=None): + super().__init__() # Make sure the DockImages singleton has been initialized: DockImages.init() @@ -3793,60 +4010,60 @@ # Finish initializing the sizer itself: self._contents = self._structure = self._max_structure = None if contents is not None: - self.SetContents( contents ) + self.SetContents(contents) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Calculates the minimum size needed by the sizer: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def CalcMin ( self ): + def CalcMin(self): if self._contents is None: - return wx.Size( 20, 20 ) + return wx.Size(20, 20) dx, dy = self._contents.calc_min() - return wx.Size( dx, dy ) + return wx.Size(dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Layout the contents of the sizer based on the sizer's current size and # position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def RecalcSizes ( self ): + def RecalcSizes(self): """ Layout the contents of the sizer based on the sizer's current size and position. """ if self._contents is None: return - x, y = self.GetPositionTuple() - dx, dy = self.GetSizeTuple() - self._contents.recalc_sizes( x, y, dx, dy ) + x, y = self.GetPosition().Get() + dx, dy = self.GetSize().Get() + self._contents.recalc_sizes(x, y, dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the current sizer contents: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def GetContents ( self ): + def GetContents(self): """ Returns the current sizer contents. """ return self._contents - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Initializes the layout of a DockWindow from a content list: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def SetContents ( self, contents ): + def SetContents(self, contents): """ Initializes the layout of a DockWindow from a content list. """ - if isinstance( contents, DockGroup ): + if isinstance(contents, DockGroup): self._contents = contents - elif isinstance( contents, tuple ): - self._contents = self._set_region( contents ) - elif isinstance( contents, list ): - self._contents = self._set_section( contents, True ) - elif isinstance( contents, DockControl ): - self._contents = self._set_section( [ contents ], True ) + elif isinstance(contents, tuple): + self._contents = self._set_region(contents) + elif isinstance(contents, list): + self._contents = self._set_section(contents, True) + elif isinstance(contents, DockControl): + self._contents = self._set_section([contents], True) else: - raise TypeError + raise TypeError() # Set the owner DockWindow for the top-level group (if possible) # so that it can notify the owner when the DockWindow becomes empty: @@ -3858,41 +4075,41 @@ if self._structure is None: self._structure = self.GetStructure() - def _set_region ( self, contents ): + def _set_region(self, contents): items = [] for item in contents: - if isinstance( item, tuple ): - items.append( self._set_region( item ) ) - elif isinstance( item, list ): - items.append( self._set_section( item, True ) ) - elif isinstance( item, DockItem ): - items.append( item ) + if isinstance(item, tuple): + items.append(self._set_region(item)) + elif isinstance(item, list): + items.append(self._set_section(item, True)) + elif isinstance(item, DockItem): + items.append(item) else: - raise TypeError + raise TypeError() - return DockRegion( contents = items ) + return DockRegion(contents=items) - def _set_section ( self, contents, is_row ): + def _set_section(self, contents, is_row): items = [] for item in contents: - if isinstance( item, tuple ): - items.append( self._set_region( item ) ) - elif isinstance( item, list ): - items.append( self._set_section( item, not is_row ) ) - elif isinstance( item, DockControl ): - items.append( DockRegion( contents = [ item ] ) ) + if isinstance(item, tuple): + items.append(self._set_region(item)) + elif isinstance(item, list): + items.append(self._set_section(item, not is_row)) + elif isinstance(item, DockControl): + items.append(DockRegion(contents=[item])) else: - raise TypeError - return DockSection( is_row = is_row ).trait_set( contents = items ) + raise TypeError() + return DockSection(is_row=is_row).trait_set(contents=items) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a copy of the layout 'structure', minus the actual content # (i.e. controls, splitters, bounds). This method is intended for use in # persisting the current user layout, so that it can be restored in a # future session: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def GetStructure ( self ): + def GetStructure(self): """ Returns a copy of the layout 'structure', minus the actual content (i.e. controls, splitters, bounds). This method is intended for use in persisting the current user layout, so that it can be restored in @@ -3903,19 +4120,19 @@ return DockSection() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Takes a previously saved 'GetStructure' result and applies it to the # contents of the sizer in order to restore a previous layout using a # new set of controls: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def SetStructure ( self, window, structure, handler = None ): + def SetStructure(self, window, structure, handler=None): """ Takes a previously saved 'GetStructure' result and applies it to the contents of the sizer in order to restore a previous layout using a new set of controls. """ section = self._contents - if (section is None) or (not isinstance( structure, DockGroup )): + if (section is None) or (not isinstance(structure, DockGroup)): return # Make sure that DockSections, which have a separate layout algorithm @@ -3930,149 +4147,158 @@ # Create a mapping for all the DockControls in the new structure: map = {} - for control in structure.get_controls( False ): + for control in structure.get_controls(False): if control.id in map: - control.parent.remove( control ) + control.parent.remove(control) else: - map[ control.id ] = control + map[control.id] = control # Try to map each current item into an equivalent item in the saved # preferences: - for control in section.get_controls( False ): - mapped_control = map.get( control.id ) + for control in section.get_controls(False): + mapped_control = map.get(control.id) if mapped_control is not None: - control.trait_set( **mapped_control.get( 'visible', 'locked', - 'closeable', 'resizable', 'width', 'height' ) ) + control.trait_set( + **mapped_control.get( + "visible", + "locked", + "closeable", + "resizable", + "width", + "height", + ) + ) if mapped_control.user_name: control.name = mapped_control.name if mapped_control.user_style: control.style = mapped_control.style - structure.replace_control( mapped_control, control ) - del map[ control.id ] + structure.replace_control(mapped_control, control) + del map[control.id] else: - extras.append( control ) + extras.append(control) # Try to resolve all unused saved items: for id, item in map.items(): # If there is a handler, see if it can resolve it: if handler is not None: - control = handler.resolve_id( id ) + control = handler.resolve_id(id) if control is not None: item.control = control continue # If nobody knows what it is, just remove it: - item.parent.remove( item ) + item.parent.remove(item) # Check if there are any new items that we have never seen before: - if len( extras ) > 0: + if len(extras) > 0: if handler is not None: # Allow the handler to decide their fate: - handler.resolve_extras( structure, extras ) + handler.resolve_extras(structure, extras) else: # Otherwise, add them to the top level as a new region (let the # user re-arrange them): - structure.contents.append( DockRegion( contents = extras ) ) + structure.contents.append(DockRegion(contents=extras)) # Finally, replace the original structure with the updated structure: - self.SetContents( structure ) + self.SetContents(structure) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Restores the previously saved structure (if any): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def ResetStructure ( self, window ): + def ResetStructure(self, window): """ Restores the previously saved structure (if any). """ if self._structure is not None: - self.SetStructure( window, self._structure ) + self.SetStructure(window, self._structure) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Toggles the current 'lock' setting of the contents: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def ToggleLock ( self ): + def ToggleLock(self): """ Toggles the current 'lock' setting of the contents. """ if self._contents is not None: self._contents.toggle_lock() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Draws the contents of the sizer: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def Draw ( self, window ): + def Draw(self, window): """ Draws the contents of the sizer. """ if self._contents is not None: - self._contents.draw( set_standard_font( wx.PaintDC( window ) ) ) + self._contents.draw(set_standard_font(wx.PaintDC(window))) else: - clear_window( window ) + clear_window(window) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object at a specified x, y position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def ObjectAt ( self, x, y, force = False ): + def ObjectAt(self, x, y, force=False): """ Returns the object at a specified window position. """ if self._contents is not None: - return self._contents.object_at( x, y, force ) + return self._contents.object_at(x, y, force) return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets a DockInfo object at a specified x, y position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def DockInfoAt ( self, x, y, size, is_control ): + def DockInfoAt(self, x, y, size, is_control): """ Gets a DockInfo object at a specified x, y position. """ if self._contents is not None: - return self._contents.dock_info_at( x, y, size, is_control, True ) + return self._contents.dock_info_at(x, y, size, is_control, True) return no_dock_info - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Minimizes/Maximizes a specified DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def MinMax ( self, window, dock_control ): + def MinMax(self, window, dock_control): """ Minimizes/Maximizes a specified DockControl. """ if self._max_structure is None: self._max_structure = self.GetStructure() for control in self.GetContents().get_controls(): - control.visible = (control is dock_control) + control.visible = control is dock_control else: - self.Reset( window ) + self.Reset(window) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Resets the DockSizer to a known state: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def Reset ( self, window ): + def Reset(self, window): """ Resets the DockSizer to a known state. """ if self._max_structure is not None: - self.SetStructure( window, self._max_structure ) + self.SetStructure(window, self._max_structure) self._max_structure = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether the sizer can be maximized now: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def IsMaximizable ( self ): + def IsMaximizable(self): """ Returns whether the sizer can be maximized now. """ - return (self._max_structure is None) + return self._max_structure is None + -def top_level_window_for ( control ): +def top_level_window_for(control): """ Returns the top-level window for a specified control. """ parent = control.GetParent() while parent is not None: control = parent - parent = control.GetParent() + parent = control.GetParent() return control diff -Nru python-pyface-6.1.2/pyface/dock/dock_window_feature.py python-pyface-7.4.0/pyface/dock/dock_window_feature.py --- python-pyface-6.1.2/pyface/dock/dock_window_feature.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/dock_window_feature.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,13 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2006, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 07/03/2006 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Implements the DockWindowFeature base class. @@ -27,26 +21,26 @@ drag and drop operations (depending upon how the feature is implemented). """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- from weakref import ref -from traits.api import HasPrivateTraits, Instance, Int, Str, Bool, Property +from traits.api import ( + HasPrivateTraits, Instance, Int, Str, Bool, Property, observe +) from traitsui.menu import Menu, Action from pyface.timer.api import do_later -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from .dock_window import DockWindow from .dock_sizer import DockControl, add_feature from .ifeature_tool import IFeatureTool -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'DockWindowFeature' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class DockWindowFeature ( HasPrivateTraits ): + +class DockWindowFeature(HasPrivateTraits): """ Implements "features" on DockWindows. See "The DockWindowFeature Feature of DockWindows" document (.doc or .pdf) @@ -59,16 +53,17 @@ component when the feature is activated, or when a new application component is added to the DockWindow (and the feature is already active). """ - #--------------------------------------------------------------------------- + + # --------------------------------------------------------------------------- # Class variables: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # A string value that is the user interface name of the feature as it # should appear in the DockWindow Features sub-menu (e.g., 'Connect'). An # empty string (the default) means that the feature does not appear in the # Features sub-menu and cannot be enabled or disabled by the user. Avoid # feature names that conflict with other, known features. - feature_name = '' + feature_name = "" # An integer that specifies th current state of the feature # (0 = uninstalled, 1 = active, 2 = disabled). Usually you do not need to @@ -79,11 +74,11 @@ # List of weak references to all current instances. instances = [] - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- -#-- Public Traits -------------------------------------------------------------- + # -- Public Traits -------------------------------------------------------------- # The DockControl instance associated with this feature. Note that features # not directly associated with application components, and instead are @@ -93,9 +88,9 @@ # contained within the same DockWindow, as well as the application # component. This trait is automatically set by the DockWindow when the # feature instance is created and associated with an application component. - dock_control = Instance( DockControl ) + dock_control = Instance(DockControl) -#-- Public Traits (new defaults can be defined by subclasses) ------------------ + # -- Public Traits (new defaults can be defined by subclasses) ------------------ # The image (icon) to display on the feature bar. If **None**, no image # is displayed. For images that never change, the value can be declared @@ -104,47 +99,47 @@ # **ImageResource** object causes the associated image to be updated on the # feature bar. Setting the value to **None** removes the image from the # feature bar. - image = Instance( ImageResource, allow_none = True ) + image = Image() # The tooltip to display when the pointer hovers over the image. The value # can be changed dynamically to reflect changes in the feature's state. - tooltip = Str + tooltip = Str() # The x-coordinate of a pointer event that occurred over the feature's # image. This can be used in cases where the event-handling for a feature is # sensitive to the position of the pointer relative to the feature image. # This is not normally the case, but the information is available if it is # needed. - x = Int + x = Int() # The y-coordinate of a pointer event that occurred over the feature's # image. - y = Int + y = Int() # A boolean value that specifies whether the shift key was being held down # when a mouse event occurred. - shift_down = Bool( False ) + shift_down = Bool(False) # A boolean value that specifies whether the control key was being held down # when a mouse event occurred. - control_down = Bool( False ) + control_down = Bool(False) # A boolean value that specifies whether the alt key was being held down # when a mouse event occurred. - alt_down = Bool( False ) + alt_down = Bool(False) -#-- Private Traits ------------------------------------------------------------- + # -- Private Traits ------------------------------------------------------------- # The current bitmap to display on the feature bar. bitmap = Property -#-- Overridable Public Methods ------------------------------------------------- + # -- Overridable Public Methods ------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user left clicking on the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def click ( self ): + def click(self): """ Handles the user left-clicking on a feature image. This method is designed to be overridden by subclasses. The default @@ -153,11 +148,11 @@ """ self.quick_drag() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user right clicking on the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def right_click ( self ): + def right_click(self): """ Handles the user right-clicking on a feature image. This method is designed to be overridden by subclasses. The default @@ -167,11 +162,11 @@ """ self.quick_right_drag() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user drags the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def drag ( self ): + def drag(self): """ Returns the object to be dragged when the user drags a feature image. @@ -182,12 +177,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user drags the feature image # while holding down the 'Ctrl' key: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def control_drag ( self ): + def control_drag(self): """ Returns the object to be dragged when the user drags a feature image while pressing the 'Ctrl' key. @@ -198,12 +193,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user drags the feature image # while holding down the 'Shift' key: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def shift_drag ( self ): + def shift_drag(self): """ Returns the object to be dragged when the user drags a feature image while pressing the 'Shift' key. @@ -215,12 +210,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user drags the feature image # while holding down the 'Alt' key: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def alt_drag ( self ): + def alt_drag(self): """ Returns the object to be dragged when the user drags a feature image while pressing the 'Alt' key. @@ -232,12 +227,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user right mouse button drags # the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def right_drag ( self ): + def right_drag(self): """ Returns the object to be dragged when the user right mouse button drags a feature image. @@ -248,12 +243,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user right mouse button drags # the feature image while holding down the 'Ctrl' key: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def control_right_drag ( self ): + def control_right_drag(self): """ Returns the object to be dragged when the user right mouse button drags a feature image while pressing the 'Ctrl' key. @@ -264,12 +259,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user right mouse button drags # the feature image while holding down the 'Shift' key: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def shift_control_drag ( self ): + def shift_control_drag(self): """ Returns the object to be dragged when the user right mouse button drags a feature image while pressing the 'Shift' key. @@ -281,12 +276,12 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the object to be dragged when the user right mouse button drags # the feature image while holding down the 'Alt' key: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def alt_right_drag ( self ): + def alt_right_drag(self): """ Returns the object to be dragged when the user right mouse button drags a feature image while pressing the 'Alt' key. @@ -298,11 +293,11 @@ """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user dropping a specified object on the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def drop ( self, object ): + def drop(self, object): """ Handles the user dropping a specified object on a feature image. Parameters @@ -324,11 +319,11 @@ """ return - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether a specified object can be dropped on the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_drop ( self, object ): + def can_drop(self, object): """ Returns whether a specified object can be dropped on a feature image. @@ -356,11 +351,11 @@ """ return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Performs any clean-up needed when the feature is being removed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dispose ( self ): + def dispose(self): """ Performs any clean-up needed when the feature is removed from its associated application component (for example, when the user disables the feature). @@ -372,13 +367,13 @@ """ pass -#-- Public Methods ------------------------------------------------------------- + # -- Public Methods ------------------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Displays a pop-up menu: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def popup_menu ( self, menu ): + def popup_menu(self, menu): """ Displays a shortcut menu. Parameters @@ -401,14 +396,15 @@ """ window = self.dock_control.control.GetParent() wx, wy = window.GetScreenPosition() - window.PopupMenuXY( menu.create_menu( window, self ), - self.x - wx - 10, self.y - wy - 10 ) + window.PopupMenu( + menu.create_menu(window, self), self.x - wx - 10, self.y - wy - 10 + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Refreshes the display of the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def refresh ( self ): + def refresh(self): """ Refreshes the display of the feature image. Returns @@ -422,11 +418,11 @@ """ self.dock_control.feature_changed = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Disables the feature: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def disable ( self ): + def disable(self): """ Disables the feature. Returns @@ -442,15 +438,15 @@ **dispose()** method is not called). """ self._image = self.image - self.image = None + self.image = None if self._image is not None: self.dock_control.feature_changed = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Enables the feature: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def enable ( self ): + def enable(self): """ Enables the feature. Returns @@ -463,18 +459,18 @@ associated application component. Enabling a feature restores the feature bar icon that the feature displayed at the time it was disabled. """ - self.image = self._image + self.image = self._image self._image = None if self.image is not None: self.dock_control.feature_changed = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Performs a quick drag and drop operation by displaying a pop-up menu # containing all targets that the feature's xxx_drag() method can be # dropped on. Selecting an item drops the item on the selected target. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def quick_drag ( self ): + def quick_drag(self): """ Performs a quick drag and drop operation by displaying a pop-up menu containing all targets that the feature's xxx_drag() method can be dropped on. Selecting an item drops the item on the selected target. @@ -491,16 +487,16 @@ # If there is an object, pop up the menu: if object is not None: - self._quick_drag_menu( object ) + self._quick_drag_menu(object) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Performs a quick drag and drop operation with the right mouse button by # displaying a pop-up menu containing all targets that the feature's # xxx_right_drag() method can be dropped on. Selecting an item drops the # item on the selected target. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def quick_right_drag ( self ): + def quick_right_drag(self): """ Performs a quick drag and drop operation with the right mouse button by displaying a pop-up menu containing all targets that the feature's xxx_right_drag() method can be dropped on. Selecting an @@ -518,17 +514,17 @@ # If there is an object, pop up the menu: if object is not None: - self._quick_drag_menu( object ) + self._quick_drag_menu(object) -#-- Overridable Class Methods --------------------------------------------------- + # -- Overridable Class Methods --------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a single new feature object or list of new feature objects for a # specified DockControl (or None if the feature does not apply to it): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @classmethod - def feature_for ( cls, dock_control ): + def feature_for(cls, dock_control): """ Returns a single new feature object or list of new feature objects for a specified DockControl. @@ -560,17 +556,17 @@ **new_feature()** class method to create the feature instances to be returned. If it does not, it simply returns **None**. """ - if cls.is_feature_for( dock_control): - return cls.new_feature( dock_control ) + if cls.is_feature_for(dock_control): + return cls.new_feature(dock_control) return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a new feature instance for a specified DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @classmethod - def new_feature ( cls, dock_control ): + def new_feature(cls, dock_control): """ Returns a new feature instance for a specified DockControl. Parameters @@ -596,15 +592,15 @@ cls( dock_control=dock_control ) """ - return cls( dock_control = dock_control ) + return cls(dock_control=dock_control) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not the DockWindowFeature is a valid feature for a # specified DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @classmethod - def is_feature_for ( self, dock_control ): + def is_feature_for(self, dock_control): """ Returns whether this class is a valid feature for the application object corresponding to a specified DockControl. @@ -629,214 +625,229 @@ """ return True -#-- Private Methods ------------------------------------------------------------ + # -- Private Methods ------------------------------------------------------------ - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the feature's 'event' traits for a specified mouse 'event': - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_event ( self, event ): + def _set_event(self, event): """ Sets the feature's 'event' traits for a specified mouse 'event'. """ x, y = event.GetEventObject().GetScreenPosition() - self.trait_set( x = event.GetX() + x, - y = event.GetY() + y, - shift_down = event.ShiftDown(), - control_down = event.ControlDown(), - alt_down = event.AltDown() ) + self.trait_set( + x=event.GetX() + x, + y=event.GetY() + y, + shift_down=event.ShiftDown(), + control_down=event.ControlDown(), + alt_down=event.AltDown(), + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Displays the quick drag menu for a specified drag object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _quick_drag_menu ( self, object ): + def _quick_drag_menu(self, object): """ Displays the quick drag menu for a specified drag object. """ # Get all the features it could be dropped on: feature_lists = [] - if isinstance( object, IFeatureTool ): - msg = 'Apply to' + if isinstance(object, IFeatureTool): + msg = "Apply to" for dc in self.dock_control.dock_controls: - if (dc.visible and - (object.feature_can_drop_on( dc.object ) or - object.feature_can_drop_on_dock_control( dc ))): - from feature_tool import FeatureTool + if dc.visible and ( + object.feature_can_drop_on(dc.object) + or object.feature_can_drop_on_dock_control(dc) + ): + from .feature_tool import FeatureTool - feature_lists.append( [ FeatureTool( dock_control = dc ) ] ) + feature_lists.append([FeatureTool(dock_control=dc)]) else: - msg = 'Send to' + msg = "Send to" for dc in self.dock_control.dock_controls: if dc.visible: - allowed = [ f for f in dc.features - if (f.feature_name != '') and - f.can_drop( object ) ] - if len( allowed ) > 0: - feature_lists.append( allowed ) + allowed = [ + f + for f in dc.features + if (f.feature_name != "") and f.can_drop(object) + ] + if len(allowed) > 0: + feature_lists.append(allowed) # If there are any compatible features: - if len( feature_lists ) > 0: + if len(feature_lists) > 0: # Create the pop-up menu: features = [] - actions = [] + actions = [] for list in feature_lists: - if len( list ) > 1: + if len(list) > 1: sub_actions = [] for feature in list: - sub_actions.append( Action( - name = '%s Feature' % feature.feature_name, - action = "self._drop_on(%d)" % len( features ) ) + sub_actions.append( + Action( + name="%s Feature" % feature.feature_name, + action="self._drop_on(%d)" % len(features), + ) + ) + features.append(feature) + actions.append( + Menu( + name="%s the %s" + % (msg, feature.dock_control.name), + *sub_actions ) - features.append( feature ) - actions.append( Menu( - name = '%s the %s' % ( msg, feature.dock_control.name ), - *sub_actions ) ) else: - actions.append( Action( - name = '%s %s' % ( msg, list[0].dock_control.name ), - action = "self._drop_on(%d)" % len( features ) ) + actions.append( + Action( + name="%s %s" % (msg, list[0].dock_control.name), + action="self._drop_on(%d)" % len(features), + ) ) - features.append( list[0] ) + features.append(list[0]) # Display the pop-up menu: - self._object = object + self._object = object self._features = features - self.popup_menu( Menu( name = 'popup', *actions ) ) + self.popup_menu(Menu(name="popup", *actions)) self._object = self._features = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Drops the current object on the feature selected by the user (used by # the 'quick_drag' method: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _drop_on ( self, index ): + def _drop_on(self, index): """ Drops the current object on the feature selected by the user. """ object = self._object - if isinstance( object, IFeatureTool ): - dc = self._features[ index ].dock_control - object.feature_dropped_on( dc.object ) - object.feature_dropped_on_dock_control( dc ) + if isinstance(object, IFeatureTool): + dc = self._features[index].dock_control + object.feature_dropped_on(dc.object) + object.feature_dropped_on_dock_control(dc) else: - self._features[ index ].drop( object ) + self._features[index].drop(object) -#-- Public Class Methods ------------------------------------------------------- + # -- Public Class Methods ------------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a feature object for use with the specified DockControl (or None # if the feature does not apply to the DockControl object): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @classmethod - def new_feature_for ( cls, dock_control ): + def new_feature_for(cls, dock_control): """ Returns a feature object for use with the specified DockControl (or **None** if the feature does not apply to the DockControl object). """ - result = cls.feature_for( dock_control ) + result = cls.feature_for(dock_control) if result is not None: - cls.instances = [ aref for aref in cls.instances - if aref() is not None ] - if isinstance( result, DockWindowFeature ): - result = [ result ] - cls.instances.extend( [ ref( feature ) for feature in result ] ) + cls.instances = [ + aref for aref in cls.instances if aref() is not None + ] + if isinstance(result, DockWindowFeature): + result = [result] + cls.instances.extend([ref(feature) for feature in result]) return result - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Toggles the feature on/off: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @classmethod - def toggle_feature ( cls, event ): + def toggle_feature(cls, event): """ Toggles the feature on or off. """ if cls.state == 0: cls.state = 1 - add_feature( cls ) + add_feature(cls) for control in event.window.control.GetChildren(): - window = getattr( control, 'owner', None ) - if isinstance( window, DockWindow ): - do_later( window.update_layout ) + window = getattr(control, "owner", None) + if isinstance(window, DockWindow): + do_later(window.update_layout) else: - method = 'disable' + method = "disable" cls.state = 3 - cls.state if cls.state == 1: - method = 'enable' - cls.instances = [ aref for aref in cls.instances - if aref() is not None ] + method = "enable" + cls.instances = [ + aref for aref in cls.instances if aref() is not None + ] for aref in cls.instances: feature = aref() if feature is not None: - getattr( feature, method )() + getattr(feature, method)() -#-- Event Handlers ------------------------------------------------------------- + # -- Event Handlers ------------------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'image' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _image_changed ( self ): + @observe('image') + def _reset_bitmap(self, event): self._bitmap = None -#-- Property Implementations --------------------------------------------------- + # -- Property Implementations --------------------------------------------------- - def _get_bitmap ( self ): + def _get_bitmap(self): if (self._bitmap is None) and (self.image is not None): self._bitmap = self.image.create_image().ConvertToBitmap() return self._bitmap -#-- Pyface menu interface implementation --------------------------------------- + # -- Pyface menu interface implementation --------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a menu item to the menu bar being constructed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add_to_menu ( self, menu_item ): + def add_to_menu(self, menu_item): """ Adds a menu item to the menu bar being constructed. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a tool bar item to the tool bar being constructed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add_to_toolbar ( self, toolbar_item ): + def add_to_toolbar(self, toolbar_item): """ Adds a tool bar item to the tool bar being constructed. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether the menu action should be defined in the user interface: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_add_to_menu ( self, action ): + def can_add_to_menu(self, action): """ Returns whether the action should be defined in the user interface. """ return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether the toolbar action should be defined in the user # interface: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_add_to_toolbar ( self, action ): + def can_add_to_toolbar(self, action): """ Returns whether the toolbar action should be defined in the user interface. """ return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Performs the action described by a specified Action object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def perform ( self, action ): + def perform(self, action): """ Performs the action described by a specified Action object. """ action = action.action - if action[ : 5 ] == 'self.': - eval( action, globals(), { 'self': self } ) + if action[:5] == "self.": + eval(action, globals(), {"self": self}) else: - getattr( self, action )() + getattr(self, action)() diff -Nru python-pyface-6.1.2/pyface/dock/dock_window.py python-pyface-7.4.0/pyface/dock/dock_window.py --- python-pyface-6.1.2/pyface/dock/dock_window.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/dock_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,13 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 10/18/2005 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Pyface 'DockWindow' support. @@ -24,114 +18,112 @@ separate notebook-like tab within the region. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- -import shelve +from operator import attrgetter import os -import wx +import shelve import sys -from pyface.api import SystemMetrics - -from traits.api \ - import HasPrivateTraits, Instance, Tuple, Property, Any, Str, List, false - -from traits.trait_base \ - import traits_home - -from traitsui.api \ - import View, HGroup, VGroup, Item, Handler, error - -from traitsui.helper \ - import user_name_for - -from traitsui.menu \ - import Menu, Action, Separator - -from traitsui.dockable_view_element \ - import DockableViewElement - -from traitsui.dock_window_theme \ - import dock_window_theme, DockWindowTheme +import wx -from pyface.wx.drag_and_drop \ - import PythonDropTarget, clipboard +from traits.api import ( + HasPrivateTraits, + Instance, + Tuple, + Property, + Any, + Str, + List, + Bool, + observe, +) +from traits.trait_base import traits_home +from traitsui.api import View, HGroup, VGroup, Item, Handler, error +from traitsui.helper import user_name_for +from traitsui.menu import Menu, Action, Separator +from traitsui.dockable_view_element import DockableViewElement +from traitsui.dock_window_theme import dock_window_theme, DockWindowTheme -from pyface.message_dialog \ - import error as warning +from pyface.api import SystemMetrics +from pyface.wx.drag_and_drop import PythonDropTarget, clipboard +from pyface.message_dialog import error as warning -from .dock_sizer import DockSizer, DockControl, DockRegion, DockStyle, \ - DockSplitter, no_dock_info, clear_window, features +from .dock_sizer import ( + DockSizer, + DockControl, + DockRegion, + DockStyle, + DockSplitter, + no_dock_info, + clear_window, + features, +) from .idockable import IDockable from .idock_ui_provider import IDockUIProvider -is_mac = (sys.platform == 'darwin') +is_mac = sys.platform == "darwin" -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Global data: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Dictionary of cursors in use: cursor_map = {} -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # DockWindow context menu: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -min_max_action = Action( name = 'Maximize', - action = 'on_min_max' ) +min_max_action = Action(name="Maximize", action="on_min_max") -undock_action = Action( name = 'Undock', - action = 'on_undock' ) +undock_action = Action(name="Undock", action="on_undock") -lock_action = Action( name = 'Lock Layout', - action = 'on_lock_layout', - style = 'toggle' ) +lock_action = Action( + name="Lock Layout", action="on_lock_layout", style="toggle" +) -layout_action = Action( name = 'Switch Layout', - action = 'on_switch_layout' ) +layout_action = Action(name="Switch Layout", action="on_switch_layout") -save_action = Action( name = 'Save Layout...', - action = 'on_save_layout' ) +save_action = Action(name="Save Layout...", action="on_save_layout") -#hide_action = Action( name = 'Hide', +# hide_action = Action( name = 'Hide', # action = 'on_hide' ) # -#show_action = Action( name = 'Show', +# show_action = Action( name = 'Show', # action = 'on_show' ) -edit_action = Action( name = 'Edit Properties...', - action = 'on_edit' ) +edit_action = Action(name="Edit Properties...", action="on_edit") -enable_features_action = Action( name = 'Show All', - action = 'on_enable_all_features' ) +enable_features_action = Action( + name="Show All", action="on_enable_all_features" +) -disable_features_action = Action( name = 'Hide All', - action = 'on_disable_all_features' ) +disable_features_action = Action( + name="Hide All", action="on_disable_all_features" +) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'DockWindowHandler' class/interface: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockWindowHandler ( HasPrivateTraits ): +class DockWindowHandler(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not a specified object can be inserted into the view: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_drop ( self, object ): + def can_drop(self, object): """ Returns whether or not a specified object can be inserted into the view. """ return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the DockControl object for a specified object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock_control_for ( self, parent, object ): + def dock_control_for(self, parent, object): """ Returns the DockControl object for a specified object. """ try: @@ -140,144 +132,156 @@ try: name = object.label except: - name = '' - if name == '': - name = user_name_for( object.__class__.__name__ ) - - image = None - export = '' - if isinstance( object, DockControl ): + name = "" + if name == "": + name = user_name_for(object.__class__.__name__) + + image = None + export = "" + if isinstance(object, DockControl): dock_control = object - image = dock_control.image - export = dock_control.export - dockable = dock_control.dockable - close = dockable.dockable_should_close() + image = dock_control.image + export = dock_control.export + dockable = dock_control.dockable + close = dockable.dockable_should_close() if close: - dock_control.close( force = True ) + dock_control.close(force=True) - control = dockable.dockable_get_control( parent ) + control = dockable.dockable_get_control(parent) # If DockControl was closed, then reset it to point to the new # control: if close: dock_control.trait_set( - control = control, - style = parent.owner.style) - dockable.dockable_init_dockcontrol( dock_control ) + control=control, style=parent.owner.style + ) + dockable.dockable_init_dockcontrol(dock_control) return dock_control - elif isinstance( object, IDockable ): + elif isinstance(object, IDockable): dockable = object - control = dockable.dockable_get_control( parent ) + control = dockable.dockable_get_control(parent) else: - ui = object.get_dockable_ui( parent ) - dockable = DockableViewElement( ui = ui ) - export = ui.view.export - control = ui.control - - dc = DockControl( control = control, - name = name, - export = export, - style = parent.owner.style, - image = image, - closeable = True ) + ui = object.get_dockable_ui(parent) + dockable = DockableViewElement(ui=ui) + export = ui.view.export + control = ui.control + + dc = DockControl( + control=control, + name=name, + export=export, + style=parent.owner.style, + image=image, + closeable=True, + ) - dockable.dockable_init_dockcontrol( dc ) + dockable.dockable_init_dockcontrol(dc) return dc - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creates a new view of a specified control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def open_view_for ( self, control, use_mouse = True ): + def open_view_for(self, control, use_mouse=True): """ Creates a new view of a specified control. """ - from dock_window_shell import DockWindowShell + from .dock_window_shell import DockWindowShell - DockWindowShell( control, use_mouse = use_mouse ) + DockWindowShell(control, use_mouse=use_mouse) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the DockWindow becoming empty: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock_window_empty ( self, dock_window ): + def dock_window_empty(self, dock_window): """ Handles the DockWindow becoming empty. """ if dock_window.auto_close: dock_window.control.GetParent().Destroy() + # Create a singleton handler: dock_window_handler = DockWindowHandler() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'LayoutName' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class LayoutName ( Handler ): +class LayoutName(Handler): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Name the user wants to assign to the layout: - name = Str + name = Str() # List of currently assigned names: - names = List( Str ) + names = List(Str) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - view = View( Item( 'name', label = 'Layout name' ), - title = 'Save Layout', - kind = 'modal', - buttons = [ 'OK', 'Cancel' ] ) + view = View( + Item("name", label="Layout name"), + title="Save Layout", + kind="modal", + buttons=["OK", "Cancel"], + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a request to close a dialog-based user interface by the user: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def close ( self, info, is_ok ): + def close(self, info, is_ok): if is_ok: name = info.object.name.strip() - if name == '': - warning( info.ui.control, 'No name specified', - title = 'Save Layout Error' ) + if name == "": + warning( + info.ui.control, + "No name specified", + title="Save Layout Error", + ) return False if name in self.names: - return error( message = '%s is already defined. Replace?' % - name, - title = 'Save Layout Warning', - parent = info.ui.control ) + return error( + message="%s is already defined. Replace?" % name, + title="Save Layout Warning", + parent=info.ui.control, + ) return True -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'DockWindow' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockWindow ( HasPrivateTraits ): +class DockWindow(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The wx.Window being used as the DockWindow: - control = Instance( wx.Window ) + control = Instance(wx.Window) # The handler used to determine how certain events should be processed: - handler = Any( dock_window_handler ) + handler = Any(dock_window_handler) # The 'extra' arguments to be passed to each handler call: - handler_args = Tuple + handler_args = Tuple() # Close the parent window if the DockWindow becomes empty: - auto_close = false + auto_close = Bool(False) # The DockWindow graphical theme style information: - theme = Instance( DockWindowTheme, allow_none = False ) + theme = Instance(DockWindowTheme, allow_none=False) # Default style for external objects dragged into the window: style = DockStyle @@ -286,160 +290,164 @@ sizer = Property # The id used to identify this DockWindow: - id = Str + id = Str() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Initializes the object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def __init__ ( self, parent, wid = -1, pos = wx.DefaultPosition, - size = wx.DefaultSize, style = wx.FULL_REPAINT_ON_RESIZE, - **traits ): - super( DockWindow, self ).__init__( **traits ) + def __init__( + self, + parent, + wid=-1, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.FULL_REPAINT_ON_RESIZE, + **traits + ): + super().__init__(**traits) # Create the actual window: - self.control = control = wx.Window( parent, wid, pos, size, style ) + self.control = control = wx.Window(parent, wid, pos, size, style) control.owner = self # Set up the 'paint' event handler: - wx.EVT_PAINT( control, self._paint ) - wx.EVT_SIZE( control, self._size ) + control.Bind(wx.EVT_PAINT, self._paint) + control.Bind(wx.EVT_SIZE, self._size) # Set up mouse event handlers: - wx.EVT_LEFT_DOWN( control, self._left_down ) - wx.EVT_LEFT_UP( control, self._left_up ) - wx.EVT_LEFT_DCLICK( control, self._left_dclick ) - wx.EVT_RIGHT_DOWN( control, self._right_down ) - wx.EVT_RIGHT_UP( control, self.right_up ) - wx.EVT_MOTION( control, self._mouse_move ) - wx.EVT_LEAVE_WINDOW( control, self._mouse_leave ) + control.Bind(wx.EVT_LEFT_DOWN, self._left_down) + control.Bind(wx.EVT_LEFT_UP, self._left_up) + control.Bind(wx.EVT_LEFT_DCLICK, self._left_dclick) + control.Bind(wx.EVT_RIGHT_DOWN, self._right_down) + control.Bind(wx.EVT_RIGHT_UP, self.right_up) + control.Bind(wx.EVT_MOTION, self._mouse_move) + control.Bind(wx.EVT_LEAVE_WINDOW, self._mouse_leave) - control.SetDropTarget( PythonDropTarget( self ) ) + control.SetDropTarget(PythonDropTarget(self)) # Initialize the window background color: if self.theme.use_theme_color: color = self.theme.tab.image_slice.bg_color else: color = SystemMetrics().dialog_background_color - color = wx.Colour(color[0]*255, color[1]*255, color[2]*255) + color = wx.Colour( + int(color[0] * 255), int(color[1] * 255), int(color[2] * 255) + ) - self.control.SetBackgroundColour( color ) + self.control.SetBackgroundColour(color) - #-- Default Trait Values --------------------------------------------------- + # -- Default Trait Values --------------------------------------------------- - def _theme_default ( self ): + def _theme_default(self): return dock_window_theme() - #-- Trait Event Handlers --------------------------------------------------- + # -- Trait Event Handlers --------------------------------------------------- - def _theme_changed ( self, theme ): + @observe("theme") + def _update_background_color_and_layout(self, event): + theme = event.new if self.control is not None: if theme.use_theme_color: color = theme.tab.image_slice.bg_color else: color = wx.NullColour - self.control.SetBackgroundColour( color ) + self.control.SetBackgroundColour(color) self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Notifies the DockWindow that its contents are empty: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dock_window_empty ( self ): + def dock_window_empty(self): """ Notifies the DockWindow that its contents are empty. """ - self.handler.dock_window_empty( self ) + self.handler.dock_window_empty(self) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the cursor to a specified cursor shape: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def set_cursor ( self, cursor = None ): + def set_cursor(self, cursor=None): """ Sets the cursor to a specified cursor shape. """ if cursor is None: - self.control.SetCursor( wx.NullCursor ) + self.control.SetCursor(wx.NullCursor) return global cursor_map if cursor not in cursor_map: - cursor_map[ cursor ] = wx.StockCursor( cursor ) + cursor_map[cursor] = wx.Cursor(cursor) - self.control.SetCursor( cursor_map[ cursor ] ) + self.control.SetCursor(cursor_map[cursor]) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Releases ownership of the mouse capture: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def release_mouse ( self ): + def release_mouse(self): """ Releases ownership of the mouse capture. """ if self._owner is not None: self._owner = None self.control.ReleaseMouse() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Updates the layout of the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def update_layout ( self ): + def update_layout(self): """ Updates the layout of the window. """ - # There are cases where a layout has been scheduled for a DockWindow, - # but then the DockWindow is destroyed, which will cause the calls - # below to fail. So we catch the 'PyDeadObjectError' exception and - # ignore it: - try: + if self.control: self.control.Layout() self.control.Refresh() - except wx.PyDeadObjectError: - pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Minimizes/Maximizes a specified DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def min_max ( self, dock_control ): + def min_max(self, dock_control): """ Minimizes/maximizes a specified DockControl. """ sizer = self.sizer if sizer is not None: - sizer.MinMax( self.control, dock_control ) + sizer.MinMax(self.control, dock_control) self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Pops up the feature bar for a specified DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_bar_popup ( self, dock_control ): + def feature_bar_popup(self, dock_control): """ Pops up the feature bar for a specified DockControl. """ fb = self._feature_bar if fb is None: - from feature_bar import FeatureBar + from .feature_bar import FeatureBar - self._feature_bar = fb = FeatureBar( parent = self.control ) - fb.on_trait_change( self._feature_bar_closed, 'completed' ) + self._feature_bar = fb = FeatureBar(parent=self.control) + fb.observe(self._feature_bar_closed, "completed") fb.dock_control = dock_control fb.show() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles closing the feature bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _feature_bar_closed ( self ): + def _feature_bar_closed(self, event): fb = self._feature_bar fb.dock_control.feature_bar_closed() fb.hide() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Perform all operations needed to close the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def close ( self ): + def close(self): """ Closes the dock window. In this case, all event handlers are unregistered. Other cleanup operations go here, but at the moment Linux (and other non-Windows platforms?) are less forgiving when things like @@ -447,11 +455,11 @@ """ self._unregister_event_handlers() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Unregister all event handlers: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _unregister_event_handlers ( self ): + def _unregister_event_handlers(self): """ Unregister all event handlers setup in the constructor. This is typically done prior to an app shutting down and is needed since Linux (and other non-Windows platforms?) trigger mouse, repaint, etc. events @@ -459,21 +467,21 @@ """ control = self.control if control is not None: - wx.EVT_PAINT( control, None ) - wx.EVT_SIZE( control, None ) - wx.EVT_LEFT_DOWN( control, None ) - wx.EVT_LEFT_UP( control, None ) - wx.EVT_LEFT_DCLICK( control, None ) - wx.EVT_RIGHT_DOWN( control, None ) - wx.EVT_RIGHT_UP( control, None ) - wx.EVT_MOTION( control, None ) - wx.EVT_LEAVE_WINDOW( control, None ) + control.Unbind(wx.EVT_PAINT) + control.Unbind(wx.EVT_SIZE) + control.Unbind(wx.EVT_LEFT_DOWN) + control.Unbind(wx.EVT_LEFT_UP) + control.Unbind(wx.EVT_LEFT_DCLICK) + control.Unbind(wx.EVT_RIGHT_DOWN) + control.Unbind(wx.EVT_RIGHT_UP) + control.Unbind(wx.EVT_MOTION) + control.Unbind(wx.EVT_LEAVE_WINDOW) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles repainting the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _paint ( self, event ): + def _paint(self, event): """ Handles repainting the window. """ # There is a problem on macs where we get paints when the update @@ -482,70 +490,73 @@ return sizer = self.sizer - if isinstance( sizer, DockSizer ): - sizer.Draw( self.control ) + if isinstance(sizer, DockSizer): + sizer.Draw(self.control) else: - clear_window( self.control ) + clear_window(self.control) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Uses wx calls to determine if we really need to paint or the children will # do it. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _is_child_paint ( self ): + def _is_child_paint(self): """ Returns whether or not the current update region is entirely within a child. """ if self.control.Children: update_rect = self.control.UpdateRegion.Box for child in self.control.Children: - if not child.HasTransparentBackground() and \ - child.Rect.ContainsRect(update_rect): + if not child.HasTransparentBackground() and child.Rect.Contains( + update_rect + ): return True return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the window being resized: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _size ( self, event ): + def _size(self, event): """ Handles the window being resized. """ sizer = self.sizer if sizer is not None: try: - dx, dy = self.control.GetSizeTuple() - sizer.SetDimension( 0, 0, dx, dy ) + dx, dy = self.control.GetSize().Get() + sizer.SetDimension(0, 0, dx, dy) except: # fixme: This is temporary code to work around a problem in # ProAVA2 that we are still trying to track down... pass - #--------------------------------------------------------------------------- + event.Skip() + + # --------------------------------------------------------------------------- # Handles the left mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _left_down ( self, event ): + def _left_down(self, event): """ Handles the left mouse button being pressed. """ sizer = self.sizer if sizer is not None: - object = sizer.ObjectAt( event.GetX(), event.GetY() ) + object = sizer.ObjectAt(event.GetX(), event.GetY()) if object is not None: self._owner = object self.control.CaptureMouse() - object.mouse_down( event ) + object.mouse_down(event) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _left_up ( self, event ): + def _left_up(self, event): """ Handles the left mouse button being released. """ window = self.control if self._owner is not None: window.ReleaseMouse() - self._owner.mouse_up( event ) + self._owner.mouse_up(event) self._owner = None # Check for the user requesting that the layout structure be reset: @@ -558,114 +569,127 @@ contents.dump() sys.stdout.flush() else: - self.sizer.ResetStructure( window ) + self.sizer.ResetStructure(window) self.update_layout() elif event.AltDown(): self.sizer.ToggleLock() self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being double clicked: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _left_dclick ( self, event ): + def _left_dclick(self, event): """ Handles the left mouse button being double clicked. """ sizer = self.sizer if sizer is not None: - object = sizer.ObjectAt( event.GetX(), event.GetY(), True ) - if isinstance( object, DockControl ): + object = sizer.ObjectAt(event.GetX(), event.GetY(), True) + if isinstance(object, DockControl): dockable = object.dockable - if (((dockable is None) or - (dockable.dockable_dclick( object, event ) is False)) and - (object.style != 'fixed')): - self.min_max( object ) - elif isinstance( object, DockRegion ): + if ( + (dockable is None) + or (dockable.dockable_dclick(object, event) is False) + ) and (object.style != "fixed"): + self.min_max(object) + elif isinstance(object, DockRegion): self._owner = object self.control.CaptureMouse() - object.mouse_down( event ) + object.mouse_down(event) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the right mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _right_down ( self, event ): + def _right_down(self, event): """ Handles the right mouse button being pressed. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the right mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def right_up ( self, event ): + def right_up(self, event): """ Handles the right mouse button being released. """ sizer = self.sizer if sizer is not None: - object = sizer.ObjectAt( event.GetX(), event.GetY(), True ) + object = sizer.ObjectAt(event.GetX(), event.GetY(), True) if object is not None: - popup_menu = None - window = self.control - is_dock_control = isinstance( object, DockControl ) - - if (is_dock_control and (object.dockable is not None) and - (event.ShiftDown() or event.ControlDown() or - event.AltDown())): + popup_menu = None + window = self.control + is_dock_control = isinstance(object, DockControl) + + if ( + is_dock_control + and (object.dockable is not None) + and ( + event.ShiftDown() + or event.ControlDown() + or event.AltDown() + ) + ): self._menu_self = object.dockable - popup_menu = object.dockable.dockable_menu( object, event ) + popup_menu = object.dockable.dockable_menu(object, event) if popup_menu is None: self._menu_self = self - section = self.sizer.GetContents() - is_splitter = isinstance( object, DockSplitter ) - self._object = object + section = self.sizer.GetContents() + is_splitter = isinstance(object, DockSplitter) + self._object = object if is_splitter: self._object = object = object.parent group = object if is_dock_control: group = group.parent if sizer.IsMaximizable(): - min_max_action.name = 'Maximize' + min_max_action.name = "Maximize" else: - min_max_action.name = 'Restore' - min_max_action.enabled = is_dock_control - undock_action.enabled = is_dock_control - edit_action.enabled = (not is_splitter) - controls = section.get_controls( False ) - lock_action.checked = ((len( controls ) > 0) and - controls[0].locked) - save_action.enabled = (self.id != '') + min_max_action.name = "Restore" + min_max_action.enabled = is_dock_control + undock_action.enabled = is_dock_control + edit_action.enabled = not is_splitter + controls = section.get_controls(False) + lock_action.checked = (len(controls) > 0) and controls[ + 0 + ].locked + save_action.enabled = self.id != "" feature_menu = self._get_feature_menu() restore_menu, delete_menu = self._get_layout_menus() - popup_menu = Menu( min_max_action, - undock_action, - Separator(), - feature_menu, - #Separator(), - #hide_action, - #show_action, - Separator(), - lock_action, - layout_action, - Separator(), - save_action, - restore_menu, - delete_menu, - Separator(), - edit_action, - name = 'popup' ) - - window.PopupMenuXY( popup_menu.create_menu( window, self ), - event.GetX() - 10, event.GetY() - 10 ) + popup_menu = Menu( + min_max_action, + undock_action, + Separator(), + feature_menu, + # Separator(), + # hide_action, + # show_action, + Separator(), + lock_action, + layout_action, + Separator(), + save_action, + restore_menu, + delete_menu, + Separator(), + edit_action, + name="popup", + ) + + window.PopupMenu( + popup_menu.create_menu(window, self), + event.GetX() - 10, + event.GetY() - 10, + ) self._object = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse moving over the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _mouse_move ( self, event ): + def _mouse_move(self, event): """ Handles the mouse moving over the window. """ if self._last_dock_control is not None: @@ -673,222 +697,227 @@ self._last_dock_control = None if self._owner is not None: - self._owner.mouse_move( event ) + self._owner.mouse_move(event) else: sizer = self.sizer if sizer is not None: - object = (self._object or - sizer.ObjectAt( event.GetX(), event.GetY() )) - self._set_cursor( event, object ) + object = self._object or sizer.ObjectAt( + event.GetX(), event.GetY() + ) + self._set_cursor(event, object) if object is not self._hover: if self._hover is not None: - self._hover.hover_exit( event ) + self._hover.hover_exit(event) if object is not None: - object.hover_enter( event ) + object.hover_enter(event) self._hover = object # Handle feature related processing: - if (isinstance( object, DockControl ) and - object.feature_activate( event )): + if isinstance(object, DockControl) and object.feature_activate( + event + ): self._last_dock_control = object - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse leaving the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _mouse_leave ( self, event ): + def _mouse_leave(self, event): """ Handles the mouse leaving the window. """ if self._hover is not None: - self._hover.hover_exit( event ) + self._hover.hover_exit(event) self._hover = None - self._set_cursor( event ) + self._set_cursor(event) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the cursor for a specified object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_cursor ( self, event, object = None ): + def _set_cursor(self, event, object=None): """ Sets the cursor for a specified object. """ if object is None: self.set_cursor() else: - self.set_cursor( object.get_cursor( event ) ) + self.set_cursor(object.get_cursor(event)) -#-- Context menu action handlers ----------------------------------------------- + # -- Context menu action handlers ----------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user asking for a DockControl to be maximized/restored: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_min_max ( self ): + def on_min_max(self): """ Handles the user asking for a DockControl to be maximized/restored. """ - self.min_max( self._object ) + self.min_max(self._object) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting an element to be undocked: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_undock ( self ): + def on_undock(self): """ Handles the user requesting an element to be undocked. """ - self.handler.open_view_for( self._object, use_mouse = False ) + self.handler.open_view_for(self._object, use_mouse=False) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting an element to be hidden: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_hide ( self ): + def on_hide(self): """ Handles the user requesting an element to be hidden. """ - self._object.show( False ) + self._object.show(False) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting an element to be shown: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_show ( self ): + def on_show(self): """ Handles the user requesting an element to be shown. """ object = self._object - if isinstance( object, DockControl ): + if isinstance(object, DockControl): object = object.parent - self._hidden_group_for( object ).show( True ) + self._hidden_group_for(object).show(True) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting that the current layout be switched: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_switch_layout ( self ): + def on_switch_layout(self): """ Handles the user requesting that the current layout be switched. """ - self.sizer.ResetStructure( self.control ) + self.sizer.ResetStructure(self.control) self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting that the layout be locked/unlocked: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_lock_layout ( self ): + def on_lock_layout(self): """ Handles the user requesting that the layout be locked/unlocked. """ self.sizer.ToggleLock() self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting that the layout be saved: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_save_layout ( self ): + def on_save_layout(self): """ Handles the user requesting that the layout be saved. """ - layout_name = LayoutName( names = self._get_layout_names() ) - if layout_name.edit_traits( parent = self.control ).result: - self._set_layout( layout_name.name, self.sizer.GetStructure() ) + layout_name = LayoutName(names=self._get_layout_names()) + if layout_name.edit_traits(parent=self.control).result: + self._set_layout(layout_name.name, self.sizer.GetStructure()) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting a specified layout to be restored: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_restore_layout ( self, name ): + def on_restore_layout(self, name): """ Handles the user requesting a specified layout to be restored. """ - self.sizer.SetStructure( self.control, self._get_layout( name ) ) + self.sizer.SetStructure(self.control, self._get_layout(name)) self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user reqesting a specified layout to be deleted: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_delete_layout ( self, name ): + def on_delete_layout(self, name): """ Handles the user reqesting a specified layout to be deleted. """ - if error( message = "Delete the '%s' layout?" % name, - title = 'Delete Layout Warning', - parent = self.control ): - self._delete_layout( name ) + if error( + message="Delete the '%s' layout?" % name, + title="Delete Layout Warning", + parent=self.control, + ): + self._delete_layout(name) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user requesting to edit an item: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_edit ( self, object = None ): + def on_edit(self, object=None): """ Handles the user requesting to edit an item. """ if object is None: object = self._object - control_info = ControlInfo( **object.get( 'name', 'user_name', - 'style', 'user_style' ) ) - if control_info.edit_traits( parent = self.control ).result: + control_info = ControlInfo( + **object.get("name", "user_name", "style", "user_style") + ) + if control_info.edit_traits(parent=self.control).result: name = control_info.name.strip() - if name != '': + if name != "": object.name = name - object.trait_set(**control_info.get('user_name', 'style', - 'user_style')) + object.trait_set( + **control_info.get("user_name", "style", "user_style") + ) self.update_layout() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Enables all features: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_enable_all_features ( self, action ): + def on_enable_all_features(self, action): """ Enables all features. """ for feature in features: - if (feature.feature_name != '') and (feature.state != 1): - feature.toggle_feature( action ) + if (feature.feature_name != "") and (feature.state != 1): + feature.toggle_feature(action) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Disables all features: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_disable_all_features ( self, action ): + def on_disable_all_features(self, action): """ Disables all features. """ for feature in features: - if (feature.feature_name != '') and (feature.state == 1): - feature.toggle_feature( action ) + if (feature.feature_name != "") and (feature.state == 1): + feature.toggle_feature(action) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Toggles the enabled/disabled state of the action's associated feature: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def on_toggle_feature ( self, action ): + def on_toggle_feature(self, action): """ Toggles the enabled/disabled state of the action's associated feature. """ - action._feature.toggle_feature( action ) + action._feature.toggle_feature(action) -#-- DockWindow user preference database methods -------------------------------- + # -- DockWindow user preference database methods -------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the layout dictionary for the DockWindow: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_layouts ( self ): + def _get_layouts(self): """ Gets the layout dictionary for the DockWindow. """ id = self.id - if id != '': + if id != "": db = self._get_dw_db() if db is not None: - layouts = db.get( id ) + layouts = db.get(id) db.close() return layouts return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the names of all current layouts defined for the DockWindow: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_layout_names ( self ): + def _get_layout_names(self): """ Gets the names of all current layouts defined for the DockWindow. """ layouts = self._get_layouts() @@ -897,75 +926,80 @@ return [] - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the layout data for a specified layout name: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_layout ( self, name ): + def _get_layout(self, name): """ Gets the layout data for a specified layout name. """ layouts = self._get_layouts() if layouts is not None: - return layouts.get( name ) + return layouts.get(name) return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Deletes the layout data for a specified layout name: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _delete_layout ( self, name ): + def _delete_layout(self, name): """ Deletes the layout data for a specified layout name. """ id = self.id - if id != '': - db = self._get_dw_db( mode = 'c' ) + if id != "": + db = self._get_dw_db(mode="c") if db is not None: - layouts = db.get( id ) + layouts = db.get(id) if layouts is not None: - del layouts[ name ] - db[ id ] = layouts + del layouts[name] + db[id] = layouts db.close() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Sets the layout data for a specified layout name: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _set_layout ( self, name, layout ): + def _set_layout(self, name, layout): """ Sets the layout data for a specified layout name. """ id = self.id - if id != '': - db = self._get_dw_db( mode = 'c' ) + if id != "": + db = self._get_dw_db(mode="c") if db is not None: - layouts = db.get( id ) + layouts = db.get(id) if layouts is None: layouts = {} - layouts[ name ] = layout - db[ id ] = layouts + layouts[name] = layout + db[id] = layouts db.close() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets a reference to the DockWindow UI preference database: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_dw_db ( self, mode = 'r' ): + def _get_dw_db(self, mode="r"): try: - return shelve.open( os.path.join( traits_home(), 'dock_window' ), - flag = mode, protocol = -1 ) + return shelve.open( + os.path.join(traits_home(), "dock_window"), + flag=mode, + protocol=-1, + ) except: return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the 'Features' sub_menu: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_feature_menu ( self ): + def _get_feature_menu(self): """ Returns the 'Features' sub_menu. """ - enable_features_action.enabled = disable_features_action.enabled = False + enable_features_action.enabled = ( + disable_features_action.enabled + ) = False for feature in features: - if feature.feature_name != '': + if feature.feature_name != "": if feature.state == 1: disable_features_action.enabled = True if enable_features_action.enabled: @@ -977,143 +1011,161 @@ actions = [] for feature in features: - if feature.feature_name != '': - actions.append( Action( name = feature.feature_name, - action = 'on_toggle_feature', - _feature = feature, - style = 'toggle', - checked = (feature.state == 1) ) ) - - if len( actions ) > 0: - actions.sort( lambda l, r: cmp( l.name, r.name ) ) - actions[0:0] = [ Separator() ] - - return Menu( name = 'Features', - *([ enable_features_action, disable_features_action ] + - actions) ) + if feature.feature_name != "": + actions.append( + Action( + name=feature.feature_name, + action="on_toggle_feature", + _feature=feature, + style="toggle", + checked=(feature.state == 1), + ) + ) + + if len(actions) > 0: + actions.sort(key=attrgetter("name")) + actions[0:0] = [Separator()] + + return Menu( + name="Features", + *([enable_features_action, disable_features_action] + actions) + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets the sub-menus for the 'Restore layout' and 'Delete layout' menu # options: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_layout_menus ( self ): + def _get_layout_menus(self): """ Gets the sub-menus for the 'Restore layout' and 'Delete layout' menu options. """ names = self._get_layout_names() - if len( names ) == 0: - restore_actions = [ Action( name = '', enabled = False ) ] - delete_actions = [ Action( name = '', enabled = False ) ] + if len(names) == 0: + restore_actions = [Action(name="", enabled=False)] + delete_actions = [Action(name="", enabled=False)] else: names.sort() - restore_actions = [ Action( - name = name, - action = "self.on_restore_layout(%s)" % - repr( name ) ) - for name in names ] - delete_actions = [ Action( - name = name, - action = "self.on_delete_layout(%s)" % - repr( name ) ) - for name in names ] - return [ Menu( name = 'Restore Layout', *restore_actions ), - Menu( name = 'Delete Layout', *delete_actions ) ] + restore_actions = [ + Action( + name=name, action="self.on_restore_layout(%s)" % repr(name) + ) + for name in names + ] + delete_actions = [ + Action( + name=name, action="self.on_delete_layout(%s)" % repr(name) + ) + for name in names + ] + return [ + Menu(name="Restore Layout", *restore_actions), + Menu(name="Delete Layout", *delete_actions), + ] -#-- Drag and drop event handlers: ---------------------------------------------- + # -- Drag and drop event handlers: ---------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a Python object being dropped on the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def wx_dropped_on ( self, x, y, data, drag_result ): + def wx_dropped_on(self, x, y, data, drag_result): """ Handles a Python object being dropped on the window. """ - if isinstance( data, ( IDockUIProvider, DockControl ) ): - window = self.control + if isinstance(data, (IDockUIProvider, DockControl)): + window = self.control dock_info = self._dock_info # See the 'wx_drag_leave' method for an explanation of this code: if dock_info is None: dock_info = self._leave_info - dock_info.draw( window ) + dock_info.draw(window) self._dock_info = None try: control = self.handler.dock_control_for( - *(self.handler_args + ( window, data )) ) + *(self.handler_args + (window, data)) + ) # Safely check to see if the object quacks like a Binding - binding = getattr( clipboard, 'node', None ) - if (hasattr(binding, "obj") and (binding.obj is data) and - hasattr(binding, "namespace_name")): - control.id = '@@%s' % binding.namespace_name - dock_info.dock( control, window ) + binding = getattr(clipboard, "node", None) + if ( + hasattr(binding, "obj") + and (binding.obj is data) + and hasattr(binding, "namespace_name") + ): + control.id = "@@%s" % binding.namespace_name + dock_info.dock(control, window) return drag_result except: - warning( window, - "An error occurred while attempting to add an item of " - "type '%s' to the window." % data.__class__.__name__, - title = 'Cannot add item to window' ) + warning( + window, + "An error occurred while attempting to add an item of " + "type '%s' to the window." % data.__class__.__name__, + title="Cannot add item to window", + ) return wx.DragNone - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a Python object being dragged over the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def wx_drag_any ( self, x, y, data, drag_result ): + def wx_drag_any(self, x, y, data, drag_result): """ Handles a Python object being dragged over the control. """ - ui_provider = isinstance( data, IDockUIProvider ) - if ui_provider or isinstance( data, DockControl ): - if (ui_provider and - (not self.handler.can_drop( *(self.handler_args + ( data, )) ))): + ui_provider = isinstance(data, IDockUIProvider) + if ui_provider or isinstance(data, DockControl): + if ui_provider and ( + not self.handler.can_drop(*(self.handler_args + (data,))) + ): return wx.DragNone # Check to see if we are in 'drag mode' yet: cur_dock_info = self._dock_info if cur_dock_info is None: cur_dock_info = no_dock_info - if isinstance( data, DockControl ): + if isinstance(data, DockControl): self._dock_size = data.tab_width else: self._dock_size = 80 # Get the window and DockInfo object associated with the event: - window = self.control - self._dock_info = dock_info = \ - self.sizer.DockInfoAt( x, y, self._dock_size, - False ) + window = self.control + self._dock_info = dock_info = self.sizer.DockInfoAt( + x, y, self._dock_size, False + ) # If the DockInfo has changed, then update the screen: - if ((cur_dock_info.kind != dock_info.kind) or - (cur_dock_info.region is not dock_info.region) or - (cur_dock_info.bounds != dock_info.bounds) or - (cur_dock_info.tab_bounds != dock_info.tab_bounds)): + if ( + (cur_dock_info.kind != dock_info.kind) + or (cur_dock_info.region is not dock_info.region) + or (cur_dock_info.bounds != dock_info.bounds) + or (cur_dock_info.tab_bounds != dock_info.tab_bounds) + ): # Erase the old region: - cur_dock_info.draw( window ) + cur_dock_info.draw(window) # Draw the new region: - dock_info.draw( window ) + dock_info.draw(window) return drag_result # Handle the case of dragging a normal object over a 'feature': - if self._can_drop_on_feature( x, y, data ) is not None: + if self._can_drop_on_feature(x, y, data) is not None: return drag_result return wx.DragNone - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a dragged Python object leaving the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def wx_drag_leave ( self, data ): + def wx_drag_leave(self, data): """ Handles a dragged Python object leaving the window. """ if self._dock_info is not None: - self._dock_info.draw( self.control ) + self._dock_info.draw(self.control) # Save the current '_dock_info' in '_leave_info' because under # Linux the drag and drop code sends a spurious 'drag_leave' event @@ -1122,108 +1174,108 @@ # 'dropped_on': self._leave_info, self._dock_info = self._dock_info, None -#-- Pyface menu interface implementation --------------------------------------- + # -- Pyface menu interface implementation --------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a menu item to the menu bar being constructed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add_to_menu ( self, menu_item ): + def add_to_menu(self, menu_item): """ Adds a menu item to the menu bar being constructed. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a tool bar item to the tool bar being constructed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add_to_toolbar ( self, toolbar_item ): + def add_to_toolbar(self, toolbar_item): """ Adds a tool bar item to the tool bar being constructed. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether the menu action should be defined in the user interface: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_add_to_menu ( self, action ): + def can_add_to_menu(self, action): """ Returns whether the action should be defined in the user interface. """ return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether the toolbar action should be defined in the user # interface: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_add_to_toolbar ( self, action ): + def can_add_to_toolbar(self, action): """ Returns whether the toolbar action should be defined in the user interface. """ return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Performs the action described by a specified Action object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def perform ( self, action_object ): + def perform(self, action_object): """ Performs the action described by a specified Action object. """ action = action_object.action - if action[ : 5 ] == 'self.': - eval( action, globals(), { 'self': self._menu_self } ) + if action[:5] == "self.": + eval(action, globals(), {"self": self._menu_self}) else: - method = getattr( self, action ) + method = getattr(self, action) try: method() except: - method( action_object ) + method(action_object) -#-- Property implementations --------------------------------------------------- + # -- Property implementations --------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Implementation of the 'sizer' property: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _get_sizer ( self ): + def _get_sizer(self): if self.control is not None: return self.control.GetSizer() return None - def _set_sizer ( self, sizer ): - self.control.SetSizer( sizer ) + def _set_sizer(self, sizer): + self.control.SetSizer(sizer) -#-- Private methods ------------------------------------------------------------ + # -- Private methods ------------------------------------------------------------ - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Finds the first group with any hidden items (if any): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _hidden_group_for ( self, group ): + def _hidden_group_for(self, group): """ Finds the first group with any hidden items (if any). """ while True: if group is None: return None - if len( group.contents ) > len( group.visible_contents ): + if len(group.contents) > len(group.visible_contents): return group group = group.parent - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a feature that the pointer is over and which can accept the # specified data: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _can_drop_on_feature ( self, x, y, data ): + def _can_drop_on_feature(self, x, y, data): """ Returns a feature that the pointer is over and which can accept the specified data. """ if self.sizer is not None: - object = self.sizer.ObjectAt( x, y ) - if isinstance( object, DockControl ): - event = FakeEvent( x, y, self.control ) + object = self.sizer.ObjectAt(x, y) + if isinstance(object, DockControl): + event = FakeEvent(x, y, self.control) - if object.feature_activate( event, data ): + if object.feature_activate(event, data): ldc = self._last_dock_control if (ldc is not None) and (ldc is not object): ldc.reset_feature_popup() @@ -1236,54 +1288,66 @@ return None -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'FakeEvent' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class FakeEvent ( object ): - def __init__ ( self, x, y, object ): +class FakeEvent(object): + def __init__(self, x, y, object): self.x, self.y, self.object = x, y, object - def GetX ( self ): return self.x - def GetY ( self ): return self.y - def GetEventObject ( self ): return self.object + def GetX(self): + return self.x + + def GetY(self): + return self.y -#------------------------------------------------------------------------------- + def GetEventObject(self): + return self.object + + +# ------------------------------------------------------------------------------- # 'ControlInfo' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class ControlInfo ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class ControlInfo(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Name to be edited: - name = Str + name = Str() # Has the user set the name of the control? - user_name = false - id = Str + user_name = Bool(False) + id = Str() # Style of drag bar/tab: style = DockStyle # Has the user set the style for this control: - user_style = false + user_style = Bool(False) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Traits view definition: - #--------------------------------------------------------------------------- - - traits_view = View( VGroup( - HGroup( HGroup( 'name<100>{Label}', '3' ), - HGroup( 'user_name{Remember label}', - show_left = False ) ), - HGroup( HGroup( 'style<101>', '3' ), - HGroup( 'user_style{Remember style}', - show_left = False ) ) ), - title = 'Edit properties', - kind = 'modal', - buttons = [ 'OK', 'Cancel' ] ) + # --------------------------------------------------------------------------- + traits_view = View( + VGroup( + HGroup( + HGroup("name<100>{Label}", "3"), + HGroup("user_name{Remember label}", show_left=False), + ), + HGroup( + HGroup("style<101>", "3"), + HGroup("user_style{Remember style}", show_left=False), + ), + ), + title="Edit properties", + kind="modal", + buttons=["OK", "Cancel"], + ) diff -Nru python-pyface-6.1.2/pyface/dock/dock_window_shell.py python-pyface-7.4.0/pyface/dock/dock_window_shell.py --- python-pyface-6.1.2/pyface/dock/dock_window_shell.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/dock_window_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,190 +1,199 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 11/04/2005 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Defines the DockWindowShell class used to house drag and drag DockWindow items that are dropped on the desktop or on the DockWindowShell window. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- import wx # Fixme: Hack to force 'image_slice' to be added via Category to Theme class: -import traitsui.wx +import traitsui.wx # noqa: F401 from traits.api import HasPrivateTraits, Instance -from traitsui.api import View, Group from pyface.api import SystemMetrics from pyface.image_resource import ImageResource from .dock_window import DockWindow -from .dock_sizer import DockSizer, DockSection, DockRegion, DockControl, \ - DOCK_RIGHT +from .dock_sizer import ( + DockSizer, + DockSection, + DockRegion, + DockControl, + DOCK_RIGHT, +) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Constants: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # DockWindowShell frame icon: -FrameIcon = ImageResource( 'shell.ico' ) +FrameIcon = ImageResource("shell.ico") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'DockWindowShell' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class DockWindowShell ( HasPrivateTraits ): +class DockWindowShell(HasPrivateTraits): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The wx.Frame window which is the actual shell: - control = Instance( wx.Frame ) + control = Instance(wx.Frame) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Initializes the object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def __init__ ( self, dock_control, use_mouse = False, **traits ): - super( DockWindowShell, self ).__init__( **traits ) + def __init__(self, dock_control, use_mouse=False, **traits): + super().__init__(**traits) old_control = dock_control.control - parent = wx.GetTopLevelParent( old_control ) + parent = wx.GetTopLevelParent(old_control) while True: next_parent = parent.GetParent() if next_parent is None: break parent = next_parent - self.control = shell = wx.Frame( parent, -1, dock_control.name, - style = wx.DEFAULT_FRAME_STYLE | - wx.FRAME_FLOAT_ON_PARENT | - wx.FRAME_NO_TASKBAR ) - shell.SetIcon( FrameIcon.create_icon() ) - shell.SetBackgroundColour( SystemMetrics().dialog_background_color ) - wx.EVT_CLOSE( shell, self._on_close ) + self.control = shell = wx.Frame( + parent, + -1, + dock_control.name, + style=wx.DEFAULT_FRAME_STYLE + | wx.FRAME_FLOAT_ON_PARENT + | wx.FRAME_NO_TASKBAR, + ) + shell.SetIcon(FrameIcon.create_icon()) + shell.SetBackgroundColour(SystemMetrics().dialog_background_color) + shell.Bind(wx.EVT_CLOSE, self._on_close) theme = dock_control.theme - dw = DockWindow( shell, auto_close = True, theme = theme ) - dw.trait_set( style = 'tab' ) + dw = DockWindow(shell, auto_close=True, theme=theme) + dw.trait_set(style="tab") self._dock_window = dw - sizer = wx.BoxSizer( wx.VERTICAL ) - sizer.Add( dw.control, 1, wx.EXPAND ) - shell.SetSizer( sizer ) + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(dw.control, 1, wx.EXPAND) + shell.SetSizer(sizer) if use_mouse: x, y = wx.GetMousePosition() else: - x, y = old_control.GetPositionTuple() - x, y = old_control.GetParent().ClientToScreenXY( x, y ) + x, y = old_control.Sizer.GetPosition().Get() + x, y = old_control.GetParent().Window.ClientToScreen(x, y) - dx, dy = old_control.GetSize() - tis = theme.tab.image_slice - tc = theme.tab.content - tdy = theme.tab_active.image_slice.dy - dx += (tis.xleft + tc.left + tis.xright + tc.right) - dy += (tis.xtop + tc.top + tis.xbottom + tc.bottom + tdy) + dx, dy = old_control.GetSize().Get() + tis = theme.tab.image_slice + tc = theme.tab.content + tdy = theme.tab_active.image_slice.dy + dx += tis.xleft + tc.left + tis.xright + tc.right + dy += tis.xtop + tc.top + tis.xbottom + tc.bottom + tdy - self.add_control( dock_control ) + self.add_control(dock_control) # Set the correct window size and position, accounting for the tab size # and window borders: - shell.SetDimensions( x, y, dx, dy ) - cdx, cdy = shell.GetClientSizeTuple() - ex_dx = dx - cdx - ex_dy = dy - cdy - shell.SetDimensions( x - (ex_dx / 2) - tis.xleft - tc.left, - y - ex_dy + (ex_dx / 2) - tdy - tis.xtop - tc.top, - dx + ex_dx, dy + ex_dy ) + shell.SetSize(x, y, dx, dy) + cdx, cdy = shell.GetClientSize().Get() + ex_dx = dx - cdx + ex_dy = dy - cdy + shell.SetSize( + x - (ex_dx // 2) - tis.xleft - tc.left, + y - ex_dy + (ex_dx // 2) - tdy - tis.xtop - tc.top, + dx + ex_dx, + dy + ex_dy, + ) shell.Show() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Adds a new DockControl to the shell window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def add_control ( self, dock_control ): + def add_control(self, dock_control): """ Adds a new DockControl to the shell window. """ - dw = self._dock_window.control + dw = self._dock_window.control dockable = dock_control.dockable # If the current DockControl should be closed, then do it: close = dockable.dockable_should_close() if close: - dock_control.close( force = True ) + dock_control.close(force=True) # Create the new control: - control = dockable.dockable_get_control( dw ) + control = dockable.dockable_get_control(dw) # If the DockControl was closed, then reset it to point to the new # control: if close: - dock_control.trait_set( control = control, style = 'tab' ) + dock_control.trait_set(control=control, style="tab") else: # Create a DockControl to describe the new control: - dock_control = DockControl( control = control, - name = dock_control.name, - export = dock_control.export, - style = 'tab', - image = dock_control.image, - closeable = True ) + dock_control = DockControl( + control=control, + name=dock_control.name, + export=dock_control.export, + style="tab", + image=dock_control.image, + closeable=True, + ) # Finish initializing the DockControl: - dockable.dockable_init_dockcontrol( dock_control ) + dockable.dockable_init_dockcontrol(dock_control) # Get the current DockSizer: sizer = dw.GetSizer() if sizer is None: # Create the initial sizer: - dw.SetSizer( DockSizer( DockSection( contents = [ DockRegion( - contents = [ dock_control ] ) ] ) ) ) + dw.SetSizer( + DockSizer( + DockSection(contents=[DockRegion(contents=[dock_control])]) + ) + ) else: # Sizer exists already, try to add the DockControl as a new # notebook tab. If the user has reorganized the layout, then just # dock it on the right side somewhere: section = sizer.GetContents() - region = section.contents[0] - if isinstance( region, DockRegion ): - region.add( dock_control ) + region = section.contents[0] + if isinstance(region, DockRegion): + region.add(dock_control) else: - section.add( dock_control, region, DOCK_RIGHT ) + section.add(dock_control, region, DOCK_RIGHT) # Force the control to update: dw.Layout() dw.Refresh() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user attempting to close the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _on_close ( self, event ): + def _on_close(self, event): """ Handles the user attempting to close the window. """ - window = self._dock_window.control + window = self._dock_window.control section = window.GetSizer().GetContents() - n = len( section.contents ) + n = len(section.contents) # Try to close each individual control: for control in section.get_controls(): - control.close( layout = False ) + control.close(layout=False) # If some, but not all, were closed, make sure the window gets updated: - if 0 < len( section.contents ) < n: + if 0 < len(section.contents) < n: window.Layout() window.Refresh() + self.control.Unbind(wx.EVT_CLOSE) diff -Nru python-pyface-6.1.2/pyface/dock/feature_bar.py python-pyface-7.4.0/pyface/dock/feature_bar.py --- python-pyface-6.1.2/pyface/dock/feature_bar.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/feature_bar.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,13 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 02/05/2007 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Pyface 'FeatureBar' support. @@ -21,64 +15,63 @@ interact with a set of DockWindowFeatures for a specified DockControl. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- import wx -from traits.api import HasPrivateTraits, Instance, Bool, Event, Color +from traits.api import HasPrivateTraits, Instance, Bool, Event from pyface.wx.drag_and_drop import PythonDropTarget, PythonDropSource +from pyface.ui_traits import TraitsUIColor as Color from .dock_sizer import DockControl, FEATURE_EXTERNAL_DRAG from .ifeature_tool import IFeatureTool -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'FeatureBar' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class FeatureBar ( HasPrivateTraits ): - #--------------------------------------------------------------------------- +class FeatureBar(HasPrivateTraits): + + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The wx.Window which is the parent for the FeatureBar: - parent = Instance( wx.Window ) + parent = Instance(wx.Window) # The DockControl whose features are being displayed: - dock_control = Instance( DockControl ) + dock_control = Instance(DockControl) # The wx.Window being used for the FeatureBar: - control = Instance( wx.Window ) + control = Instance(wx.Window) # Event posted when the user has completed using the FeatureBar: - completed = Event + completed = Event() # The background color for the FeatureBar: - bg_color = Color( 0xDBEEF7, allow_none = True ) + bg_color = Color(0xDBEEF7, allow_none=True) # The border color for the FeatureBar: - border_color = Color( 0X2583AF, allow_none = True ) + border_color = Color(0x2583AF, allow_none=True) # Should the feature bar display horizontally (or vertically)? - horizontal = Bool( True ) + horizontal = Bool(True) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Hides the feature bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def hide ( self ): + def hide(self): """ Hides the feature bar. """ if self.control is not None: self.control.Hide() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Shows the feature bar: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def show ( self ): + def show(self): """ Shows the feature bar. """ # Make sure all prerequisites are met: @@ -89,77 +82,78 @@ # Create the actual control (if needed): control = self.control if control is None: - self.control = control = wx.Frame( None, -1, '', - style = wx.BORDER_NONE ) + self.control = control = wx.Frame( + None, -1, "", style=wx.BORDER_NONE + ) # Set up the 'erase background' event handler: - wx.EVT_ERASE_BACKGROUND( control, self._erase_background ) + control.Bind(wx.EVT_ERASE_BACKGROUND, self._erase_background) # Set up the 'paint' event handler: - wx.EVT_PAINT( control, self._paint ) + control.Bind(wx.EVT_PAINT, self._paint) # Set up mouse event handlers: - wx.EVT_LEFT_DOWN( control, self._left_down ) - wx.EVT_LEFT_UP( control, self._left_up ) - wx.EVT_RIGHT_DOWN( control, self._right_down ) - wx.EVT_RIGHT_UP( control, self._right_up ) - wx.EVT_MOTION( control, self._mouse_move ) - wx.EVT_ENTER_WINDOW( control, self._mouse_enter ) + control.Bind(wx.EVT_LEFT_DOWN, self._left_down) + control.Bind(wx.EVT_LEFT_UP, self._left_up) + control.Bind(wx.EVT_RIGHT_DOWN, self._right_down) + control.Bind(wx.EVT_RIGHT_UP, self._right_up) + control.Bind(wx.EVT_MOTION, self._mouse_move) + control.Bind(wx.EVT_ENTER_WINDOW, self._mouse_enter) - control.SetDropTarget( PythonDropTarget( self ) ) + control.SetDropTarget(PythonDropTarget(self)) # Calculate the best size and position for the feature bar: - size = wx.Size( 32, 32 ) - width = height = 0 + size = wx.Size(32, 32) + width = height = 0 horizontal = self.horizontal for feature in dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: if horizontal: - width += (bitmap.GetWidth() + 3) - height = max( height, bitmap.GetHeight() ) + width += bitmap.GetWidth() + 3 + height = max(height, bitmap.GetHeight()) else: - width = max( width, bitmap.GetWidth() ) - height += (bitmap.GetHeight() + 3) + width = max(width, bitmap.GetWidth()) + height += bitmap.GetHeight() + 3 if width > 0: if horizontal: - size = wx.Size( width + 5, height + 8 ) + size = wx.Size(width + 5, height + 8) else: - size = wx.Size( width + 8, height + 5 ) + size = wx.Size(width + 8, height + 5) - control.SetSize( size ) + control.SetSize(size) px, py = parent.GetScreenPosition() fx, fy = dock_control.feature_popup_position - control.SetPosition( wx.Point( px + fx, py + fy ) ) + control.SetPosition(wx.Point(px + fx, py + fy)) control.Show() - #-- Window Event Handlers -------------------------------------------------- + # -- Window Event Handlers -------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles repainting the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _paint ( self, event ): + def _paint(self, event): """ Handles repainting the window. """ window = self.control - dx, dy = window.GetSizeTuple() - dc = wx.PaintDC( window ) + dx, dy = window.GetSize().Get() + dc = wx.PaintDC(window) # Draw the feature container: - bg_color = self.bg_color + bg_color = self.bg_color border_color = self.border_color if (bg_color is not None) or (border_color is not None): if border_color is None: - dc.SetPen( wx.TRANSPARENT_PEN ) + dc.SetPen(wx.TRANSPARENT_PEN) else: - dc.SetPen( wx.Pen( border_color, 1, wx.SOLID ) ) + dc.SetPen(wx.Pen(border_color, 1, wx.SOLID)) if bg_color is None: - dc.SetBrush( wx.TRANSPARENT_PEN ) + dc.SetBrush(wx.TRANSPARENT_PEN) else: - dc.SetBrush( wx.Brush( bg_color, wx.SOLID ) ) - dc.DrawRectangle( 0, 0, dx, dy ) + dc.SetBrush(wx.Brush(bg_color, wx.SOLID)) + dc.DrawRectangle(0, 0, dx, dy) # Draw the feature icons: if self.horizontal: @@ -167,117 +161,117 @@ for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: - dc.DrawBitmap( bitmap, x, 4, True ) - x += (bitmap.GetWidth() + 3) + dc.DrawBitmap(bitmap, x, 4, True) + x += bitmap.GetWidth() + 3 else: y = 4 for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: - dc.DrawBitmap( bitmap, 4, y, True ) - y += (bitmap.GetHeight() + 3) + dc.DrawBitmap(bitmap, 4, y, True) + y += bitmap.GetHeight() + 3 - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles erasing the window background: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _erase_background ( self, event ): + def _erase_background(self, event): """ Handles erasing the window background. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _left_down ( self, event ): + def _left_down(self, event): """ Handles the left mouse button being pressed. """ - self._feature = self._feature_at( event ) + self._feature = self._feature_at(event) self._dragging = False - self._xy = ( event.GetX(), event.GetY() ) - #self.control.CaptureMouse() + self._xy = (event.GetX(), event.GetY()) + # self.control.CaptureMouse() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the left mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _left_up ( self, event ): + def _left_up(self, event): """ Handles the left mouse button being released. """ - #self.control.ReleaseMouse() - self._dragging = None + # self.control.ReleaseMouse() + self._dragging = None feature, self._feature = self._feature, None if feature is not None: - if feature is self._feature_at( event ): + if feature is self._feature_at(event): self.control.ReleaseMouse() self.completed = True - feature._set_event( event ) + feature._set_event(event) feature.click() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the right mouse button being pressed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _right_down ( self, event ): + def _right_down(self, event): """ Handles the right mouse button being pressed. """ - self._feature = self._feature_at( event ) + self._feature = self._feature_at(event) self._dragging = False - self._xy = ( event.GetX(), event.GetY() ) - #self.control.CaptureMouse() + self._xy = (event.GetX(), event.GetY()) + # self.control.CaptureMouse() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the right mouse button being released: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _right_up ( self, event ): + def _right_up(self, event): """ Handles the right mouse button being released. """ - #self.control.ReleaseMouse() - self._dragging = None + # self.control.ReleaseMouse() + self._dragging = None feature, self._feature = self._feature, None if feature is not None: - if feature is self._feature_at( event ): + if feature is self._feature_at(event): self.control.ReleaseMouse() self.completed = True - feature._set_event( event ) + feature._set_event(event) feature.right_click() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse moving over the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _mouse_move ( self, event ): + def _mouse_move(self, event): """ Handles the mouse moving over the window. """ # Update tooltips if no mouse button is currently pressed: if self._dragging is None: - feature = self._feature_at( event ) + feature = self._feature_at(event) if feature is not self._tooltip_feature: self._tooltip_feature = feature - tooltip = '' + tooltip = "" if feature is not None: tooltip = feature.tooltip - wx.ToolTip.Enable( False ) - wx.ToolTip.Enable( True ) - self.control.SetToolTip( wx.ToolTip( tooltip ) ) + wx.ToolTip.Enable(False) + wx.ToolTip.Enable(True) + self.control.SetToolTip(wx.ToolTip(tooltip)) # Check to see if the mouse has left the window, and mark it # completed if it has: - x, y = event.GetX(), event.GetY() - dx, dy = self.control.GetSizeTuple() + x, y = event.GetX(), event.GetY() + dx, dy = self.control.GetSize().Get() if (x < 0) or (y < 0) or (x >= dx) or (y >= dy): self.control.ReleaseMouse() self._tooltip_feature = None - self.completed = True + self.completed = True return # Check to see if we are in 'drag mode' yet: if not self._dragging: x, y = self._xy - if (abs( x - event.GetX() ) + abs( y - event.GetY() )) < 3: + if (abs(x - event.GetX()) + abs(y - event.GetY())) < 3: return self._dragging = True @@ -285,120 +279,120 @@ # Check to see if user is trying to drag a 'feature': feature = self._feature if feature is not None: - feature._set_event( event ) + feature._set_event(event) - prefix = button = '' + prefix = button = "" if event.RightIsDown(): - button = 'right_' + button = "right_" if event.ControlDown(): - prefix = 'control_' + prefix = "control_" elif event.AltDown(): - prefix = 'alt_' + prefix = "alt_" elif event.ShiftDown(): - prefix = 'shift_' + prefix = "shift_" - object = getattr( feature, '%s%sdrag' % ( prefix, button ) )() + object = getattr(feature, "%s%sdrag" % (prefix, button))() if object is not None: self.control.ReleaseMouse() - self._feature = None + self._feature = None self.completed = True - self.dock_control.pre_drag_all( object ) - PythonDropSource( self.control, object ) + self.dock_control.pre_drag_all(object) + PythonDropSource(self.control, object) self.dock_control.post_drag_all() self._dragging = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the mouse entering the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _mouse_enter ( self, event ): + def _mouse_enter(self, event): """ Handles the mouse entering the window. """ self.control.CaptureMouse() -#-- Drag and drop event handlers: ---------------------------------------------- + # -- Drag and drop event handlers: ---------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a Python object being dropped on the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def wx_dropped_on ( self, x, y, data, drag_result ): + def wx_dropped_on(self, x, y, data, drag_result): """ Handles a Python object being dropped on the window. """ # Determine what, if any, feature the object was dropped on: - feature = self._can_drop_on_feature( x, y, data ) + feature = self._can_drop_on_feature(x, y, data) # Indicate use of the feature bar is complete: self.completed = True # Reset any drag state information: - self.dock_control.post_drag( FEATURE_EXTERNAL_DRAG ) + self.dock_control.post_drag(FEATURE_EXTERNAL_DRAG) # Check to see if the data was dropped on a feature or not: if feature is not None: - if isinstance( data, IFeatureTool ): + if isinstance(data, IFeatureTool): # Handle an object implementing IFeatureTool being dropped: dock_control = feature.dock_control - data.feature_dropped_on_dock_control( dock_control ) - data.feature_dropped_on( dock_control.object ) + data.feature_dropped_on_dock_control(dock_control) + data.feature_dropped_on(dock_control.object) else: # Handle a normal object being dropped: wx, wy = self.control.GetScreenPosition() - feature.trait_set( x = wx + x, y = wy + y ) - feature.drop( data ) + feature.trait_set(x=wx + x, y=wy + y) + feature.drop(data) return drag_result return wx.DragNone - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a Python object being dragged over the control: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def wx_drag_over ( self, x, y, data, drag_result ): + def wx_drag_over(self, x, y, data, drag_result): """ Handles a Python object being dragged over the control. """ # Handle the case of dragging a normal object over a 'feature': - if self._can_drop_on_feature( x, y, data ) is not None: + if self._can_drop_on_feature(x, y, data) is not None: return drag_result return wx.DragNone - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a dragged Python object leaving the window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def wx_drag_leave ( self, data ): + def wx_drag_leave(self, data): """ Handles a dragged Python object leaving the window. """ # Indicate use of the feature bar is complete: self.completed = True # Reset any drag state information: - self.dock_control.post_drag( FEATURE_EXTERNAL_DRAG ) + self.dock_control.post_drag(FEATURE_EXTERNAL_DRAG) - #-- Private Methods -------------------------------------------------------- + # -- Private Methods -------------------------------------------------------- - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a feature that the pointer is over and which can accept the # specified data: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _can_drop_on_feature ( self, x, y, data ): + def _can_drop_on_feature(self, x, y, data): """ Returns a feature that the pointer is over and which can accept the specified data. """ - feature = self._feature_at( FakeEvent( x, y ) ) - if (feature is not None) and feature.can_drop( data ): + feature = self._feature_at(FakeEvent(x, y)) + if (feature is not None) and feature.can_drop(data): return feature return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the DockWindowFeature (if any) at a specified window position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _feature_at ( self, event ): + def _feature_at(self, event): """ Returns the DockWindowFeature (if any) at a specified window position. """ @@ -408,42 +402,46 @@ bitmap = feature.bitmap if bitmap is not None: bdx = bitmap.GetWidth() - if self._is_in( event, x, 4, bdx, bitmap.GetHeight() ): + if self._is_in(event, x, 4, bdx, bitmap.GetHeight()): return feature - x += (bdx + 3) + x += bdx + 3 else: y = 4 for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: bdy = bitmap.GetHeight() - if self._is_in( event, 4, y, bitmap.GetWidth(), bdy ): + if self._is_in(event, 4, y, bitmap.GetWidth(), bdy): return feature - y += (bdy + 3) + y += bdy + 3 return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not an event is within a specified bounds: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _is_in ( self, event, x, y, dx, dy ): + def _is_in(self, event, x, y, dx, dy): """ Returns whether or not an event is within a specified bounds. """ - return ((x <= event.GetX() < (x + dx)) and - (y <= event.GetY() < (y + dy))) + return (x <= event.GetX() < (x + dx)) and ( + y <= event.GetY() < (y + dy) + ) + -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'FakeEvent' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class FakeEvent ( object ): - def __init__ ( self, x, y ): +class FakeEvent(object): + def __init__(self, x, y): self.x, self.y = x, y - def GetX ( self ): return self.x - def GetY ( self ): return self.y + def GetX(self): + return self.x + def GetY(self): + return self.y diff -Nru python-pyface-6.1.2/pyface/dock/feature_tool.py python-pyface-7.4.0/pyface/dock/feature_tool.py --- python-pyface-6.1.2/pyface/dock/feature_tool.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/feature_tool.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,51 +1,42 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2006, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 07/04/2006 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Implements the FeatureTool feature that allows a dragged object implementing the IFeatureTool interface to be dropped onto any compatible object. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- from pyface.image_resource import ImageResource from .dock_window_feature import DockWindowFeature -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'FeatureTool' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class FeatureTool ( DockWindowFeature ): +class FeatureTool(DockWindowFeature): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - image = ImageResource( 'feature_tool' ) + image = ImageResource("feature_tool") - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether a specified object can be dropped on the feature image: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def can_drop ( self, object ): + def can_drop(self, object): """ Returns whether a specified object can be dropped on the feature image. """ return True - diff -Nru python-pyface-6.1.2/pyface/dock/idockable.py python-pyface-7.4.0/pyface/dock/idockable.py --- python-pyface-6.1.2/pyface/dock/idockable.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/idockable.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,105 +1,94 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 12/14/2005 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Defines the IDockable interface which objects contained in a DockWindow DockControl can implement in order to allow themselves to be dragged into a different DockWindow. """ -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- -import wx - -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'IDockable' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class IDockable ( object ): +class IDockable(object): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Should the current DockControl be closed before creating the new one: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_should_close ( self ): + def dockable_should_close(self): """ Should the current DockControl be closed before creating the new one. """ return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not it is OK to close the control, and if it is OK, # then it closes the DockControl itself: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_close ( self, dock_control, force ): + def dockable_close(self, dock_control, force): """ Returns whether or not it is OK to close the control. """ return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Gets a control that can be docked into a DockWindow: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_get_control ( self, parent ): + def dockable_get_control(self, parent): """ Gets a control that can be docked into a DockWindow. """ - print("The 'IDockable.dockable_get_control' method must be overridden") - panel = wx.Panel( parent, -1 ) - panel.SetBackgroundColour( wx.RED ) - return panel + raise NotImplementedError( + "The 'IDockable.dockable_get_control' method must be overridden" + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Allows the object to override the default DockControl settings: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_init_dockcontrol ( self, dock_control ): + def dockable_init_dockcontrol(self, dock_control): """ Allows the object to override the default DockControl settings. """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns the right-click popup menu for a DockControl (if any): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_menu ( self, dock_control, event ): + def dockable_menu(self, dock_control, event): """ Returns the right-click popup menu for a DockControl (if any). """ return None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the user double-clicking on the DockControl: # A result of False indicates the event was not handled; all other results # indicate that the event was handled successfully. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_dclick ( self, dock_control, event ): + def dockable_dclick(self, dock_control, event): """ Handles the user double-clicking on the DockControl. A result of False indicates the event was not handled; all other results indicate that the event was handled successfully. """ return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles a notebook tab being activated or deactivated: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_tab_activated ( self, dock_control, activated ): + def dockable_tab_activated(self, dock_control, activated): """ Handles a notebook tab being activated or deactivated. 'dock_control' is the control being activated or deactivated. @@ -109,12 +98,11 @@ """ pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the IDockable being bound to a specified DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def dockable_bind ( self, dock_control ): + def dockable_bind(self, dock_control): """ Handles the dockable being bound to a specified DockControl. """ pass - diff -Nru python-pyface-6.1.2/pyface/dock/idock_ui_provider.py python-pyface-7.4.0/pyface/dock/idock_ui_provider.py --- python-pyface-6.1.2/pyface/dock/idock_ui_provider.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/idock_ui_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,32 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2006, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 06/17/2006 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Defines the IDockUIProvider interface which objects which support being dragged and dropped into a DockWindow must implement. """ -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'IDockUIProvider' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class IDockUIProvider ( object ): +class IDockUIProvider(object): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns a Traits UI which a DockWindow can imbed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def get_dockable_ui ( self, parent ): + def get_dockable_ui(self, parent): """ Returns a Traits UI which a DockWindow can imbed. """ - return self.edit_traits( parent = parent, - kind = 'subpanel', - scrollable = True ) - + return self.edit_traits( + parent=parent, kind="subpanel", scrollable=True + ) diff -Nru python-pyface-6.1.2/pyface/dock/ifeature_tool.py python-pyface-7.4.0/pyface/dock/ifeature_tool.py --- python-pyface-6.1.2/pyface/dock/ifeature_tool.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/ifeature_tool.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,72 +1,66 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 02/07/2007 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! + """ Defines the IFeatureTool interface which allows objects dragged using the DockWindowFeature API to control the drag target and drop events. Useful for implementing tools which can be dropped onto compatible view objects. """ -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'IFeatureTool' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class IFeatureTool ( object ): +class IFeatureTool(object): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not the object being dragged (i.e. self) can be # dropped on the specified target object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_can_drop_on ( self, object ): + def feature_can_drop_on(self, object): """ Returns whether or not the object being dragged (i.e. self) can be dropped on the specified target object. """ return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Returns whether or not the object being dragged (i.e. self) can be # dropped on the specified target object's DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_can_drop_on_dock_control ( self, dock_control ): + def feature_can_drop_on_dock_control(self, dock_control): """ Returns whether or not the object being dragged (i.e. self) can be dropped on the specified target object's DockControl. """ return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Allows the dragged object (i.e. self) to handle being dropped on the # specified target object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_dropped_on ( self, object ): + def feature_dropped_on(self, object): """ Allows the dragged object (i.e. self) to handle being dropped on the specified target object. """ return - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Allows the dragged object (i.e. self) to handle being dropped on the # specified target object's DockControl: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def feature_dropped_on_dock_control ( self, dock_control ): + def feature_dropped_on_dock_control(self, dock_control): """ Allows the dragged object (i.e. self) to handle being dropped on the specified target object's DockControl. """ return - diff -Nru python-pyface-6.1.2/pyface/dock/images/image_LICENSE.txt python-pyface-7.4.0/pyface/dock/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/dock/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Eclipse Eclipse Public License image_LICENSE_Eclipse.txt -Enthought BSD 3-Clause LICENSE.txt -GV (Gael Varoquaux) Public Domain N/A -OOo LGPL image_LICENSE_OOo.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/dock/images: - bar_feature_changed.png | Enthought - bar_feature_disabled.png | Enthought - bar_feature_drop.png | Enthought - bar_feature_no_drop.png | Enthought - bar_feature_normal.png | Enthought - close_drag.png | GV - close_tab.png | GV - feature_tool.png | GV - sh_bottom.png | Enthought - sh_middle.png | Enthought - sh_top.png | Enthought - shell.ico | Enthought - sv_left.png | Enthought - sv_middle.png | Enthought - sv_right.png | Enthought - tab_feature_changed.png | Enthought - tab_feature_disabled.png | Enthought - tab_feature_drop.png | Enthought - tab_feature_no_drop.png | OOo - tab_feature_normal.png | OOo - tab_scroll_l.png | Enthought - tab_scroll_lr.png | Enthought - tab_scroll_r.png | Enthought - window.png | Eclipse diff -Nru python-pyface-6.1.2/pyface/dock/__init__.py python-pyface-7.4.0/pyface/dock/__init__.py --- python-pyface-6.1.2/pyface/dock/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,12 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: David C. Morrill -# Date: 10/18/2005 -# -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ Pyface 'DockWindow' support. @@ -23,4 +16,3 @@ sub-region of the DockWindow, in which case each sub-window appears as a separate notebook-like tab within the region. """ - diff -Nru python-pyface-6.1.2/pyface/dock/tests/test_dock_sizer.py python-pyface-7.4.0/pyface/dock/tests/test_dock_sizer.py --- python-pyface-6.1.2/pyface/dock/tests/test_dock_sizer.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/dock/tests/test_dock_sizer.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,28 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest +from unittest.mock import MagicMock + +from pyface.toolkit import toolkit + +not_wx = toolkit.toolkit != "wx" + + +class TestDockControl(unittest.TestCase): + + @unittest.skipIf(not_wx, "This test is specific to the wx backend") + def test_feature_changed(self): + from pyface.dock.dock_sizer import DockControl + dock_control = DockControl() + DockControl.set_feature_mode = MagicMock() + dock_control.feature_changed = True + + dock_control.set_feature_mode.assert_called_once_with() diff -Nru python-pyface-6.1.2/pyface/drop_handler.py python-pyface-7.4.0/pyface/drop_handler.py --- python-pyface-6.1.2/pyface/drop_handler.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/drop_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,11 +1,22 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from pyface.i_drop_handler import IDropHandler from traits.api import Callable, HasTraits, List, provides, Str + @provides(IDropHandler) class BaseDropHandler(HasTraits): """ Basic drop handler """ - ### BaseDropHandler interface ############################################# + + # BaseDropHandler interface --------------------------------------------- #: Returns True if the current drop handler can handle the given drag event #: occurring on the given target widget. @@ -14,7 +25,7 @@ #: Performs drop action when drop event occurs on target widget. on_handle = Callable - ### IDropHandler interface ################################################ + # IDropHandler interface ------------------------------------------------ def can_handle_drop(self, event, target): return self.on_can_handle(event, target) @@ -27,7 +38,8 @@ class FileDropHandler(HasTraits): """ Class to handle backward compatible file drop events """ - ### FileDropHandler interface ############################################# + + # FileDropHandler interface --------------------------------------------- #: supported extensions extensions = List(Str) @@ -35,7 +47,7 @@ #: Called when file is opened. Takes single argument: path of file open_file = Callable - ### IDropHandler interface ################################################ + # IDropHandler interface ------------------------------------------------ def can_handle_drop(self, event, target): """ Does the drop event contails file data with matching extensions """ diff -Nru python-pyface-6.1.2/pyface/expandable_header.py python-pyface-7.4.0/pyface/expandable_header.py --- python-pyface-6.1.2/pyface/expandable_header.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/expandable_header.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.expandable_header, use pyface.ui.wx.expandable_header instead. ' - 'Will be removed in Pyface 7.') - -from pyface.ui.wx.expandable_header import * diff -Nru python-pyface-6.1.2/pyface/expandable_panel.py python-pyface-7.4.0/pyface/expandable_panel.py --- python-pyface-6.1.2/pyface/expandable_panel.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/expandable_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # Note: The ExpandablePanel is currently wx-specific # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -ExpandablePanel = toolkit_object('expandable_panel:ExpandablePanel') + +ExpandablePanel = toolkit_object("expandable_panel:ExpandablePanel") diff -Nru python-pyface-6.1.2/pyface/fields/api.py python-pyface-7.4.0/pyface/fields/api.py --- python-pyface-6.1.2/pyface/fields/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,47 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.fields`` subpackage. + +- :class:`~.CheckBoxField` +- :class:`~.ComboField` +- :class:`~.RadioButtonField` +- :class:`~.SpinField` +- :class:`~.TextField` +- :class:`~.TimeField` +- :class:`~.ToggleButtonField` + +Interfaces +---------- +- :class:`~.IComboField` +- :class:`~.IField` +- :class:`~.ISpinField` +- :class:`~.ITextField` +- :class:`~.ITimeField` +- :class:`~.IToggleField` + +""" from .i_combo_field import IComboField from .i_field import IField from .i_spin_field import ISpinField from .i_text_field import ITextField +from .i_time_field import ITimeField +from .i_toggle_field import IToggleField from .combo_field import ComboField from .spin_field import SpinField -from .text_field import TextField \ No newline at end of file +from .text_field import TextField +from .time_field import TimeField +from .toggle_field import ( + CheckBoxField, RadioButtonField, ToggleButtonField, +) diff -Nru python-pyface-6.1.2/pyface/fields/combo_field.py python-pyface-7.4.0/pyface/fields/combo_field.py --- python-pyface-6.1.2/pyface/fields/combo_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/combo_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. @@ -15,4 +16,4 @@ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -ComboField = toolkit_object('fields.combo_field:ComboField') +ComboField = toolkit_object("fields.combo_field:ComboField") diff -Nru python-pyface-6.1.2/pyface/fields/i_combo_field.py python-pyface-7.4.0/pyface/fields/i_combo_field.py --- python-pyface-6.1.2/pyface/fields/i_combo_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/i_combo_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,15 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" The text field interface. """ - -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) +# Thanks for using Enthought open source! + +""" The combo field interface. """ -from six import text_type from traits.api import Callable, HasTraits, Enum, List @@ -31,21 +23,21 @@ """ #: The current value of the combobox. - value = Enum(values='values') + value = Enum(values="values") #: The list of available values for the combobox. - values = List + values = List() #: Callable that converts a value to text plus an optional icon. #: Should return either a uncode string or a tuple of image resource #: and string. - formatter = Callable(text_type, allow_none=False) + formatter = Callable(str, allow_none=False) class MComboField(HasTraits): #: The current text value of the combobox. - value = Enum(values='values') + value = Enum(values="values") #: The list of available values for the combobox. values = List(minlen=1) @@ -53,16 +45,16 @@ #: Callable that converts a value to text plus an optional icon. #: Should return either a uncode string or a tuple of image resource #: and string. - formatter = Callable(text_type, allow_none=False) + formatter = Callable(str, allow_none=False) # ------------------------------------------------------------------------ # object interface # ------------------------------------------------------------------------ def __init__(self, values, **traits): - value = traits.pop('value', values[0]) - traits['values'] = values - super(MComboField, self).__init__(**traits) + value = traits.pop("value", values[0]) + traits["values"] = values + super().__init__(**traits) self.value = value # ------------------------------------------------------------------------ @@ -70,15 +62,16 @@ # ------------------------------------------------------------------------ def _initialize_control(self): - super(MComboField, self)._initialize_control() + super()._initialize_control() self._set_control_values(self.values) self._set_control_value(self.value) def _add_event_listeners(self): """ Set up toolkit-specific bindings for events """ - super(MComboField, self)._add_event_listeners() - self.on_trait_change(self._values_updated, 'values[],formatter', - dispatch='ui') + super()._add_event_listeners() + self.observe( + self._values_updated, "values.items,formatter", dispatch="ui" + ) if self.control is not None: self._observe_control_value() @@ -86,22 +79,26 @@ """ Remove toolkit-specific bindings for events """ if self.control is not None: self._observe_control_value(remove=True) - self.on_trait_change(self._values_updated, 'values[],formatter', - dispatch='ui', remove=True) - super(MComboField, self)._remove_event_listeners() + self.observe( + self._values_updated, + "values.items,formatter", + dispatch="ui", + remove=True, + ) + super()._remove_event_listeners() # Toolkit control interface --------------------------------------------- def _get_control_text_values(self): """ Toolkit specific method to get the control's text values. """ - raise NotImplementedError + raise NotImplementedError() def _set_control_values(self, values): """ Toolkit specific method to set the control's values. """ - raise NotImplementedError + raise NotImplementedError() # Trait change handlers -------------------------------------------------- - def _values_updated(self): + def _values_updated(self, event): if self.control is not None: self._set_control_values(self.values) diff -Nru python-pyface-6.1.2/pyface/fields/i_field.py python-pyface-7.4.0/pyface/fields/i_field.py --- python-pyface-6.1.2/pyface/fields/i_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/i_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" The text field interface. """ +# Thanks for using Enthought open source! + +""" The field interface. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) -from traits.api import Any, HasTraits, Instance, Unicode +from traits.api import Any, HasTraits -from pyface.i_widget import IWidget +from pyface.i_layout_widget import ILayoutWidget -class IField(IWidget): +class IField(ILayoutWidget): """ The field interface. A field is a widget that displays a value and (potentially) allows a user @@ -30,32 +24,14 @@ """ #: The value held by the field. - value = Any - - #: A tooltip for the field. - tooltip = Unicode - - #: An optional context menu for the field. - context_menu = Instance('pyface.action.menu_manager.MenuManager') - - def show_context_menu(self, x, y): - """ Create and show the context menu at a position. """ - - def _initialize_control(self, control): - """ Perform any toolkit-specific initialization for the control. """ + value = Any() class MField(HasTraits): """ The field mix-in. """ #: The value held by the field. - value = Any - - #: A tooltip for the field. - tooltip = Unicode - - #: An optional context menu for the field. - context_menu = Instance('pyface.action.menu_manager.MenuManager') + value = Any() # ------------------------------------------------------------------------ # IWidget interface @@ -63,25 +39,15 @@ def _add_event_listeners(self): """ Set up toolkit-specific bindings for events """ - super(MField, self)._add_event_listeners() - self.on_trait_change(self._value_updated, 'value', dispatch='ui') - self.on_trait_change(self._tooltip_updated, 'tooltip', dispatch='ui') - self.on_trait_change(self._context_menu_updated, 'context_menu', - dispatch='ui') - if self.control is not None and self.context_menu is not None: - self._observe_control_context_menu() + super()._add_event_listeners() + self.observe(self._value_updated, "value", dispatch="ui") def _remove_event_listeners(self): """ Remove toolkit-specific bindings for events """ - if self.control is not None and self.context_menu is not None: - self._observe_control_context_menu(remove=True) - self.on_trait_change(self._value_updated, 'value', dispatch='ui', - remove=True) - self.on_trait_change(self._tooltip_updated, 'tooltip', dispatch='ui', - remove=True) - self.on_trait_change(self._context_menu_updated, 'context_menu', - dispatch='ui', remove=True) - super(MField, self)._remove_event_listeners() + self.observe( + self._value_updated, "value", dispatch="ui", remove=True + ) + super()._remove_event_listeners() # ------------------------------------------------------------------------ # Private interface @@ -93,17 +59,11 @@ This method should create the control and assign it to the :py:attr:``control`` trait. """ - self.control = self._create_control(self.parent) - self._initialize_control() - self._add_event_listeners() + super()._create() self.show(self.visible) self.enable(self.enabled) - def _initialize_control(self): - """ Perform any toolkit-specific initialization for the control. """ - self._set_control_tooltip(self.tooltip) - def _update_value(self, value): """ Handle a change to the value from user interaction @@ -122,54 +82,19 @@ def _get_control_value(self): """ Toolkit specific method to get the control's value. """ - raise NotImplementedError + raise NotImplementedError() def _set_control_value(self, value): """ Toolkit specific method to set the control's value. """ - raise NotImplementedError + raise NotImplementedError() def _observe_control_value(self, remove=False): """ Toolkit specific method to change the control value observer. """ - raise NotImplementedError - - def _get_control_tooltip(self): - """ Toolkit specific method to get the control's tooltip. """ - raise NotImplementedError - - def _set_control_tooltip(self, tooltip): - """ Toolkit specific method to set the control's tooltip. """ - raise NotImplementedError - - def _observe_control_context_menu(self, remove=False): - """ Toolkit specific method to change the control menu observer. - - This should use _handle_control_context_menu as the event handler. - """ - raise NotImplementedError - - def _handle_control_context_menu(self, event): - """ Handle a context menu event. - - This should call show_context_menu with appropriate position x and y - arguments. - - The function signature will likely vary from toolkit to toolkit. - """ - raise NotImplementedError + raise NotImplementedError() # Trait change handlers ------------------------------------------------- - def _value_updated(self, value): + def _value_updated(self, event): + value = event.new if self.control is not None: self._set_control_value(value) - - def _tooltip_updated(self, tooltip): - if self.control is not None: - self._set_control_tooltip(tooltip) - - def _context_menu_updated(self, old, new): - if self.control is not None: - if new is None: - self._observe_control_context_menu(remove=True) - if old is None: - self._observe_control_context_menu() diff -Nru python-pyface-6.1.2/pyface/fields/i_spin_field.py python-pyface-7.4.0/pyface/fields/i_spin_field.py --- python-pyface-6.1.2/pyface/fields/i_spin_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/i_spin_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,15 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The spin field interface. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) from traits.api import HasTraits, Int, Property, Range, Tuple @@ -29,41 +23,41 @@ """ #: The current value of the spinner - value = Range(low='minimum', high='maximum') + value = Range(low="minimum", high="maximum") #: The bounds of the spinner bounds = Tuple(Int, Int) #: The minimum value - minimum = Property(Int, depends_on='bounds') + minimum = Property(Int, observe="bounds") #: The maximum value - maximum = Property(Int, depends_on='bounds') + maximum = Property(Int, observe="bounds") class MSpinField(HasTraits): #: The current value of the spinner - value = Range(low='minimum', high='maximum') + value = Range(low="minimum", high="maximum") #: The bounds for the spinner bounds = Tuple(Int, Int) #: The minimum value for the spinner - minimum = Property(Int, depends_on='bounds') + minimum = Property(Int, observe="bounds") #: The maximum value for the spinner - maximum = Property(Int, depends_on='bounds') + maximum = Property(Int, observe="bounds") # ------------------------------------------------------------------------ # object interface # ------------------------------------------------------------------------ def __init__(self, **traits): - value = traits.pop('value', None) - if 'bounds' in traits: - traits['value'] = traits['bounds'][0] - super(MSpinField, self).__init__(**traits) + value = traits.pop("value", None) + if "bounds" in traits: + traits["value"] = traits["bounds"][0] + super().__init__(**traits) if value is not None: self.value = value @@ -72,15 +66,14 @@ # ------------------------------------------------------------------------ def _initialize_control(self): - super(MSpinField, self)._initialize_control() + super()._initialize_control() self._set_control_bounds(self.bounds) self._set_control_value(self.value) def _add_event_listeners(self): """ Set up toolkit-specific bindings for events """ - super(MSpinField, self)._add_event_listeners() - self.on_trait_change(self._bounds_updated, 'bounds', - dispatch='ui') + super()._add_event_listeners() + self.observe(self._bounds_updated, "bounds", dispatch="ui") if self.control is not None: self._observe_control_value() @@ -88,9 +81,10 @@ """ Remove toolkit-specific bindings for events """ if self.control is not None: self._observe_control_value(remove=True) - self.on_trait_change(self._bounds_updated, 'bounds', - dispatch='ui', remove=True) - super(MSpinField, self)._remove_event_listeners() + self.observe( + self._bounds_updated, "bounds", dispatch="ui", remove=True + ) + super()._remove_event_listeners() # Toolkit control interface --------------------------------------------- @@ -133,6 +127,6 @@ # Trait change handlers -------------------------------------------------- - def _bounds_updated(self): + def _bounds_updated(self, event): if self.control is not None: self._set_control_bounds(self.bounds) diff -Nru python-pyface-6.1.2/pyface/fields/i_text_field.py python-pyface-7.4.0/pyface/fields/i_text_field.py --- python-pyface-6.1.2/pyface/fields/i_text_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/i_text_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The text field interface. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) -from traits.api import Bool, Enum, HasTraits, Unicode +from traits.api import Bool, Enum, HasTraits, Str from pyface.fields.i_field import IField @@ -26,38 +20,38 @@ """ The text field interface. """ #: The value held by the field. - value = Unicode + value = Str() #: Should the text trait be updated on user edits, or when done editing. - update_text = Enum('auto', 'editing_finished') + update_text = Enum("auto", "editing_finished") #: Placeholder text for an empty field. - placeholder = Unicode + placeholder = Str() #: Display typed text, or one of several hidden "password" modes. - echo = Enum('normal', 'password') + echo = Enum("normal", "password") #: Whether or not the field is read-only. - read_only = Bool + read_only = Bool() class MTextField(HasTraits): """ The text field mix-in. """ #: The value held by the field. - value = Unicode + value = Str() #: Should the value be updated on every keystroke, or when done editing. - update_text = Enum('auto', 'editing_finished') + update_text = Enum("auto", "editing_finished") #: Placeholder text for an empty field. - placeholder = Unicode + placeholder = Str() #: Display typed text, or one of several hidden "password" modes. - echo = Enum('normal', 'password') + echo = Enum("normal", "password") #: Whether or not the field is read-only. - read_only = Bool + read_only = Bool() # ------------------------------------------------------------------------ # Private interface @@ -69,20 +63,17 @@ self._set_control_placeholder(self.placeholder) self._set_control_read_only(self.read_only) - super(MTextField, self)._initialize_control() + super()._initialize_control() def _add_event_listeners(self): """ Set up toolkit-specific bindings for events """ - super(MTextField, self)._add_event_listeners() - self.on_trait_change(self._update_text_updated, 'update_text', - dispatch='ui') - self.on_trait_change(self._placeholder_updated, 'placeholder', - dispatch='ui') - self.on_trait_change(self._echo_updated, 'echo', dispatch='ui') - self.on_trait_change(self._read_only_updated, 'read_only', - dispatch='ui') + super()._add_event_listeners() + self.observe(self._update_text_updated, "update_text", dispatch="ui") + self.observe(self._placeholder_updated, "placeholder", dispatch="ui") + self.observe(self._echo_updated, "echo", dispatch="ui") + self.observe(self._read_only_updated, "read_only", dispatch="ui") if self.control is not None: - if self.update_text == 'editing_finished': + if self.update_text == "editing_finished": self._observe_control_editing_finished() else: self._observe_control_value() @@ -90,19 +81,29 @@ def _remove_event_listeners(self): """ Remove toolkit-specific bindings for events """ if self.control is not None: - if self.update_text == 'editing_finished': + if self.update_text == "editing_finished": self._observe_control_editing_finished(remove=True) else: self._observe_control_value(remove=True) - self.on_trait_change(self._update_text_updated, 'update_text', - dispatch='ui', remove=True) - self.on_trait_change(self._placeholder_updated, 'placeholder', - dispatch='ui', remove=True) - self.on_trait_change(self._echo_updated, 'echo', dispatch='ui', - remove=True) - self.on_trait_change(self._read_only_updated, 'read_only', - dispatch='ui', remove=True) - super(MTextField, self)._remove_event_listeners() + self.observe( + self._update_text_updated, + "update_text", + dispatch="ui", + remove=True, + ) + self.observe( + self._placeholder_updated, + "placeholder", + dispatch="ui", + remove=True, + ) + self.observe( + self._echo_updated, "echo", dispatch="ui", remove=True + ) + self.observe( + self._read_only_updated, "read_only", dispatch="ui", remove=True + ) + super()._remove_event_listeners() def _editing_finished(self): if self.control is not None: @@ -113,50 +114,50 @@ def _get_control_placeholder(self): """ Toolkit specific method to set the control's placeholder. """ - raise NotImplementedError + raise NotImplementedError() def _set_control_placeholder(self, placeholder): """ Toolkit specific method to set the control's placeholder. """ - raise NotImplementedError + raise NotImplementedError() def _get_control_echo(self): """ Toolkit specific method to get the control's echo. """ - raise NotImplementedError + raise NotImplementedError() def _set_control_echo(self, echo): """ Toolkit specific method to set the control's echo. """ - raise NotImplementedError + raise NotImplementedError() def _get_control_read_only(self): """ Toolkit specific method to get the control's read_only state. """ - raise NotImplementedError + raise NotImplementedError() def _set_control_read_only(self, read_only): """ Toolkit specific method to set the control's read_only state. """ - raise NotImplementedError + raise NotImplementedError() def _observe_control_editing_finished(self, remove=False): """ Change observation of whether editing is finished. """ - raise NotImplementedError + raise NotImplementedError() # Trait change handlers -------------------------------------------------- - def _placeholder_updated(self): + def _placeholder_updated(self, event): if self.control is not None: self._set_control_placeholder(self.placeholder) - def _echo_updated(self): + def _echo_updated(self, event): if self.control is not None: self._set_control_echo(self.echo) - def _read_only_updated(self): + def _read_only_updated(self, event): if self.control is not None: self._set_control_read_only(self.read_only) - def _update_text_updated(self, new): + def _update_text_updated(self, event): """ Change how we listen to for updates to text value. """ if self.control is not None: - if new == 'editing_finished': + if event.new == "editing_finished": self._observe_control_value(remove=True) self._observe_control_editing_finished() else: diff -Nru python-pyface-6.1.2/pyface/fields/i_time_field.py python-pyface-7.4.0/pyface/fields/i_time_field.py --- python-pyface-6.1.2/pyface/fields/i_time_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/i_time_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,59 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The time field interface. """ + +from datetime import time + +from traits.api import HasTraits, Time + +from pyface.fields.i_field import IField + + +class ITimeField(IField): + """ The time field interface. + + This is for a field that edits a datetime.time value. + """ + + #: The current value of the time field + value = Time() + + +class MTimeField(HasTraits): + """ Mixin class for TimeField implementations """ + + #: The current value of the time field + value = Time() + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _initialize_control(self): + super(MTimeField, self)._initialize_control() + self._set_control_value(self.value) + + def _add_event_listeners(self): + """ Set up toolkit-specific bindings for events """ + super(MTimeField, self)._add_event_listeners() + if self.control is not None: + self._observe_control_value() + + def _remove_event_listeners(self): + """ Remove toolkit-specific bindings for events """ + if self.control is not None: + self._observe_control_value(remove=True) + super(MTimeField, self)._remove_event_listeners() + + # Trait defaults -------------------------------------------------------- + + def _value_default(self): + return time.now() diff -Nru python-pyface-6.1.2/pyface/fields/i_toggle_field.py python-pyface-7.4.0/pyface/fields/i_toggle_field.py --- python-pyface-6.1.2/pyface/fields/i_toggle_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/i_toggle_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,98 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The toggle field interface. """ + + +from traits.api import Bool, HasTraits, Str + +from pyface.fields.i_field import IField +from pyface.ui_traits import Image + + +class IToggleField(IField): + """ The toggle field interface. + + This is for a toggle between two states, represented by a boolean value. + """ + + #: The current value of the toggle. + value = Bool() + + #: The text to display in the toggle. + text = Str() + + #: The icon to display with the toggle. + icon = Image() + + +class MToggleField(HasTraits): + """ The toggle field mixin class. + + This is for a toggle between two states, represented by a boolean value. + """ + + #: The current value of the toggle. + value = Bool() + + #: The text to display in the toggle. + text = Str() + + #: The icon to display with the toggle. + icon = Image() + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _initialize_control(self): + super()._initialize_control() + self._set_control_text(self.text) + self._set_control_icon(self.icon) + + def _add_event_listeners(self): + """ Set up toolkit-specific bindings for events """ + super()._add_event_listeners() + self.observe(self._text_updated, "text", dispatch="ui") + self.observe(self._icon_updated, "icon", dispatch="ui") + if self.control is not None: + self._observe_control_value() + + def _remove_event_listeners(self): + """ Remove toolkit-specific bindings for events """ + if self.control is not None: + self._observe_control_value(remove=True) + self.observe(self._text_updated, "text", dispatch="ui", remove=True) + self.observe(self._icon_updated, "icon", dispatch="ui", remove=True) + super()._remove_event_listeners() + + # Toolkit control interface --------------------------------------------- + + def _get_control_text(self): + """ Toolkit specific method to set the control's text. """ + raise NotImplementedError() + + def _set_control_text(self, text): + """ Toolkit specific method to set the control's text. """ + raise NotImplementedError() + + def _set_control_icon(self, icon): + """ Toolkit specific method to set the control's icon. """ + raise NotImplementedError() + + # Trait change handlers ------------------------------------------------- + + def _text_updated(self, event): + if self.control is not None: + self._set_control_text(self.text) + + def _icon_updated(self, event): + if self.control is not None: + self._set_control_icon(self.icon) diff -Nru python-pyface-6.1.2/pyface/fields/spin_field.py python-pyface-7.4.0/pyface/fields/spin_field.py --- python-pyface-6.1.2/pyface/fields/spin_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/spin_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The spin field widget. """ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -SpinField = toolkit_object('fields.spin_field:SpinField') +SpinField = toolkit_object("fields.spin_field:SpinField") diff -Nru python-pyface-6.1.2/pyface/fields/tests/field_mixin.py python-pyface-7.4.0/pyface/fields/tests/field_mixin.py --- python-pyface-6.1.2/pyface/fields/tests/field_mixin.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/tests/field_mixin.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,68 +1,17 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - -from traits.testing.unittest_tools import UnittestTools -from pyface.action.api import Action, MenuManager -from pyface.gui import GUI -from pyface.window import Window +from pyface.testing.layout_widget_mixin import LayoutWidgetMixin -class FieldMixin(UnittestTools): +class FieldMixin(LayoutWidgetMixin): """ Mixin which provides standard methods for all fields. """ - - def setUp(self): - self.gui = GUI() - - self.parent = Window() - self.parent._create() - self.addCleanup(self._destroy_parent) - self.gui.process_events() - - self.widget = self._create_widget() - - self.parent.open() - self.gui.process_events() - - def _create_widget(self): - raise NotImplementedError() - - def _create_widget_control(self): - self.widget._create() - self.addCleanup(self._destroy_widget) - self.widget.show(True) - self.gui.process_events() - - def _destroy_parent(self): - self.parent.destroy() - self.gui.process_events() - self.parent = None - - def _destroy_widget(self): - self.widget.destroy() - self.gui.process_events() - self.widget = None - - # Tests ------------------------------------------------------------------ - - def test_field_tooltip(self): - self._create_widget_control() - self.widget.tooltip = "New tooltip." - self.gui.process_events() - - self.assertEqual(self.widget._get_control_tooltip(), "New tooltip.") - - def test_field_menu(self): - self._create_widget_control() - self.widget.menu = MenuManager(Action(name='Test'), name='Test') - self.gui.process_events() + pass diff -Nru python-pyface-6.1.2/pyface/fields/tests/test_combo_field.py python-pyface-7.4.0/pyface/fields/tests/test_combo_field.py --- python-pyface-6.1.2/pyface/fields/tests/test_combo_field.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/tests/test_combo_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,16 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import unittest -from six import text_type from pyface.image_resource import ImageResource from ..combo_field import ComboField @@ -21,13 +18,12 @@ class TestComboField(FieldMixin, unittest.TestCase): - def _create_widget(self): return ComboField( parent=self.parent.control, - value='one', - values=['one', 'two', 'three', 'four'], - tooltip='Dummy', + value="one", + values=["one", "two", "three", "four"], + tooltip="Dummy", ) # Tests ------------------------------------------------------------------ @@ -35,23 +31,23 @@ def test_combo_field(self): self._create_widget_control() - self.widget.value = 'two' + self.widget.value = "two" self.gui.process_events() - self.assertEqual(self.widget._get_control_value(), 'two') - self.assertEqual(self.widget._get_control_text(), 'two') + self.assertEqual(self.widget._get_control_value(), "two") + self.assertEqual(self.widget._get_control_text(), "two") def test_combo_field_set(self): self._create_widget_control() - with self.assertTraitChanges(self.widget, 'value', count=1): - self.widget._set_control_value('two') + with self.assertTraitChanges(self.widget, "value", count=1): + self.widget._set_control_value("two") self.gui.process_events() - self.assertEqual(self.widget.value, 'two') + self.assertEqual(self.widget.value, "two") def test_combo_field_formatter(self): - self.widget.formatter = text_type + self.widget.formatter = str self.widget.values = [0, 1, 2, 3] self._create_widget_control() @@ -59,33 +55,33 @@ self.gui.process_events() self.assertEqual(self.widget._get_control_value(), 2) - self.assertEqual(self.widget._get_control_text(), '2') + self.assertEqual(self.widget._get_control_text(), "2") def test_combo_field_formatter_changed(self): self.widget.values = [1, 2, 3, 4] self.widget.value = 2 - self.widget.formatter = text_type + self.widget.formatter = str self._create_widget_control() - self.widget.formatter = 'Number {}'.format + self.widget.formatter = "Number {}".format self.gui.process_events() self.assertEqual(self.widget._get_control_value(), 2) - self.assertEqual(self.widget._get_control_text(), 'Number 2') + self.assertEqual(self.widget._get_control_text(), "Number 2") def test_combo_field_formatter_set(self): self.widget.values = [1, 2, 3, 4] - self.widget.formatter = text_type + self.widget.formatter = str self._create_widget_control() - with self.assertTraitChanges(self.widget, 'value', count=1): + with self.assertTraitChanges(self.widget, "value", count=1): self.widget._set_control_value(2) self.gui.process_events() self.assertEqual(self.widget.value, 2) def test_combo_field_icon_formatter(self): - image = ImageResource('question') + image = ImageResource("question") self.widget.values = [1, 2, 3, 4] self.widget.formatter = lambda x: (image, str(x)) self._create_widget_control() @@ -94,12 +90,12 @@ self.gui.process_events() self.assertEqual(self.widget._get_control_value(), 2) - self.assertEqual(self.widget._get_control_text(), '2') + self.assertEqual(self.widget._get_control_text(), "2") def test_combo_field_values(self): self._create_widget_control() - self.widget.values = ['four', 'five', 'one', 'six'] + self.widget.values = ["four", "five", "one", "six"] self.gui.process_events() # XXX different results in Wx and Qt @@ -108,4 +104,4 @@ # handler sees it. On Wx it remains as "one" at point of handler # call. Possibly down to dictionary ordering or something # similar. - self.assertIn(self.widget.value, {'one', 'four'}) + self.assertIn(self.widget.value, {"one", "four"}) diff -Nru python-pyface-6.1.2/pyface/fields/tests/test_spin_field.py python-pyface-7.4.0/pyface/fields/tests/test_spin_field.py --- python-pyface-6.1.2/pyface/fields/tests/test_spin_field.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/tests/test_spin_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,13 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import unittest @@ -18,13 +16,12 @@ class TestSpinField(FieldMixin, unittest.TestCase): - def _create_widget(self): return SpinField( parent=self.parent.control, value=1, bounds=(0, 100), - tooltip='Dummy', + tooltip="Dummy", ) # Tests ------------------------------------------------------------------ @@ -40,7 +37,7 @@ def test_spin_field_set(self): self._create_widget_control() - with self.assertTraitChanges(self.widget, 'value', count=1): + with self.assertTraitChanges(self.widget, "value", count=1): self.widget._set_control_value(5) self.gui.process_events() diff -Nru python-pyface-6.1.2/pyface/fields/tests/test_text_field.py python-pyface-7.4.0/pyface/fields/tests/test_text_field.py --- python-pyface-6.1.2/pyface/fields/tests/test_text_field.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/tests/test_text_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,14 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) - import unittest from pyface.toolkit import toolkit @@ -19,16 +16,13 @@ from .field_mixin import FieldMixin -is_wx = (toolkit.toolkit == 'wx') +is_wx = toolkit.toolkit == "wx" class TestTextField(FieldMixin, unittest.TestCase): - def _create_widget(self): return TextField( - parent=self.parent.control, - value='test', - tooltip='Dummy', + parent=self.parent.control, value="test", tooltip="Dummy" ) # Tests ------------------------------------------------------------------ @@ -36,43 +30,44 @@ def test_text_field(self): self._create_widget_control() - self.widget.value = 'new value' + self.widget.value = "new value" self.gui.process_events() - self.assertEqual(self.widget._get_control_value(), 'new value') + self.assertEqual(self.widget._get_control_value(), "new value") def test_text_field_set(self): self._create_widget_control() - with self.assertTraitChanges(self.widget, 'value', count=1): - self.widget._set_control_value('new value') + with self.assertTraitChanges(self.widget, "value", count=1): + self.widget._set_control_value("new value") self.gui.process_events() - self.assertEqual(self.widget.value, 'new value') + self.assertEqual(self.widget.value, "new value") def test_text_field_echo(self): - self.widget.echo = 'password' + self.widget.echo = "password" self._create_widget_control() - self.assertEqual(self.widget._get_control_echo(), 'password') + self.assertEqual(self.widget._get_control_echo(), "password") - @unittest.skipIf(is_wx, "Can't change password mode for wx after control " - "creation.") + @unittest.skipIf( + is_wx, "Can't change password mode for wx after control " "creation." + ) def test_text_field_echo_change(self): self._create_widget_control() - self.widget.echo = 'password' + self.widget.echo = "password" self.gui.process_events() - self.assertEqual(self.widget._get_control_echo(), 'password') + self.assertEqual(self.widget._get_control_echo(), "password") def test_text_field_placeholder(self): self._create_widget_control() - self.widget.placeholder = 'test' + self.widget.placeholder = "test" self.gui.process_events() - self.assertEqual(self.widget._get_control_placeholder(), 'test') + self.assertEqual(self.widget._get_control_placeholder(), "test") def test_text_field_readonly(self): self.widget.read_only = True @@ -82,8 +77,9 @@ self.assertEqual(self.widget._get_control_read_only(), True) - @unittest.skipIf(is_wx, "Can't change read_only mode for wx after control " - "creation.") + @unittest.skipIf( + is_wx, "Can't change read_only mode for wx after control " "creation." + ) def test_text_field_readonly_change(self): self._create_widget_control() @@ -91,4 +87,3 @@ self.gui.process_events() self.assertEqual(self.widget._get_control_read_only(), True) - diff -Nru python-pyface-6.1.2/pyface/fields/tests/test_time_field.py python-pyface-7.4.0/pyface/fields/tests/test_time_field.py --- python-pyface-6.1.2/pyface/fields/tests/test_time_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/tests/test_time_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,45 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +from datetime import time +import unittest + +from ..time_field import TimeField +from .field_mixin import FieldMixin + + +class TestTimeField(FieldMixin, unittest.TestCase): + + def _create_widget(self): + return TimeField( + parent=self.parent.control, + value=time(12, 0, 0), + tooltip="Dummy", + ) + + # Tests ------------------------------------------------------------------ + + def test_time_field(self): + self._create_widget_control() + + self.widget.value = time(13, 1, 1) + self.gui.process_events() + + self.assertEqual(self.widget._get_control_value(), time(13, 1, 1)) + + def test_time_field_set(self): + self._create_widget_control() + + with self.assertTraitChanges(self.widget, "value", count=1): + self.widget._set_control_value(time(13, 1, 1)) + self.gui.process_events() + + self.assertEqual(self.widget.value, time(13, 1, 1)) diff -Nru python-pyface-6.1.2/pyface/fields/tests/test_toggle_field.py python-pyface-7.4.0/pyface/fields/tests/test_toggle_field.py --- python-pyface-6.1.2/pyface/fields/tests/test_toggle_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/tests/test_toggle_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,88 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import unittest + + +from pyface.image_resource import ImageResource +from ..toggle_field import ( + CheckBoxField, RadioButtonField, ToggleButtonField +) +from .field_mixin import FieldMixin + + +class ToggleFieldMixin(FieldMixin): + + # Tests ------------------------------------------------------------------ + + def test_toggle_field(self): + self._create_widget_control() + + self.widget.value = True + self.gui.process_events() + + self.assertEqual(self.widget._get_control_value(), True) + + def test_toggle_field_set(self): + self._create_widget_control() + + with self.assertTraitChanges(self.widget, "value", count=1): + self.widget._set_control_value(True) + self.gui.process_events() + + self.assertEqual(self.widget.value, True) + + def test_text_field_text(self): + self._create_widget_control() + self.assertEqual(self.widget._get_control_text(), "Toggle") + + self.widget.text = "Test" + self.gui.process_events() + + self.assertEqual(self.widget._get_control_text(), "Test") + + def test_text_field_image(self): + self._create_widget_control() + image = ImageResource("question") + + # XXX can't validate icon values currently, so just a smoke test + self.widget.image = image + self.gui.process_events() + + +class TestCheckboxField(ToggleFieldMixin, unittest.TestCase): + + def _create_widget(self): + return CheckBoxField( + parent=self.parent.control, + text="Toggle", + tooltip="Dummy", + ) + + +class TestRadioButtonField(ToggleFieldMixin, unittest.TestCase): + + def _create_widget(self): + return RadioButtonField( + parent=self.parent.control, + text="Toggle", + tooltip="Dummy", + ) + + +class TestToggleButtonField(ToggleFieldMixin, unittest.TestCase): + + def _create_widget(self): + return ToggleButtonField( + parent=self.parent.control, + text="Toggle", + tooltip="Dummy", + ) diff -Nru python-pyface-6.1.2/pyface/fields/text_field.py python-pyface-7.4.0/pyface/fields/text_field.py --- python-pyface-6.1.2/pyface/fields/text_field.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/text_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The text field widget. """ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -TextField = toolkit_object('fields.text_field:TextField') +TextField = toolkit_object("fields.text_field:TextField") diff -Nru python-pyface-6.1.2/pyface/fields/time_field.py python-pyface-7.4.0/pyface/fields/time_field.py --- python-pyface-6.1.2/pyface/fields/time_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/time_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,16 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The time field widget. """ + +# Import the toolkit specific version. +from pyface.toolkit import toolkit_object + +TimeField = toolkit_object("fields.time_field:TimeField") diff -Nru python-pyface-6.1.2/pyface/fields/toggle_field.py python-pyface-7.4.0/pyface/fields/toggle_field.py --- python-pyface-6.1.2/pyface/fields/toggle_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/fields/toggle_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,19 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The toggle field widgets. """ + +# Import the toolkit specific version. +from pyface.toolkit import toolkit_object + +ToggleField = toolkit_object("fields.toggle_field:ToggleField") +CheckBoxField = toolkit_object("fields.toggle_field:CheckBoxField") +RadioButtonField = toolkit_object("fields.toggle_field:RadioButtonField") +ToggleButtonField = toolkit_object("fields.toggle_field:ToggleButtonField") diff -Nru python-pyface-6.1.2/pyface/file_dialog.py python-pyface-7.4.0/pyface/file_dialog.py --- python-pyface-6.1.2/pyface/file_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/file_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a dialog that allows the user to open/save files etc. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -FileDialog = toolkit_object('file_dialog:FileDialog') -#### EOF ###################################################################### +FileDialog = toolkit_object("file_dialog:FileDialog") diff -Nru python-pyface-6.1.2/pyface/filter.py python-pyface-7.4.0/pyface/filter.py --- python-pyface-6.1.2/pyface/filter.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/filter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Base class for all filters. """ -# Enthought library imports. from traits.api import HasPrivateTraits class Filter(HasPrivateTraits): """ Abstract base class for all filters. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'Filter' interface. - ########################################################################### + # ------------------------------------------------------------------------ def filter(self, widget, parent, nodes): """ Filters a list of nodes. @@ -66,5 +62,3 @@ """ return False - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/font_dialog.py python-pyface-7.4.0/pyface/font_dialog.py --- python-pyface-6.1.2/pyface/font_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/font_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,41 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The implementation of a dialog that allows the user to select a font. +""" + +from .constant import OK +from .toolkit import toolkit_object + + +FontDialog = toolkit_object("font_dialog:FontDialog") + + +def get_font(parent, font): + """ Convenience function that displays a font dialog. + + Parameters + ---------- + parent : toolkit control + The parent toolkit control for the modal dialog. + font : Font or font description + The initial Font object or string describing the font. + + Returns + ------- + font : Font or None + The selected font, or None if the user made no selection. + """ + dialog = FontDialog(parent=parent, font=font) + result = dialog.open() + if result == OK: + return dialog.font + else: + return None diff -Nru python-pyface-6.1.2/pyface/font.py python-pyface-7.4.0/pyface/font.py --- python-pyface-6.1.2/pyface/font.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/font.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,406 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Toolkit-independent font utilities. + +Pyface fonts are intended to be generic, but able to be mapped fairly well +to most backend toolkit font descriptions. In most cases we can describe +fonts along the common dimensions that are used by CSS, Wx, and Qt. However +when it comes to actually working with a font, the toolkit needs to take the +description and produce something that is as close as possible to the +specification, but within the constraints of the toolkit, operating system +and available fonts on the machine where this is being executed. + +Because of this inherent ambiguity in font specification, this system tries to +be flexible in what it accepts as a font specification, rather than trying to +specify a unique canoncial form. + +Font Properties +--------------- + +The properties that fonts have are: + +Font Family + A list of font family names in order of preference, such as "Helvetica" + or "Comic Sans". In the case of a font that has been selected by the + toolkit this list will have one value which is the actual font family name. + + There are several generic font family names that can be used as fall-backs + in case all preferred fonts are unavailable. The allowed values are: + + "default" + The application's default system font. + + "fantasy" + A primarily decorative font, but with recognisable characters. + + "decorative" + A synonym for "fantasy". + + "serif" + A proportional serif font, such as Times New Roman or Garamond. + + "roman" + A synonym for "serif". + + "cursive" + A font which resembles hand-written cursive text, such as Zapf + Chancery. + + "script" + A synonym for "cursive". + + "sans-serif" + A proportional sans-serif font, such as Helvetica or Arial. + + "swiss" + A synonym for "sans-serif". + + "monospace" + A fixed-pitch sans-serif font, such as Source Code Pro or Roboto Mono. + Commonly used for display of code. + + "modern" + A synonym for "monospace". + + "typewriter" + A fixed-pitch serif font which resembles typewritten text, such as + Courier. Commonly used for display of code. + + "teletype" + A synonym for "typewriter". + + These special names will be converted into appropriate toolkit flags which + correspond to these generic font specifications. + +Weight + How thick or dark the font glyphs are. These can be given as a number + from 1 (lightest) to 999 (darkest), but are typically specified by a + multiple of 100 from 100 to 900, with a number of synonyms such as 'light' + and 'bold' available for those values. + +Stretch + The amount of horizontal compression or expansion to apply to the glyphs. + These can be given as a percentage between 50% and 200%, or by strings + such as 'condensed' and 'expanded' that correspond to those values. + +Style + This selects either 'oblique' or 'italic' variants typefaces of the given + font family. If neither is wanted, the value is 'normal'. + +Size + The overall size of the glyphs. This can be expressed either as the + numeric size in points, or as a string such as "small" or "large". + +Variants + A set of additional font style specifiers, such as "small-caps", + "strikethrough", "underline" or "overline", where supported by the + underlying toolkit. + +Font Specificiation Class +------------------------- + +The Pyface Font class is a HasStrictTraits class which specifies a requested +font. It has methods that convert the Font class to and from a toolkit Font +class. + +""" +from traits.api import ( + BaseCFloat, CList, CSet, Enum, HasStrictTraits, Map, Str, +) +from traits.trait_type import NoDefaultSpecified + + +#: Font weight synonyms. +#: These are alternate convenience names for font weights. +#: The intent is to allow a developer to use a common name (eg. "bold") instead +#: of having to remember the corresponding number (eg. 700). +#: These come from: +#: - the OpenType specification: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass +#: - QFont weights: https://doc.qt.io/qt-5/qfont.html#Weight-enum +#: - WxPython font weights: https://wxpython.org/Phoenix/docs/html/wx.FontWeight.enumeration.html +#: - CSS Common weight name mapping: https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#common_weight_name_mapping +#: - values used by Enable: https://github.com/enthought/enable/blob/78d2e494097fac71cc5c73efef5fb464963fb4db/kiva/fonttools/_constants.py#L90-L105 +#: See also: https://gist.github.com/lukaszgrolik/5849599 +WEIGHTS = {str(i): i for i in range(100, 1001, 100)} +WEIGHTS.update({ + 'thin': 100, + 'hairline': 100, + 'extra-light': 200, + 'ultra-light': 200, + 'ultralight': 200, + 'light': 300, + 'normal': 400, + 'regular': 400, + 'book': 400, + 'medium': 500, + 'roman': 500, + 'semi-bold': 600, + 'demi-bold': 600, + 'demi': 600, + 'bold': 700, + 'extra-bold': 800, + 'ultra-bold': 800, + 'extra bold': 800, + 'black': 900, + 'heavy': 900, + 'extra-heavy': 1000, +}) + +#: Font stretch synonyms. +#: These are alternate convenience names for font stretch/width values. +#: The intent is to allow a developer to use a common name (eg. "expanded") +#: instead of having to remember the corresponding number (eg. 125). +#: These come from: +#: - the OpenType specification: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass +#: - QFont stetch: https://doc.qt.io/qt-5/qfont.html#Stretch-enum +#: - CSS font-stretch: https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch +#: - values used by Enable: https://github.com/enthought/enable/blob/78d2e494097fac71cc5c73efef5fb464963fb4db/kiva/fonttools/_constants.py#L78-L88 +STRETCHES = { + 'ultra-condensed': 50, + 'extra-condensed': 62.5, + 'condensed': 75, + 'semi-condensed': 87.5, + 'normal': 100, + 'semi-expanded': 112.5, + 'expanded': 125, + 'extra-expanded': 150, + 'ultra-expanded': 200, +} + +#: Font size synonyms. +#: These are alternate convenience names for font size values. +#: The intent is to allow a developer to use a common name (eg. "small") +#: instead of having to remember the corresponding number (eg. 10). +#: These come from CSS font-size: https://developer.mozilla.org/en-US/docs/Web/CSS/font-size +SIZES = { + 'xx-small': 7.0, + 'x-small': 9.0, + 'small': 10.0, + 'medium': 12.0, + 'large': 14.0, + 'x-large': 18.0, + 'xx-large': 20.0, +} + +STYLES = ('normal', 'italic', 'oblique') + +#: Font variants. Currently only small caps variants are exposed in Qt, and +#: nothing in Wx. In the future this could include things like swashes, +#: numeric variants, and so on, as exposed in the toolkit. +VARIANTS = ['small-caps'] + +#: Additional markings on or around the glyphs of the font that are not part +#: of the glyphs themselves. Currently Qt and Wx support underline and +#: strikethrough, and Qt supports overline. In the future overlines and other +#: decorations may be supported, as exposed in the toolkit. +DECORATIONS = ['underline', 'strikethrough', 'overline'] + +#: A trait for font families. +FontFamily = CList(Str, ['default']) + +#: A trait for font weights. +FontWeight = Map(WEIGHTS, default_value='normal') + +#: A trait for font styles. +FontStyle = Enum(STYLES) + +#: A trait for font variant properties. +FontVariants = CSet(Enum(VARIANTS)) + +#: A trait for font decorator properties. +FontDecorations = CSet(Enum(DECORATIONS)) + + +class FontStretch(BaseCFloat): + """ Trait type for font stretches. + + The is a CFloat trait which holds floating point values between 50 and 200, + inclusive. In addition to values which can be converted to floats, this + trait also accepts named synonyms for sizes which are converted to the + associated commonly accepted weights: + + - 'ultra-condensed': 50 + - 'extra-condensed': 62.5 + - 'condensed': 75 + - 'semi-condensed': 87.5 + - 'normal': 100 + - 'semi-expanded': 112.5 + - 'expanded': 125 + - 'extra-expanded': 150 + - 'ultra-expanded': 200 + """ + + #: The default value for the trait. + default_value = 100.0 + + def __init__(self, default_value=NoDefaultSpecified, **metadata): + if default_value != NoDefaultSpecified: + default_value = self.validate(None, None, default_value) + super().__init__(default_value, **metadata) + + def validate(self, object, name, value): + if isinstance(value, str) and value.endswith('%'): + value = value[:-1] + value = STRETCHES.get(value, value) + value = super().validate(object, name, value) + if not 50 <= value <= 200: + self.error(object, name, value) + return value + + def info(self): + info = ( + "a float from 50 to 200, " + "a value that can convert to a float from 50 to 200, " + ) + info += ', '.join(repr(key) for key in SIZES) + info += ( + " or a string with a float value from 50 to 200 followed by '%'" + ) + return info + + +class FontSize(BaseCFloat): + """ Trait type for font sizes. + + The is a CFloat trait which also allows values which are keys of the + size dictionary, and also ignores trailing 'pt' ot 'px' annotation in + string values. The value stored is a float. + """ + + #: The default value for the trait. + default_value = 12.0 + + def __init__(self, default_value=NoDefaultSpecified, **metadata): + if default_value != NoDefaultSpecified: + default_value = self.validate(None, None, default_value) + super().__init__(default_value, **metadata) + + def validate(self, object, name, value): + if ( + isinstance(value, str) + and (value.endswith('pt') or value.endswith('px')) + ): + value = value[:-2] + value = SIZES.get(value, value) + value = super().validate(object, name, value) + if value <= 0: + self.error(object, name, value) + return value + + def info(self): + info = ( + "a positive float, a value that can convert to a positive float, " + ) + info += ', '.join(repr(key) for key in SIZES) + info += ( + " or a string with a positive float value followed by 'pt' or 'px'" + ) + return info + + +class Font(HasStrictTraits): + """A toolkit-independent font specification. + + This class represents a *request* for a font with certain characteristics, + not a concrete font that can be used for drawing. Font objects returned + from the toolkit may or may not match what was requested, depending on the + capabilities of the toolkit, OS, and the fonts installed on a particular + computer. + """ + + #: The preferred font families. + family = FontFamily() + + #: The weight of the font. + weight = FontWeight() + + #: How much the font is expanded or compressed. + stretch = FontStretch() + + #: The style of the font. + style = FontStyle() + + #: The size of the font. + size = FontSize() + + #: The font variants. + variants = FontVariants() + + #: The font decorations. + decorations = FontDecorations() + + @classmethod + def from_toolkit(cls, toolkit_font): + """ Create a Font from a toolkit font object. + + Parameters + ---------- + toolkit_font : any + A toolkit font to be converted to a corresponding class instance, + within the limitations of the options supported by the class. + """ + from pyface.toolkit import toolkit_object + toolkit_font_to_properties = toolkit_object( + 'font:toolkit_font_to_properties') + + return cls(**toolkit_font_to_properties(toolkit_font)) + + def to_toolkit(self): + """ Create a toolkit font object from the Font instance. + + Returns + ------- + toolkit_font : any + A toolkit font which matches the property of the font as + closely as possible given the constraints of the toolkit. + """ + from pyface.toolkit import toolkit_object + font_to_toolkit_font = toolkit_object('font:font_to_toolkit_font') + + return font_to_toolkit_font(self) + + def __str__(self): + """ Produce a CSS2-style representation of the font. """ + terms = [] + if self.style != 'normal': + terms.append(self.style) + terms.extend( + variant for variant in VARIANTS + if variant in self.variants + ) + terms.extend( + decoration for decoration in DECORATIONS + if decoration in self.decorations + ) + if self.weight != 'normal': + terms.append(self.weight) + if self.stretch != 100: + terms.append("{:g}%".format(self.stretch)) + size = self.size + # if size is an integer we want "12pt" not "12.pt" + if int(size) == size: + size = int(size) + terms.append("{}pt".format(size)) + terms.append( + ', '.join( + repr(family) if ' ' in family else family + for family in self.family + ) + ) + return ' '.join(terms) + + def __repr__(self): + traits = self.trait_get(self.editable_traits()) + trait_args = ', '.join( + "{}={!r}".format(name, value) + for name, value in traits.items() + ) + return "{}({})".format(self.__class__.__name__, trait_args) diff -Nru python-pyface-6.1.2/pyface/grid/api.py python-pyface-7.4.0/pyface/grid/api.py --- python-pyface-6.1.2/pyface/grid/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") from pyface.ui.wx.grid.api import * diff -Nru python-pyface-6.1.2/pyface/grid/checkbox_image_renderer.py python-pyface-7.4.0/pyface/grid/checkbox_image_renderer.py --- python-pyface-6.1.2/pyface/grid/checkbox_image_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/checkbox_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.checkbox_image_renderer import * +from pyface.ui.wx.grid.checkbox_image_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/checkbox_renderer.py python-pyface-7.4.0/pyface/grid/checkbox_renderer.py --- python-pyface-6.1.2/pyface/grid/checkbox_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/checkbox_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.checkbox_renderer import * +from pyface.ui.wx.grid.checkbox_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/combobox_focus_handler.py python-pyface-7.4.0/pyface/grid/combobox_focus_handler.py --- python-pyface-6.1.2/pyface/grid/combobox_focus_handler.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/combobox_focus_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.combobox_focus_handler import * +from pyface.ui.wx.grid.combobox_focus_handler import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/composite_grid_model.py python-pyface-7.4.0/pyface/grid/composite_grid_model.py --- python-pyface-6.1.2/pyface/grid/composite_grid_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/composite_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.composite_grid_model import * +from pyface.ui.wx.grid.composite_grid_model import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/edit_image_renderer.py python-pyface-7.4.0/pyface/grid/edit_image_renderer.py --- python-pyface-6.1.2/pyface/grid/edit_image_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/edit_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.edit_image_renderer import * +from pyface.ui.wx.grid.edit_image_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/edit_renderer.py python-pyface-7.4.0/pyface/grid/edit_renderer.py --- python-pyface-6.1.2/pyface/grid/edit_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/edit_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.edit_renderer import * +from pyface.ui.wx.grid.edit_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/grid_cell_image_renderer.py python-pyface-7.4.0/pyface/grid/grid_cell_image_renderer.py --- python-pyface-6.1.2/pyface/grid/grid_cell_image_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/grid_cell_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.grid_cell_image_renderer import * +from pyface.ui.wx.grid.grid_cell_image_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/grid_cell_renderer.py python-pyface-7.4.0/pyface/grid/grid_cell_renderer.py --- python-pyface-6.1.2/pyface/grid/grid_cell_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/grid_cell_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.grid_cell_renderer import * +from pyface.ui.wx.grid.grid_cell_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/grid_model.py python-pyface-7.4.0/pyface/grid/grid_model.py --- python-pyface-6.1.2/pyface/grid/grid_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.grid_model import * +from pyface.ui.wx.grid.grid_model import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/grid.py python-pyface-7.4.0/pyface/grid/grid.py --- python-pyface-6.1.2/pyface/grid/grid.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/grid.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.grid import * +from pyface.ui.wx.grid.grid import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/inverted_grid_model.py python-pyface-7.4.0/pyface/grid/inverted_grid_model.py --- python-pyface-6.1.2/pyface/grid/inverted_grid_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/inverted_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.inverted_grid_model import * +from pyface.ui.wx.grid.inverted_grid_model import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/mapped_grid_cell_image_renderer.py python-pyface-7.4.0/pyface/grid/mapped_grid_cell_image_renderer.py --- python-pyface-6.1.2/pyface/grid/mapped_grid_cell_image_renderer.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/mapped_grid_cell_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.mapped_grid_cell_image_renderer import * +from pyface.ui.wx.grid.mapped_grid_cell_image_renderer import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/simple_grid_model.py python-pyface-7.4.0/pyface/grid/simple_grid_model.py --- python-pyface-6.1.2/pyface/grid/simple_grid_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/simple_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.simple_grid_model import * +from pyface.ui.wx.grid.simple_grid_model import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/trait_grid_cell_adapter.py python-pyface-7.4.0/pyface/grid/trait_grid_cell_adapter.py --- python-pyface-6.1.2/pyface/grid/trait_grid_cell_adapter.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/trait_grid_cell_adapter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.trait_grid_cell_adapter import * +from pyface.ui.wx.grid.trait_grid_cell_adapter import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/grid/trait_grid_model.py python-pyface-7.4.0/pyface/grid/trait_grid_model.py --- python-pyface-6.1.2/pyface/grid/trait_grid_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/grid/trait_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import logging logger = logging.getLogger(__name__) -logger.warning('DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.') +logger.warning("DEPRECATED: pyface.grid, use pyface.ui.wx.grid instead.") -from pyface.ui.wx.grid.trait_grid_model import * +from pyface.ui.wx.grid.trait_grid_model import * # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/gui_application.py python-pyface-7.4.0/pyface/gui_application.py --- python-pyface-6.1.2/pyface/gui_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/gui_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2014-2017 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ This module defines a :py:class:`GUIApplication` subclass of @@ -18,14 +19,19 @@ """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) + import logging from traits.api import ( - Bool, Callable, Instance, List, ReadOnly, Tuple, Undefined, Vetoable, - on_trait_change + Bool, + Callable, + Instance, + List, + ReadOnly, + Tuple, + Undefined, + Vetoable, + observe, ) from .application import Application @@ -44,6 +50,7 @@ ground with the base class. """ from pyface.application_window import ApplicationWindow + return ApplicationWindow(**kwargs) @@ -155,7 +162,7 @@ """ from pyface.gui import GUI - ok = super(GUIApplication, self).start() + ok = super().start() if ok: # create the GUI so that the splash screen comes up first thing if self.gui is Undefined: @@ -192,7 +199,7 @@ # started. A listener for this event is a good place to do things # where you want the event loop running. self.gui.invoke_later( - self._fire_application_event, 'application_initialized' + self._fire_application_event, "application_initialized" ) # start the GUI - script blocks here @@ -207,7 +214,7 @@ The fires closing events for each window, and returns False if any listener vetos. """ - if not super(GUIApplication, self)._can_exit(): + if not super()._can_exit(): return False for window in reversed(self.windows): @@ -237,6 +244,7 @@ ground with the base class. """ from pyface.application_window import ApplicationWindow + return lambda application, **kwargs: ApplicationWindow(**kwargs) def _splash_screen_default(self): @@ -250,25 +258,21 @@ def _about_dialog_default(self): """ Default AboutDialog """ - from sys import version_info - if (version_info.major, version_info.minor) >= (3, 2): - from html import escape - else: - from cgi import escape + from html import escape + from pyface.about_dialog import AboutDialog additions = [ - u"

{}

".format(escape(self.name)), - u"Copyright © 2018 {}, all rights reserved".format( - escape(self.company), + "

{}

".format(escape(self.name)), + "Copyright © 2022 {}, all rights reserved".format( + escape(self.company) ), - u"", + "", ] - additions += [escape(line) for line in self.description.split('\n\n')] + additions += [escape(line) for line in self.description.split("\n\n")] dialog = AboutDialog( - title=u"About {}".format(self.name), - additions=additions, + title="About {}".format(self.name), additions=additions ) if self.logo: dialog.image = self.logo @@ -276,22 +280,24 @@ # Trait listeners -------------------------------------------------------- - @on_trait_change('windows:activated') - def _on_activate_window(self, window, trait, old, new): + @observe("windows:items:activated") + def _on_activate_window(self, event): """ Listener that tracks currently active window. """ + window = event.object if window in self.windows: self.active_window = window - @on_trait_change('windows:deactivated') - def _on_deactivate_window(self, window, trait, old, new): + @observe("windows:items:deactivated") + def _on_deactivate_window(self, event): """ Listener that tracks currently active window. """ self.active_window = None - @on_trait_change('windows:closed') - def _on_window_closed(self, window, trait, old, new): + @observe("windows:items:closed") + def _on_window_closed(self, event): """ Listener that ensures window handles are released when closed. """ + window = event.object if window in self.windows: self.windows.remove(window) diff -Nru python-pyface-6.1.2/pyface/gui.py python-pyface-7.4.0/pyface/gui.py --- python-pyface-6.1.2/pyface/gui.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/gui.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a pyface GUI. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -GUI = toolkit_object('gui:GUI') -#### EOF ###################################################################### +GUI = toolkit_object("gui:GUI") diff -Nru python-pyface-6.1.2/pyface/heading_text.py python-pyface-7.4.0/pyface/heading_text.py --- python-pyface-6.1.2/pyface/heading_text.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/heading_text.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Heading text. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -HeadingText = toolkit_object('heading_text:HeadingText') -#### EOF ###################################################################### +HeadingText = toolkit_object("heading_text:HeadingText") diff -Nru python-pyface-6.1.2/pyface/i_about_dialog.py python-pyface-7.4.0/pyface/i_about_dialog.py --- python-pyface-6.1.2/pyface/i_about_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_about_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,39 +1,39 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a simple 'About' dialog. """ -# Enthought library imports. -from traits.api import Instance, List, Unicode -# Local imports. +from traits.api import HasTraits, List, Str + + from pyface.i_dialog import IDialog -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image class IAboutDialog(IDialog): """ The interface for a simple 'About' dialog. """ - #### 'IAboutDialog' interface ############################################# + # 'IAboutDialog' interface --------------------------------------------- #: Additional strings to be added to the dialog. - additions = List(Unicode) + additions = List(Str) + + #: Additional copyright strings to be added above the standard ones. + copyrights = List(Str) #: The image displayed in the dialog. - image = Instance(ImageResource, ImageResource('about')) + image = Image() -class MAboutDialog(object): +class MAboutDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IAboutDialog interface. """ diff -Nru python-pyface-6.1.2/pyface/i_application_window.py python-pyface-7.4.0/pyface/i_application_window.py --- python-pyface-6.1.2/pyface/i_application_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface of a top-level application window. """ -# Enthought library imports. -from traits.api import Instance, List -# Local imports. +from traits.api import HasTraits, Instance, List + + from pyface.action.api import MenuBarManager, StatusBarManager, ToolBarManager -from pyface.i_image_resource import IImageResource from pyface.i_window import IWindow +from pyface.ui_traits import Image class IApplicationWindow(IWindow): @@ -35,10 +32,10 @@ :py:meth:`._create_contents` method. """ - #### 'IApplicationWindow' interface ####################################### + # 'IApplicationWindow' interface --------------------------------------- #: The window icon. The default is toolkit specific. - icon = Instance(IImageResource) + icon = Image() #: The menu bar manager (None iff there is no menu bar). menu_bar_manager = Instance(MenuBarManager) @@ -53,9 +50,9 @@ #: list instead of the single ToolBarManager instance above. tool_bar_managers = List(ToolBarManager) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create and return the window's contents. @@ -112,16 +109,16 @@ """ Sets the window icon (if required). """ -class MApplicationWindow(object): +class MApplicationWindow(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the :py:class:`IApplicationWindow` interface. Implements: destroy(), _create_trim_widgets() """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Destroy the control if it exists. """ @@ -134,11 +131,11 @@ for tool_bar_manager in self.tool_bar_managers: tool_bar_manager.destroy() - super(MApplicationWindow, self).destroy() + super().destroy() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_trim_widgets(self, parent): """ Creates the 'trim' widgets (the widgets around the window). diff -Nru python-pyface-6.1.2/pyface/i_clipboard.py python-pyface-7.4.0/pyface/i_clipboard.py --- python-pyface-6.1.2/pyface/i_clipboard.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_clipboard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2009, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Evan Patterson # Date: 06/26/09 -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ """ The interface for manipulating the toolkit clipboard. """ -try: - from collections.abc import Sequence -except ImportError: # Python 3.8 deprecation - from collections import Sequence -# ETS imports +from collections.abc import Sequence + from traits.api import HasStrictTraits, Interface, Property -import six class IClipboard(Interface): @@ -104,18 +100,18 @@ return None def _set_data(self, data): - if isinstance(data, six.string_types): + if isinstance(data, str): self.text_data = data elif isinstance(data, Sequence): self.file_data = data else: self.object_data = data - def _get_data_type ( self ): + def _get_data_type(self): if self.has_text_data: - return 'str' + return "str" if self.has_file_data: - return 'file' + return "file" if self.has_object_data: return self.object_type - return '' + return "" diff -Nru python-pyface-6.1.2/pyface/i_color_dialog.py python-pyface-7.4.0/pyface/i_color_dialog.py --- python-pyface-6.1.2/pyface/i_color_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_color_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,29 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The interface for a dialog that allows the user to select a color. """ + +from traits.api import Bool + +from pyface.ui_traits import PyfaceColor +from pyface.i_dialog import IDialog + + +class IColorDialog(IDialog): + """ The interface for a dialog that allows the user to choose a color. + """ + + # 'IColorDialog' interface ---------------------------------------------# + + #: The color in the dialog. + color = PyfaceColor() + + #: Whether or not to allow the user to chose an alpha value. + show_alpha = Bool(False) diff -Nru python-pyface-6.1.2/pyface/i_confirmation_dialog.py python-pyface-7.4.0/pyface/i_confirmation_dialog.py --- python-pyface-6.1.2/pyface/i_confirmation_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_confirmation_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a dialog that prompts the user for confirmation. """ -# Enthought library imports. -from traits.api import Bool, Enum, Instance, Unicode +from traits.api import Bool, Enum, HasTraits, Str + -# Local imports. from pyface.constant import CANCEL, NO, YES from pyface.i_dialog import IDialog -from pyface.i_image_resource import IImageResource +from pyface.ui_traits import Image class IConfirmationDialog(IDialog): """ The interface for a dialog that prompts the user for confirmation. """ - #### 'IConfirmationDialog' interface ###################################### + # 'IConfirmationDialog' interface -------------------------------------# #: Should the cancel button be displayed? cancel = Bool(False) @@ -35,26 +31,26 @@ default = Enum(NO, YES, CANCEL) #: The image displayed with the message. The default is toolkit specific. - image = Instance(IImageResource) + image = Image() #: The message displayed in the body of the dialog (use the inherited #: 'title' trait to set the title of the dialog itself). - message = Unicode + message = Str() #: Some informative text to display below the main message - informative = Unicode + informative = Str() #: Some additional details that can be exposed by the user - detail = Unicode + detail = Str() #: The label for the 'no' button. The default is toolkit specific. - no_label = Unicode + no_label = Str() #: The label for the 'yes' button. The default is toolkit specific. - yes_label = Unicode + yes_label = Str() -class MConfirmationDialog(object): +class MConfirmationDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IConfirmationDialog interface. """ diff -Nru python-pyface-6.1.2/pyface/i_dialog.py python-pyface-7.4.0/pyface/i_dialog.py --- python-pyface-6.1.2/pyface/i_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The abstract interface for all pyface dialogs. """ -# Enthought library imports. -from traits.api import Bool, Enum, Int, Str, Unicode +from traits.api import Bool, Enum, HasTraits, Int, Str + -# Local imports. from pyface.constant import OK from pyface.i_window import IWindow @@ -32,20 +28,20 @@ 2) '_create_buttons' creates the dialog buttons. """ - #### 'IDialog' interface ################################################## + # 'IDialog' interface -------------------------------------------------# #: The label for the 'cancel' button. The default is toolkit specific. - cancel_label = Unicode + cancel_label = Str() #: The context sensitive help Id (the 'Help' button is only shown iff this #: is set). - help_id = Str + help_id = Str() #: The label for the 'help' button. The default is toolkit specific. - help_label = Unicode + help_label = Str() #: The label for the 'ok' button. The default is toolkit specific. - ok_label = Unicode + ok_label = Str() #: Is the dialog resizeable? resizeable = Bool(True) @@ -57,11 +53,11 @@ #: The dialog style (is it modal or not). # FIXME v3: It doesn't seem possible to use non-modal dialogs. (How do you # get access to the buttons?) - style = Enum('modal', 'nonmodal') + style = Enum("modal", "nonmodal") - ########################################################################### + # ------------------------------------------------------------------------ # 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Opens the dialog. @@ -78,9 +74,9 @@ The value of the ``return_code`` trait. """ - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_buttons(self, parent): """ Create and return the buttons. @@ -138,7 +134,7 @@ """ -class MDialog(object): +class MDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IDialog interface. @@ -146,9 +142,9 @@ Reimplements: _add_event_listeners(), _create() """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Opens the dialog. @@ -167,7 +163,7 @@ if self.control is None: self._create() - if self.style == 'modal': + if self.style == "modal": self.return_code = self._show_modal() self.close() @@ -177,23 +173,13 @@ return self.return_code - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create(self): """ Creates the window's widget hierarchy. """ - super(MDialog, self)._create() + super()._create() self._create_contents(self.control) - - ########################################################################### - # Protected 'IWindow' interface. - ########################################################################### - - def _add_event_listeners(self): - """ Adds any event listeners required by the window. """ - - # We don't bother for dialogs. - pass diff -Nru python-pyface-6.1.2/pyface/i_directory_dialog.py python-pyface-7.4.0/pyface/i_directory_dialog.py --- python-pyface-6.1.2/pyface/i_directory_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_directory_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a dialog that allows the user to browse for a directory. """ -# Enthought library imports. -from traits.api import Bool, Unicode +from traits.api import Bool, HasTraits, Str + -# Local imports. from pyface.i_dialog import IDialog @@ -27,26 +23,26 @@ directory. """ - #### 'IDirectoryDialog' interface ######################################### + # 'IDirectoryDialog' interface ----------------------------------------- #: The default path. The default (ie. the default default path) is toolkit #: specific. # FIXME v3: The default should be the current directory. (It seems wx is # the problem, although the file dialog does the right thing.) - default_path = Unicode + default_path = Str() #: The message to display in the dialog. The default is toolkit specific. - message = Unicode + message = Str() #: True iff the dialog should include a button that allows the user to #: create a new directory. new_directory = Bool(True) #: The path of the chosen directory. - path = Unicode + path = Str() -class MDirectoryDialog(object): +class MDirectoryDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IDirectoryDialog interface. """ diff -Nru python-pyface-6.1.2/pyface/i_drop_handler.py python-pyface-7.4.0/pyface/i_drop_handler.py --- python-pyface-6.1.2/pyface/i_drop_handler.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_drop_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from traits.api import Interface diff -Nru python-pyface-6.1.2/pyface/i_file_dialog.py python-pyface-7.4.0/pyface/i_file_dialog.py --- python-pyface-6.1.2/pyface/i_file_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_file_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,67 +1,68 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a dialog that allows the user to open/save files etc. """ -# Standard library imports. import sys -# Enthought library imports. -from traits.api import Enum, Unicode, Int -# Local imports. +from traits.api import Enum, HasTraits, Int, List, Str + + from pyface.i_dialog import IDialog -import six class IFileDialog(IDialog): """ The interface for a dialog that allows the user to open/save files etc. """ - #### 'IFileDialog' interface ############################################## + # 'IFileDialog' interface ---------------------------------------------# - #: The 'action' that the user is peforming on the directory. - action = Enum('open', 'save as') + #: The 'action' that the user is peforming on the directory ("open files" + #: differs from "open" in that the former supports multiple selections). + action = Enum("open", "open files", "save as") #: The default directory. - default_directory = Unicode + default_directory = Str() #: The default filename. - default_filename = Unicode + default_filename = Str() #: The default path (directory and filename) of the chosen file. This is #: only used when the *default_directory* and *default_filename* are not set #: and is equivalent to setting both. - default_path = Unicode + default_path = Str() #: The directory containing the chosen file. - directory = Unicode + directory = Str() - #: The name of the chosen file. - filename = Unicode + #: The name (basename only) of the chosen file. + filename = Str() - #: The path (directory and filename) of the chosen file. - path = Unicode + #: The path (directory and filename) of the chosen file. To be used when only + #: single selection is allowed: if *action* is "open files", use *paths* instead. + path = Str() + + #: The paths (directory and filename) of the chosen files. To be used when + #: multiple selection is allowed. + paths = List(Str()) #: The wildcard used to restrict the set of files. - wildcard = Unicode + wildcard = Str() #: The index of the selected wildcard. wildcard_index = Int(0) -class MFileDialog(object): +class MFileDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IFileDialog interface. @@ -83,7 +84,7 @@ WILDCARD_TXT = "Text files (*.txt)|*.txt|" #: A file dialog wildcard for all files. - if sys.platform == 'win32': + if sys.platform == "win32": WILDCARD_ALL = "All files (*.*)|*.*|" else: WILDCARD_ALL = "All files (*)|*" @@ -91,9 +92,9 @@ #: A file dialog wildcard for Zip archives. WILDCARD_ZIP = "Zip files (*.zip)|*.zip|" - ########################################################################### + # ------------------------------------------------------------------------ # 'MFileDialog' *CLASS* interface. - ########################################################################### + # ------------------------------------------------------------------------ @classmethod def create_wildcard(cls, description, extension): @@ -107,10 +108,10 @@ The wildcard patterns for the extension. """ - if isinstance(extension, six.string_types): + if isinstance(extension, str): pattern = extension else: - pattern = ';'.join(extension) + pattern = ";".join(extension) return "%s (%s)|%s|" % (description, pattern, pattern) diff -Nru python-pyface-6.1.2/pyface/i_font_dialog.py python-pyface-7.4.0/pyface/i_font_dialog.py --- python-pyface-6.1.2/pyface/i_font_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_font_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,24 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The interface for a dialog that allows the user to select a color. """ + +from pyface.ui_traits import PyfaceFont +from pyface.i_dialog import IDialog + + +class IFontDialog(IDialog): + """ The interface for a dialog that allows the user to choose a font. + """ + + # 'IFontDialog' interface ---------------------------------------------# + + #: The font in the dialog. + font = PyfaceFont() diff -Nru python-pyface-6.1.2/pyface/i_gui.py python-pyface-7.4.0/pyface/i_gui.py --- python-pyface-6.1.2/pyface/i_gui.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_gui.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface of a pyface GUI. """ -# Standard library imports. import logging import os -# Enthought library imports. + from traits.etsconfig.api import ETSConfig -from traits.api import Bool, Interface, Unicode +from traits.api import Bool, HasTraits, Interface, Str # Logging. @@ -30,7 +26,7 @@ class IGUI(Interface): """ The interface of a pyface GUI. """ - #### 'GUI' interface ###################################################### + # 'GUI' interface -----------------------------------------------------# #: Is the GUI busy (i.e. should the busy cursor, often an hourglass, be #: displayed)? @@ -42,11 +38,11 @@ #: A directory on the local file system that we can read and write to at #: will. This is used to persist layout information etc. Note that #: individual toolkits will have their own directory. - state_location = Unicode + state_location = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, splash_screen=None): """ Initialise a new GUI. @@ -58,9 +54,9 @@ loop is started. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'GUI' class interface. - ########################################################################### + # ------------------------------------------------------------------------ @staticmethod def allow_interrupt(): @@ -152,9 +148,9 @@ Passing ``False`` will reset the cursor to the default. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'GUI' interface. - ########################################################################### + # ------------------------------------------------------------------------ def start_event_loop(self): """ Start the GUI event loop. """ @@ -163,7 +159,7 @@ """ Stop the GUI event loop. """ -class MGUI(object): +class MGUI(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IGUI interface. @@ -182,16 +178,19 @@ run interactively. """ import signal + signal.signal(signal.SIGINT, signal.SIG_DFL) def _default_state_location(self): """ Return the default state location. """ - state_location = os.path.join(ETSConfig.application_home, 'pyface', ETSConfig.toolkit) + state_location = os.path.join( + ETSConfig.application_home, "pyface", ETSConfig.toolkit + ) if not os.path.exists(state_location): os.makedirs(state_location) - logger.debug('GUI state location is <%s>', state_location) + logger.debug("GUI state location is <%s>", state_location) return state_location diff -Nru python-pyface-6.1.2/pyface/i_heading_text.py python-pyface-7.4.0/pyface/i_heading_text.py --- python-pyface-6.1.2/pyface/i_heading_text.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_heading_text.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,45 +1,119 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Heading text. """ +import warnings -# Enthought library imports. -from traits.api import Instance, Int, Interface, Unicode +from traits.api import HasTraits, Int, Interface, Str -# Local imports. -from pyface.i_image_resource import IImageResource + +from pyface.ui_traits import Image class IHeadingText(Interface): - """ Heading text. """ + """ A widget which shows heading text in a panel. """ - #### 'IHeadingText' interface ############################################# + # 'IHeadingText' interface --------------------------------------------- - #: Heading level. - # - # fixme: Currently we ignore anything but one, but in future we could - # have different visualizations based on the level. + #: Heading level. This is currently unused. level = Int(1) #: The heading text. - text = Unicode('Default') + text = Str("Default") #: The background image. - image = Instance(IImageResource) + image = Image() -class MHeadingText(object): +class MHeadingText(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IHeadingText interface. """ + + # 'IHeadingText' interface --------------------------------------------- + + #: Heading level. This is currently unused. + level = Int(1) + + #: The heading text. + text = Str("Default") + + def __init__(self, parent=None, **traits): + """ Creates the heading text. """ + + if "image" in traits: + warnings.warn( + "background images are no-longer supported for Wx and the " + "'image' trait will be removed in a future Pyface update", + PendingDeprecationWarning, + ) + + create = traits.pop("create", True) + + # Base class constructor. + super().__init__(parent=parent, **traits) + + if create: + # Create the widget's toolkit-specific control. + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + # ------------------------------------------------------------------------ + # 'IWidget' interface. + # ------------------------------------------------------------------------ + + def _initialize_control(self): + """ Perform any toolkit-specific initialization for the control. """ + super()._initialize_control() + self._set_control_text(self.text) + + def _add_event_listeners(self): + super()._add_event_listeners() + self.observe(self._text_updated, 'text', dispatch="ui") + + def _remove_event_listeners(self): + self.observe(self._text_updated, 'text', dispatch="ui", remove=True) + super()._remove_event_listeners() + + # ------------------------------------------------------------------------ + # Private interface. + # ------------------------------------------------------------------------ + + def _set_control_text(self, text): + """ Set the text on the control. + + Parameters + ---------- + text : str + The text to display. This can contain basic HTML-like markeup. + """ + raise NotImplementedError() + + def _get_control_text(self): + """ Get the text on the control. + + Returns + ---------- + text : str + The text to displayed in the widget. + """ + raise NotImplementedError() + + # Trait change handlers -------------------------------------------------- + + def _text_updated(self, event): + if self.control is not None: + self._set_control_text(self.text) diff -Nru python-pyface-6.1.2/pyface/i_image_cache.py python-pyface-7.4.0/pyface/i_image_cache.py --- python-pyface-6.1.2/pyface/i_image_cache.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_image_cache.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for an image cache. """ -# Enthought library imports. -from traits.api import Interface +from traits.api import HasTraits, Interface class IImageCache(Interface): """ The interface for an image cache. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, width, height): """ Creates a new image cache for images of the given size. @@ -36,9 +32,9 @@ The height of the images in pixels """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageCache' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, filename): """ Returns the scaled image specified. @@ -74,7 +70,7 @@ """ -class MImageCache(object): +class MImageCache(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IImageCache interface. """ diff -Nru python-pyface-6.1.2/pyface/i_image.py python-pyface-7.4.0/pyface/i_image.py --- python-pyface-6.1.2/pyface/i_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,85 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" The base interface for an image. """ +from traits.api import Interface + + +class IImage(Interface): + """ The base interface for an image. + + This provides the interface specification that different types of image + classes need to provide to be used by Pyface. + """ + + def create_image(self, size=None): + """ Creates a toolkit-specific image for this image. + + An image is a toolkit datastructure that is optimized for I/O, + pixel-level access and modification, such as a wx.Image or a + QImage. + + Parameters + ---------- + size : (int, int) or None + The desired size as a width, height tuple, or None if wanting + default image size. This is a *preferred* size and concrete + implementations may or may not return an image of the precise + size requested (indeed, this may be ignored). + + Returns + ------- + image : toolkit image + The toolkit image corresponding to the image and the specified + size. + """ + + def create_bitmap(self, size=None): + """ Creates a toolkit-specific bitmap image for this image. + + A bitmap is a toolkit datastructure that is optimized for rendering + to the screen, such as a wx.Bitmap or a QPixmap. + + Parameters + ---------- + size : (int, int) or None + The desired size as a width, height tuple, or None if wanting + default image size. This is a *preferred* size and concrete + implementations may or may not return an image of the precise + size requested (indeed, this may be ignored). + + Returns + ------- + image : toolkit bitmap + The toolkit bitmap corresponding to the image and the specified + size. + """ + + def create_icon(self, size=None): + """ Creates a toolkit-specific icon for this image. + + An icon is a toolkit datastructure that holds several different + variants of an image (eg. selected, disabled, etc.), such as a wx.Icon + or a QIcon. Most toolkits can automatically create these from other + image classes. + + Parameters + ---------- + size : (int, int) or None + The desired size as a width, height tuple, or None if wanting + default icon size. This is a *preferred* size and concrete + implementations may or may not return an icon of the precise + size requested (indeed, this may be ignored). + + Returns + ------- + image : toolkit icon + The toolkit image corresponding to the image and the specified + size as an icon. + """ diff -Nru python-pyface-6.1.2/pyface/i_image_resource.py python-pyface-7.4.0/pyface/i_image_resource.py --- python-pyface-6.1.2/pyface/i_image_resource.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_image_resource.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,106 +1,41 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2009 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -#------------------------------------------------------------------------------ """ The interface for an image resource. """ -try: - from collections.abc import Sequence -except ImportError: # Python 3.8 deprecation - from collections import Sequence +from collections.abc import Sequence + +from traits.api import HasTraits, List, Str +from pyface.i_image import IImage from pyface.resource_manager import resource_manager from pyface.resource.resource_path import resource_module, resource_path -from traits.api import Interface, List, Unicode -import six -class IImageResource(Interface): +class IImageResource(IImage): """ The interface for an image resource. An image resource describes the location of an image and provides a way to create a toolkit-specific image on demand. """ - #### 'ImageResource' interface ############################################ + # 'ImageResource' interface -------------------------------------------- #: The absolute path to the image. - absolute_path = Unicode + absolute_path = Str() #: The name of the image. - name = Unicode + name = Str() #: A list of directories, classes or instances that will be used to search #: for the image (see the resource manager for more details). - search_path = List - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, name, search_path=None): - """ Creates a new image resource. """ - - ########################################################################### - # 'ImageResource' interface. - ########################################################################### - - def create_image(self, size=None): - """ Creates a toolkit specific image for this resource. - - Parameters - ---------- - size : (int, int) or None - The desired size as a width, height tuple, or None if wanting - default image size. - - Returns - ------- - image : toolkit image - The toolkit image corresponding to the resource and the specified - size. - """ - - # FIXME v3: The need to distinguish between bitmaps and images is toolkit - # specific so, strictly speaking, the conversion to a bitmap should be done - # wherever the toolkit actually needs it. - def create_bitmap(self, size=None): - """ Creates a toolkit specific bitmap for this resource. - - Parameters - ---------- - size : (int, int) or None - The desired size as a width, height tuple, or None if wanting - default image size. - - Returns - ------- - image : toolkit image - The toolkit image corresponding to the resource and the specified - size as a bitmap. - """ - - def create_icon(self, size=None): - """ Creates a toolkit-specific icon for this resource. - - Parameters - ---------- - size : (int, int) or None - The desired size as a width, height tuple, or None if wanting - default image size. - - Returns - ------- - image : toolkit image - The toolkit image corresponding to the resource and the specified - size as an icon. - """ + search_path = List() @classmethod def image_size(cls, image): @@ -118,27 +53,26 @@ """ - -class MImageResource(object): +class MImageResource(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IImageResource interface. Implements: __init__(), create_image() """ - #### Private interface #################################################### + # Private interface ---------------------------------------------------- #: The image-not-found image. Note that it is not a trait. _image_not_found = None - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, name, search_path=None): self.name = name - if isinstance(search_path, six.string_types): + if isinstance(search_path, str): _path = [search_path] elif isinstance(search_path, Sequence): _path = search_path @@ -148,12 +82,12 @@ _path = [resource_path()] self.search_path = _path + [resource_module()] - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageResource' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_image(self, size=None): - """ Creates a toolkit specific image for this resource. + """ Creates a toolkit-specific image for this resource. Parameters ---------- @@ -176,9 +110,9 @@ return image - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_ref(self, size=None): """ Return the resource manager reference to the image. @@ -196,8 +130,9 @@ """ if self._ref is None: - self._ref = resource_manager.locate_image(self.name, - self.search_path, size) + self._ref = resource_manager.locate_image( + self.name, self.search_path, size + ) return self._ref @@ -233,6 +168,6 @@ if cls._image_not_found is None: from pyface.image_resource import ImageResource - cls._image_not_found = ImageResource('image_not_found') + cls._image_not_found = ImageResource("image_not_found") return cls._image_not_found diff -Nru python-pyface-6.1.2/pyface/i_layered_panel.py python-pyface-7.4.0/pyface/i_layered_panel.py --- python-pyface-6.1.2/pyface/i_layered_panel.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_layered_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,107 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Interace and mixins for layered panels. + +A layered panel contains one or more named layers, with only one layer +visible at any one time (think of a 'tab' control minus the tabs!). +""" + +import warnings + +from traits.api import Any, Dict, HasTraits, Interface, Str + + +class ILayeredPanel(Interface): + """ A Layered panel. + + A layered panel contains one or more named layers, with only one layer + visible at any one time (think of a 'tab' control minus the tabs!). Each + layer is a toolkit-specific control. + + """ + + # "ILayeredPanel' interface -------------------------------------------- + + # The toolkit-specific control of the currently displayed layer. + current_layer = Any() + + # The name of the currently displayed layer. + current_layer_name = Str() + + # ------------------------------------------------------------------------ + # 'ILayeredPanel' interface. + # ------------------------------------------------------------------------ + + def add_layer(self, name, layer): + """ Adds a layer with the specified name. + + All layers are hidden when they are added. Use 'show_layer' to make a + layer visible. + + """ + + def show_layer(self, name): + """ Shows the layer with the specified name. """ + + def has_layer(self, name): + """ Does the panel contain a layer with the specified name? """ + + +class MLayeredPanel(HasTraits): + """ A Layered panel mixin. + + A layered panel contains one or more named layers, with only one layer + visible at any one time (think of a 'tab' control minus the tabs!). Each + layer is a toolkit-specific control. + """ + + # "ILayeredPanel' interface -------------------------------------------- + + # The toolkit-specific control of the currently displayed layer. + current_layer = Any() + + # The name of the currently displayed layer. + current_layer_name = Str() + + # Private traits ------------------------------------------------------- + + # The a map of layer names to toolkit controls in the panel. + _layers = Dict(Str) + + # ------------------------------------------------------------------------ + # 'ILayeredPanel' interface. + # ------------------------------------------------------------------------ + + def has_layer(self, name): + """ Does the panel contain a layer with the specified name? """ + return name in self._layers + + # ------------------------------------------------------------------------ + # 'object' interface. + # ------------------------------------------------------------------------ + + def __init__(self, parent=None, **traits): + """ Creates a new LayeredPanel. """ + + create = traits.pop("create", True) + + # Base class constructor. + super().__init__(parent=parent, **traits) + + if create: + # Create the toolkit-specific control that represents the widget. + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) diff -Nru python-pyface-6.1.2/pyface/i_layout_item.py python-pyface-7.4.0/pyface/i_layout_item.py --- python-pyface-6.1.2/pyface/i_layout_item.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_layout_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,36 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Enum, Int, Interface, Tuple + +#: Value that indicates the default size values should be used. +DEFAULT_SIZE = -1 + +#: Trait for sizes of widgets. +Size = Tuple(Int(DEFAULT_SIZE), Int(DEFAULT_SIZE)) + +#: Trait for size policy values. +SizePolicy = Enum("default", "fixed", "preferred", "expand") + + +class ILayoutItem(Interface): + """ An item that can participate in layout. """ + + #: The minimum size that the item can take. + minimum_size = Size + + #: The maximum size that the item can take. + maximum_size = Size + + #: Weight factor used to distribute extra space between widgets. + stretch = Tuple(Int, Int) + + #: How the item should behave when more space is available. + size_policy = Tuple(SizePolicy, SizePolicy) diff -Nru python-pyface-6.1.2/pyface/i_layout_widget.py python-pyface-7.4.0/pyface/i_layout_widget.py --- python-pyface-6.1.2/pyface/i_layout_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_layout_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,183 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import HasTraits, Int, Tuple + +from pyface.i_layout_item import ILayoutItem, Size, SizePolicy +from pyface.i_widget import IWidget + + +class ILayoutWidget(IWidget, ILayoutItem): + """ Interface for widgets that can participate in layout. + + Most widgets implement ILayoutWidget, but widgets like top-level windows, + menus, toolbars, etc. do not. + """ + pass + + +class MLayoutWidget(HasTraits): + """ A mixin for Widgets that can participate in layouts. + + Most widgets implement ILayoutWidget, but widgets like top-level windows, + menus, toolbars, etc. do not. + """ + + #: The minimum size that the widget can take. + minimum_size = Size + + #: The maximum size that the widget can take. + maximum_size = Size + + #: Weight factor used to distribute extra space between widgets. + stretch = Tuple(Int, Int) + + #: How the widget should behave when more space is available. + size_policy = Tuple(SizePolicy, SizePolicy) + + def _initialize_control(self): + """ Initialize the toolkit control. """ + super()._initialize_control() + self._set_control_minimum_size(self.minimum_size) + self._set_control_maximum_size(self.maximum_size) + self._set_control_stretch(self.stretch) + self._set_control_size_policy(self.size_policy) + + def _add_event_listeners(self): + """ Add trait observers and toolkit binding. """ + super()._add_event_listeners() + self.observe( + self._minimum_size_updated, + "minimum_size", + dispatch="ui", + ) + self.observe( + self._maximum_size_updated, + "maximum_size", + dispatch="ui", + ) + self.observe( + self._stretch_updated, + "stretch", + dispatch="ui", + ) + self.observe( + self._size_policy_updated, + "size_policy", + dispatch="ui", + ) + + def _remove_event_listeners(self): + """ Remove trait observers and toolkit binding. """ + self.observe( + self._minimum_size_updated, + "minimum_size", + dispatch="ui", + remove=True, + ) + self.observe( + self._maximum_size_updated, + "maximum_size", + dispatch="ui", + remove=True, + ) + self.observe( + self._stretch_updated, + "stretch", + dispatch="ui", + remove=True, + ) + self.observe( + self._size_policy_updated, + "size_policy", + dispatch="ui", + remove=True, + ) + super()._remove_event_listeners() + + def _minimum_size_updated(self, event): + """ Trait observer for minimum size. """ + if self.control is not None: + self._set_control_minimum_size(event.new) + + def _maximum_size_updated(self, event): + """ Trait observer for maximum size. """ + if self.control is not None: + self._set_control_maximum_size(event.new) + + def _stretch_updated(self, event): + """ Trait observer for stretch. """ + if self.control is not None: + self._set_control_stretch(event.new) + + def _size_policy_updated(self, event): + """ Trait observer for size policy. """ + if self.control is not None: + self._set_control_size_policy(event.new) + + def _set_control_minimum_size(self, size): + """ Set the minimum size of the control. + + Toolkit implementations will need to override this method. + """ + raise NotImplementedError() + + def _get_control_minimum_size(self): + """ Get the minimum size of the control. + + Toolkit implementations will need to override this method. + This method is only used for testing. + """ + raise NotImplementedError() + + def _set_control_maximum_size(self, size): + """ Set the maximum size of the control. + + Toolkit implementations will need to override this method. + """ + raise NotImplementedError() + + def _get_control_maximum_size(self): + """ Get the maximum size of the control. + + Toolkit implementations will need to override this method. + This method is only used for testing. + """ + raise NotImplementedError() + + def _set_control_stretch(self, stretch): + """ Set the stretch factor of the control. + + Toolkit implementations will need to override this method. + """ + raise NotImplementedError() + + def _get_control_stretch(self): + """ Get the stretch factor of the control. + + Toolkit implementations will need to override this method. + This method is only used for testing. + """ + raise NotImplementedError() + + def _set_control_size_policy(self, size_policy): + """ Set the size policy of the control. + + Toolkit implementations will need to override this method. + """ + raise NotImplementedError() + + def _get_control_size_policy(self): + """ Get the size policy of the control. + + Toolkit implementations will need to override this method. + This method is only used for testing. + """ + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/image/image.py python-pyface-7.4.0/pyface/image/image.py --- python-pyface-6.1.2/pyface/image/image.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image/image.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,61 +1,91 @@ -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! -# -# Author: David C. Morrill +# Thanks for using Enthought open source! # Date: 11/03/2007 """ Defines the ImageLibrary object used to manage Pyface image libraries. """ import sys -from os import (environ, listdir, remove, stat, makedirs, rename, access, - R_OK, W_OK, X_OK) -from os.path import (join, isdir, isfile, splitext, abspath, dirname, - basename, exists) +from os import ( + environ, + listdir, + remove, + stat, + makedirs, + rename, + access, + R_OK, + W_OK, + X_OK, +) +from os.path import ( + join, + isdir, + isfile, + splitext, + abspath, + dirname, + basename, + exists, +) from stat import ST_MTIME from platform import system from zipfile import is_zipfile, ZipFile, ZIP_DEFLATED import datetime import time -from six.moves._thread import allocate_lock +from _thread import allocate_lock from threading import Thread -from traits.api import (HasPrivateTraits, Property, Str, Int, List, Dict, - File, Instance, Bool, Undefined, TraitError, Float, - Any, cached_property) +from traits.api import ( + HasPrivateTraits, + Property, + Str, + Int, + List, + Dict, + File, + Instance, + Bool, + Undefined, + TraitError, + Float, + Any, + cached_property, +) from traits.trait_base import get_resource_path, traits_home from pyface.api import ImageResource from pyface.resource_manager import resource_manager -from pyface.resource.resource_reference import (ImageReference, - ResourceReference) +from pyface.resource.resource_reference import ( + ImageReference, + ResourceReference, +) from pyface.ui_traits import HasMargin, HasBorder, Alignment -#--------------------------------------------------------------------------- +# --------------------------------------------------------------------------- # Constants: -#--------------------------------------------------------------------------- +# --------------------------------------------------------------------------- # Standard image file extensions: -ImageFileExts = ( '.png', '.gif', '.jpg', 'jpeg' ) +ImageFileExts = (".png", ".gif", ".jpg", "jpeg") # The image_cache root directory: -image_cache_path = join( traits_home(), 'image_cache' ) +image_cache_path = join(traits_home(), "image_cache") # Names of files that should not be copied when ceating a new library copy: -dont_copy_list = ( 'image_volume.py', 'image_info.py', 'license.txt' ) +dont_copy_list = ("image_volume.py", "image_info.py", "license.txt") -#-- Code Generation Templates ---------------------------------------------- +# -- Code Generation Templates ---------------------------------------------- # Template for creating an ImageVolumeInfo object: -ImageVolumeInfoCodeTemplate = \ -""" ImageVolumeInfo( +ImageVolumeInfoCodeTemplate = """ ImageVolumeInfo( description=%(description)s, copyright=%(copyright)s, license=%(license)s, @@ -63,8 +93,7 @@ )""" # Template for creating an ImageVolumeInfo license text: -ImageVolumeInfoTextTemplate = \ -"""Description: +ImageVolumeInfoTextTemplate = """Description: %s Copyright: @@ -77,8 +106,7 @@ %s""" # Template for creating an ImageVolume object: -ImageVolumeTemplate = \ -"""from pyface.image.image import ImageVolume, ImageVolumeInfo +ImageVolumeTemplate = """from pyface.image.image import ImageVolume, ImageVolumeInfo volume = ImageVolume( category=%(category)s, @@ -91,8 +119,7 @@ )""" # Template for creating an ImageVolume 'images' list: -ImageVolumeImagesTemplate = \ -"""from pyface.image.image import ImageInfo +ImageVolumeImagesTemplate = """from pyface.image.image import ImageInfo from pyface.ui_traits import Margin, Border images = [ @@ -100,8 +127,7 @@ ]""" # Template for creating an ImageInfo object: -ImageInfoTemplate = \ -""" ImageInfo( +ImageInfoTemplate = """ ImageInfo( name=%(name)s, image_name=%(image_name)s, description=%(description)s, @@ -116,87 +142,89 @@ )""" -def read_file ( file_name ): +def read_file(file_name): """ Returns the contents of the specified *file_name*. """ - with open(file_name, 'rb') as fh: + with open(file_name, "rb") as fh: return fh.read() -def write_file ( file_name, data ): +def write_file(file_name, data): """ Writes the specified data to the specified file. """ - with open( file_name, 'wb' ) as fh: - fh.write( data ) + if isinstance(data, str): + data = data.encode('utf8') + with open(file_name, "wb") as fh: + fh.write(data) -def get_python_value ( source, name ): +def get_python_value(source, name): """ Returns the value of a Python symbol loaded from a specified source code string. """ temp = {} - exec(source.replace( b'\r', b'' ), globals(), temp) - return temp[ name ] + exec(source.replace(b"\r", b""), globals(), temp) + return temp[name] def time_stamp_for(time): """ Returns a specified time as a text string. """ - return datetime.datetime.utcfromtimestamp(time).strftime('%Y%m%d%H%M%S') + return datetime.datetime.utcfromtimestamp(time).strftime("%Y%m%d%H%M%S") -def add_object_prefix ( dict, object, prefix ): +def add_object_prefix(dict, object, prefix): """ Adds all traits from a specified object to a dictionary with a specified name prefix. """ for name, value in object.trait_get().items(): - dict[ prefix + name ] = value + dict[prefix + name] = value -def split_image_name ( image_name ): +def split_image_name(image_name): """ Splits a specified **image_name** into its constituent volume and file names and returns a tuple of the form: ( volume_name, file_name ). """ - col = image_name.find( ':' ) - volume_name = image_name[ 1: col ] - file_name = image_name[ col + 1: ] - if file_name.find( '.' ) < 0: - file_name += '.png' + col = image_name.find(":") + volume_name = image_name[1:col] + file_name = image_name[col + 1:] + if file_name.find(".") < 0: + file_name += ".png" - return ( volume_name, file_name ) + return (volume_name, file_name) -def join_image_name ( volume_name, file_name ): +def join_image_name(volume_name, file_name): """ Joins a specified **volume_name** and **file_name** into an image name, and return the resulting image name. """ - root, ext = splitext( file_name ) - if (ext == '.png') and (root.find( '.' ) < 0): + root, ext = splitext(file_name) + if (ext == ".png") and (root.find(".") < 0): file_name = root - return '@%s:%s' % ( volume_name, file_name ) + return "@%s:%s" % (volume_name, file_name) -class FastZipFile ( HasPrivateTraits ): +class FastZipFile(HasPrivateTraits): """ Provides fast access to zip files by keeping the underlying zip file open across multiple uses. """ #: The path to the zip file: - path = File + path = File() #: The open zip file object (if None, the file is closed): zf = Property #: The time stamp of when the zip file was most recently accessed: - time_stamp = Float + time_stamp = Float() #: The lock used to manage access to the 'zf' trait between the two threads: - access = Any + access = Any() - #-- Public Methods --------------------------------------------------------- + # -- Public Methods --------------------------------------------------------- - def namelist ( self ): + def namelist(self): """ Returns the names of all files in the top-level zip file directory. """ self.access.acquire() @@ -205,17 +233,17 @@ finally: self.access.release() - def read ( self, file_name ): + def read(self, file_name): """ Returns the contents of the specified **file_name** from the zip file. """ self.access.acquire() try: - return self.zf.read( file_name ) + return self.zf.read(file_name) finally: self.access.release() - def close ( self ): + def close(self): """ Temporarily closes the zip file (usually while the zip file is being replaced by a different version). """ @@ -227,33 +255,33 @@ finally: self.access.release() - #-- Default Value Implementations ------------------------------------------ + # -- Default Value Implementations ------------------------------------------ - def _access_default ( self ): + def _access_default(self): return allocate_lock() - #-- Property Implementations ----------------------------------------------- + # -- Property Implementations ----------------------------------------------- - def _get_zf ( self ): + def _get_zf(self): # Restart the time-out: self.time_stamp = time.time() if self._zf is None: - self._zf = ZipFile( self.path, 'r' ) + self._zf = ZipFile(self.path, "r") if self._running is None: - Thread( target = self._process ).start() + Thread(target=self._process).start() self._running = True return self._zf - #-- Private Methods -------------------------------------------------------- + # -- Private Methods -------------------------------------------------------- - def _process ( self ): + def _process(self): """ Waits until the zip file has not been accessed for a while, then closes the file and exits. """ while True: - time.sleep( 1 ) + time.sleep(1) self.access.acquire() if time.time() > (self.time_stamp + 2.0): if self._zf is not None: @@ -266,38 +294,40 @@ self.access.release() -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'ImageInfo' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class ImageInfo ( HasPrivateTraits ): + +class ImageInfo(HasPrivateTraits): """ Defines a class that contains information about a specific Traits UI image. """ #: The volume this image belongs to: - volume = Instance( 'ImageVolume' ) + volume = Instance("ImageVolume") #: The user friendly name of the image: - name = Str + name = Str() #: The full image name (e.g. '@standard:floppy'): - image_name = Str + image_name = Str() #: A description of the image: - description = Str + description = Str() #: The category that the image belongs to: - category = Str( 'General' ) + category = Str("General") #: A list of keywords used to describe/categorize the image: - keywords = List( Str ) + keywords = List(Str) #: The image width (in pixels): - width = Int + width = Int() #: The image height (in pixels): - height = Int + height = Int() #: The border inset: border = HasBorder @@ -321,16 +351,16 @@ #: ImageInfo object: image_info_code = Property - #-- Default Value Implementations ------------------------------------------ + # -- Default Value Implementations ------------------------------------------ - def _name_default ( self ): - return split_image_name( self.image_name )[1] + def _name_default(self): + return split_image_name(self.image_name)[1] - def _width_default ( self ): + def _width_default(self): if self.volume is None: return 0 - image = self.volume.image_resource( self.image_name ) + image = self.volume.image_resource(self.image_name) if image is None: self.height = 0 return 0 @@ -339,11 +369,11 @@ return width - def _height_default ( self ): + def _height_default(self): if self.volume is None: return 0 - image = self.volume.image_resource( self.image_name ) + image = self.volume.image_resource(self.image_name) if image is None: self.width = 0 return 0 @@ -352,57 +382,67 @@ return height - #-- Property Implementations ----------------------------------------------- + # -- Property Implementations ----------------------------------------------- - def _get_image_info_code ( self ): - data = dict((name, repr(value)) - for name, value in self.trait_get( - 'name', 'image_name', 'description', 'category', - 'keywords', 'alignment' - ).items()) - data.update(self.trait_get('width', 'height')) - sides = ['left', 'right', 'top', 'bottom'] - data.update(('b'+name, getattr(self.border, name)) for name in sides) - data.update(('c'+name, getattr(self.content, name)) for name in sides) - data.update(('l'+name, getattr(self.label, name)) for name in sides) - return (ImageInfoTemplate % data) - - def _get_copyright ( self ): - return self._volume_info( 'copyright' ) + def _get_image_info_code(self): + data = dict( + (name, repr(value)) + for name, value in self.trait_get( + "name", + "image_name", + "description", + "category", + "keywords", + "alignment", + ).items() + ) + data.update(self.trait_get("width", "height")) + sides = ["left", "right", "top", "bottom"] + data.update(("b" + name, getattr(self.border, name)) for name in sides) + data.update( + ("c" + name, getattr(self.content, name)) for name in sides + ) + data.update(("l" + name, getattr(self.label, name)) for name in sides) + return ImageInfoTemplate % data + + def _get_copyright(self): + return self._volume_info("copyright") - def _get_license ( self ): - return self._volume_info( 'license' ) + def _get_license(self): + return self._volume_info("license") - #-- Private Methods -------------------------------------------------------- + # -- Private Methods -------------------------------------------------------- - def _volume_info ( self, name ): + def _volume_info(self, name): """ Returns the VolumeInfo object that applies to this image. """ - info = self.volume.volume_info( self.image_name ) + info = self.volume.volume_info(self.image_name) if info is not None: - return getattr( info, name, 'Unknown' ) + return getattr(info, name, "Unknown") + + return "Unknown" - return 'Unknown' -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'ImageVolumeInfo' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class ImageVolumeInfo ( HasPrivateTraits ): + +class ImageVolumeInfo(HasPrivateTraits): #: A general description of the images: - description = Str( 'No volume description specified.' ) + description = Str("No volume description specified.") #: The copyright that applies to the images: - copyright = Str( 'No copyright information specified.' ) + copyright = Str("No copyright information specified.") #: The license that applies to the images: - license = Str( 'No license information specified.' ) + license = Str("No license information specified.") #: The list of image names within the volume the information applies to. #: Note that an empty list means that the information applies to all images #: in the volume: - image_names = List( Str ) + image_names = List(Str) #: A read-only string containing the Python code needed to construct this #: ImageVolumeInfo object: @@ -411,75 +451,81 @@ #: A read-only string containing the text describing the volume info: image_volume_info_text = Property - #-- Property Implementations ----------------------------------------------- + # -- Property Implementations ----------------------------------------------- @cached_property - def _get_image_volume_info_code ( self ): - data = dict((name, repr(getattr(self, name))) - for name in ['description', 'copyright', 'license', - 'image_names']) + def _get_image_volume_info_code(self): + data = dict( + (name, repr(getattr(self, name))) + for name in ["description", "copyright", "license", "image_names"] + ) - return (ImageVolumeInfoCodeTemplate % data) + return ImageVolumeInfoCodeTemplate % data @cached_property - def _get_image_volume_info_text ( self ): - description = self.description.replace( '\n', '\n ' ) - license = self.license.replace( '\n', '\n ' ).strip() + def _get_image_volume_info_text(self): + description = self.description.replace("\n", "\n ") + license = self.license.replace("\n", "\n ").strip() image_names = self.image_names image_names.sort() - if len( image_names ) == 0: - image_names = [ 'All' ] - images = '\n'.join( [ ' - ' + image_name - for image_name in image_names ] ) - - return (ImageVolumeInfoTextTemplate % ( description, self.copyright, - license, images )) + if len(image_names) == 0: + image_names = ["All"] + images = "\n".join([" - " + image_name for image_name in image_names]) + + return ImageVolumeInfoTextTemplate % ( + description, + self.copyright, + license, + images, + ) - #-- Public Methods --------------------------------------------------------- + # -- Public Methods --------------------------------------------------------- - def clone ( self ): + def clone(self): """ Returns a copy of the ImageVolumeInfo object. """ - return self.clone(['description', 'copyright', 'license']) + return self.clone(["description", "copyright", "license"]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'ImageVolume' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + -class ImageVolume ( HasPrivateTraits ): +class ImageVolume(HasPrivateTraits): #: The canonical name of this volume: - name = Str + name = Str() #: The list of volume descriptors that apply to this volume: - info = List( ImageVolumeInfo ) + info = List(ImageVolumeInfo) #: The category that the volume belongs to: - category = Str( 'General' ) + category = Str("General") #: A list of keywords used to describe the volume: - keywords = List( Str ) + keywords = List(Str) #: The list of aliases for this volume: - aliases = List( Str ) + aliases = List(Str) #: The path of the file that defined this volume: - path = File + path = File() #: Is the path a zip file? - is_zip_file = Bool( True ) + is_zip_file = Bool(True) #: The FastZipFile object used to access the underlying zip file: - zip_file = Instance( FastZipFile ) + zip_file = Instance(FastZipFile) #: The list of images available in the volume: - images = List( ImageInfo ) + images = List(ImageInfo) #: A dictionary mapping image names to ImageInfo objects: - catalog = Property( depends_on = 'images' ) + catalog = Property(observe="images") #: The time stamp of when the image library was last modified: - time_stamp = Str + time_stamp = Str() #: A read-only string containing the Python code needed to construct this #: ImageVolume object: @@ -494,9 +540,9 @@ #: apply to): license_text = Property - #-- Public Methods --------------------------------------------------------- + # -- Public Methods --------------------------------------------------------- - def update ( self ): + def update(self): """ Updates the contents of the image volume from the underlying image store, and saves the results. """ @@ -505,12 +551,12 @@ image.volume = None # Make sure the images are up to date by deleting any current value: - self.reset_traits(['images']) + self.reset_traits(["images"]) # Save the new image volume information: self.save() - def save ( self ): + def save(self): """ Saves the contents of the image volume using the current contents of the **ImageVolume**. """ @@ -518,12 +564,13 @@ if not self.is_zip_file: # Make sure the directory is writable: - if not access( path, R_OK | W_OK | X_OK ): + if not access(path, R_OK | W_OK | X_OK): return False # Make sure the directory and zip file are writable: - elif ((not access( dirname( path ), R_OK | W_OK | X_OK )) or - (exists( path ) and (not access( path, W_OK )))): + elif (not access(dirname(path), R_OK | W_OK | X_OK)) or ( + exists(path) and (not access(path, W_OK)) + ): return False # Pre-compute the images code, because it can require a long time @@ -537,25 +584,24 @@ # it needs to be the same or newer then the time stamp of the file # it is in. So we use the current time plus a 'fudge factor' to # allow for some slop in when the OS actually time stamps the file: - self.time_stamp = time_stamp_for( time.time() + 5.0 ) + self.time_stamp = time_stamp_for(time.time() + 5.0) # Write the volume manifest source code to a file: - write_file( join( path, 'image_volume.py' ), - self.image_volume_code ) + write_file(join(path, "image_volume.py"), self.image_volume_code) # Write the image info source code to a file: - write_file( join( path, 'image_info.py' ), images_code ) + write_file(join(path, "image_info.py"), images_code) # Write a separate license file for human consumption: - write_file( join( path, 'license.txt' ), self.license_text ) + write_file(join(path, "license.txt"), self.license_text) return True # Create a temporary name for the new .zip file: - file_name = path + '.###' + file_name = path + ".###" # Create the new zip file: - new_zf = ZipFile( file_name, 'w', ZIP_DEFLATED ) + new_zf = ZipFile(file_name, "w", ZIP_DEFLATED) try: # Get the current zip file: @@ -565,7 +611,7 @@ # zip file: for name in cur_zf.namelist(): if name not in dont_copy_list: - new_zf.writestr( name, cur_zf.read( name ) ) + new_zf.writestr(name, cur_zf.read(name)) # Temporarily close the current zip file while we replace it with # the new version: @@ -575,16 +621,16 @@ # it needs to be the same or newer then the time stamp of the file # it is in. So we use the current time plus a 'fudge factor' to # allow for some slop in when the OS actually time stamps the file: - self.time_stamp = time_stamp_for( time.time() + 10.0 ) + self.time_stamp = time_stamp_for(time.time() + 10.0) # Write the volume manifest source code to the zip file: - new_zf.writestr( 'image_volume.py', self.image_volume_code ) + new_zf.writestr("image_volume.py", self.image_volume_code) # Write the image info source code to the zip file: - new_zf.writestr( 'image_info.py', images_code ) + new_zf.writestr("image_info.py", images_code) # Write a separate license file for human consumption: - new_zf.writestr( 'license.txt', self.license_text ) + new_zf.writestr("license.txt", self.license_text) # Done creating the new zip file: new_zf.close() @@ -595,132 +641,141 @@ # file after the previous close sometimes seems to take a while, # which is why we repeatedly try the rename until it either succeeds # or takes so long that it must have failed for another reason: - temp_name = path + '.$$$' - for i in range( 50 ): + temp_name = path + ".$$$" + for i in range(50): try: - rename( path, temp_name ) + rename(path, temp_name) break except Exception: - time.sleep( 0.1 ) + time.sleep(0.1) try: - rename( file_name, path ) + rename(file_name, path) file_name = temp_name except: - rename( temp_name, path ) + rename(temp_name, path) raise finally: if new_zf is not None: new_zf.close() - remove( file_name ) + remove(file_name) return True - def image_resource ( self, image_name ): + def image_resource(self, image_name): """ Returns the ImageResource object for the specified **image_name**. """ # Get the name of the image file: - volume_name, file_name = split_image_name( image_name ) + volume_name, file_name = split_image_name(image_name) if self.is_zip_file: # See if we already have the image file cached in the file system: - cache_file = self._check_cache( file_name ) + cache_file = self._check_cache(file_name) if cache_file is None: # If not cached, then create a zip file reference: ref = ZipFileReference( - resource_factory = resource_manager.resource_factory, - zip_file = self.zip_file, - path = self.path, - volume_name = self.name, - file_name = file_name ) + resource_factory=resource_manager.resource_factory, + zip_file=self.zip_file, + path=self.path, + volume_name=self.name, + file_name=file_name, + ) else: # Otherwise, create a cache file reference: - ref = ImageReference( resource_manager.resource_factory, - filename = cache_file ) + ref = ImageReference( + resource_manager.resource_factory, filename=cache_file + ) else: # Otherwise, create a normal file reference: - ref = ImageReference( resource_manager.resource_factory, - filename = join( self.path, file_name ) ) + ref = ImageReference( + resource_manager.resource_factory, + filename=join(self.path, file_name), + ) # Create the ImageResource object using the reference (note that the # ImageResource class will not allow us to specify the reference in the # constructor): - resource = ImageResource( file_name ) + resource = ImageResource(file_name) resource._ref = ref # Return the ImageResource: return resource - def image_data ( self, image_name ): + def image_data(self, image_name): """ Returns the image data (i.e. file contents) for the specified image name. """ - volume_name, file_name = split_image_name( image_name ) + volume_name, file_name = split_image_name(image_name) if self.is_zip_file: - return self.zip_file.read( file_name ) + return self.zip_file.read(file_name) else: - return read_file( join( self.path, file_name ) ) + return read_file(join(self.path, file_name)) - def volume_info ( self, image_name ): + def volume_info(self, image_name): """ Returns the ImageVolumeInfo object that corresponds to the image specified by **image_name**. """ for info in self.info: - if ((len( info.image_names ) == 0) or - (image_name in info.image_names)): + if (len(info.image_names) == 0) or ( + image_name in info.image_names + ): return info - raise ValueError('Volume info for image name {} not found.'.format( - repr(info))) + raise ValueError( + "Volume info for image name {} not found.".format(repr(info)) + ) - #-- Default Value Implementations ------------------------------------------ + # -- Default Value Implementations ------------------------------------------ - def _info_default ( self ): - return [ ImageVolumeInfo() ] + def _info_default(self): + return [ImageVolumeInfo()] - def _images_default ( self ): + def _images_default(self): return self._load_image_info() - #-- Property Implementations ----------------------------------------------- + # -- Property Implementations ----------------------------------------------- @cached_property - def _get_catalog ( self ): + def _get_catalog(self): return dict((image.image_name, image) for image in self.images) - def _get_image_volume_code ( self ): - data = dict((name, repr(value)) - for name, value in self.trait_get( - 'description', 'category', 'keywords', 'aliases', - 'time_stamp' - ).items()) - data['info'] = ',\n'.join(info.image_volume_info_code - for info in self.info) - return (ImageVolumeTemplate % data) - - def _get_images_code ( self ): - images = ',\n'.join(info.image_info_code for info in self.images) - - return (ImageVolumeImagesTemplate % images) - - def _get_license_text ( self ): - return (('\n\n%s\n' % ('-' * 79)).join( [ info.image_volume_info_text - for info in self.info ] )) + def _get_image_volume_code(self): + data = dict( + (name, repr(value)) + for name, value in self.trait_get( + "description", "category", "keywords", "aliases", "time_stamp" + ).items() + ) + data["info"] = ",\n".join( + info.image_volume_info_code for info in self.info + ) + return ImageVolumeTemplate % data + + def _get_images_code(self): + images = ",\n".join(info.image_info_code for info in self.images) + + return ImageVolumeImagesTemplate % images + + def _get_license_text(self): + return ("\n\n%s\n" % ("-" * 79)).join( + [info.image_volume_info_text for info in self.info] + ) - #-- Private Methods -------------------------------------------------------- + # -- Private Methods -------------------------------------------------------- - def _load_image_info ( self ): + def _load_image_info(self): """ Returns the list of ImageInfo objects for the images in the volume. """ # If there is no current path, then return a default list of images: - if self.path == '': + if self.path == "": return [] - time_stamp = time_stamp_for( stat( self.path )[ ST_MTIME ] ) + time_stamp = time_stamp_for(stat(self.path)[ST_MTIME]) volume_name = self.name - old_images = [] - cur_images = [] + old_images = [] + cur_images = [] if self.is_zip_file: zf = self.zip_file @@ -729,54 +784,63 @@ names = zf.namelist() # Check to see if there is an image info manifest file: - if 'image_info.py' in names: + if "image_info.py" in names: # Load the manifest code and extract the images list: - old_images = get_python_value( zf.read( 'image_info.py' ), - 'images' ) + old_images = get_python_value( + zf.read("image_info.py"), "images" + ) - # Check to see if our time stamp is up to data with the file: + # Check to see if our time stamp is up to date with the file: if self.time_stamp < time_stamp: # If not, create an ImageInfo object for all image files # contained in the .zip file: for name in names: - root, ext = splitext( name ) + root, ext = splitext(name) if ext in ImageFileExts: - cur_images.append( ImageInfo( - name = root, - image_name = join_image_name( volume_name, name ) ) ) + cur_images.append( + ImageInfo( + name=root, + image_name=join_image_name(volume_name, name), + ) + ) else: - image_info_path = join( self.path, 'image_info.py' ) - if exists( image_info_path ): + image_info_path = join(self.path, "image_info.py") + if exists(image_info_path): # Load the manifest code and extract the images list: - old_images = get_python_value( read_file( image_info_path ), - 'images' ) + old_images = get_python_value( + read_file(image_info_path), "images" + ) # Check to see if our time stamp is up to data with the file: if self.time_stamp < time_stamp: # If not, create an ImageInfo object for each image file # contained in the path: - for name in listdir( self.path ): - root, ext = splitext( name ) + for name in listdir(self.path): + root, ext = splitext(name) if ext in ImageFileExts: - cur_images.append( ImageInfo( - name = root, - image_name = join_image_name( volume_name, name ) ) ) + cur_images.append( + ImageInfo( + name=root, + image_name=join_image_name(volume_name, name), + ) + ) # Merge the old and current images into a single up to date list: - if len( cur_images ) == 0: + if len(cur_images) == 0: images = old_images else: - cur_image_set = dict( [ ( image.image_name, image ) - for image in cur_images ] ) + cur_image_set = dict( + [(image.image_name, image) for image in cur_images] + ) for old_image in old_images: - cur_image = cur_image_set.get( old_image.image_name ) + cur_image = cur_image_set.get(old_image.image_name) if cur_image is not None: - cur_image_set[ old_image.image_name ] = old_image + cur_image_set[old_image.image_name] = old_image cur_image.volume = self - old_image.width = cur_image.width + old_image.width = cur_image.width old_image.height = cur_image.height cur_image.volume = None @@ -786,7 +850,7 @@ self.time_stamp = time_stamp # Return the resulting sorted list as the default value: - images.sort( key = lambda item: item.image_name ) + images.sort(key=lambda item: item.image_name) # Make sure all images reference this volume: for image in images: @@ -794,68 +858,70 @@ return images - def _check_cache ( self, file_name ): + def _check_cache(self, file_name): """ Checks to see if the specified zip file name has been saved in the image cache. If it has, it returns the fully-qualified cache file name to use; otherwise it returns None. """ - cache_file = join( image_cache_path, self.name, file_name ) - if (exists( cache_file ) and - (time_stamp_for( stat( cache_file )[ ST_MTIME ] ) > - self.time_stamp)): + cache_file = join(image_cache_path, self.name, file_name) + if exists(cache_file) and ( + time_stamp_for(stat(cache_file)[ST_MTIME]) > self.time_stamp + ): return cache_file return None -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'ZipFileReference' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class ZipFileReference ( ResourceReference ): + +class ZipFileReference(ResourceReference): #: The zip file to read; - zip_file = Instance( FastZipFile ) + zip_file = Instance(FastZipFile) #: The volume name: - volume_name = Str + volume_name = Str() #: The file within the zip file: - file_name = Str + file_name = Str() #: The name of the cached image file: - cache_file = File + cache_file = File() - #-- The 'ResourceReference' API -------------------------------------------- + # -- The 'ResourceReference' API -------------------------------------------- #: The file name of the image (in this case, the cache file name): filename = Property - #-- ResourceReference Interface Implementation ----------------------------- + # -- ResourceReference Interface Implementation ----------------------------- - def load ( self ): + def load(self): """ Loads the resource. """ # Check if the cache file has already been created: cache_file = self.cache_file - if cache_file == '': + if cache_file == "": # Extract the data from the zip file: - data = self.zip_file.read( self.file_name ) + data = self.zip_file.read(self.file_name) # Try to create an image from the data, without writing it to a # file first: - image = self.resource_factory.image_from_data( data, Undefined ) + image = self.resource_factory.image_from_data(data, Undefined) if image is not None: return image # Make sure the correct image cache directory exists: - cache_dir = join( image_cache_path, self.volume_name ) - if not exists( cache_dir ): - makedirs( cache_dir ) + cache_dir = join(image_cache_path, self.volume_name) + if not exists(cache_dir): + makedirs(cache_dir) # Write the image data to the cache file: - cache_file = join( cache_dir, self.file_name ) - with open(cache_file, 'wb') as fh: - fh.write( data ) + cache_file = join(cache_dir, self.file_name) + with open(cache_file, "wb") as fh: + fh.write(data) # Save the cache file name in case we are called again: self.cache_file = cache_file @@ -864,86 +930,88 @@ self.zip_file = None # Return the image data from the image cache file: - return self.resource_factory.image_from_file( cache_file ) + return self.resource_factory.image_from_file(cache_file) - #-- Property Implementations ----------------------------------------------- + # -- Property Implementations ----------------------------------------------- - def _get_filename ( self ): - if self.cache_file == '': + def _get_filename(self): + if self.cache_file == "": self.load() return self.cache_file -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'ImageLibrary' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class ImageLibrary ( HasPrivateTraits ): + +class ImageLibrary(HasPrivateTraits): """ Manages Traits UI image libraries. """ #: The list of available image volumes in the library: - volumes = List( ImageVolume ) + volumes = List(ImageVolume) #: The volume dictionary (the keys are volume names, and the values are the #: corresponding ImageVolume objects): - catalog = Dict( Str, ImageVolume ) + catalog = Dict(Str, ImageVolume) #: The list of available images in the library: - images = Property( List, depends_on = 'volumes.images' ) + images = Property(List, observe="volumes.items.images") - #-- Private Traits --------------------------------------------------------- + # -- Private Traits --------------------------------------------------------- #: Mapping from a 'virtual' library name to a 'real' library name: - aliases = Dict + aliases = Dict() - #-- Public methods --------------------------------------------------------- + # -- Public methods --------------------------------------------------------- - def image_info ( self, image_name ): + def image_info(self, image_name): """ Returns the ImageInfo object corresponding to a specified **image_name**. """ - volume = self.find_volume( image_name ) + volume = self.find_volume(image_name) if volume is not None: - return volume.catalog.get( image_name ) + return volume.catalog.get(image_name) return None - def image_resource ( self, image_name ): + def image_resource(self, image_name): """ Returns an ImageResource object for the specified image name. """ # If no volume was specified, use the standard volume: - if image_name.find( ':' ) < 0: - image_name = '@images:%s' % image_name[1:] + if image_name.find(":") < 0: + image_name = "@images:%s" % image_name[1:] # Find the correct volume, possible resolving any aliases used: - volume = self.find_volume( image_name ) + volume = self.find_volume(image_name) # Find the image within the volume and return its ImageResource object: if volume is not None: - return volume.image_resource( image_name ) + return volume.image_resource(image_name) # Otherwise, the volume was not found: return None - def find_volume ( self, image_name ): + def find_volume(self, image_name): """ Returns the ImageVolume object corresponding to the specified **image_name** or None if the volume cannot be found. """ # Extract the volume name from the image name: - volume_name, file_name = split_image_name( image_name ) + volume_name, file_name = split_image_name(image_name) # Find the correct volume, possibly resolving any aliases used: catalog = self.catalog aliases = self.aliases while volume_name not in catalog: - volume_name = aliases.get( volume_name ) + volume_name = aliases.get(volume_name) if volume_name is None: return None - return catalog[ volume_name ] + return catalog[volume_name] - def add_volume ( self, file_name = None ): + def add_volume(self, file_name=None): """ If **file_name** is a file, it adds an image volume specified by **file_name** to the image library. If **file_name** is a directory, it adds all image libraries contained in the directory @@ -954,38 +1022,41 @@ # If no file name was specified, derive a path from the caller's # source code location: if file_name is None: - file_name = join( get_resource_path( 2 ), 'images' ) + file_name = join(get_resource_path(2), "images") - if isfile( file_name ): + if isfile(file_name): # Load an image volume from the specified file: - volume = self._add_volume( file_name ) + volume = self._add_volume(file_name) if volume is None: - raise TraitError( "'%s' is not a valid image volume." % - file_name ) + raise TraitError( + "'%s' is not a valid image volume." % file_name + ) if volume.name in self.catalog: - self._duplicate_volume( volume.name ) + self._duplicate_volume(volume.name) - self.catalog[ volume.name ] = volume - self.volumes.append( volume ) + self.catalog[volume.name] = volume + self.volumes.append(volume) - elif isdir( file_name ): + elif isdir(file_name): # Load all image volumes from the specified path: catalog = self.catalog - volumes = self._add_path( file_name ) + volumes = self._add_path(file_name) for volume in volumes: if volume.name in catalog: - self._duplicate_volume( volume.name ) + self._duplicate_volume(volume.name) - catalog[ volume.name ] = volume + catalog[volume.name] = volume - self.volumes.extend( volumes ) + self.volumes.extend(volumes) else: # Handle an unrecognized argument: - raise TraitError( "The add method argument must be None or a file " - "or directory path, but '%s' was specified." % file_name ) + raise TraitError( + "The add method argument must be None or a file " + "or directory path, but '%s' was specified." % file_name + ) - def add_path ( self, volume_name, path = None ): + def add_path(self, volume_name, path=None): """ Adds the directory specified by **path** as a *virtual* volume called **volume_name**. All image files contained within path define the contents of the volume. If **path** is None, the @@ -995,34 +1066,34 @@ """ # Make sure we don't already have a volume with that name: if volume_name in self.catalog: - raise TraitError( ("The volume name '%s' is already in the image " - "library.") % volume_name ) + raise TraitError( + ("The volume name '%s' is already in the image " "library.") + % volume_name + ) # If no path specified, derive one from the caller's source code # location: if path is None: - path = join( get_resource_path( 2 ), 'images' ) + path = join(get_resource_path(2), "images") # Make sure that the specified path is a directory: - if not isdir( path ): - raise TraitError( "The image volume path '%s' does not exist." % - path ) + if not isdir(path): + raise TraitError( + "The image volume path '%s' does not exist." % path + ) # Create the ImageVolume to describe the path's contents: - image_volume_path = join( path, 'image_volume.py' ) - if exists( image_volume_path ): - volume = get_python_value( read_file( image_volume_path ), - 'volume' ) + image_volume_path = join(path, "image_volume.py") + if exists(image_volume_path): + volume = get_python_value(read_file(image_volume_path), "volume") else: volume = ImageVolume() # Set up the rest of the volume information: - volume.trait_set( name = volume_name, - path = path, - is_zip_file = False ) + volume.trait_set(name=volume_name, path=path, is_zip_file=False) # Try to bring the volume information up to date if necessary: - if volume.time_stamp < time_stamp_for( stat( path )[ ST_MTIME ] ): + if volume.time_stamp < time_stamp_for(stat(path)[ST_MTIME]): # Note that the save could fail if the volume is read-only, but # that's OK, because we're only trying to do the save in case # a developer had added or deleted some image files, which would @@ -1030,222 +1101,234 @@ volume.save() # Add the new volume to the library: - self.catalog[ volume_name ] = volume - self.volumes.append( volume ) + self.catalog[volume_name] = volume + self.volumes.append(volume) - def extract ( self, file_name, image_names ): + def extract(self, file_name, image_names): """ Builds a new image volume called **file_name** from the list of image names specified by **image_names**. Each image name should be of the form: '@volume:name'. """ # Get the volume name and file extension: - volume_name, ext = splitext( basename( file_name ) ) + volume_name, ext = splitext(basename(file_name)) # If no extension specified, add the '.zip' file extension: - if ext == '': - file_name += '.zip' + if ext == "": + file_name += ".zip" # Create the ImageVolume object to describe the new volume: - volume = ImageVolume( name = volume_name ) + volume = ImageVolume(name=volume_name) # Make sure the zip file does not already exists: - if exists( file_name ): - raise TraitError( "The '%s' file already exists." % file_name ) + if exists(file_name): + raise TraitError("The '%s' file already exists." % file_name) # Create the zip file: - zf = ZipFile( file_name, 'w', ZIP_DEFLATED ) + zf = ZipFile(file_name, "w", ZIP_DEFLATED) # Add each of the specified images to it and the ImageVolume: - error = True - aliases = set() + error = True + aliases = set() keywords = set() - images = [] - info = {} + images = [] + info = {} try: - for image_name in set( image_names ): + for image_name in set(image_names): # Verify the image name is legal: - if (image_name[:1] != '@') or (image_name.find( ':' ) < 0): - raise TraitError( ("The image name specified by '%s' is " - "not of the form: @volume:name.") % image_name ) + if (image_name[:1] != "@") or (image_name.find(":") < 0): + raise TraitError( + ( + "The image name specified by '%s' is " + "not of the form: @volume:name." + ) + % image_name + ) # Get the reference volume and image file names: - image_volume_name, image_file_name = \ - split_image_name( image_name ) + image_volume_name, image_file_name = split_image_name( + image_name + ) # Get the volume for the image name: - image_volume = self.find_volume( image_name ) + image_volume = self.find_volume(image_name) if image_volume is None: - raise TraitError( ("Could not find the image volume " - "specified by '%s'.") % image_name ) + raise TraitError( + ( + "Could not find the image volume " + "specified by '%s'." + ) + % image_name + ) # Get the image info: - image_info = image_volume.catalog.get( image_name ) + image_info = image_volume.catalog.get(image_name) if image_info is None: - raise TraitError( ("Could not find the image specified by " - "'%s'.") % image_name ) + raise TraitError( + ("Could not find the image specified by " "'%s'.") + % image_name + ) # Add the image info to the list of images: - images.append( image_info ) + images.append(image_info) # Add the image file to the zip file: - zf.writestr( image_file_name, - image_volume.image_data( image_name ) ) + zf.writestr( + image_file_name, image_volume.image_data(image_name) + ) # Add the volume alias needed by the image (if any): if image_volume_name != volume_name: if image_volume_name not in aliases: - aliases.add( image_volume_name ) + aliases.add(image_volume_name) # Add the volume keywords as well: for keyword in image_volume.keywords: - keywords.add( keyword ) + keywords.add(keyword) # Add the volume info for the image: - volume_info = image_volume.volume_info( image_name ) - vinfo = info.get( image_volume_name ) + volume_info = image_volume.volume_info(image_name) + vinfo = info.get(image_volume_name) if vinfo is None: - info[ image_volume_name ] = vinfo = volume_info.clone() + info[image_volume_name] = vinfo = volume_info.clone() - vinfo.image_names.append( image_name ) + vinfo.image_names.append(image_name) # Create the list of images for the volume: - images.sort( key = lambda item: item.image_name ) + images.sort(key=lambda item: item.image_name) volume.images = images # Create the list of aliases for the volume: - volume.aliases = list( aliases ) + volume.aliases = list(aliases) # Create the list of keywords for the volume: - volume.keywords = list( keywords ) + volume.keywords = list(keywords) # Create the final volume info list for the volume: volume.info = list(info.values()) # Write the volume manifest source code to the zip file: - zf.writestr( 'image_volume.py', volume.image_volume_code ) + zf.writestr("image_volume.py", volume.image_volume_code) # Write the image info source code to the zip file: - zf.writestr( 'image_info.py', volume.images_code ) + zf.writestr("image_info.py", volume.images_code) # Write a separate licenses file for human consumption: - zf.writestr( 'license.txt', volume.license_text ) + zf.writestr("license.txt", volume.license_text) # Indicate no errors occurred: error = False finally: zf.close() if error: - remove( file_name ) + remove(file_name) - #-- Default Value Implementations ------------------------------------------ + # -- Default Value Implementations ------------------------------------------ - def _volumes_default ( self ): + def _volumes_default(self): result = [] # Check for and add the 'application' image library: - app_library = join( dirname( abspath( sys.argv[0] ) ), 'library' ) - if isdir( app_library ): - result.extend( self._add_path( app_library ) ) + app_library = join(dirname(abspath(sys.argv[0])), "library") + if isdir(app_library): + result.extend(self._add_path(app_library)) # Get all volumes in the standard Traits UI image library directory: - result.extend( - self._add_path( join( get_resource_path( 1 ), 'library' ) ) ) + result.extend(self._add_path(join(get_resource_path(1), "library"))) # Check to see if there is an environment variable specifying a list # of paths containing image libraries: - paths = environ.get( 'TRAITS_IMAGES' ) + paths = environ.get("TRAITS_IMAGES") if paths is not None: # Determine the correct OS path separator to use: - separator = ';' - if system() != 'Windows': - separator = ':' + separator = ";" + if system() != "Windows": + separator = ":" # Add all image volumes found in each path in the environment # variable: - for path in paths.split( separator ): - result.extend( self._add_path( path ) ) + for path in paths.split(separator): + result.extend(self._add_path(path)) # Return the list of default volumes found: return result - def _catalog_default ( self ): - return dict( [ ( volume.name, volume ) for volume in self.volumes ] ) + def _catalog_default(self): + return dict([(volume.name, volume) for volume in self.volumes]) - #-- Property Implementations ----------------------------------------------- + # -- Property Implementations ----------------------------------------------- @cached_property - def _get_images ( self ): + def _get_images(self): return self._get_images_list() - #-- Private Methods -------------------------------------------------------- + # -- Private Methods -------------------------------------------------------- - def _get_images_list ( self ): + def _get_images_list(self): """ Returns the list of all library images. """ # Merge the list of images from each volume: images = [] for volume in self.volumes: - images.extend( volume.images ) + images.extend(volume.images) # Sort the result: - images.sort( key = lambda image: image.image_name ) + images.sort(key=lambda image: image.image_name) # Return the images list: return images - def _add_path ( self, path ): + def _add_path(self, path): """ Returns a list of ImageVolume objects, one for each image library located in the specified **path**. """ result = [] # Make sure the path is a directory: - if isdir( path ): + if isdir(path): # Find each zip file in the directory: - for base in listdir( path ): - if splitext( base )[1] == '.zip': + for base in listdir(path): + if splitext(base)[1] == ".zip": # Try to create a volume from the zip file and add it to # the result: - volume = self._add_volume( join( path, base ) ) + volume = self._add_volume(join(path, base)) if volume is not None: - result.append( volume ) + result.append(volume) # Return the list of volumes found: return result - def _add_volume ( self, path ): + def _add_volume(self, path): """ Returns an ImageVolume object for the image library specified by **path**. If **path** does not specify a valid ImageVolume, None is returned. """ - path = abspath( path ) + path = abspath(path) # Make sure the path is a valid zip file: - if is_zipfile( path ): + if is_zipfile(path): # Create a fast zip file for reading: - zf = FastZipFile( path = path ) + zf = FastZipFile(path=path) # Extract the volume name from the path: - volume_name = splitext( basename( path ) )[0] + volume_name = splitext(basename(path))[0] # Get the names of all top-level entries in the zip file: names = zf.namelist() # Check to see if there is a manifest file: - if 'image_volume.py' in names: + if "image_volume.py" in names: # Load the manifest code and extract the volume object: - volume = get_python_value( zf.read( 'image_volume.py' ), - 'volume' ) + volume = get_python_value(zf.read("image_volume.py"), "volume") # Set the volume name: volume.name = volume_name # Try to add all of the external volume references as # aliases for this volume: - self._add_aliases( volume ) + self._add_aliases(volume) # Set the path to this volume: volume.path = path @@ -1255,12 +1338,10 @@ else: # Create a new volume from the zip file: - volume = ImageVolume( name = volume_name, - path = path, - zip_file = zf ) + volume = ImageVolume(name=volume_name, path=path, zip_file=zf) # If this volume is not up to date, update it: - if volume.time_stamp < time_stamp_for( stat( path )[ ST_MTIME ] ): + if volume.time_stamp < time_stamp_for(stat(path)[ST_MTIME]): # Note that the save could fail if the volume is read-only, but # that's OK, because we're only trying to do the save in case # a developer had added or deleted some image files, which would @@ -1273,26 +1354,35 @@ # Indicate no volume was found: return None - def _add_aliases ( self, volume ): + def _add_aliases(self, volume): """ Try to add all of the external volume references as aliases for this volume. """ - aliases = self.aliases + aliases = self.aliases volume_name = volume.name for vname in volume.aliases: - if ((vname in aliases) and - (volume_name != aliases[ vname ])): - raise TraitError( ("Image library error: " - "Attempt to alias '%s' to '%s' when it is " - "already aliased to '%s'") % - ( vname, volume_name, aliases[ volume_name ] ) ) - aliases[ vname ] = volume_name + if (vname in aliases) and (volume_name != aliases[vname]): + raise TraitError( + ( + "Image library error: " + "Attempt to alias '%s' to '%s' when it is " + "already aliased to '%s'" + ) + % (vname, volume_name, aliases[volume_name]) + ) + aliases[vname] = volume_name - def _duplicate_volume ( self, volume_name ): + def _duplicate_volume(self, volume_name): """ Raises a duplicate volume name error. """ - raise TraitError( ("Attempted to add an image volume called '%s' when " - "a volume with that name is already defined.") % volume_name ) + raise TraitError( + ( + "Attempted to add an image volume called '%s' when " + "a volume with that name is already defined." + ) + % volume_name + ) + # Create the singleton image object: ImageLibrary = ImageLibrary() diff -Nru python-pyface-6.1.2/pyface/image/__init__.py python-pyface-7.4.0/pyface/image/__init__.py --- python-pyface-6.1.2/pyface/image/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/image/library/icons.zip and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/image/library/icons.zip differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/image/library/std.zip and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/image/library/std.zip differ diff -Nru python-pyface-6.1.2/pyface/image/tests/__init__.py python-pyface-7.4.0/pyface/image/tests/__init__.py --- python-pyface-6.1.2/pyface/image/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/image/tests/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru python-pyface-6.1.2/pyface/image/tests/test_image.py python-pyface-7.4.0/pyface/image/tests/test_image.py --- python-pyface-6.1.2/pyface/image/tests/test_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/image/tests/test_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,559 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from contextlib import closing +from os import stat +from importlib_resources import files +from pathlib import Path +import shutil +import tempfile +import time +import unittest +from zipfile import ZipFile, ZIP_DEFLATED + +from pyface.image_resource import ImageResource +from pyface.ui_traits import Border, Margin +from ..image import ( + FastZipFile, ImageLibrary, ImageVolume, ImageVolumeInfo, ZipFileReference, + join_image_name, split_image_name, time_stamp_for, +) + + +LIBRARY_DIR = files('pyface.image') / "library" +ICONS_FILE = LIBRARY_DIR / "icons.zip" +TEST_IMAGES_DIR = files('pyface.tests') / "images" + + +class TestJoinImageName(unittest.TestCase): + + def test_simple(self): + image_name = join_image_name("icons", "red_ball.jpg") + + self.assertEqual(image_name, "@icons:red_ball.jpg") + + def test_extension(self): + image_name = join_image_name("icons", "red_ball.png") + + self.assertEqual(image_name, "@icons:red_ball") + + def test_double_extension(self): + image_name = join_image_name("icons", "red_ball.foo.png") + + self.assertEqual(image_name, "@icons:red_ball.foo.png") + + +class TestSplitImageName(unittest.TestCase): + + def test_simple(self): + volume_name, file_name = split_image_name("@icons:red_ball.jpg") + + self.assertEqual(volume_name, "icons") + self.assertEqual(file_name, "red_ball.jpg") + + def test_extension(self): + volume_name, file_name = split_image_name("@icons:red_ball") + + self.assertEqual(volume_name, "icons") + self.assertEqual(file_name, "red_ball.png") + + def test_no_at_symbol(self): + volume_name, file_name = split_image_name("icons:red_ball.jpg") + + # XXX this should probably raise rather than doing this + self.assertEqual(volume_name, "cons") + self.assertEqual(file_name, "red_ball.jpg") + + def test_no_colon(self): + volume_name, file_name = split_image_name("@red_ball.jpg") + + # XXX this should probably raise rather than doing this + self.assertEqual(volume_name, "red_ball.jp") + self.assertEqual(file_name, "@red_ball.jpg") + + def test_no_colon_or_at_symbol(self): + volume_name, file_name = split_image_name("red_ball.jpg") + + # XXX this should probably raise rather than doing this + self.assertEqual(volume_name, "ed_ball.jp") + self.assertEqual(file_name, "red_ball.jpg") + + +class TestFastZipFile(unittest.TestCase): + + def test_read_icons_red_ball(self): + zf = FastZipFile(path=ICONS_FILE) + + file_bytes = zf.read("red_ball.png") + + self.assertTrue(file_bytes.startswith(b"\x89PNG")) + + def test_namelist_icons(self): + zf = FastZipFile(path=ICONS_FILE) + + names = zf.namelist() + + self.assertTrue("red_ball.png" in names) + self.assertEqual(names, zf.zf.namelist()) + + def test_close_icons(self): + zf = FastZipFile(path=ICONS_FILE) + + actual_zf = zf.zf + + self.assertIs(zf._zf, actual_zf) + + zf.close() + + self.assertIsNone(zf._zf) + + def test_eventual_zipfile_close(self): + zf = FastZipFile(path=ICONS_FILE) + + self.assertFalse(zf._running) + start_time = time.time() + + actual_zf = zf.zf + + end_time = time.time() + + self.assertIsNotNone(actual_zf) + self.assertGreaterEqual(zf.time_stamp, start_time) + self.assertLessEqual(zf.time_stamp, end_time) + self.assertTrue(zf._running) + self.assertIs(zf._zf, actual_zf) + + # wait for thread to clean-up zipfile + # XXX this is not nice + while time.time() <= end_time + 3.0: + time.sleep(1.0) + + self.assertFalse(zf._running) + self.assertIsNone(zf._zf) + + +class TestImageVolume(unittest.TestCase): + + def test_init_empty(self): + volume = ImageVolume() + + self.assertEqual(volume.name, "") + self.assertEqual(volume.images, []) + self.assertEqual(volume.catalog, {}) + self.assertEqual(volume.category, "General") + self.assertEqual(volume.keywords, []) + self.assertEqual(volume.aliases, []) + self.assertEqual(volume.path, "") + self.assertTrue(volume.is_zip_file) + self.assertIsNone(volume.zip_file) + self.assertEqual(volume.time_stamp, "") + self.assertEqual(len(volume.info), 1) + self.assertIsInstance(volume.info[0], ImageVolumeInfo) + + def test_empty_zipfile(self): + with tempfile.TemporaryDirectory() as dir_path: + path = Path(dir_path) / "test.zip" + ZipFile(path, "w", ZIP_DEFLATED).close() + with closing(FastZipFile(path=path)) as zf: + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume = ImageVolume(name="test", path=path, zip_file=zf) + + self.assertEqual(volume.name, "test") + self.assertEqual(volume.images, []) + self.assertEqual(volume.catalog, {}) + self.assertEqual(volume.category, "General") + self.assertEqual(volume.keywords, []) + self.assertEqual(volume.aliases, []) + self.assertEqual(Path(volume.path), path) + self.assertTrue(volume.is_zip_file) + self.assertIs(volume.zip_file, zf) + self.assertEqual(volume.time_stamp, time_stamp) + self.assertEqual(len(volume.info), 1) + self.assertIsInstance(volume.info[0], ImageVolumeInfo) + + def test_empty_directory(self): + with tempfile.TemporaryDirectory() as dir_path: + path = Path(dir_path) / "test" + path.mkdir() + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume = ImageVolume(name="test", path=path, is_zip_file=False) + + self.assertEqual(volume.name, "test") + self.assertEqual(volume.images, []) + self.assertEqual(volume.catalog, {}) + self.assertEqual(volume.category, "General") + self.assertEqual(volume.keywords, []) + self.assertEqual(volume.aliases, []) + self.assertEqual(Path(volume.path), path) + self.assertFalse(volume.is_zip_file) + self.assertIsNone(volume.zip_file) + self.assertEqual(volume.time_stamp, time_stamp) + self.assertEqual(len(volume.info), 1) + self.assertIsInstance(volume.info[0], ImageVolumeInfo) + + def test_empty_directory_save(self): + with tempfile.TemporaryDirectory() as dir_path: + path = Path(dir_path) / "test" + path.mkdir() + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume = ImageVolume(name="test", path=path, is_zip_file=False) + result = volume.save() + + self.assertTrue(result) + + filenames = {file.name for file in path.iterdir()} + self.assertEqual(filenames, {"image_volume.py", "image_info.py", "license.txt"}) + + # test that new file is readable + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume_2 = ImageVolume(name="test", path=path, is_zip_file=False) + + self.assertEqual(volume_2.name, "test") + self.assertEqual(volume_2.images, []) + self.assertEqual(volume_2.catalog, {}) + self.assertEqual(volume_2.category, "General") + self.assertEqual(volume_2.keywords, []) + self.assertEqual(volume_2.aliases, []) + self.assertEqual(Path(volume_2.path), path) + self.assertFalse(volume_2.is_zip_file) + self.assertIsNone(volume_2.zip_file) + self.assertEqual(volume_2.time_stamp, time_stamp) + self.assertEqual(len(volume_2.info), 1) + self.assertIsInstance(volume_2.info[0], ImageVolumeInfo) + + def test_empty_zipfile_save(self): + with tempfile.TemporaryDirectory() as dir_path: + path = Path(dir_path) / "test.zip" + ZipFile(path, "w", ZIP_DEFLATED).close() + with closing(FastZipFile(path=path)) as zf: + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume = ImageVolume(name="test", path=path, zip_file=zf) + + result = volume.save() + + self.assertTrue(result) + + with closing(FastZipFile(path=path)) as zf: + self.assertEqual(set(zf.namelist()), {"image_volume.py", "image_info.py", "license.txt"}) + + # test that new file is readable + with closing(FastZipFile(path=path)) as zf: + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume_2 = ImageVolume(name="test", path=path, zip_file=zf) + + self.assertEqual(volume_2.name, "test") + self.assertEqual(volume_2.images, []) + self.assertEqual(volume_2.catalog, {}) + self.assertEqual(volume_2.category, "General") + self.assertEqual(volume_2.keywords, []) + self.assertEqual(volume_2.aliases, []) + self.assertEqual(Path(volume_2.path), path) + self.assertTrue(volume_2.is_zip_file) + self.assertIs(volume_2.zip_file, zf) + self.assertEqual(volume_2.time_stamp, time_stamp) + self.assertEqual(len(volume_2.info), 1) + self.assertIsInstance(volume_2.info[0], ImageVolumeInfo) + + def test_save_directory(self): + with tempfile.TemporaryDirectory() as dir_path: + path = Path(dir_path) / "test" + path.mkdir() + time_stamp = time_stamp_for(stat(path).st_mtime) + image_file = path / "core.png" + shutil.copyfile(TEST_IMAGES_DIR / "core.png", image_file) + + # this should create an default ImageInfo object for the image + # file as a side-effect of loading. + volume = ImageVolume(name="test", path=path, is_zip_file=False) + + self.assertEqual(len(volume.images), 1) + self.assertGreaterEqual(volume.time_stamp, time_stamp) + + image = volume.images[0] + self.assertEqual(image.image_name, "@test:core") + self.assertEqual(image.name, "core") + self.assertEqual(image.volume, volume) + self.assertEqual(image.description, "") + self.assertEqual(image.category, "General") + self.assertEqual(image.keywords, []) + self.assertEqual(image.width, 64) + self.assertEqual(image.height, 64) + self.assertIsInstance(image.border, Border) + self.assertIsInstance(image.content, Margin) + self.assertIsInstance(image.label, Margin) + self.assertEqual(image.alignment, "default") + self.assertEqual(image.copyright, 'No copyright information specified.') + + result = volume.save() + + self.assertTrue(result) + + filenames = {file.name for file in path.iterdir()} + self.assertEqual(filenames, {"core.png", "image_volume.py", "image_info.py", "license.txt"}) + + # test that new file is readable + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume_2 = ImageVolume(name="test", path=path, is_zip_file=False) + + self.assertEqual(volume_2.name, "test") + self.assertEqual(len(volume_2.images), 1) + self.assertEqual(len(volume_2.catalog), 1) + self.assertIn("@test:core", volume_2.catalog) + self.assertEqual(volume_2.category, "General") + self.assertEqual(volume_2.keywords, []) + self.assertEqual(volume_2.aliases, []) + self.assertEqual(Path(volume_2.path), path) + self.assertFalse(volume_2.is_zip_file) + self.assertIsNone(volume_2.zip_file) + self.assertEqual(volume_2.time_stamp, time_stamp) + self.assertEqual(len(volume_2.info), 1) + self.assertIsInstance(volume_2.info[0], ImageVolumeInfo) + + image = volume_2.images[0] + self.assertEqual(image.image_name, "@test:core") + self.assertEqual(image.name, "core") + self.assertEqual(image.volume, volume_2) + self.assertEqual(image.description, "") + self.assertEqual(image.category, "General") + self.assertEqual(image.keywords, []) + self.assertEqual(image.width, 64) + self.assertEqual(image.height, 64) + self.assertIsInstance(image.border, Border) + self.assertIsInstance(image.content, Margin) + self.assertIsInstance(image.label, Margin) + self.assertEqual(image.alignment, "default") + self.assertEqual(image.copyright, 'No copyright information specified.') + + # do one more save to smoke-test other code paths + volume_2.save() + + def test_save_zipfile(self): + with tempfile.TemporaryDirectory() as dir_path: + path = Path(dir_path) / "test.zip" + with ZipFile(path, "w", ZIP_DEFLATED) as zf: + zf.write(TEST_IMAGES_DIR / "core.png", "core.png") + + with closing(FastZipFile(path=path)) as zf: + time_stamp = time_stamp_for(stat(path).st_mtime) + + # this should create an default ImageInfo object for the image + # file as a side-effect of loading. + volume = ImageVolume(name="test", path=path, zip_file=zf) + + self.assertEqual(len(volume.images), 1) + self.assertGreaterEqual(volume.time_stamp, time_stamp) + + image = volume.images[0] + self.assertEqual(image.image_name, "@test:core") + self.assertEqual(image.name, "core") + self.assertEqual(image.volume, volume) + self.assertEqual(image.description, "") + self.assertEqual(image.category, "General") + self.assertEqual(image.keywords, []) + self.assertEqual(image.width, 64) + self.assertEqual(image.height, 64) + self.assertIsInstance(image.border, Border) + self.assertIsInstance(image.content, Margin) + self.assertIsInstance(image.label, Margin) + self.assertEqual(image.alignment, "default") + self.assertEqual(image.copyright, 'No copyright information specified.') + + result = volume.save() + + self.assertTrue(result) + + with closing(FastZipFile(path=path)) as zf: + self.assertEqual(set(zf.namelist()), {"core.png", "image_volume.py", "image_info.py", "license.txt"}) + + # test that new file is readable + with closing(FastZipFile(path=path)) as zf: + time_stamp = time_stamp_for(stat(path).st_mtime) + + volume_2 = ImageVolume(name="test", path=path, zip_file=zf) + + self.assertEqual(volume_2.name, "test") + self.assertEqual(len(volume_2.images), 1) + self.assertEqual(len(volume_2.catalog), 1) + self.assertIn("@test:core", volume_2.catalog) + self.assertEqual(volume_2.category, "General") + self.assertEqual(volume_2.keywords, []) + self.assertEqual(volume_2.aliases, []) + self.assertEqual(Path(volume_2.path), path) + self.assertTrue(volume_2.is_zip_file) + self.assertIs(volume_2.zip_file, zf) + self.assertEqual(volume_2.time_stamp, time_stamp) + self.assertEqual(len(volume_2.info), 1) + self.assertIsInstance(volume_2.info[0], ImageVolumeInfo) + + image = volume_2.images[0] + self.assertEqual(image.image_name, "@test:core") + self.assertEqual(image.name, "core") + self.assertEqual(image.volume, volume_2) + self.assertEqual(image.description, "") + self.assertEqual(image.category, "General") + self.assertEqual(image.keywords, []) + self.assertEqual(image.height, 64) + self.assertEqual(image.width, 64) + self.assertIsInstance(image.border, Border) + self.assertIsInstance(image.content, Margin) + self.assertIsInstance(image.label, Margin) + self.assertEqual(image.alignment, "default") + self.assertEqual(image.copyright, 'No copyright information specified.') + + # do one more save to smoke-test other code paths + volume_2.save() + + def test_icons_zipfile_volume(self): + time_stamp = time_stamp_for(stat(ICONS_FILE).st_mtime) + with closing(FastZipFile(path=ICONS_FILE)) as zf: + volume = ImageVolume(name="icons", path=ICONS_FILE, zip_file=zf) + + self.assertEqual(volume.name, "icons") + self.assertTrue( + any( + image.image_name == "@icons:red_ball" + for image in volume.images + ) + ) + self.assertTrue("@icons:red_ball" in volume.catalog) + self.assertEqual(volume.category, "General") + self.assertEqual(volume.keywords, []) + self.assertEqual(volume.aliases, []) + self.assertEqual(Path(volume.path), ICONS_FILE) + self.assertTrue(volume.is_zip_file) + self.assertIs(volume.zip_file, zf) + self.assertEqual(volume.time_stamp, time_stamp) + self.assertEqual(len(volume.info), 1) + self.assertIsInstance(volume.info[0], ImageVolumeInfo) + + def test_icons_image_resource(self): + with closing(FastZipFile(path=ICONS_FILE)) as zf: + volume = ImageVolume(name="icons", path=ICONS_FILE, zip_file=zf) + + image = volume.image_resource("@icons:red_ball") + + self.assertIsInstance(image, ImageResource) + self.assertIsInstance(image._ref, ZipFileReference) + self.assertTrue(ICONS_FILE.samefile(image._ref.path)) + + def test_icons_image_resource_missing(self): + with closing(FastZipFile(path=ICONS_FILE)) as zf: + volume = ImageVolume(name="icons", path=ICONS_FILE, zip_file=zf) + + image = volume.image_resource("@icons:does_not_exist") + + self.assertIsInstance(image, ImageResource) + self.assertIsInstance(image._ref, ZipFileReference) + self.assertTrue(ICONS_FILE.samefile(image._ref.path)) + + def test_icons_image_data(self): + with closing(FastZipFile(path=ICONS_FILE)) as zf: + volume = ImageVolume(name="icons", path=ICONS_FILE, zip_file=zf) + + data = volume.image_data("@icons:red_ball") + + self.assertTrue(data.startswith(b"\x89PNG")) + + def test_icons_image_data_missing(self): + with closing(FastZipFile(path=ICONS_FILE)) as zf: + volume = ImageVolume(name="icons", path=ICONS_FILE, zip_file=zf) + + with self.assertRaises(KeyError): + volume.image_data("@icons:does_not_exist") + + def test_icons_volume_info(self): + with closing(FastZipFile(path=ICONS_FILE)) as zf: + volume = ImageVolume(name="icons", path=ICONS_FILE, zip_file=zf) + + volume_info = volume.volume_info("@icons:red_ball") + + self.assertIs(volume_info, volume.info[0]) + + def test_volume_info(self): + volume = ImageVolume( + name="test", + info=[ + ImageVolumeInfo(image_names=["@test:one", "@test:two"]), + ImageVolumeInfo(image_names=["@test:three", "@test:two"]), + ] + ) + + volume_info = volume.volume_info("@test:two") + self.assertIs(volume_info, volume.info[0]) + + volume_info = volume.volume_info("@test:three") + self.assertIs(volume_info, volume.info[1]) + + with self.assertRaises(ValueError): + volume.volume_info("@test:four") + + +class TestImageLibrary(unittest.TestCase): + + # XXX These are more in the flavor of integration tests + + def test_default_volumes(self): + volume_names = {volume.name for volume in ImageLibrary.volumes} + self.assertEqual(volume_names, {'icons', 'std'}) + + def test_default_catalog(self): + self.assertEqual(set(ImageLibrary.catalog.keys()), {'icons', 'std'}) + + def test_default_images(self): + image_names = {image.image_name for image in ImageLibrary.images} + self.assertTrue("@icons:red_ball" in image_names) + + def test_image_info(self): + red_ball_image_info = ImageLibrary.image_info("@icons:red_ball") + + self.assertEqual(red_ball_image_info.name, "red_ball") + self.assertEqual(red_ball_image_info.image_name, "@icons:red_ball") + self.assertEqual(red_ball_image_info.width, 16) + self.assertEqual(red_ball_image_info.height, 16) + + def test_image_info_missing(self): + missing_image_info = ImageLibrary.image_info("@icons:does_not_exist") + + self.assertIsNone(missing_image_info) + + def test_image_info_volume_missing(self): + missing_image_info = ImageLibrary.image_info("@missing:does_not_exist") + + self.assertIsNone(missing_image_info) + + def test_image_resource(self): + red_ball_image = ImageLibrary.image_resource("@icons:red_ball") + + self.assertIsInstance(red_ball_image, ImageResource) + self.assertIsInstance(red_ball_image._ref, ZipFileReference) + self.assertTrue(ICONS_FILE.samefile(red_ball_image._ref.path)) + + def test_image_resource_missing(self): + missing_image = ImageLibrary.image_info("@icons:does_not_exist") + + self.assertIsNone(missing_image) + + def test_image_resource_missing_volume(self): + missing_image = ImageLibrary.image_info("@missing:does_not_exist") + + self.assertIsNone(missing_image) + + def test_find_volume(self): + volume = ImageLibrary.find_volume("@icons:red_ball") + + self.assertIsInstance(volume, ImageVolume) + self.assertEqual(volume.name, "icons") + self.assertTrue(ICONS_FILE.samefile(volume.path)) diff -Nru python-pyface-6.1.2/pyface/image_button.py python-pyface-7.4.0/pyface/image_button.py --- python-pyface-6.1.2/pyface/image_button.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image_button.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.image_button, use pyface.ui.wx.image_button instead. ' - 'Will be removed in Pyface 7.') - -from pyface.ui.wx.image_button import * diff -Nru python-pyface-6.1.2/pyface/image_cache.py python-pyface-7.4.0/pyface/image_cache.py --- python-pyface-6.1.2/pyface/image_cache.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image_cache.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of an image cache. """ -from __future__ import absolute_import # Import the toolkit specific version. from .toolkit import toolkit_object -ImageCache = toolkit_object('image_cache:ImageCache') -#### EOF ###################################################################### +ImageCache = toolkit_object("image_cache:ImageCache") diff -Nru python-pyface-6.1.2/pyface/image_list.py python-pyface-7.4.0/pyface/image_list.py --- python-pyface-6.1.2/pyface/image_list.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image_list.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.image_list, use pyface.ui.wx.image_list instead. ' - 'Will be removed in Pyface 7.') - -from pyface.ui.wx.image_list import * diff -Nru python-pyface-6.1.2/pyface/image_resource.py python-pyface-7.4.0/pyface/image_resource.py --- python-pyface-6.1.2/pyface/image_resource.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image_resource.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of an image resource. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -ImageResource = toolkit_object('image_resource:ImageResource') -#### EOF ###################################################################### +ImageResource = toolkit_object("image_resource:ImageResource") Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/about.jpg and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/about.jpg differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/about.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/about.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/carat_closed.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/carat_closed.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/carat_open.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/carat_open.png differ diff -Nru python-pyface-6.1.2/pyface/images/image_LICENSE.txt python-pyface-7.4.0/pyface/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Enthought BSD 3-Clause LICENSE.txt -GV (Gael Varoquaux) Public Domain N/A -Nuvola LGPL image_LICENSE_Nuvola.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/images: - about.jpg | Enthought - background.jpg | Enthought - caret_close.png | Enthought - caret_open.png | Enthought - close.png | GV - image_not_found.png | Nuvola - panel_gradient.png | Enthought - panel_gradient_over.png | Enthought - question.png | GV - splash.png | Enthought Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/panel_gradient_over.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/panel_gradient_over.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/panel_gradient.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/panel_gradient.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/splash.jpg and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/splash.jpg differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/images/splash.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/images/splash.png differ diff -Nru python-pyface-6.1.2/pyface/image_widget.py python-pyface-7.4.0/pyface/image_widget.py --- python-pyface-6.1.2/pyface/image_widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/image_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # Note: The ImageWidget is currently wx-specific # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -ImageWidget = toolkit_object('image_widget:ImageWidget') + +ImageWidget = toolkit_object("image_widget:ImageWidget") diff -Nru python-pyface-6.1.2/pyface/i_message_dialog.py python-pyface-7.4.0/pyface/i_message_dialog.py --- python-pyface-6.1.2/pyface/i_message_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_message_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,45 +1,41 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a dialog that displays a message. """ -# Enthought library imports. -from traits.api import Enum, Unicode +from traits.api import Enum, HasTraits, Str + -# Local imports. from pyface.i_dialog import IDialog class IMessageDialog(IDialog): """ The interface for a dialog that displays a message. """ - #### 'IMessageDialog' interface ########################################### + # 'IMessageDialog' interface ------------------------------------------- #: The message to display in the dialog. - message = Unicode + message = Str() #: More information about the message to be displayed. - informative = Unicode + informative = Str() #: More detail about the message to be displayed in the dialog. - detail = Unicode + detail = Str() #: The severity of the message. - severity = Enum('information', 'warning', 'error') + severity = Enum("information", "warning", "error") -class MMessageDialog(object): +class MMessageDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IMessageDialog interface. """ diff -Nru python-pyface-6.1.2/pyface/__init__.py python-pyface-7.4.0/pyface/__init__.py --- python-pyface-6.1.2/pyface/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,85 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2013, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Reusable MVC-based components for Traits-based applications. - Part of the TraitsGUI project of the Enthought Tool Suite. + Part of the TraitsUI project of the Enthought Tool Suite. """ try: from pyface._version import full_version as __version__ except ImportError: - __version__ = 'not-built' + __version__ = "not-built" -__requires__ = ['traits'] +__requires__ = [ + 'importlib-metadata>=3.6.0; python_version<"3.8"', + 'importlib-resources>=1.1.0; python_version<"3.9"', + "traits>=6.2", +] __extras_require__ = { - 'wx': ['wxpython>=2.8.10', 'numpy'], - 'pyqt': ['pyqt>=4.10', 'pygments'], - 'pyqt5': ['pyqt>=5', 'pygments'], - 'pyside': ['pyside>=1.2', 'pygments'], + "wx": ["wxpython>=4", "numpy"], + "pyqt": ["pyqt>=4.10", "pygments"], + "pyqt5": ["pyqt5", "pygments"], + "pyqt6": ["pyqt6", "pygments"], + "pyside2": ["pyside2", "shiboken2", "pygments"], + "pyside6": ["pyside6", "pygments"], + "pillow": ["pillow"], + "test": ["packaging"], } + + +# ============================= Test Loader ================================== +def load_tests(loader, standard_tests, pattern): + """ Custom test loading function that enables test filtering using regex + exclusion pattern. + + Parameters + ---------- + loader : unittest.TestLoader + The instance of test loader + standard_tests : unittest.TestSuite + Tests that would be loaded by default from this module (no tests) + pattern : str + An inclusion pattern used to match test files (test*.py default) + + Returns + ------- + filtered_package_tests : unittest.TestSuite + TestSuite representing all package tests that did not match specified + exclusion pattern. + """ + from os import environ + from os.path import dirname + from pyface.util.testing import filter_tests + from unittest import TestSuite + + # Make sure the right toolkit is up and running before importing tests + from pyface.toolkit import toolkit_object # noqa: F401 + + this_dir = dirname(__file__) + package_tests = loader.discover(start_dir=this_dir, pattern=pattern) + + # List of regular expression for filtering test using the test id. + exclusion_patterns = [] + + # Environment variable for skipping more tests. + # e.g. etstool.py in the source tree root sets this to skip packages for + # specific toolkit + additional_exclude = environ.get("EXCLUDE_TESTS", None) + if additional_exclude is not None: + exclusion_patterns.append(additional_exclude) + + filtered_package_tests = TestSuite() + for test_suite in package_tests: + for exclusion_pattern in exclusion_patterns: + test_suite = filter_tests(test_suite, exclusion_pattern) + filtered_package_tests.addTest(test_suite) + + return filtered_package_tests diff -Nru python-pyface-6.1.2/pyface/i_pil_image.py python-pyface-7.4.0/pyface/i_pil_image.py --- python-pyface-6.1.2/pyface/i_pil_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_pil_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,73 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" The interface for a PIL Image. """ + +from traits.api import HasStrictTraits, Instance + +from pyface.i_image import IImage + + +class IPILImage(IImage): + """ The interface for a image that wraps a PIL Image. + """ + + # 'IPILImage' interface -------------------------------------------- + + #: The PIL Image instance. + image = Instance("PIL.Image.Image") + + +class MPILImage(HasStrictTraits): + """ The base implementation mixin for a image that wraps a PIL Image. + """ + + # 'IPILImage' interface -------------------------------------------- + + #: The PIL Image instance. + image = Instance("PIL.Image.Image") + + def __init__(self, image, **traits): + super().__init__(image=image, **traits) + + def create_bitmap(self, size=None): + """ Creates a bitmap image for this image. + + Parameters + ---------- + size : (int, int) or None + The desired size as a (width, height) tuple, or None if wanting + default image size. + + Returns + ------- + image : bitmap + The toolkit bitmap corresponding to the image and the specified + size. + """ + from pyface.util.image_helpers import image_to_bitmap + return image_to_bitmap(self.create_image(size)) + + def create_icon(self, size=None): + """ Creates an icon for this image. + + Parameters + ---------- + size : (int, int) or None + The desired size as a (width, height) tuple, or None if wanting + default icon size. + + Returns + ------- + image : icon + The toolkit image corresponding to the image and the specified + size as an icon. + """ + from pyface.util.image_helpers import bitmap_to_icon + return bitmap_to_icon(self.create_bitmap(size)) diff -Nru python-pyface-6.1.2/pyface/i_progress_dialog.py python-pyface-7.4.0/pyface/i_progress_dialog.py --- python-pyface-6.1.2/pyface/i_progress_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_progress_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" The interface for a dialog that allows the user to open/save files etc. """ +# Thanks for using Enthought open source! + +"""The interface for a dialog that allows the user to display progress of an +operation.""" + + +from traits.api import Any, Bool, HasTraits, Int, Str -# Enthought library imports. -from traits.api import Any, Bool, Int, Str -# Local imports. from pyface.i_dialog import IDialog @@ -24,16 +22,16 @@ """ A simple progress dialog window which allows itself to be updated """ - #### 'IProgressDialog' interface ################################## + # 'IProgressDialog' interface ---------------------------------# #: The message to display in the dialog - message = Str + message = Str() #: The minimum progress value - min = Int + min = Int() #: The maximum progress value - max = Int + max = Int() #: The margin around the progress bar margin = Int(5) @@ -48,11 +46,11 @@ show_percent = Bool(False) #: Label for the 'cancel' button - cancel_button_label = Str + cancel_button_label = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'IProgressDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def update(self, value): """ Update the progress bar to the desired value @@ -77,7 +75,7 @@ """ -class MProgressDialog(object): +class MProgressDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IProgressDialog interface. @@ -86,11 +84,11 @@ #: The progress bar toolkit object # XXX why not the control? - progress_bar = Any + progress_bar = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Open the dialog """ @@ -98,11 +96,11 @@ msg = "Dialog min ({}) is greater than dialog max ({})." raise AttributeError(msg.format(self.min, self.max)) - super(MProgressDialog, self).open() + super().open() - ########################################################################### + # ------------------------------------------------------------------------ # 'IProgressDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def update(self, value): """ Update the progress bar to the desired value diff -Nru python-pyface-6.1.2/pyface/i_python_editor.py python-pyface-7.4.0/pyface/i_python_editor.py --- python-pyface-6.1.2/pyface/i_python_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,51 +1,47 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A widget for editing Python code. """ -# Enthought library imports. -from traits.api import Bool, Event, Interface, Unicode +from traits.api import Bool, Event, HasTraits, Str -# Local imports. +from pyface.i_layout_widget import ILayoutWidget from pyface.key_pressed_event import KeyPressedEvent -class IPythonEditor(Interface): +class IPythonEditor(ILayoutWidget): """ A widget for editing Python code. """ - #### 'IPythonEditor' interface ############################################ + # 'IPythonEditor' interface -------------------------------------------- #: Has the file in the editor been modified? dirty = Bool(False) #: The pathname of the file being edited. - path = Unicode + path = Str() #: Should line numbers be shown in the margin? show_line_numbers = Bool(True) - #### Events #### + # Events ---- #: The contents of the editor has changed. - changed = Event + changed = Event() #: A key has been pressed. key_pressed = Event(KeyPressedEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'IPythonEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def load(self, path=None): """ Loads the contents of the editor. @@ -81,7 +77,7 @@ """ -class MPythonEditor(object): +class MPythonEditor(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IPythonEditor interface. diff -Nru python-pyface-6.1.2/pyface/i_python_shell.py python-pyface-7.4.0/pyface/i_python_shell.py --- python-pyface-6.1.2/pyface/i_python_shell.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_python_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,40 +1,37 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for an interactive Python shell. """ -# Enthought library imports. -from traits.api import Event -# Local imports. +from traits.api import Event, HasTraits + + from pyface.key_pressed_event import KeyPressedEvent -from pyface.i_widget import IWidget +from pyface.i_layout_widget import ILayoutWidget -class IPythonShell(IWidget): +class IPythonShell(ILayoutWidget): """ The interface for an interactive Python shell. """ - #### 'IPythonShell' interface ############################################# + # 'IPythonShell' interface --------------------------------------------- #: A command has been executed. - command_executed = Event + command_executed = Event() #: A key has been pressed. key_pressed = Event(KeyPressedEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'IPythonShell' interface. - ########################################################################### + # ------------------------------------------------------------------------ def interpreter(self): """ Get the shell's interpreter @@ -103,16 +100,16 @@ """ -class MPythonShell(object): +class MPythonShell(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IPythonShell interface. Implements: bind(), _on_command_executed() """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IPythonShell' interface. - ########################################################################### + # ------------------------------------------------------------------------ def bind(self, name, value): """ Binds a name to a value in the interpreter's namespace. @@ -126,9 +123,9 @@ """ self.interpreter().locals[name] = value - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _on_command_executed(self): """ Called when a command has been executed in the shell. """ diff -Nru python-pyface-6.1.2/pyface/ipython_widget.py python-pyface-7.4.0/pyface/ipython_widget.py --- python-pyface-6.1.2/pyface/ipython_widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/ipython_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2008, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of an IPython shell. """ -from __future__ import absolute_import # Import the toolkit specific version. try: - import IPython.frontend + import IPython.frontend # noqa: F401 except ImportError: - raise ImportError(''' + raise ImportError( + """ ________________________________________________________________________________ Could not load the Wx frontend for ipython. -You need to have ipython >= 0.9 installed to use the ipython widget.''') +You need to have ipython >= 0.9 installed to use the ipython widget.""" + ) from .toolkit import toolkit_object -IPythonWidget= toolkit_object('ipython_widget:IPythonWidget') + +IPythonWidget = toolkit_object("ipython_widget:IPythonWidget") diff -Nru python-pyface-6.1.2/pyface/i_single_choice_dialog.py python-pyface-7.4.0/pyface/i_single_choice_dialog.py --- python-pyface-6.1.2/pyface/i_single_choice_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_single_choice_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,45 +1,41 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2016, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a dialog that prompts for a choice from a list. """ -# Enthought library imports. -from traits.api import Any, List, Str +from traits.api import Any, HasTraits, List, Str + -# Local imports. from pyface.i_dialog import IDialog class ISingleChoiceDialog(IDialog): """ The interface for a dialog that prompts for a choice from a list. """ - #### 'ISingleChoiceDialog' interface ###################################### + # 'ISingleChoiceDialog' interface -------------------------------------# #: List of objects to choose from. choices = List(Any) #: The object chosen, if any. - choice = Any + choice = Any() #: An optional attribute to use for the name of each object in the dialog. - name_attribute = Str + name_attribute = Str() #: The message to display to the user. - message = Str + message = Str() -class MSingleChoiceDialog(object): +class MSingleChoiceDialog(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IConfirmationDialog interface. """ @@ -47,7 +43,7 @@ def _choice_strings(self): """ Returns the list of strings to display in the dialog. """ choices = self.choices - if self.name_attribute != '': + if self.name_attribute != "": # choices is a list of objects with this attribute choices = [getattr(obj, self.name_attribute) for obj in choices] @@ -56,6 +52,7 @@ if len(choices) == 0: raise ValueError("SingleChoiceDialog requires at least 1 choice.") elif len(choices) != len(set(choices)): - raise ValueError("Dialog choices {} contain repeated string value." - % choices) + raise ValueError( + "Dialog choices {} contain repeated string value." % choices + ) return choices diff -Nru python-pyface-6.1.2/pyface/i_splash_screen.py python-pyface-7.4.0/pyface/i_splash_screen.py --- python-pyface-6.1.2/pyface/i_splash_screen.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_splash_screen.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a splash screen. """ -# Standard library imports. import logging -# Enthought library imports. -from traits.api import Any, Bool, Instance, Int, Tuple, Unicode -# Local imports. -from pyface.image_resource import ImageResource +from traits.api import Any, Bool, HasTraits, Int, Tuple, Str + + +from pyface.ui_traits import Image from pyface.splash_screen_log_handler import SplashScreenLogHandler from pyface.i_window import IWindow @@ -29,10 +25,10 @@ class ISplashScreen(IWindow): """ The interface for a splash screen. """ - #### 'ISplashScreen' interface ############################################ + # 'ISplashScreen' interface -------------------------------------------- #: The image to display on the splash screen. - image = Instance(ImageResource, ImageResource('splash')) + image = Image() #: If log messages are to be displayed then this is the logging level. See #: the Python documentation for the 'logging' module for more details. @@ -42,41 +38,41 @@ show_log_messages = Bool(True) #: Optional text to display on top of the splash image. - text = Unicode + text = Str() #: The text color. # FIXME v3: When TraitsUI supports PyQt then change this to 'Color', # (unless that needs the toolkit to be selected too soon, in which case it # may need to stay as Any - or Str?) - #text_color = WxColor('black') - text_color = Any + # text_color = WxColor('black') + text_color = Any() #: The text font. # FIXME v3: When TraitsUI supports PyQt then change this back to # 'Font(None)' with the actual default being toolkit specific. - #text_font = Font(None) - text_font = Any + # text_font = Font(None) + text_font = Any() #: The x, y location where the text will be drawn. # FIXME v3: Remove this. - text_location = Tuple(5, 5) + text_location = Tuple(5, 5) -class MSplashScreen(object): +class MSplashScreen(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the ISplashScreen interface. Reimplements: open(), close() """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Creates the toolkit-specific control for the widget. """ - super(MSplashScreen, self).open() + super().open() if self.show_log_messages: self._log_handler = SplashScreenLogHandler(self) @@ -94,4 +90,4 @@ logger = logging.getLogger() logger.removeHandler(self._log_handler) - super(MSplashScreen, self).close() + super().close() diff -Nru python-pyface-6.1.2/pyface/i_split_widget.py python-pyface-7.4.0/pyface/i_split_widget.py --- python-pyface-6.1.2/pyface/i_split_widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_split_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Mix-in class for split widgets. """ -# Enthought library imports. -from traits.api import Callable, Enum, Float, Interface +from traits.api import Callable, Float, HasTraits, Interface + +from pyface.ui_traits import Orientation class ISplitWidget(Interface): @@ -25,14 +23,14 @@ vertically. """ - #### 'ISplitWidget' interface ############################################# + # 'ISplitWidget' interface --------------------------------------------- #: The direction in which the widget is split. # #: Splitting vertically means there will be a left hand panel and a right #: hand panel, splitting horizontally means there will be a top panel and #: a bottom panel. - direction = Enum('vertical', 'vertical', 'horizontal') + direction = Orientation() #: The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.5) @@ -43,9 +41,9 @@ #: An optional callable that provides the right hand/bottom panel. rhs = Callable - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'ISplitWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_splitter(self, parent): """ Create the toolkit-specific control that represents the widget. @@ -90,7 +88,55 @@ """ -class MSplitWidget(object): +class MSplitWidget(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the ISplitWidget interface. """ + + # 'ISplitWidget' interface --------------------------------------------- + + #: The direction in which the widget is split. + # + #: Splitting vertically means there will be a left hand panel and a right + #: hand panel, splitting horizontally means there will be a top panel and + #: a bottom panel. + direction = Orientation() + + #: The ratio of the size of the left/top pane to the right/bottom pane. + ratio = Float(0.5) + + #: An optional callable that provides the left hand/top panel. + lhs = Callable + + #: An optional callable that provides the right hand/bottom panel. + rhs = Callable + + def _create_lhs(self, parent): + """ Creates the left hand/top panel depending on the direction. + + Parameters + ---------- + parent : toolkit control + The splitter's toolkit control. + + Returns + ------- + lhs : toolkit control + The toolkit control for the lhs. + """ + raise NotImplementedError() + + def _create_rhs(self, parent): + """ Creates the right hand/bottom panel depending on the direction. + + Parameters + ---------- + parent : toolkit control + The splitter's toolkit control. + + Returns + ------- + rhs : toolkit control + The toolkit control for the rhs. + """ + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/i_system_metrics.py python-pyface-7.4.0/pyface/i_system_metrics.py --- python-pyface-6.1.2/pyface/i_system_metrics.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_system_metrics.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,41 +1,37 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface to system metrics (screen width and height etc). """ -# Enthought library imports. -from traits.api import Int, Interface, Tuple +from traits.api import HasTraits, Int, Interface, Tuple class ISystemMetrics(Interface): """ The interface to system metrics (screen width and height etc). """ - #### 'ISystemMetrics' interface ########################################### + # 'ISystemMetrics' interface ------------------------------------------- #: The width of the screen in pixels. - screen_width = Int + screen_width = Int() #: The height of the screen in pixels. - screen_height = Int + screen_height = Int() #: Background color of a standard dialog window as a tuple of RGB values #: between 0.0 and 1.0. # FIXME v3: Why isn't this a traits colour? - dialog_background_color = Tuple + dialog_background_color = Tuple() -class MSystemMetrics(object): +class MSystemMetrics(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the ISystemMetrics interface. """ diff -Nru python-pyface-6.1.2/pyface/i_widget.py python-pyface-7.4.0/pyface/i_widget.py --- python-pyface-6.1.2/pyface/i_widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The base interface for all pyface widgets. """ -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, Interface +from traits.api import Any, Bool, HasTraits, Interface, Instance, Str class IWidget(Interface): @@ -25,10 +21,10 @@ """ #: The toolkit specific control that represents the widget. - control = Any + control = Any() #: The control's optional parent control. - parent = Any + parent = Any() #: Whether or not the control is visible visible = Bool(True) @@ -36,6 +32,12 @@ #: Whether or not the control is enabled enabled = Bool(True) + #: A tooltip for the widget. + tooltip = Str() + + #: An optional context menu for the widget. + context_menu = Instance("pyface.action.menu_manager.MenuManager") + # ------------------------------------------------------------------------ # 'IWidget' interface. # ------------------------------------------------------------------------ @@ -43,8 +45,8 @@ def show(self, visible): """ Show or hide the widget. - Parameter - --------- + Parameters + ---------- visible : bool Visible should be ``True`` if the widget should be shown. """ @@ -52,26 +54,39 @@ def enable(self, enabled): """ Enable or disable the widget. - Parameter - --------- + Parameters + ---------- enabled : bool The enabled state to set the widget to. """ - def destroy(self): - """ Destroy the control if it exists. """ + def focus(self): + """ Set the keyboard focus to this widget. + """ - # ------------------------------------------------------------------------ - # Protected 'IWidget' interface. - # ------------------------------------------------------------------------ + def has_focus(self): + """ Does the widget currently have keyboard focus? - def _create(self): + Returns + ------- + focus_state : bool + Whether or not the widget has keyboard focus. + """ + + def create(self): """ Creates the toolkit specific control. This method should create the control and assign it to the :py:attr:``control`` trait. """ + def destroy(self): + """ Destroy the control if it exists. """ + + # ------------------------------------------------------------------------ + # Protected 'IWidget' interface. + # ------------------------------------------------------------------------ + def _create_control(self, parent): """ Create toolkit specific control that represents the widget. @@ -94,11 +109,34 @@ """ Remove toolkit-specific bindings for events """ -class MWidget(object): +class MWidget(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IWidget interface. """ + #: A tooltip for the widget. + tooltip = Str() + + #: An optional context menu for the widget. + context_menu = Instance("pyface.action.menu_manager.MenuManager") + + def create(self): + """ Creates the toolkit specific control. + + The default implementation simply calls _create() + """ + self._create() + + def destroy(self): + """ Call clean-up code and destroy toolkit objects. + + Subclasses should override to perform any additional clean-up, ensuring + that they call super() after that clean-up. + """ + if self.control is not None: + self._remove_event_listeners() + self.control = None + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. # ------------------------------------------------------------------------ @@ -110,6 +148,7 @@ :py:attr:``control`` trait. """ self.control = self._create_control(self.parent) + self._initialize_control() self._add_event_listeners() def _create_control(self, parent): @@ -126,12 +165,78 @@ control : toolkit control A control for the widget. """ - raise NotImplementedError + raise NotImplementedError() + + def _initialize_control(self): + """ Perform any post-creation initialization for the control. + """ + self._set_control_tooltip(self.tooltip) def _add_event_listeners(self): """ Set up toolkit-specific bindings for events """ - pass + self.observe(self._tooltip_updated, "tooltip", dispatch="ui") + self.observe( + self._context_menu_updated, "context_menu", dispatch="ui" + ) + if self.control is not None and self.context_menu is not None: + self._observe_control_context_menu() def _remove_event_listeners(self): """ Remove toolkit-specific bindings for events """ - pass + if self.control is not None and self.context_menu is not None: + self._observe_control_context_menu(remove=True) + self.observe( + self._context_menu_updated, + "context_menu", + dispatch="ui", + remove=True, + ) + self.observe( + self._tooltip_updated, "tooltip", dispatch="ui", remove=True + ) + + # Toolkit control interface --------------------------------------------- + + def _get_control_tooltip(self): + """ Toolkit specific method to get the control's tooltip. """ + raise NotImplementedError() + + def _set_control_tooltip(self, tooltip): + """ Toolkit specific method to set the control's tooltip. """ + raise NotImplementedError() + + def _observe_control_context_menu(self, remove=False): + """ Toolkit specific method to change the context menu observer. + + This should use _handle_control_context_menu as the event handler. + + Parameters + ---------- + remove : bool + Whether the context menu handler should be removed or added. + """ + raise NotImplementedError() + + def _handle_control_context_menu(self, event): + """ Handle a context menu event. + + Implementations should override this with a method suitable to be used + as a toolkit event handler that invokes a context menu. + + The function signature will likely vary from toolkit to toolkit. + """ + raise NotImplementedError() + + # Trait change handlers ------------------------------------------------- + + def _tooltip_updated(self, event): + tooltip = event.new + if self.control is not None: + self._set_control_tooltip(tooltip) + + def _context_menu_updated(self, event): + if self.control is not None: + if event.new is None: + self._observe_control_context_menu(remove=True) + if event.old is None: + self._observe_control_context_menu() diff -Nru python-pyface-6.1.2/pyface/i_window.py python-pyface-7.4.0/pyface/i_window.py --- python-pyface-6.1.2/pyface/i_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/i_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,21 @@ -# Copyright (c) 2005-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: """ The abstract interface for all pyface top-level windows. """ -# Enthought library imports. -from traits.api import Event, Tuple, Unicode, Vetoable, VetoableEvent -# Local imports. +from traits.api import Event, HasTraits, Tuple, Str, Vetoable, VetoableEvent + + from pyface.constant import NO from pyface.key_pressed_event import KeyPressedEvent from pyface.i_widget import IWidget @@ -30,37 +31,37 @@ # 'IWindow' interface ----------------------------------------------------- #: The position of the window. - position = Tuple + position = Tuple() #: The size of the window. - size = Tuple + size = Tuple() #: The window title. - title = Unicode + title = Str() # Window Events ---------------------------------------------------------- #: The window has been opened. - opened = Event + opened = Event() #: The window is about to open. - opening = VetoableEvent + opening = VetoableEvent() #: The window has been activated. - activated = Event + activated = Event() #: The window has been closed. - closed = Event + closed = Event() #: The window is about to be closed. - closing = VetoableEvent + closing = VetoableEvent() #: The window has been deactivated. - deactivated = Event + deactivated = Event() #: A key was pressed while the window had focus. # FIXME v3: This smells of a hack. What's so special about key presses? - # FIXME v3: Unicode + # FIXME v3: Str key_pressed = Event(KeyPressedEvent) # ------------------------------------------------------------------------- @@ -119,7 +120,12 @@ """ def information( - self, message, title='Information', detail='', informative='' + self, + message, + title="Information", + detail="", + informative="", + text_format="auto" ): """ Convenience method to show an information message dialog. @@ -133,10 +139,20 @@ Further details about the message. informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". """ - def warning(self, message, title='Warning', detail='', informative=''): + def warning( + self, + message, + title="Warning", + detail="", + informative="", + text_format="auto" + ): """ Convenience method to show a warning message dialog. Parameters @@ -149,10 +165,20 @@ Further details about the message. informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". """ - def error(self, message, title='Error', detail='', informative=''): + def error( + self, + message, + title="Error", + detail="", + informative="", + text_format="auto" + ): """ Convenience method to show an error message dialog. Parameters @@ -165,11 +191,14 @@ Further details about the message. informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". """ -class MWindow(object): +class MWindow(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IWindow interface. @@ -253,7 +282,12 @@ return confirm(self.control, message, title, cancel, default) def information( - self, message, title='Information', detail='', informative='' + self, + message, + title="Information", + detail="", + informative="", + text_format="auto" ): """ Convenience method to show an information message dialog. @@ -267,13 +301,26 @@ Further details about the message. informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". Only supported on the qt + backend. """ from .message_dialog import information - information(self.control, message, title, detail, informative) - - def warning(self, message, title='Warning', detail='', informative=''): + information( + self.control, message, title, detail, informative, text_format + ) + + def warning( + self, + message, + title="Warning", + detail="", + informative="", + text_format="auto" + ): """ Convenience method to show a warning message dialog. Parameters @@ -286,13 +333,26 @@ Further details about the message. informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". Only supported on the qt + backend. """ from .message_dialog import warning - warning(self.control, message, title, detail, informative) - - def error(self, message, title='Error', detail='', informative=''): + warning( + self.control, message, title, detail, informative, text_format + ) + + def error( + self, + message, + title="Error", + detail="", + informative="", + text_format="auto" + ): """ Convenience method to show an error message dialog. Parameters @@ -305,8 +365,12 @@ Further details about the message. informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". Only supported on the qt + backend. """ from .message_dialog import error - error(self.control, message, title, detail, informative) + error(self.control, message, title, detail, informative, text_format) diff -Nru python-pyface-6.1.2/pyface/key_pressed_event.py python-pyface-7.4.0/pyface/key_pressed_event.py --- python-pyface-6.1.2/pyface/key_pressed_event.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/key_pressed_event.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,34 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The event that is generated when a key is pressed. """ -# Enthought library imports. from traits.api import Bool, HasTraits, Int, Any class KeyPressedEvent(HasTraits): """ The event that is generated when a key is pressed. """ - #### 'KeyPressedEvent' interface ########################################## + # 'KeyPressedEvent' interface -----------------------------------------# #: Is the alt key down? - alt_down = Bool + alt_down = Bool() #: Is the control key down? - control_down = Bool + control_down = Bool() #: Is the shift key down? - shift_down = Bool + shift_down = Bool() #: The keycode. - key_code = Int + key_code = Int() #: The original toolkit specific event. - event = Any - -#### EOF ###################################################################### + event = Any() diff -Nru python-pyface-6.1.2/pyface/layered_panel.py python-pyface-7.4.0/pyface/layered_panel.py --- python-pyface-6.1.2/pyface/layered_panel.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/layered_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # Note: The MultiToolbarWindow is currently wx-specific # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -LayeredPanel = toolkit_object('layered_panel:LayeredPanel') + +LayeredPanel = toolkit_object("layered_panel:LayeredPanel") diff -Nru python-pyface-6.1.2/pyface/layout_widget.py python-pyface-7.4.0/pyface/layout_widget.py --- python-pyface-6.1.2/pyface/layout_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/layout_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Multi-pane splitter widget. """ + +from .toolkit import toolkit_object + +LayoutWidget = toolkit_object("layout_widget:LayoutWidget") diff -Nru python-pyface-6.1.2/pyface/list_box_model.py python-pyface-7.4.0/pyface/list_box_model.py --- python-pyface-6.1.2/pyface/list_box_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/list_box_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The model for list boxes. """ -# Enthought library imports + from traits.api import Event, HasTraits @@ -12,10 +21,10 @@ class ListBoxModel(HasTraits): """ The model for list boxes. """ - #### Events #### + # Events ---- #: Fired when the contents of the list have changed. - list_changed = Event + list_changed = Event() def get_item_count(self): """ Get the number of items in the list. @@ -25,7 +34,7 @@ item_count : int The number of items in the list. """ - raise NotImplementedError + raise NotImplementedError() def get_item_at(self, index): """ Returns the item at the specified index. @@ -40,7 +49,7 @@ label, item : str, any The user-visible string and model data of the item. """ - raise NotImplementedError + raise NotImplementedError() def fire_list_changed(self): """ Invoke this method when the list has changed. """ diff -Nru python-pyface-6.1.2/pyface/list_box.py python-pyface-7.4.0/pyface/list_box.py --- python-pyface-6.1.2/pyface/list_box.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/list_box.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # Note: The ListBox is currently wx-specific # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -ListBox = toolkit_object('list_box:ListBox') + +ListBox = toolkit_object("list_box:ListBox") diff -Nru python-pyface-6.1.2/pyface/mdi_application_window.py python-pyface-7.4.0/pyface/mdi_application_window.py --- python-pyface-6.1.2/pyface/mdi_application_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/mdi_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,20 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # Note: The MDIApplicationWindow is currently wx-specific # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -MDIApplicationWindow = toolkit_object('mdi_application_window:MDIApplicationWindow') + +MDIApplicationWindow = toolkit_object( + "mdi_application_window:MDIApplicationWindow" +) diff -Nru python-pyface-6.1.2/pyface/mdi_window_menu.py python-pyface-7.4.0/pyface/mdi_window_menu.py --- python-pyface-6.1.2/pyface/mdi_window_menu.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/mdi_window_menu.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A menu that mimics the standard MDI window menu. This is the menu that has the tile/cascade actions etc. @@ -18,119 +15,113 @@ """ -# Enthought library imports. from traits.api import Str -# Local imports. + from .action.api import MenuManager, Separator, WindowAction class Cascade(WindowAction): """ Cascades the windows. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("Ca&scade") + name = Str("Ca&scade") tooltip = Str("Cascade the windows") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Cascades the windows. """ self.window.control.Cascade() - return class Tile(WindowAction): """ Tiles the windows horizontally. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("&Tile") + name = Str("&Tile") tooltip = Str("Tile the windows") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Tiles the windows horizontally. """ self.window.control.Tile() - return class ArrangeIcons(WindowAction): """ Arranges the icons. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("&Arrange Icons") + name = Str("&Arrange Icons") tooltip = Str("Arrange the icons") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Arranges the icons. """ self.window.control.ArrangeIcons() - return class Next(WindowAction): """ Activates the next window. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("&Next") + name = Str("&Next") tooltip = Str("Activate the next window") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Activates the next window. """ self.window.control.ActivateNext() - return class Previous(WindowAction): """ Activates the previous window. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("&Previous") + name = Str("&Previous") tooltip = Str("Activate the previous window") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Activates the previous window. """ self.window.control.ActivatePrevious() - return class Close(WindowAction): """ Closes the current window. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("&Close") + name = Str("&Close") tooltip = Str("Close the current window") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Closes the current window. """ @@ -139,19 +130,18 @@ if page is not None: page.Close() - return class CloseAll(WindowAction): """ Closes all of the child windows. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Str("Close A&ll") + name = Str("Close A&ll") tooltip = Str("Close all of the windows.") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Closes the child windows. """ @@ -159,7 +149,6 @@ for page in self.window.control.GetChildren(): page.Close() - return class MDIWindowMenu(MenuManager): """ A menu that mimics the standard MDI window menus. @@ -168,15 +157,15 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, window): """ Creates a new MDI window menu. """ # Base class constructor. - super(MDIWindowMenu, self).__init__( + super().__init__( Cascade(window=window), Tile(window=window), Separator(), @@ -185,7 +174,5 @@ Previous(window=window), Close(window=window), CloseAll(window=window), - name = '&Window' + name="&Window", ) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/message_dialog.py python-pyface-7.4.0/pyface/message_dialog.py --- python-pyface-6.1.2/pyface/message_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/message_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,32 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a dialog that displays a message. """ -from __future__ import absolute_import + +# Import the toolkit specific version. +from .toolkit import toolkit_object + + +MessageDialog = toolkit_object("message_dialog:MessageDialog") # Convenience functions. -def information(parent, message, title='Information', - detail='', informative=''): +def information( + parent, + message, + title="Information", + detail="", + informative="", + text_format="auto" +): """ Convenience method to show an information message dialog. Parameters @@ -34,16 +42,31 @@ "Show details"). informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". Only supported on the qt backend. """ dialog = MessageDialog( - parent=parent, message=message, title=title, - severity='information', detail=detail, informative=informative + parent=parent, + message=message, + title=title, + severity="information", + detail=detail, + informative=informative, + text_format=text_format, ) dialog.open() -def warning(parent, message, title='Warning', detail='', informative=''): +def warning( + parent, + message, + title="Warning", + detail="", + informative="", + text_format="auto" +): """ Convenience function to show a warning message dialog. Parameters @@ -59,16 +82,31 @@ "Show details"). informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". Only supported on the qt backend. """ dialog = MessageDialog( - parent=parent, message=message, title=title, - severity='warning', detail=detail, informative=informative + parent=parent, + message=message, + title=title, + severity="warning", + detail=detail, + informative=informative, + text_format=text_format, ) dialog.open() -def error(parent, message, title='Error', detail='', informative=''): +def error( + parent, + message, + title="Error", + detail="", + informative="", + text_format="auto" +): """ Convenience function to show an error message dialog. Parameters @@ -84,17 +122,18 @@ "Show details"). informative : str Explanatory text to display along with the message. + text_format : str + Specifies what text format to use in the resulting message dialog. + One of "auto", "plain", or "rich". Only supported on the qt backend. """ dialog = MessageDialog( - parent=parent, message=message, title=title, severity='error', - detail=detail, informative=informative + parent=parent, + message=message, + title=title, + severity="error", + detail=detail, + informative=informative, + text_format=text_format, ) dialog.open() - - -# Import the toolkit specific version. -from .toolkit import toolkit_object -MessageDialog = toolkit_object('message_dialog:MessageDialog') - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/mimedata.py python-pyface-7.4.0/pyface/mimedata.py --- python-pyface-6.1.2/pyface/mimedata.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/mimedata.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # Import the toolkit specific version. from pyface.toolkit import toolkit_object # WIP: Currently only supports qt4 backend. API might change without # prior notification -PyMimeData = toolkit_object('mimedata:PyMimeData') +PyMimeData = toolkit_object("mimedata:PyMimeData") diff -Nru python-pyface-6.1.2/pyface/multi_toolbar_window.py python-pyface-7.4.0/pyface/multi_toolbar_window.py --- python-pyface-6.1.2/pyface/multi_toolbar_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/multi_toolbar_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # Note: The MultiToolbarWindow is currently wx-specific # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -MultiToolbarWindow = toolkit_object('multi_toolbar_window:MultiToolbarWindow') + +MultiToolbarWindow = toolkit_object("multi_toolbar_window:MultiToolbarWindow") diff -Nru python-pyface-6.1.2/pyface/pil_image.py python-pyface-7.4.0/pyface/pil_image.py --- python-pyface-6.1.2/pyface/pil_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/pil_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,19 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The implementation of a IPILImage. """ + + +# Import the toolkit specific version. + + +from .toolkit import toolkit_object + +PILImage = toolkit_object("pil_image:PILImage") diff -Nru python-pyface-6.1.2/pyface/preference/api.py python-pyface-7.4.0/pyface/preference/api.py --- python-pyface-6.1.2/pyface/preference/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/preference/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2015, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + + +""" + +API for the ``pyface.preference`` subpackage. + +- :class:`~.PreferencePage` +- :class:`~.PreferenceDialog` +- :class:`~.PreferenceNode` -from __future__ import absolute_import +""" from .preference_page import PreferencePage from .preference_dialog import PreferenceDialog diff -Nru python-pyface-6.1.2/pyface/preference/__init__.py python-pyface-7.4.0/pyface/preference/__init__.py --- python-pyface-6.1.2/pyface/preference/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/preference/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ - +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/preference/preference_dialog.py python-pyface-7.4.0/pyface/preference/preference_dialog.py --- python-pyface-6.1.2/pyface/preference/preference_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/preference/preference_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ The preference dialog. """ # Import the toolkit specific version. -from __future__ import absolute_import + from pyface.toolkit import toolkit_object -TreeViewer = toolkit_object('preference.preference_dialog:PreferenceDialog') + +PreferenceDialog = toolkit_object("preference.preference_dialog:PreferenceDialog") diff -Nru python-pyface-6.1.2/pyface/preference/preference_node.py python-pyface-7.4.0/pyface/preference/preference_node.py --- python-pyface-6.1.2/pyface/preference/preference_node.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/preference/preference_node.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for a node in a preference dialog. A preference node has a label and an image which are used to represent the node @@ -19,7 +16,6 @@ """ -# Enthought library imports. from pyface.viewer.tree_item import TreeItem from traits.api import Str @@ -32,37 +28,37 @@ """ - #### 'PreferenceNode' interface ########################################### + # 'PreferenceNode' interface ------------------------------------------- # The node's unique Id. - id = Str + id = Str() # The node's image. - image = Str + image = Str() # The node name. - name = Str + name = Str() # The Id of the help topic for the node. - help_id = Str + help_id = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __str__(self): """ Returns the string representation of the item. """ return self.name - ########################################################################### + # ------------------------------------------------------------------------ # 'PreferenceNode' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_page(self): """ Creates the preference page for this node. """ - raise NotImplementedError + raise NotImplementedError() def lookup(self, id): """ Returns the child of this node with the specified Id. @@ -79,5 +75,3 @@ node = None return node - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/preference/preference_page.py python-pyface-7.4.0/pyface/preference/preference_page.py --- python-pyface-6.1.2/pyface/preference/preference_page.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/preference/preference_page.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all preference pages. """ -# Enthought library imports. from traits.api import HasTraits @@ -22,14 +18,14 @@ class PreferencePage(HasTraits): """ Abstract base class for all preference pages. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'PreferencePage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_control(self, parent): """ Creates the toolkit-specific control for the page. """ - raise NotImplementedError + raise NotImplementedError() def restore_defaults(self): """ Restore the default preferences. """ @@ -40,5 +36,3 @@ """ Show the help topic for this preference page.""" pass - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/progress_dialog.py python-pyface-7.4.0/pyface/progress_dialog.py --- python-pyface-6.1.2/pyface/progress_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/progress_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A simple progress dialog window which allows itself to be updated """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -ProgressDialog = toolkit_object('progress_dialog:ProgressDialog') -#### EOF ###################################################################### +ProgressDialog = toolkit_object("progress_dialog:ProgressDialog") diff -Nru python-pyface-6.1.2/pyface/python_editor.py python-pyface-7.4.0/pyface/python_editor.py --- python-pyface-6.1.2/pyface/python_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A widget for editing Python code. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -PythonEditor = toolkit_object('python_editor:PythonEditor') -#### EOF ###################################################################### +PythonEditor = toolkit_object("python_editor:PythonEditor") diff -Nru python-pyface-6.1.2/pyface/python_shell.py python-pyface-7.4.0/pyface/python_shell.py --- python-pyface-6.1.2/pyface/python_shell.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/python_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of an interactive Python shell. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -PythonShell = toolkit_object('python_shell:PythonShell') -#### EOF ###################################################################### +PythonShell = toolkit_object("python_shell:PythonShell") diff -Nru python-pyface-6.1.2/pyface/qt/__init__.py python-pyface-7.4.0/pyface/qt/__init__.py --- python-pyface-6.1.2/pyface/qt/__init__.py 2019-05-08 10:09:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,53 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: Qt API selector. Can be used to switch between pyQt and PySide -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import importlib import os import sys QtAPIs = [ - ('pyside', 'PySide'), - ('pyside2', 'PySide2'), - ('pyqt5', 'PyQt5'), - ('pyqt', 'PyQt4'), + ("pyside2", "PySide2"), + ("pyside6", "PySide6"), + ("pyqt5", "PyQt5"), + ("pyqt6", "PyQt6"), + ("pyqt", "PyQt4"), ] - - -def prepare_pyqt4(): - """ Set PySide compatible APIs. """ - # This is not needed for Python 3 and can be removed when we no longer - # support Python 2. - import sip - try: - sip.setapi('QDate', 2) - sip.setapi('QDateTime', 2) - sip.setapi('QString', 2) - sip.setapi('QTextStream', 2) - sip.setapi('QTime', 2) - sip.setapi('QUrl', 2) - sip.setapi('QVariant', 2) - except ValueError as exc: - if sys.version_info[0] <= 2: - # most likely caused by something else setting the API version - # before us: try to give a better error message to direct the user - # how to fix. - msg = exc.args[0] - msg += (". Pyface expects PyQt API 2 under Python 2. " - "Either import Pyface before any other Qt-using packages, " - "or explicitly set the API before importing any other " - "Qt-using packages.") - raise ValueError(msg) - else: - # don't expect the above on Python 3, so just re-raise - raise +api_names, modules = zip(*QtAPIs) qt_api = None @@ -56,41 +29,35 @@ for api_name, module in QtAPIs: if module in sys.modules: qt_api = api_name - if qt_api == 'pyqt': - # set the PyQt4 APIs - # this is a likely place for failure - pyface really wants to be - # imported first, before eg. matplotlib - prepare_pyqt4() break else: # does our environment give us a preferred API? - qt_api = os.environ.get('QT_API') - if qt_api == 'pyqt': - # set the PyQt4 APIs - prepare_pyqt4() + qt_api = os.environ.get("QT_API") # if we have no preference, is a Qt API available? Or fail with ImportError. if qt_api is None: for api_name, module in QtAPIs: try: - if api_name == 'pyqt': - # set the PyQt4 APIs - prepare_pyqt4() importlib.import_module(module) - importlib.import_module('.QtCore', module) + importlib.import_module(".QtCore", module) qt_api = api_name break except ImportError: continue else: - raise ImportError('Cannot import PySide, PySide2, PyQt5 or PyQt4') + raise ImportError("Cannot import any of " + ", ".join(modules)) # otherwise check QT_API value is valid -elif qt_api not in {api_name for api_name, module in QtAPIs}: - msg = ("Invalid Qt API %r, valid values are: " + - "'pyside, 'pyside2', 'pyqt' or 'pyqt5'") % qt_api +elif qt_api not in api_names: + msg = ( + "Invalid Qt API %r, valid values are: " + + ', '.join(api_names) + ) % qt_api raise RuntimeError(msg) # useful constants -is_qt4 = (qt_api in {'pyqt', 'pyside'}) -is_qt5 = (qt_api in {'pyqt5', 'pyside2'}) +is_qt4 = qt_api in {"pyqt"} +is_qt5 = qt_api in {"pyqt5", "pyside2"} +is_qt6 = qt_api in {"pyqt6", "pyside6"} +is_pyqt = qt_api in {"pyqt", "pyqt5", "pyqt6"} +is_pyside = qt_api in {"pyside", "pyside2", "pyside6"} diff -Nru python-pyface-6.1.2/pyface/qt/QtCore.py python-pyface-7.4.0/pyface/qt/QtCore.py --- python-pyface-6.1.2/pyface/qt/QtCore.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtCore.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtCore import * from PyQt4.QtCore import pyqtProperty as Property @@ -10,9 +19,9 @@ from PyQt4.Qt import Qt __version__ = QT_VERSION_STR - __version_info__ = tuple(map(int, QT_VERSION_STR.split('.'))) + __version_info__ = tuple(map(int, QT_VERSION_STR.split("."))) -elif qt_api == 'pyqt5': +elif qt_api == "pyqt5": from PyQt5.QtCore import * from PyQt5.QtCore import pyqtProperty as Property @@ -22,16 +31,24 @@ from PyQt5.Qt import Qt __version__ = QT_VERSION_STR - __version_info__ = tuple(map(int, QT_VERSION_STR.split('.'))) + __version_info__ = tuple(map(int, QT_VERSION_STR.split("."))) +elif qt_api == "pyqt6": + from PyQt6.QtCore import * -elif qt_api == 'pyside2': + from PyQt6.QtCore import pyqtProperty as Property + from PyQt6.QtCore import pyqtSignal as Signal + from PyQt6.QtCore import pyqtSlot as Slot + + __version__ = QT_VERSION_STR + __version_info__ = tuple(map(int, QT_VERSION_STR.split("."))) + +elif qt_api == "pyside6": + from PySide6.QtCore import * + + from PySide6 import __version__, __version_info__ + +else: from PySide2.QtCore import * from PySide2 import __version__, __version_info__ -else: - try: - from PySide import __version__, __version_info__ - except ImportError: - pass - from PySide.QtCore import * diff -Nru python-pyface-6.1.2/pyface/qt/QtGui.py python-pyface-7.4.0/pyface/qt/QtGui.py --- python-pyface-6.1.2/pyface/qt/QtGui.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtGui.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,32 +1,86 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.Qt import QKeySequence, QTextCursor from PyQt4.QtGui import * -elif qt_api == 'pyqt5': + # forward-compatible font weights + # see https://doc.qt.io/qt-5/qfont.html#Weight-enum + QFont.Weight.ExtraLight = 12 + QFont.Weight.Medium = 57 + QFont.Weight.ExtraBold = 81 + +elif qt_api == "pyqt5": from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtPrintSupport import * from PyQt5.QtCore import ( - QAbstractProxyModel, QItemSelection, QItemSelectionModel, - QItemSelectionRange, QSortFilterProxyModel, QStringListModel + QAbstractProxyModel, + QItemSelection, + QItemSelectionModel, + QItemSelectionRange, + QSortFilterProxyModel, + QStringListModel, + ) + + QStyleOptionTabV2 = QStyleOptionTab + QStyleOptionTabV3 = QStyleOptionTab + QStyleOptionTabBarBaseV2 = QStyleOptionTabBarBase + +elif qt_api == "pyqt6": + from PyQt6.QtGui import * + from PyQt6.QtWidgets import * + from PyQt6.QtPrintSupport import * + from PyQt6.QtCore import ( + QAbstractProxyModel, + QItemSelection, + QItemSelectionModel, + QItemSelectionRange, + QSortFilterProxyModel, + QStringListModel, + ) + + QStyleOptionTabV2 = QStyleOptionTab + QStyleOptionTabV3 = QStyleOptionTab + QStyleOptionTabBarBaseV2 = QStyleOptionTabBarBase + +elif qt_api == "pyside6": + from PySide6.QtGui import * + from PySide6.QtWidgets import * + from PySide6.QtPrintSupport import * + from PySide6.QtCore import ( + QAbstractProxyModel, + QItemSelection, + QItemSelectionModel, + QItemSelectionRange, + QSortFilterProxyModel, ) + QStyleOptionTabV2 = QStyleOptionTab QStyleOptionTabV3 = QStyleOptionTab QStyleOptionTabBarBaseV2 = QStyleOptionTabBarBase -elif qt_api == 'pyside2': +else: from PySide2.QtGui import * from PySide2.QtWidgets import * from PySide2.QtPrintSupport import * from PySide2.QtCore import ( - QAbstractProxyModel, QItemSelection, QItemSelectionModel, - QItemSelectionRange, QSortFilterProxyModel + QAbstractProxyModel, + QItemSelection, + QItemSelectionModel, + QItemSelectionRange, + QSortFilterProxyModel, ) + QStyleOptionTabV2 = QStyleOptionTab QStyleOptionTabV3 = QStyleOptionTab QStyleOptionTabBarBaseV2 = QStyleOptionTabBarBase - -else: - from PySide.QtGui import * diff -Nru python-pyface-6.1.2/pyface/qt/QtMultimedia.py python-pyface-7.4.0/pyface/qt/QtMultimedia.py --- python-pyface-6.1.2/pyface/qt/QtMultimedia.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtMultimedia.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,28 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import warnings + +from . import qt_api + +if qt_api == "pyqt5": + from PyQt5.QtMultimedia import * + +elif qt_api == "pyqt6": + from PyQt6.QtMultimedia import * + +elif qt_api == "pyside6": + from PySide6.QtMultimedia import * + +elif qt_api == "pyside2": + from PySide2.QtMultimedia import * + +else: + warnings.warn("Qt 4 does not support QtMultimedia", DeprecationWarning) diff -Nru python-pyface-6.1.2/pyface/qt/QtMultimediaWidgets.py python-pyface-7.4.0/pyface/qt/QtMultimediaWidgets.py --- python-pyface-6.1.2/pyface/qt/QtMultimediaWidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtMultimediaWidgets.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,30 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import warnings + +from . import qt_api + +if qt_api == "pyqt5": + from PyQt5.QtMultimediaWidgets import * + +elif qt_api == "pyqt6": + from PyQt6.QtMultimediaWidgets import * + +elif qt_api == "pyside6": + from PySide6.QtMultimediaWidgets import * + +elif qt_api == "pyside2": + from PySide2.QtMultimediaWidgets import * + +else: + warnings.warn( + "Qt 4 does not support QtMultimediaWidgets", DeprecationWarning + ) diff -Nru python-pyface-6.1.2/pyface/qt/QtNetwork.py python-pyface-7.4.0/pyface/qt/QtNetwork.py --- python-pyface-6.1.2/pyface/qt/QtNetwork.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtNetwork.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,25 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtNetwork import * -elif qt_api == 'pyqt5': +elif qt_api == "pyqt5": from PyQt5.QtNetwork import * -elif qt_api == 'pyside2': - from PySide2.QtNetwork import * +elif qt_api == "pyqt6": + from PyQt6.QtNetwork import * + +elif qt_api == "pyside6": + from PySide6.QtNetwork import * else: - from PySide.QtNetwork import * + from PySide2.QtNetwork import * diff -Nru python-pyface-6.1.2/pyface/qt/QtOpenGL.py python-pyface-7.4.0/pyface/qt/QtOpenGL.py --- python-pyface-6.1.2/pyface/qt/QtOpenGL.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtOpenGL.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,25 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtOpenGL import * -elif qt_api == 'pyqt5': +elif qt_api == "pyqt5": from PyQt5.QtOpenGL import * -elif qt_api == 'pyside2': - from PySide2.QtOpenGL import * +elif qt_api == "pyqt6": + from PyQt6.QtOpenGL import * + +elif qt_api == "pyside6": + from PySide6.QtOpenGL import * else: - from PySide.QtOpenGL import * + from PySide2.QtOpenGL import * diff -Nru python-pyface-6.1.2/pyface/qt/QtScript.py python-pyface-7.4.0/pyface/qt/QtScript.py --- python-pyface-6.1.2/pyface/qt/QtScript.py 2019-06-18 10:52:41.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtScript.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,18 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtScript import * -elif qt_api == 'pyqt5': - import warnings - warnings.warn(DeprecationWarning("QtScript is not supported in PyQt5")) - -elif qt_api == 'pyside2': +else: import warnings - warnings.warn(DeprecationWarning("QtScript is not supported in PyQt5")) -else: - from PySide.QtScript import * + warnings.warn(DeprecationWarning("QtScript is not supported in PyQt5/PySide2")) diff -Nru python-pyface-6.1.2/pyface/qt/QtSvg.py python-pyface-7.4.0/pyface/qt/QtSvg.py --- python-pyface-6.1.2/pyface/qt/QtSvg.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtSvg.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,25 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtSvg import * -elif qt_api == 'pyqt5': +elif qt_api == "pyqt5": from PyQt5.QtSvg import * -elif qt_api == 'pyside2': - from PySide2.QtSvg import * +elif qt_api == "pyqt6": + from PyQt6.QtSvg import * + +elif qt_api == "pyside6": + from PySide6.QtSvg import * else: - from PySide.QtSvg import * + from PySide2.QtSvg import * diff -Nru python-pyface-6.1.2/pyface/qt/QtTest.py python-pyface-7.4.0/pyface/qt/QtTest.py --- python-pyface-6.1.2/pyface/qt/QtTest.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtTest.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,25 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtTest import * -elif qt_api == 'pyqt5': +elif qt_api == "pyqt5": from PyQt5.QtTest import * -elif qt_api == 'pyside2': - from PySide2.QtTest import * +elif qt_api == "pyqt6": + from PyQt6.QtTest import * + +elif qt_api == "pyside6": + from PySide6.QtTest import * else: - from PySide.QtTest import * + from PySide2.QtTest import * diff -Nru python-pyface-6.1.2/pyface/qt/QtWebKit.py python-pyface-7.4.0/pyface/qt/QtWebKit.py --- python-pyface-6.1.2/pyface/qt/QtWebKit.py 2019-06-18 10:52:41.000000000 +0000 +++ python-pyface-7.4.0/pyface/qt/QtWebKit.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,20 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from . import qt_api -if qt_api == 'pyqt': +if qt_api == "pyqt": from PyQt4.QtWebKit import * -elif qt_api == 'pyqt5': +elif qt_api == "pyqt5": from PyQt5.QtWidgets import * + try: from PyQt5.QtWebEngine import * from PyQt5.QtWebEngineWidgets import ( @@ -18,12 +28,39 @@ from PyQt5.QtWebKit import * from PyQt5.QtWebKitWidgets import * -elif qt_api == 'pyside2': +elif qt_api == "pyqt6": + from PyQt6.QtWidgets import * + + from PyQt6.QtWebEngineCore import ( + QWebEngineHistory as QWebHistory, + QWebEngineHistoryItem as QWebHistoryItem, + QWebEnginePage as QWebPage, + QWebEngineSettings as QWebSettings, + ) + from PyQt6.QtWebEngineWidgets import ( + QWebEngineView as QWebView, + ) + +elif qt_api == "pyside6": + from PySide6.QtWidgets import * + + from PySide6.QtWebEngineCore import ( + QWebEngineHistory as QWebHistory, + QWebEngineSettings as QWebSettings, + QWebEnginePage as QWebPage, + QWebEngineHistoryItem as QWebHistoryItem, + ) + from PySide6.QtWebEngineWidgets import ( + QWebEngineView as QWebView, + ) + +else: from PySide2.QtWidgets import * + # WebKit is currently in flux in PySide2 try: from PySide2.QtWebEngineWidgets import ( - #QWebEngineHistory as QWebHistory, + # QWebEngineHistory as QWebHistory, QWebEngineHistoryItem as QWebHistoryItem, QWebEnginePage as QWebPage, QWebEngineView as QWebView, @@ -32,6 +69,3 @@ except ImportError: from PySide2.QtWebKit import * from PySide2.QtWebKitWidgets import * - -else: - from PySide.QtWebKit import * diff -Nru python-pyface-6.1.2/pyface/resource/api.py python-pyface-7.4.0/pyface/resource/api.py --- python-pyface-6.1.2/pyface/resource/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2015, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from __future__ import absolute_import +# Thanks for using Enthought open source! + + +""" + +API for the ``pyface.resource`` subpackage. + +- :class:`~.ResourceFactory` +- :class:`~.ResourceManager` +- :func:`~.resource_path` + +""" from .resource_factory import ResourceFactory from .resource_manager import ResourceManager -from .resource_path import resource_path +from .resource_path import resource_path diff -Nru python-pyface-6.1.2/pyface/resource/__init__.py python-pyface-7.4.0/pyface/resource/__init__.py --- python-pyface-6.1.2/pyface/resource/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,5 +1,13 @@ -# Copyright (c) 2005-2011, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + """ Support for managing resources such as images and sounds. - Part of the TraitsGUI project of the Enthought Tool Suite. + Part of the TraitsUI project of the Enthought Tool Suite. """ diff -Nru python-pyface-6.1.2/pyface/resource/resource_factory.py python-pyface-7.4.0/pyface/resource/resource_factory.py --- python-pyface-6.1.2/pyface/resource/resource_factory.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource/resource_factory.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,34 +1,29 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Default base-class for resource factories. """ -class ResourceFactory: +class ResourceFactory(object): """ Default base-class for resource factories. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceFactory' interface. - ########################################################################### + # ------------------------------------------------------------------------ def image_from_file(self, filename): """ Creates an image from the data in the specified filename. """ - raise NotImplemented + raise NotImplementedError() def image_from_data(self, data): """ Creates an image from the specified data. """ - raise NotImplemented - -#### EOF ###################################################################### + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/resource/resource_manager.py python-pyface-7.4.0/pyface/resource/resource_manager.py --- python-pyface-6.1.2/pyface/resource/resource_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource/resource_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,39 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2009, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -#------------------------------------------------------------------------------ """ The default resource manager. A resource manager locates and loads application resources such as images and sounds etc. """ -# Standard library imports. -import collections, glob, inspect, os, sys, types +import collections.abc +import glob +import inspect +import os from os.path import join +import types from zipfile import is_zipfile, ZipFile -# Enthought library imports. +# importlib.resources is new in Python 3.7, and importlib.resources.files is +# new in Python 3.9, so for Python < 3.9 we must rely on the 3rd party +# importlib_resources package. +try: + from importlib.resources import files +except ImportError: + from importlib_resources import files + from traits.api import HasTraits, Instance, List from traits.util.resource import get_path -# Local imports. from pyface.resource.resource_factory import ResourceFactory from pyface.resource.resource_reference import ImageReference -import six class ResourceManager(HasTraits): @@ -37,30 +44,51 @@ """ # Allowed extensions for image resources. - IMAGE_EXTENSIONS = ['.png', '.jpg', '.bmp', '.gif', '.ico'] + IMAGE_EXTENSIONS = [".png", ".jpg", ".bmp", ".gif", ".ico"] # A list of additional search paths. These paths are fallbacks, and hence # have lower priority than the paths provided by resource objects. - extra_paths = List + extra_paths = List() # The resource factory is responsible for actually creating resources. # This is used so that (for example) different GUI toolkits can create # a images in the format that they require. resource_factory = Instance(ResourceFactory) - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def locate_image(self, image_name, path, size=None): - """ Locates an image. """ + """ Locates an image. - if not isinstance(path, collections.Sequence): + Parameters + ---------- + image_name : str + Name of the image file. + path : list of (str or ModuleType) + Paths from which image files will be searched. Note that for each + path, a subdirectory named 'images' will be search first. + The first match will be returned. + size : tuple of (m: int, n: int), optional + Specific size of the image requested. If provided, then + the subdirectory ``images/{m}x{n}`` will be searched first, + followed by the ``images`` subdirectory and its containing folder. + Default is None. + + Returns + ------- + image_ref : ImageReference or None + ImageReference to the image found, or None if no matching images + are found. + """ + + if not isinstance(path, collections.abc.Sequence): path = [path] resource_path = [] for item in list(path) + self.extra_paths: - if isinstance(item, six.string_types): + if isinstance(item, str): resource_path.append(item) elif isinstance(item, types.ModuleType): resource_path.append(item) @@ -81,9 +109,9 @@ return image - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _locate_image(self, image_name, resource_path, size): """ Attempts to locate an image resource. @@ -103,33 +131,34 @@ # Otherwise, we will search for common image suffixes. else: extensions = self.IMAGE_EXTENSIONS - pattern = image_name + '.*' + pattern = image_name + ".*" # Try the 'images' sub-directory first (since that is commonly # where we put them!). If the image is not found there then look # in the directory itself. if size is None: - subdirs = ['images', ''] + subdirs = ["images", ""] else: - subdirs = ['images/%dx%d' % (size[0], size[1]), 'images', ''] + subdirs = ["images/%dx%d" % (size[0], size[1]), "images", ""] + + # Concrete image filenames to be searched + image_filenames = [basename + extension for extension in extensions] for dirname in resource_path: - # If we come across a reference to a module, use pkg_resources - # to try and find the image inside of an .egg, .zip, etc. + # If we come across a reference to a module, try and find the + # image inside of an .egg, .zip, etc. if isinstance(dirname, types.ModuleType): - from pkg_resources import resource_string - for path in subdirs: - for extension in extensions: - searchpath = '%s/%s%s' % (path, basename, extension) - try: - data = resource_string(dirname.__name__, searchpath) - return ImageReference(self.resource_factory, - data = data) - except IOError: - pass - else: + try: + data = _find_resource_data( + dirname, subdirs, image_filenames + ) + except OSError: continue + else: + return ImageReference( + self.resource_factory, data=data + ) # Is there anything resembling the image name in the directory? for path in subdirs: @@ -144,9 +173,9 @@ return reference # Is there an 'images' zip file in the directory? - zip_filename = join(dirname, 'images.zip') + zip_filename = join(dirname, "images.zip") if os.path.isfile(zip_filename): - zip_file = ZipFile(zip_filename, 'r') + zip_file = ZipFile(zip_filename, "r") # Try the image name itself, and then the image name with # common images suffixes. for extension in extensions: @@ -164,36 +193,36 @@ # is this a path within a zip file? # first, find the zip file in the path filepath = dirname - zippath = '' - while not is_zipfile(filepath) and \ - os.path.splitdrive(filepath)[1].startswith('\\') and \ - os.path.splitdrive(filepath)[1].startswith('/'): + zippath = "" + while ( + not is_zipfile(filepath) + and os.path.splitdrive(filepath)[1].startswith("\\") + and os.path.splitdrive(filepath)[1].startswith("/") + ): filepath, tail = os.path.split(filepath) - if zippath != '': - zippath = tail + '/' + zippath + if zippath != "": + zippath = tail + "/" + zippath else: zippath = tail - - # if we found a zipfile, then look inside it for the image! if is_zipfile(filepath): zip_file = ZipFile(filepath) - for subpath in ['images', '']: + for subpath in ["images", ""]: for extension in extensions: try: # this is a little messy. since zip files don't # recognize a leading slash, we have to be very # particular about how we build this path when # there are empty strings - if zippath != '': - path = zippath + '/' + if zippath != "": + path = zippath + "/" else: - path = '' + path = "" - if subpath != '': - path = path + subpath + '/' + if subpath != "": + path = path + subpath + "/" path = path + basename + extension # now that we have the path we can attempt to load @@ -201,7 +230,7 @@ image_data = zip_file.read(path) reference = ImageReference( self.resource_factory, data=image_data - ) + ) # if there was no exception then return the result return reference @@ -214,7 +243,7 @@ def _get_resource_path(self, object): """ Returns the resource path for an object. """ - if hasattr(object, 'resource_path'): + if hasattr(object, "resource_path"): resource_path = object.resource_path else: @@ -238,4 +267,79 @@ return resource_path -#### EOF ###################################################################### + +def _get_package_data(module, rel_path): + """ Return package data in bytes for the given module and resource path. + + Parameters + ---------- + module : ModuleType + A module from which package data will be discovered. + If the module name does not conform to the package requirement, then + its "__file__" attribute is used for locating the directory to search + for resource files. + rel_path : str + "/"-separated path for loading data file. + + Returns + ------- + data : bytes + Loaded data in bytes. + + Raises + ------ + OSError + If the path referenced does not resolve to an existing file or the + file cannot be read. + """ + + if (module.__spec__ is None + or module.__spec__.submodule_search_locations is None): + module_dir_path = os.path.dirname(module.__file__) + path = os.path.join(module_dir_path, *rel_path.split("/")) + with open(path, "rb") as fp: + return fp.read() + + return ( + files(module).joinpath(rel_path).read_bytes() + ) + + +def _find_resource_data(module, subdirs, filenames): + """ For the given module, search directories and names, find a matching + resource file and return its content as bytes. + + Parameters + ---------- + module : ModuleType + A module from which package data will be discovered. + If the module name does not conform to the package requirement, then + its "__file__" attribute is used for locating the directory to search + for resource files. + subdirs : list of str + Name of subdirectories to try. Each value can be a "/"-separated + string to represent more nested subdirectories. + filenames : list of str + File names to try. + + Returns + ------- + data : bytes + Loaded data in bytes. + + Raises + ------ + OSError + If the path referenced does not resolve to an existing file or the + file cannot be read. + """ + for path in subdirs: + for filename in filenames: + searchpath = "%s/%s" % (path, filename) + try: + return _get_package_data(module, searchpath) + except OSError: + pass + raise OSError( + "Unable to load data for the given module and search paths." + ) diff -Nru python-pyface-6.1.2/pyface/resource/resource_path.py python-pyface-7.4.0/pyface/resource/resource_path.py --- python-pyface-6.1.2/pyface/resource/resource_path.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource/resource_path.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,12 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2009 Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -#------------------------------------------------------------------------------ """ Functions to determine resource locations from the call stack. This type of resource location is normally requested from the constructor for @@ -20,7 +19,7 @@ from traits.trait_base import get_resource_path -def resource_module(level = 2): +def resource_module(level=2): """Return a module reference calculated from the caller's stack. Note that what we want is the reference to the package containing the @@ -28,18 +27,16 @@ for our default resource sub-dirs as children. """ - module_name = sys._getframe(level).f_globals.get('__name__', '__main__') - if '.' in module_name: - module_name = '.'.join(module_name.split('.')[:-1]) + module_name = sys._getframe(level).f_globals.get("__name__", "__main__") + if "." in module_name: + module_name = ".".join(module_name.split(".")[:-1]) module = sys.modules.get(module_name) return module -def resource_path(level = 2): +def resource_path(level=2): """Return a resource path calculated from the caller's stack. """ return get_resource_path(level + 1) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/resource/resource_reference.py python-pyface-7.4.0/pyface/resource/resource_reference.py --- python-pyface-6.1.2/pyface/resource/resource_reference.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource/resource_reference.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Resource references. """ -# Enthought library imports. from traits.api import Any, HasTraits, Instance -# Local imports. + from pyface.resource.resource_factory import ResourceFactory @@ -30,28 +26,27 @@ """ # The resource factory that will be used to load the resource. - resource_factory = Instance(ResourceFactory) # ReadOnly + resource_factory = Instance(ResourceFactory) # ReadOnly - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceReference' interface. - ########################################################################### + # ------------------------------------------------------------------------ def load(self): """ Loads the resource. """ - raise NotImplementedError + raise NotImplementedError() class ImageReference(ResourceReference): """ A reference to an image resource. """ # Iff the image was found in a file then this is the name of that file. - filename = Any # ReadOnly + filename = Any # ReadOnly # Iff the image was found in a zip file then this is the image data that # was read from the zip file. - data = Any # ReadOnly - + data = Any # ReadOnly def __init__(self, resource_factory, filename=None, data=None): """ Creates a new image reference. """ @@ -62,9 +57,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceReference' interface. - ########################################################################### + # ------------------------------------------------------------------------ def load(self): """ Loads the resource. """ @@ -79,5 +74,3 @@ raise ValueError("Image reference has no filename OR data") return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/resource_manager.py python-pyface-7.4.0/pyface/resource_manager.py --- python-pyface-6.1.2/pyface/resource_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/resource_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a shared resource manager. """ -from __future__ import absolute_import -# Enthought library imports. from pyface.resource.api import ResourceManager # Import the toolkit specific version. from .toolkit import toolkit_object -PyfaceResourceFactory = toolkit_object('resource_manager:PyfaceResourceFactory') + +PyfaceResourceFactory = toolkit_object( + "resource_manager:PyfaceResourceFactory" +) #: A shared instance. resource_manager = ResourceManager(resource_factory=PyfaceResourceFactory()) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/single_choice_dialog.py python-pyface-7.4.0/pyface/single_choice_dialog.py --- python-pyface-6.1.2/pyface/single_choice_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/single_choice_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A dialog that allows the user to chose a single item from a list. """ -from __future__ import absolute_import from .constant import OK from .toolkit import toolkit_object # Import the toolkit specific version. -SingleChoiceDialog = toolkit_object('single_choice_dialog:SingleChoiceDialog') +SingleChoiceDialog = toolkit_object("single_choice_dialog:SingleChoiceDialog") # Convenience functions. -def choose_one(parent, message, choices, title='Choose', cancel=True): +def choose_one(parent, message, choices, title="Choose", cancel=True): """ Convenience method to show an information message dialog. Parameters @@ -46,8 +42,11 @@ The selected object, or None if cancelled. """ dialog = SingleChoiceDialog( - parent=parent, message=message, choices=choices, title=title, - cancel=cancel + parent=parent, + message=message, + choices=choices, + title=title, + cancel=cancel, ) result = dialog.open() if result == OK: diff -Nru python-pyface-6.1.2/pyface/sizers/flow.py python-pyface-7.4.0/pyface/sizers/flow.py --- python-pyface-6.1.2/pyface/sizers/flow.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/sizers/flow.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,155 +1,149 @@ -#------------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Defines a horizontal or vertical flow layout sizer for wxPython +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Written by: David C. Morrill -# -# Date: 01/12/2006 -# -# (c) Copyright 2006 by Enthought, Inc. -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports: -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! import wx -from pyface.timer.api \ - import do_later +from pyface.timer.api import do_later -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'FlowSizer' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class FlowSizer ( wx.PySizer ): - #--------------------------------------------------------------------------- +class FlowSizer(wx.PySizer): + + # --------------------------------------------------------------------------- # Initializes the object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def __init__ ( self, orient = wx.HORIZONTAL ): - super( FlowSizer, self ).__init__() - self._orient = orient - self._frozen = False - self._needed_size = None + def __init__(self, orient=wx.HORIZONTAL): + super().__init__() + self._orient = orient + self._frozen = False + self._needed_size = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Calculates the minimum size needed by the sizer: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def CalcMin ( self ): + def CalcMin(self): """ Calculates the minimum size needed by the sizer. """ if self._needed_size is not None: return self._needed_size - horizontal = (self._orient == wx.HORIZONTAL) + horizontal = self._orient == wx.HORIZONTAL dx = dy = 0 - for item in self.GetChildren() : + for item in self.GetChildren(): idx, idy = item.CalcMin() if horizontal: - dy = max( dy, idy ) + dy = max(dy, idy) else: - dx = max( dx, idx ) + dx = max(dx, idx) - return wx.Size( dx, dy ) + return wx.Size(dx, dy) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Layout the contents of the sizer based on the sizer's current size and # position: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def RecalcSizes ( self ): + def RecalcSizes(self): """ Layout the contents of the sizer based on the sizer's current size and position. """ - horizontal = (self._orient == wx.HORIZONTAL) - x, y = self.GetPosition() - dx, dy = self.GetSize() - x0, y0 = x, y - ex = x + dx - ey = y + dy - mdx = mdy = sdx = sdy = 0 + horizontal = self._orient == wx.HORIZONTAL + x, y = self.GetPosition() + dx, dy = self.GetSize().Get() + x0, y0 = x, y + ex = x + dx + ey = y + dy + mdx = mdy = sdx = sdy = 0 visible = True cur_max = 0 - for item in self.GetChildren() : - idx, idy = item.CalcMin() - expand = item.GetFlag() & wx.EXPAND + for item in self.GetChildren(): + idx, idy = item.CalcMin() + expand = item.GetFlag() & wx.EXPAND if horizontal: if (x > x0) and ((x + idx) > ex): - x = x0 - y += (mdy + sdy) + x = x0 + y += mdy + sdy mdy = sdy = 0 if y >= ey: visible = False - cur_max = max( idy, cur_max ) + cur_max = max(idy, cur_max) if expand: idy = cur_max if item.IsSpacer(): - sdy = max( sdy, idy ) + sdy = max(sdy, idy) if x == x0: idx = 0 - item.SetDimension( wx.Point( x, y ), wx.Size( idx, idy ) ) - item.Show( visible ) - x += idx - mdy = max( mdy, idy ) + item.SetDimension(wx.Point(x, y), wx.Size(idx, idy)) + item.Show(visible) + x += idx + mdy = max(mdy, idy) else: if (y > y0) and ((y + idy) > ey): - y = y0 - x += (mdx + sdx) + y = y0 + x += mdx + sdx mdx = sdx = 0 if x >= ex: visible = False - cur_max = max( idx, cur_max ) + cur_max = max(idx, cur_max) if expand: idx = cur_max if item.IsSpacer(): - sdx = max( sdx, idx ) + sdx = max(sdx, idx) if y == y0: idy = 0 - item.SetDimension( wx.Point( x, y ), wx.Size( idx, idy ) ) - item.Show( visible ) - y += idy - mdx = max( mdx, idx ) + item.SetDimension(wx.Point(x, y), wx.Size(idx, idy)) + item.Show(visible) + y += idy + mdx = max(mdx, idx) if (not visible) and (self._needed_size is None): max_dx = max_dy = 0 if horizontal: - max_dy = max( dy, y + mdy + sdy - y0 ) + max_dy = max(dy, y + mdy + sdy - y0) else: - max_dx = max( dx, x + mdx + sdx - x0 ) - self._needed_size = wx.Size( max_dx, max_dy ) + max_dx = max(dx, x + mdx + sdx - x0) + self._needed_size = wx.Size(max_dx, max_dy) if not self._frozen: - self._do_parent( '_freeze' ) - do_later( self._do_parent, '_thaw' ) + self._do_parent("_freeze") + do_later(self._do_parent, "_thaw") else: self._needed_size = None - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Prevents the specified window from doing any further screen updates: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _freeze ( self, window ): + def _freeze(self, window): """ Prevents the specified window from doing any further screen updates. """ window.Freeze() self._frozen = True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Lays out a specified window and then allows it to be updated again: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _thaw ( self, window ): + def _thaw(self, window): """ Lays out a specified window and then allows it to be updated again. """ window.Layout() @@ -158,17 +152,17 @@ self._frozen = False window.Thaw() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Does a specified operation on the sizer's parent window: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _do_parent ( self, method ): + def _do_parent(self, method): """ Does a specified operation on the sizer's parent window. """ i = 0 while True: try: - item = self.GetItem( i ) + item = self.GetItem(i) if item is None: break i += 1 @@ -176,6 +170,5 @@ return if item.IsWindow(): - getattr( self, method )( item.GetWindow().GetParent() ) + getattr(self, method)(item.GetWindow().GetParent()) return - diff -Nru python-pyface-6.1.2/pyface/sizers/__init__.py python-pyface-7.4.0/pyface/sizers/__init__.py --- python-pyface-6.1.2/pyface/sizers/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/sizers/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1 +1,9 @@ - +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/sorter.py python-pyface-7.4.0/pyface/sorter.py --- python-pyface-6.1.2/pyface/sorter.py 2019-05-31 13:42:40.000000000 +0000 +++ python-pyface-7.4.0/pyface/sorter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Base class for all sorters. """ +# Thanks for using Enthought open source! +""" Base class for all sorters. """ -# Enthought library imports. from traits.api import HasTraits class Sorter(HasTraits): """ Abstract base class for all sorters. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ViewerSorter' interface. - ########################################################################### + # ------------------------------------------------------------------------ def sort(self, widget, parent, nodes): """ Sorts a list of nodes IN PLACE. @@ -38,42 +33,19 @@ # This creates a comparison function with the names 'widget' and # 'parent' bound to the corresponding arguments to this method. - def comparator(node_a, node_b): + def key(node): """ Comparator. """ - return self.compare(widget, parent, node_a, node_b) + return self.key(widget, parent, node) - nodes.sort(comparator) + nodes.sort(key=key) return nodes - def compare(self, widget, parent, node_a, node_b): - """ Returns the result of comparing two nodes. - - 'widget' is the widget that we are sorting nodes for. - 'parent' is the parent node. - 'node_a' is the the first node to compare. - 'node_b' is the the second node to compare. - - """ - - # Get the category for each node. - category_a = self.category(widget, parent, node_a) - category_b = self.category(widget, parent, node_b) - - # If they are not in the same category then return the result of - # comparing the categories. - if category_a != category_b: - result = cmp(category_a, category_b) - - else: - label_a = widget.model.get_text(node_a) - label_b = widget.model.get_text(node_b) - - # Compare the label text. - result = cmp(label_a, label_b) - - return result + def key(self, widget, parent, node): + category = self.category(widget, parent, node) + text = widget.model.get_text(node) + return (category, text) def category(self, widget, parent, node): """ Returns the category (an integer) for an node. @@ -105,5 +77,3 @@ """ return False - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/splash_screen_log_handler.py python-pyface-7.4.0/pyface/splash_screen_log_handler.py --- python-pyface-6.1.2/pyface/splash_screen_log_handler.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/splash_screen_log_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2015, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A log handler that emits records to a splash screen. """ -# Standard library imports. from logging import Handler -import six class SplashScreenLogHandler(Handler): @@ -31,7 +26,7 @@ The splash screen being used to display the log messages """ # Base class constructor. - super(SplashScreenLogHandler, self).__init__() + super().__init__() # The splash screen that we will display log messages on. self._splash_screen = splash_screen @@ -44,4 +39,4 @@ record : logging record instance The log record to be displayed. """ - self._splash_screen.text = six.text_type(record.getMessage()) + u'...' + self._splash_screen.text = str(record.getMessage()) + "..." diff -Nru python-pyface-6.1.2/pyface/splash_screen.py python-pyface-7.4.0/pyface/splash_screen.py --- python-pyface-6.1.2/pyface/splash_screen.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/splash_screen.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a splash screen. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -SplashScreen = toolkit_object('splash_screen:SplashScreen') -#### EOF ###################################################################### +SplashScreen = toolkit_object("splash_screen:SplashScreen") diff -Nru python-pyface-6.1.2/pyface/split_application_window.py python-pyface-7.4.0/pyface/split_application_window.py --- python-pyface-6.1.2/pyface/split_application_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/split_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A window that is split in two either horizontally or vertically. """ -# Local imports. from pyface.application_window import ApplicationWindow from pyface.split_widget import SplitWidget @@ -22,9 +18,9 @@ class SplitApplicationWindow(ApplicationWindow, SplitWidget): """ A window that is split in two either horizontally or vertically. """ - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'ApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Creates the window contents. diff -Nru python-pyface-6.1.2/pyface/split_dialog.py python-pyface-7.4.0/pyface/split_dialog.py --- python-pyface-6.1.2/pyface/split_dialog.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/split_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A dialog that is split in two either horizontally or vertically. """ -# Local imports. from pyface.dialog import Dialog from pyface.split_widget import SplitWidget @@ -22,9 +18,9 @@ class SplitDialog(Dialog, SplitWidget): """ A dialog that is split in two either horizontally or vertically. """ - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Dialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_dialog_area(self, parent): """ Creates the main content of the dialog. diff -Nru python-pyface-6.1.2/pyface/split_panel.py python-pyface-7.4.0/pyface/split_panel.py --- python-pyface-6.1.2/pyface/split_panel.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/split_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A panel that is split in two either horizontally or vertically. """ +import warnings -# Local imports. from pyface.split_widget import SplitWidget from pyface.widget import Widget @@ -22,15 +19,28 @@ class SplitPanel(Widget, SplitWidget): """ A panel that is split in two either horizontally or vertically. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, **traits): + def __init__(self, parent=None, **traits): """ Creates a new panel. """ + create = traits.pop("create", True) + # Base class constructor. - super(SplitPanel, self).__init__(**traits) + super().__init__(parent=parent, **traits) - # Create the widget's toolkit-specific control. - self.control = self._create_splitter(parent) + if create: + # Create the widget's toolkit-specific control. + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + def _create_control(self, parent): + """ Create the toolkit control """ + return self._create_splitter(parent) diff -Nru python-pyface-6.1.2/pyface/split_widget.py python-pyface-7.4.0/pyface/split_widget.py --- python-pyface-6.1.2/pyface/split_widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/split_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Mix-in class for split widgets. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -SplitWidget = toolkit_object('split_widget:SplitWidget') -#### EOF ###################################################################### +SplitWidget = toolkit_object("split_widget:SplitWidget") diff -Nru python-pyface-6.1.2/pyface/system_metrics.py python-pyface-7.4.0/pyface/system_metrics.py --- python-pyface-6.1.2/pyface/system_metrics.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/system_metrics.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of access to system metrics (screen width and height etc). """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -SystemMetrics = toolkit_object('system_metrics:SystemMetrics') -#### EOF ###################################################################### +SystemMetrics = toolkit_object("system_metrics:SystemMetrics") diff -Nru python-pyface-6.1.2/pyface/tasks/action/api.py python-pyface-7.4.0/pyface/tasks/action/api.py --- python-pyface-6.1.2/pyface/tasks/action/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,96 @@ -# Copyright (c) 2010-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import absolute_import +""" + +API for the ``pyface.tasks.action`` subpackage. + +Tasks-specific Action Controller +-------------------------------- + +- :class:`~.TaskActionController` + +Tasks-specific Action Manager factory +------------------------------------- + +- :class:`~.TaskActionManagerBuilder` + +Tasks-specific Actions +---------------------- + +- :class:`~.CentralPaneAction` +- :class:`~.DockPaneAction` +- :class:`~.EditorAction` +- :class:`~.TaskAction` +- :class:`~.TaskWindowAction` +- :class:`~.TasksApplicationAction` + +Useful Tasks Actions and Groups +------------------------------- + +- :class:`~.DockPaneToggleGroup` +- :class:`~.TaskToggleGroup` +- :class:`~.TaskWindowToggleGroup` +- :class:`~.CreateTaskWindowAction` + +ActionSchemas and aliases +------------------------- + +Import of these objects from pyface.tasks.actions.api is deprecated and will +be removed in a future Pyface release. Instead use pyface.action.schema.api. + +- :class:`~.ActionSchema` +- :class:`~.GroupSchema` +- :class:`~.MenuSchema` +- :class:`~.MenuBarSchema` +- :class:`~.ToolBarSchema` +- :attr:`~.SGroup` +- :attr:`~.SMenu` +- :attr:`~.SMenuBar` +- :attr:`~.SToolBar` + +Schema Addition +--------------- + +Import of this object from pyface.tasks.actions.api is deprecated and will +be removed in a future Pyface release. Instead use pyface.action.schema.api. + +- :class:`~.SchemaAddition` + +""" -# Local imports. from .dock_pane_toggle_group import DockPaneToggleGroup from .schema import ( - ActionSchema, GroupSchema, MenuSchema, MenuBarSchema, ToolBarSchema, - SGroup, SMenu, SMenuBar, SToolBar + ActionSchema, + GroupSchema, + MenuSchema, + MenuBarSchema, + ToolBarSchema, + SGroup, + SMenu, + SMenuBar, + SToolBar, ) from .schema_addition import SchemaAddition from .task_action import ( - CentralPaneAction, DockPaneAction, EditorAction, TaskAction, - TaskWindowAction + CentralPaneAction, + DockPaneAction, + EditorAction, + TaskAction, + TaskWindowAction, ) from .task_action_controller import TaskActionController from .task_action_manager_builder import TaskActionManagerBuilder from .task_toggle_group import TaskToggleGroup from .task_window_toggle_group import TaskWindowToggleGroup from .tasks_application_action import ( - CreateTaskWindowAction, TasksApplicationAction + CreateTaskWindowAction, + TasksApplicationAction, ) diff -Nru python-pyface-6.1.2/pyface/tasks/action/dock_pane_toggle_group.py python-pyface-7.4.0/pyface/tasks/action/dock_pane_toggle_group.py --- python-pyface-6.1.2/pyface/tasks/action/dock_pane_toggle_group.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/dock_pane_toggle_group.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,12 +1,26 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A Group for toggling the visibility of a task's dock panes. """ -# Enthought library imports. from pyface.action.api import Action, ActionItem, Group -from traits.api import cached_property, Instance, List, on_trait_change, \ - Property, Unicode +from traits.api import ( + cached_property, + Instance, + List, + observe, + Property, + Str, +) + -# Local imports. from pyface.tasks.i_dock_pane import IDockPane @@ -14,22 +28,22 @@ """ An Action for toggling the visibility of a dock pane. """ - #### 'DockPaneToggleAction' interface ##################################### + # 'DockPaneToggleAction' interface ------------------------------------- dock_pane = Instance(IDockPane) - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- - name = Property(Unicode, depends_on='dock_pane.name') - style = 'toggle' - tooltip = Property(Unicode, depends_on='name') + name = Property(Str, observe="dock_pane.name") + style = "toggle" + tooltip = Property(Str, observe="name") - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): - super(DockPaneToggleAction, self).destroy() + super().destroy() # Make sure that we are not listening to changes to the pane anymore. # In traits style, we will set the basic object to None and have the @@ -40,41 +54,42 @@ if self.dock_pane: self.dock_pane.visible = not self.dock_pane.visible - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_name(self): if self.dock_pane is None: - return 'UNDEFINED' + return "UNDEFINED" return self.dock_pane.name def _get_tooltip(self): - return u'Toggles the visibility of the %s pane.' % self.name + return "Toggles the visibility of the %s pane." % self.name - @on_trait_change('dock_pane.visible') - def _update_checked(self): + @observe("dock_pane.visible") + def _update_checked(self, event): if self.dock_pane: self.checked = self.dock_pane.visible - @on_trait_change('dock_pane.closable') - def _update_visible(self): + @observe("dock_pane.closable") + def _update_visible(self, event): if self.dock_pane: self.visible = self.dock_pane.closable + class DockPaneToggleGroup(Group): """ A Group for toggling the visibility of a task's dock panes. """ - #### 'Group' interface #################################################### + # 'Group' interface ---------------------------------------------------- - id = 'DockPaneToggleGroup' + id = "DockPaneToggleGroup" - items = List + items = List() - #### 'DockPaneToggleGroup' interface ###################################### + # 'DockPaneToggleGroup' interface -------------------------------------# - task = Property(depends_on='parent.controller') + task = Property(observe="parent.controller") @cached_property def _get_task(self): @@ -85,7 +100,7 @@ return manager.controller.task - dock_panes = Property(depends_on='task.window._states.dock_panes') + dock_panes = Property(observe="task.window._states.items.dock_panes") @cached_property def _get_dock_panes(self): @@ -102,10 +117,10 @@ manager = manager.parent return manager - #### Private interface #################################################### + # Private interface ---------------------------------------------------- - @on_trait_change('dock_panes[]') - def _dock_panes_updated(self): + @observe("dock_panes.items") + def _dock_panes_updated(self, event): """Recreate the group items when dock panes have been added/removed. """ diff -Nru python-pyface-6.1.2/pyface/tasks/action/listening_action.py python-pyface-7.4.0/pyface/tasks/action/listening_action.py --- python-pyface-6.1.2/pyface/tasks/action/listening_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/listening_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,15 @@ -# Backward compatibility import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -from pyface.action.listening_action import ListeningAction \ No newline at end of file +""" This module exists purely for backwards compatibility. +Please use :class:`pyface.action.listening_action.ListeningAction` instead. +""" + +from pyface.action.listening_action import ListeningAction # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/tasks/action/schema_addition.py python-pyface-7.4.0/pyface/tasks/action/schema_addition.py --- python-pyface-6.1.2/pyface/tasks/action/schema_addition.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/schema_addition.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,46 +1,13 @@ -# Enthought library imports. -from traits.api import Callable, HasTraits, Str, Enum +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# Deprecated module - will be removed in a future Pyface release. -class SchemaAddition(HasTraits): - """ An addition to an existing menu bar or tool bar schema. - """ - - # The schema addition's identifier. This optional, but if left unspecified, - # other schema additions will be unable to refer to this one. - id = Str - - # A callable to create the item. Should have the following signature: - # callable() -> Action | ActionItem | Group | MenuManager | - # GroupSchema | MenuSchema - # If the result is a schema, it will itself admit of extension by other - # additions. If not, the result will be fixed. - factory = Callable - - # A forward-slash-separated path through the action hierarchy to the menu - # to add the action, group or menu to. For example: - # - To add an item to the menu bar: ``path = "MenuBar"`` - # - To add an item to the tool bar: ``path = "ToolBar"`` - # - To add an item to a sub-menu: ``path = "MenuBar/File/New"`` - path = Str - - # The item appears after the item with this ID. - # - for groups, this is the ID of another group. - # - for menus and actions, this is the ID of another menu or action. - after = Str - - # The action appears before the item with this ID. - # - for groups, this is the ID of another group. - # - for menus and actions, this is the ID of another menu or action. - before = Str - - # The action appears at the absolute specified position first or last. - # This is useful for example to keep the File menu the first menu in a - # menubar, the help menu the last etc. - # If multiple actions in a schema have absolute_position 'first', they - # will appear in the same order specified; unless 'before' and 'after' - # traits are set to sort these multiple items. - # This trait takes precedence over 'after' and 'before', and values of - # those traits that are not compatible with the absolute_position are - # ignored. - absolute_position = Enum(None, 'first', 'last') +from pyface.action.schema.schema_addition import SchemaAddition # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/tasks/action/schema.py python-pyface-7.4.0/pyface/tasks/action/schema.py --- python-pyface-6.1.2/pyface/tasks/action/schema.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/schema.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,160 +1,25 @@ -# Enthought library imports. -from pyface.action.api import Action, ActionItem, Group, \ - MenuManager, MenuBarManager, ToolBarManager -from pyface.util.id_helper import get_unique_id -from traits.api import Bool, Callable, Enum, HasTraits, Instance, \ - List, Property, Str, Trait, Tuple, Unicode - -# Trait definitions. -SubSchema = Trait(None, Action, ActionItem, Group, MenuManager, - Instance('pyface.tasks.action.schema.GroupSchema'), - Instance('pyface.tasks.action.schema.MenuSchema'), - Instance('pyface.tasks.action.schema.Schema')) - - -class Schema(HasTraits): - """ The abstract base class for all Tasks action schemas. - """ - - # The schema's identifier (unique within its parent schema). - id = Str - - def _id_default(self): - return get_unique_id(self) - - # The list of sub-items in the schema. These items can be other - # (non-top-level) schema or concrete instances from the Pyface API. - items = List(SubSchema) - - def __init__(self, *items, **traits): - """ Creates a new schema. - """ - super(Schema, self).__init__(**traits) - self.items.extend(items) - - def create(self, children): - """ Create the appropriate pyface.action instance with the specified - child items. - """ - raise NotImplementedError - - -class ActionSchema(Schema): - """ Action schema for Pyface Actions. - - An action schema cannot have children. It is used as an action factory - to make sure a larger schema (e.g., a menu schema) can be used multiple - times. Without using an ActionSchema, a reference to the action is added - to every menu created from the schema. When one of the menus is destroyed, - the action is also destroyed and is made unusable. - - """ - - #: A factory for the Action instance. - action_factory = Callable(Action) - - #: Items is overwritten to be empty and read-only to avoid assigning to - #: it by mistake. - items = Property() - def _get_items(self): - return [] - - def create(self, children): - """ Create the appropriate Pyface Action instance. """ - - traits = dict(id=self.id) - return self.action_factory(**traits) - - -class GroupSchema(Schema): - """ A schema for a Pyface Group. - """ - - # A factory for instantiating a pyface Group. - group_factory = Callable(Group) - - # Does the group require a separator when it is visualized? - separator = Bool(True) - - def create(self, children): - traits = dict(id=self.id, separator=self.separator) - return self.group_factory(*children, **traits) - - -class MenuSchema(Schema): - """ A schema for a Pyface MenuManager. - """ - - # The menu's user visible name. - name = Unicode - - # Does the menu require a separator before the menu item? - separator = Bool(False) - - # The default action for tool button when shown in a toolbar (Qt only) - action = Instance(Action) - - # A factory for instantiating a pyface MenuManager. - menu_manager_factory = Callable(MenuManager) - - def create(self, children): - traits = dict(id=self.id, name=self.name, separator=self.separator) - if self.action: - traits['action'] = self.action - return self.menu_manager_factory(*children, **traits) - - -class MenuBarSchema(Schema): - """ A schema for a Pyface MenuBarManager. - """ - - # Assign a default ID for menu bar schemas. - id = 'MenuBar' - - # A factory for instantiating a pyface MenuBarManager. - menu_bar_manager_factory = Callable(MenuBarManager) - - def create(self, children): - traits = dict(id=self.id) - return self.menu_bar_manager_factory(*children, **traits) - - -class ToolBarSchema(Schema): - """ A schema for a Pyface ToolBarManager. - """ - - # Assign a default ID for tool bar schemas. - id = 'ToolBar' - - # The tool bar's user visible name. Note that this name may not be used on - # all platforms. - name = Unicode('Tool Bar') - - # The size of tool images (width, height). - image_size = Tuple((16, 16)) - - # The orientation of the toolbar. - orientation = Enum('horizontal', 'vertical') - - # Should we display the horizontal divider? - show_divider = Bool(True) - - # Should we display the name of each tool bar tool under its image? - show_tool_names = Bool(True) - - # A factory for instantiating a pyface ToolBarManager - tool_bar_manager_factory = Callable(ToolBarManager) - - def create(self, children): - traits = dict(id=self.id, name=self.name, image_size=self.image_size, - orientation=self.orientation, - show_divider=self.show_divider, - show_tool_names=self.show_tool_names) - return self.tool_bar_manager_factory(*children, **traits) - - -# Convenience abbreviations. -SGroup = GroupSchema -SMenu = MenuSchema -SMenuBar = MenuBarSchema -SToolBar = ToolBarSchema +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# Deprecated module - will be removed in a future Pyface release. + +from pyface.action.schema.schema import ( # noqa: F401 + ActionSchema, + GroupSchema, + MenuBarSchema, + MenuSchema, + Schema, + SubSchema, + SGroup, + SMenu, + SMenuBar, + SToolBar, + ToolBarSchema, +) diff -Nru python-pyface-6.1.2/pyface/tasks/action/task_action_controller.py python-pyface-7.4.0/pyface/tasks/action/task_action_controller.py --- python-pyface-6.1.2/pyface/tasks/action/task_action_controller.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/task_action_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.action.api import ActionController from traits.api import Instance -# Local imports. + from pyface.tasks.task import Task from pyface.tasks.action.task_action import TaskAction @@ -15,14 +24,14 @@ can be added to action events. Currently, we attach a reference to the Task. """ - #### TaskActionController interface ####################################### + # TaskActionController interface --------------------------------------- - # The task that this is the controller for. + #: The task that this is the controller for. task = Instance(Task) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, action, event): """ Control an action invocation. diff -Nru python-pyface-6.1.2/pyface/tasks/action/task_action_manager_builder.py python-pyface-7.4.0/pyface/tasks/action/task_action_manager_builder.py --- python-pyface-6.1.2/pyface/tasks/action/task_action_manager_builder.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/task_action_manager_builder.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,47 +1,37 @@ -# Standard library imports. -from collections import defaultdict -import logging - -# Enthought library imports. -from pyface.action.api import ActionController, ActionManager -from traits.api import HasTraits, Instance +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -# Local imports. +from traits.api import Instance, List, Property + +from pyface.action.schema.action_manager_builder import ActionManagerBuilder +from pyface.action.schema.schema_addition import SchemaAddition from pyface.tasks.task import Task -from pyface.tasks.topological_sort import before_after_sort -from pyface.tasks.action.schema import Schema, ToolBarSchema -from pyface.tasks.action.schema_addition import SchemaAddition -# Logging. -logger = logging.getLogger(__name__) +class TaskActionManagerBuilder(ActionManagerBuilder): + """ ActionManagerBuilder for Tasks. -class TaskActionManagerBuilder(HasTraits): - """ Builds menu bars and tool bars from menu bar and tool bar schema, along - with any additions provided by the task. + This provides some additional convenience methods for extracting schema + information from a task and using it to build menu bar and toolbar + managers directly. """ - # The controller to assign to the menubar and toolbars. - controller = Instance(ActionController) - - # The Task to build menubars and toolbars for. + #: The Task to build menubars and toolbars for. task = Instance(Task) - ########################################################################### - # 'TaskActionManagerBuilder' interface. - ########################################################################### + #: The schema additions provided by the Task. + additions = Property(List(SchemaAddition), observe='task.extra_actions') - def create_action_manager(self, schema): - """ Create a manager for the given schema using the task's additions. - """ - additions_map = defaultdict(list) - for addition in self.task.extra_actions: - if addition.path: - additions_map[addition.path].append(addition) - - manager = self._create_action_manager_recurse(schema, additions_map) - manager.controller = self.controller - return manager + # ------------------------------------------------------------------------ + # 'TaskActionManagerBuilder' interface. + # ------------------------------------------------------------------------ def create_menu_bar_manager(self): """ Create a menu bar manager from the task's menu bar schema and @@ -55,206 +45,28 @@ """ Create tool bar managers from the tasks's tool bar schemas and additions. """ - schemas = self.task.tool_bars[:] - for addition in self.task.extra_actions: - if not addition.path: - schema = addition.factory() - if isinstance(schema, ToolBarSchema): - schemas.append(schema) - else: - logger.error('Invalid top-level schema addition: %r. Only ' - 'ToolBar schemas can be path-less.', schema) - return [ self.create_action_manager(schema) - for schema in self._get_ordered_schemas(schemas) ] - - def prepare_item(self, item, path): - """ Called immediately after a concrete Pyface item has been created - (or, in the case of items that are not produced from schemas, - immediately before they are processed). - - This hook can be used to perform last-minute transformations or - configuration. Returns a concrete Pyface item. - """ - return item + schemas = self.task.tool_bars + self.get_additional_toolbar_schemas() + return [ + self.create_action_manager(schema) + for schema in self._get_ordered_schemas(schemas) + ] - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### - - def _get_ordered_schemas(self, schemas): - begin = [] - middle = [] - end = [] - - for schema in schemas: - absolute_position = getattr(schema, 'absolute_position', None) - if absolute_position is None: - middle.append(schema) - elif absolute_position == 'last': - end.append(schema) - else: - begin.append(schema) - - schemas = (before_after_sort(begin) - + before_after_sort(middle) - + before_after_sort(end)) - return schemas - - def _group_items_by_id(self, items): - """ Group a list of action items by their ID. - - Action items are Schemas and Groups, MenuManagers, etc. - - Return a dictionary {item_id: list_of_items}, and a list containing - all the ids ordered by their appearance in the `all_items` list. The - ordered IDs are used as a replacement for an ordered dictionary, to - keep compatibility with Python <2.7 . - - """ - - ordered_items_ids = [] - id_to_items = defaultdict(list) - - for item in items: - if item.id not in id_to_items: - ordered_items_ids.append(item.id) - id_to_items[item.id].append(item) - - return id_to_items, ordered_items_ids - - def _group_items_by_class(self, items): - """ Group a list of action items by their class. - - Action items are Schemas and Groups, MenuManagers, etc. - - Return a dictionary {item_class: list_of_items}, and a list containing - all the classes ordered by their appearance in the `all_items` list. - The ordered classes are used as a replacement for an ordered - dictionary, to keep compatibility with Python <2.7 . - - """ - - ordered_items_class = [] - class_to_items = defaultdict(list) - - for item in items: - if item.__class__ not in class_to_items: - ordered_items_class.append(item.__class__) - class_to_items[item.__class__].append(item) - - return class_to_items, ordered_items_class - - def _unpack_schema_additions(self, items): - """ Unpack additions, since they may themselves be schemas. """ - - unpacked_items = [] - - for item in items: - if isinstance(item, SchemaAddition): - unpacked_items.append(item.factory()) - else: - unpacked_items.append(item) + # ------------------------------------------------------------------------ - return unpacked_items + # Trait initializers --------------------------------------------------- - def _merge_items_with_same_path(self, id_to_items, ordered_items_ids): - """ Merge items with the same path if possible. - - Items must be subclasses of `Schema` and they must be instances of - the same class to be merged. - - """ - - merged_items = [] - for item_id in ordered_items_ids: - items_with_same_id = id_to_items[item_id] - - # Group items by class. - class_to_items, ordered_items_class =\ - self._group_items_by_class(items_with_same_id) - - for items_class in ordered_items_class: - items_with_same_class = class_to_items[items_class] - - if len(items_with_same_class) == 1: - merged_items.extend(items_with_same_class) - - else: - # Only schemas can be merged. - if issubclass(items_class, Schema): - # Merge into a single schema. - items_content = sum( - (item.items for item in items_with_same_class), [] - ) - - merged_item = items_with_same_class[0].clone_traits() - merged_item.items = items_content - merged_items.append(merged_item) - - else: - merged_items.extend(items_with_same_class) - - return merged_items - - def _preprocess_schemas(self, schema, additions, path): - """ Sort and merge a schema and a set of schema additions. """ - - # Determine the order of the items at this path. - if additions[path]: - all_items = self._get_ordered_schemas(schema.items+additions[path]) - else: - all_items = schema.items - - unpacked_items = self._unpack_schema_additions(all_items) - - id_to_items, ordered_items_ids = self._group_items_by_id(unpacked_items) - - merged_items = self._merge_items_with_same_path(id_to_items, - ordered_items_ids) - - return merged_items - - def _create_action_manager_recurse(self, schema, additions, path=''): - """ Recursively create a manager for the given schema and additions map. - - Items with the same path are merged together in a single entry if - possible (i.e., if they have the same class). + def _controller_default(self): + from .task_action_controller import TaskActionController - When a list of items is merged, their children are added to a clone - of the first item in the list. As a consequence, traits like menu - names etc. are inherited from the first item. + return TaskActionController(task=self.task) - """ + # Trait properties ----------------------------------------------------- - # Compute the new action path. - if path: - path = path + '/' + schema.id + def _get_additions(self): + # keep synchronized to task's extra actions, since that may change + if self.task: + return self.task.extra_actions else: - path = schema.id - - preprocessed_items = self._preprocess_schemas(schema, additions, path) - - # Create the actual children by calling factory items. - children = [] - for item in preprocessed_items: - if isinstance(item, Schema): - item = self._create_action_manager_recurse(item,additions,path) - else: - item = self.prepare_item(item, path+'/'+item.id) - - if isinstance(item, ActionManager): - # Give even non-root action managers a reference to the - # controller so that custom Groups, MenuManagers, etc. can get - # access to their Tasks. - item.controller = self.controller - - children.append(item) - - # Finally, create the pyface.action instance for this schema. - return self.prepare_item(schema.create(children), path) - - #### Trait initializers ################################################### - - def _controller_default(self): - from .task_action_controller import TaskActionController - return TaskActionController(task=self.task) + return [] diff -Nru python-pyface-6.1.2/pyface/tasks/action/task_action.py python-pyface-7.4.0/pyface/tasks/action/task_action.py --- python-pyface-6.1.2/pyface/tasks/action/task_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/task_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,17 @@ -# Copyright (c) 2010-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -# Enthought library imports. -from traits.api import Bool, Instance, Property, Str, cached_property -# Local imports. +from traits.api import Instance, Property, Str, cached_property + + from pyface.tasks.api import Editor, Task, TaskPane from pyface.action.listening_action import ListeningAction @@ -22,18 +23,18 @@ not inherit TaskAction, although they must, of course, inherit Action. """ - #### ListeningAction interface ############################################ + # ListeningAction interface -------------------------------------------- - object = Property(depends_on='task') + object = Property(observe="task") - #### TaskAction interface ################################################# + # TaskAction interface ------------------------------------------------- - # The Task with which the action is associated. Set by the framework. + #: The Task with which the action is associated. Set by the framework. task = Instance(Task) - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_object(self): return self.task @@ -41,20 +42,20 @@ def destroy(self): # Disconnect listeners to task and dependent properties. self.task = None - super(TaskAction, self).destroy() + super().destroy() class TaskWindowAction(TaskAction): """ An Action that makes a callback to a Task's window. """ - #### ListeningAction interface ############################################ + # ListeningAction interface -------------------------------------------- - object = Property(depends_on='task.window') + object = Property(observe="task.window") - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_object(self): if self.task: @@ -66,18 +67,18 @@ """ An Action that makes a callback to a Task's central pane. """ - #### ListeningAction interface ############################################ + # ListeningAction interface -------------------------------------------- - object = Property(depends_on='central_pane') + object = Property(observe="central_pane") - #### CentralPaneAction interface ########################################## + # CentralPaneAction interface -----------------------------------------# - # The central pane with which the action is associated. - central_pane = Property(Instance(TaskPane), depends_on='task') + #: The central pane with which the action is associated. + central_pane = Property(Instance(TaskPane), observe="task") - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ @cached_property def _get_central_pane(self): @@ -93,21 +94,21 @@ """ An Action the makes a callback to one of a Task's dock panes. """ - #### ListeningAction interface ############################################ + # ListeningAction interface -------------------------------------------- - object = Property(depends_on='dock_pane') + object = Property(observe="dock_pane") - #### DockPaneAction interface ############################################# + # DockPaneAction interface --------------------------------------------- - # The dock pane with which the action is associated. Set by the framework. - dock_pane = Property(Instance(TaskPane), depends_on='task') + #: The dock pane with which the action is associated. Set by the framework. + dock_pane = Property(Instance(TaskPane), observe="task") - # The ID of the dock pane with which the action is associated. - dock_pane_id = Str + #: The ID of the dock pane with which the action is associated. + dock_pane_id = Str() - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ @cached_property def _get_dock_pane(self): @@ -123,20 +124,20 @@ """ An action that makes a callback to the active editor in an editor pane. """ - #### ListeningAction interface ############################################ + # ListeningAction interface -------------------------------------------- - object = Property(depends_on='active_editor') + object = Property(observe="active_editor") - #### EditorAction interface ############################################### + # EditorAction interface ----------------------------------------------- - # The active editor in the central pane with which the action is associated. + #: The active editor in the central pane with which the action is associated. active_editor = Property( - Instance(Editor), depends_on='central_pane.active_editor' + Instance(Editor), observe="central_pane.active_editor" ) - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ @cached_property def _get_active_editor(self): diff -Nru python-pyface-6.1.2/pyface/tasks/action/tasks_application_action.py python-pyface-7.4.0/pyface/tasks/action/tasks_application_action.py --- python-pyface-6.1.2/pyface/tasks/action/tasks_application_action.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/tasks_application_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from traits.api import Instance from pyface.action.api import GUIApplicationAction @@ -13,11 +22,12 @@ class CreateTaskWindowAction(TasksApplicationAction): """ A standard 'New Window' menu action. """ - name = u'New Window' - accelerator = 'Ctrl+N' + + name = "New Window" + accelerator = "Ctrl+N" #: The task window wayout to use when creating the new window. - layout = Instance('pyface.tasks.task_window_layout.TaskWindowLayout') + layout = Instance("pyface.tasks.task_window_layout.TaskWindowLayout") def perform(self, event=None): window = self.application.create_window(layout=self.layout) diff -Nru python-pyface-6.1.2/pyface/tasks/action/task_toggle_group.py python-pyface-7.4.0/pyface/tasks/action/task_toggle_group.py --- python-pyface-6.1.2/pyface/tasks/action/task_toggle_group.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/task_toggle_group.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,18 @@ -# Copyright (c) 2010-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -# Enthought library imports. + from pyface.action.api import Action, ActionItem, Group -from traits.api import Any, List, Instance, Property, Unicode, on_trait_change +from traits.api import Any, List, Instance, Property, Str, observe + -# Local imports. from pyface.tasks.task import Task from pyface.tasks.task_window import TaskWindow @@ -20,27 +21,28 @@ """ An action for activating a task. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- #: The user-visible name of the action, matches the task name. - name = Property(Unicode, depends_on='task.name') + name = Property(Str, observe="task.name") #: The action is a toggle menu item. - style = 'toggle' + style = "toggle" #: The tooltip to display for the menu item. - tooltip = Property(Unicode, depends_on='name') + tooltip = Property(Str, observe="name") - #### 'TaskActivateAction' interface ####################################### + # 'TaskToggleAction' interface ----------------------------------------- + #: The Task with which the action is associated. task = Instance(Task) - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): - super(TaskToggleAction, self).destroy() + super().destroy() # Make sure that we are not listening to changes in the task anymore # In traits style, we will set the basic object to None and have the @@ -51,20 +53,20 @@ window = self.task.window window.activate_task(self.task) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_name(self): if self.task is None: - return 'UNDEFINED' + return "UNDEFINED" return self.task.name def _get_tooltip(self): - return u'Switch to the %s task.' % self.name + return "Switch to the %s task." % self.name - @on_trait_change('task.window.active_task') - def _update_checked(self): + @observe("task.window.active_task") + def _update_checked(self, event): if self.task: window = self.task.window self.checked = ( @@ -76,35 +78,34 @@ """ A menu for changing the active task in a task window. """ - #### 'ActionManager' interface ############################################ + # 'ActionManager' interface -------------------------------------------- - id = 'TaskToggleGroup' - items = List + id = "TaskToggleGroup" + items = List() - #### 'TaskChangeMenuManager' interface #################################### + # 'TaskToggleGroup' interface ------------------------------------------ - # The ActionManager to which the group belongs. - manager = Any + #: The ActionManager to which the group belongs. + manager = Any() - # The window that contains the group. + #: The window that contains the group. window = Instance(TaskWindow) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_items(self): items = [] if len(self.window.tasks) > 1: # at least two tasks, so something to toggle items = [ - ActionItem( - action=TaskToggleAction(task=task), - ) for task in self.window.tasks + ActionItem(action=TaskToggleAction(task=task)) + for task in self.window.tasks ] return items - def _rebuild(self): + def _rebuild(self, event): # Clear out the old group, then build the new one. self.destroy() self.items = self._get_items() @@ -112,10 +113,10 @@ # Inform our manager that it needs to be rebuilt. self.manager.changed = True - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _items_default(self): - self.window.on_trait_change(self._rebuild, 'tasks[]') + self.window.observe(self._rebuild, "tasks.items") return self._get_items() def _manager_default(self): @@ -125,4 +126,4 @@ return manager def _window_default(self): - return self.manager.controller.task.window \ No newline at end of file + return self.manager.controller.task.window diff -Nru python-pyface-6.1.2/pyface/tasks/action/task_window_toggle_group.py python-pyface-7.4.0/pyface/tasks/action/task_window_toggle_group.py --- python-pyface-6.1.2/pyface/tasks/action/task_window_toggle_group.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/action/task_window_toggle_group.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,16 @@ -# Copyright (c) 2010-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -# Enthought library imports. + from pyface.action.api import Action, ActionItem, Group -from traits.api import Any, Instance, List, Property, Unicode, on_trait_change +from traits.api import Any, Instance, List, Property, Str, observe class TaskWindowToggleAction(Action): @@ -19,15 +20,15 @@ # 'Action' interface ----------------------------------------------------- #: The name of the action for the window. - name = Property(Unicode, depends_on='window.title') + name = Property(Str, observe="window.title") #: The action is a toggle action. - style = 'toggle' + style = "toggle" # 'TaskWindowToggleAction' interface ------------------------------------- - # The window to use for this action. - window = Instance('pyface.tasks.task_window.TaskWindow') + #: The window to use for this action. + window = Instance("pyface.tasks.task_window.TaskWindow") # ------------------------------------------------------------------------- # 'Action' interface. @@ -44,14 +45,14 @@ def _get_name(self): if self.window.title: return self.window.title - return u'' + return "" - @on_trait_change('window:activated') - def _window_activated(self): + @observe("window:activated") + def _window_activated(self, event): self.checked = True - @on_trait_change('window:deactivated') - def _window_deactivated(self): + @observe("window:deactivated") + def _window_deactivated(self, event): self.checked = False @@ -62,18 +63,18 @@ # 'Group' interface ------------------------------------------------------ #: The id of the action group. - id = 'TaskWindowToggleGroup' + id = "TaskWindowToggleGroup" #: The actions in the action group - items = List + items = List() # 'TaskWindowToggleGroup' interface -------------------------------------- #: The application that contains the group. - application = Instance('pyface.tasks.tasks_application.TasksApplication') + application = Instance("pyface.tasks.tasks_application.TasksApplication") #: The ActionManager to which the group belongs. - manager = Any + manager = Any() # ------------------------------------------------------------------------- # 'Group' interface. @@ -82,10 +83,10 @@ def destroy(self): """ Called when the group is no longer required. """ - super(TaskWindowToggleGroup, self).destroy() + super().destroy() if self.application: - self.application.on_trait_change( - self._rebuild, 'windows[]', remove=True + self.application.observe( + self._rebuild, "windows.items", remove=True ) # ------------------------------------------------------------------------- @@ -100,7 +101,7 @@ items.append(ActionItem(action=action)) return items - def _rebuild(self): + def _rebuild(self, event): # Clear out the old group, then build the new one. for item in self.items: item.destroy() @@ -112,7 +113,7 @@ # Trait initializers ----------------------------------------------------- def _items_default(self): - self.application.on_trait_change(self._rebuild, 'windows[]') + self.application.observe(self._rebuild, "windows.items") return self._get_items() def _manager_default(self): diff -Nru python-pyface-6.1.2/pyface/tasks/advanced_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/advanced_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/advanced_editor_area_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/advanced_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,19 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.AdvancedEditorAreaPane` +""" + from pyface.toolkit import toolkit_object -AdvancedEditorAreaPane = toolkit_object('tasks.advanced_editor_area_pane:' - 'AdvancedEditorAreaPane') + +AdvancedEditorAreaPane = toolkit_object( + "tasks.advanced_editor_area_pane:" "AdvancedEditorAreaPane" +) diff -Nru python-pyface-6.1.2/pyface/tasks/api.py python-pyface-7.4.0/pyface/tasks/api.py --- python-pyface-6.1.2/pyface/tasks/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,62 @@ -# Copyright (c) 2010-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import absolute_import +""" + +API for the ``pyface.tasks`` submodule. + +Tasks-specific Interfaces +------------------------- +- :class:`~.IDockPane` +- :class:`~pyface.tasks.i_editor.IEditor` +- :class:`~.IEditorAreaPane` +- :class:`~.ITaskPane` + +Tasks, Tasks Application and related classes +-------------------------------------------- + +- :class:`~.AdvancedEditorAreaPane` +- :class:`~.DockPane` +- :class:`~.Editor` +- :class:`~.EditorAreaPane` +- :class:`~.SplitEditorAreaPane` +- :class:`~.Task` +- :class:`~.TasksApplication` +- :class:`~.TaskFactory` +- :class:`~.TaskPane` +- :class:`~.TaskWindow` + +Tasks layout +------------ +- :class:`~.TaskLayout` +- :class:`~.TaskWindowLayout` +- :class:`~.PaneItem` +- :class:`~.Tabbed` +- :class:`~.Splitter` +- :class:`~.HSplitter` +- :class:`~.VSplitter` + +Traits-specific Tasks classes +----------------------------- +- :class:`~.TraitsDockPane` +- :class:`~.TraitsEditor` +- :class:`~.TraitsTaskPane` + +Enaml-specific Tasks functionality +---------------------------------- +- :class:`~.EnamlDockPane` +- :class:`~.EnamlEditor` +- :class:`~.EnamlTaskPane` + +""" -# Local imports. from .advanced_editor_area_pane import AdvancedEditorAreaPane from .split_editor_area_pane import SplitEditorAreaPane from .dock_pane import DockPane @@ -24,8 +71,14 @@ from .i_task_pane import ITaskPane from .task import Task from .tasks_application import TasksApplication, TaskFactory -from .task_layout import TaskLayout, PaneItem, Tabbed, Splitter, HSplitter, \ - VSplitter +from .task_layout import ( + TaskLayout, + PaneItem, + Tabbed, + Splitter, + HSplitter, + VSplitter, +) from .task_pane import TaskPane from .task_window import TaskWindow from .task_window_layout import TaskWindowLayout diff -Nru python-pyface-6.1.2/pyface/tasks/contrib/python_shell.py python-pyface-7.4.0/pyface/tasks/contrib/python_shell.py --- python-pyface-6.1.2/pyface/tasks/contrib/python_shell.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/contrib/python_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Module defining a simple Python shell task. @@ -5,14 +14,15 @@ confused with a more full-featured shell, such as those provided by IPython. """ -# Std lib imports + import logging -# Enthought library imports. + from traits.api import Str, List, Dict, Instance from pyface.api import PythonShell, FileDialog, OK +from pyface.action.schema.api import SMenu, SMenuBar from pyface.tasks.api import Task, TaskPane -from pyface.tasks.action.api import SMenuBar, SMenu, TaskAction +from pyface.tasks.action.api import TaskAction # set up logging logger = logging.getLogger() @@ -21,76 +31,83 @@ class PythonShellPane(TaskPane): """ A Tasks Pane containing a Pyface PythonShell """ - id = 'pyface.tasks.contrib.python_shell.pane' - name = 'Python Shell' - + + id = "pyface.tasks.contrib.python_shell.pane" + name = "Python Shell" + editor = Instance(PythonShell) - + bindings = List(Dict) commands = List(Str) - + def create(self, parent): """ Create the python shell task pane - + This wraps the standard pyface PythonShell """ - logger.debug('PythonShellPane: creating python shell pane') + logger.debug("PythonShellPane: creating python shell pane") self.editor = PythonShell(parent) self.control = self.editor.control - + # bind namespace - logger.debug('PythonShellPane: binding variables') + logger.debug("PythonShellPane: binding variables") for binding in self.bindings: for name, value in binding.items(): self.editor.bind(name, value) - + # execute commands - logger.debug('PythonShellPane: executing startup commands') + logger.debug("PythonShellPane: executing startup commands") for command in self.commands: self.editor.execute_command(command) - - logger.debug('PythonShellPane: created') - + + logger.debug("PythonShellPane: created") + def destroy(self): """ Destroy the python shell task pane """ - logger.debug('PythonShellPane: destroying python shell pane') + logger.debug("PythonShellPane: destroying python shell pane") self.editor.destroy() self.control = self.editor = None - logger.debug('PythonShellPane: destroyed') + logger.debug("PythonShellPane: destroyed") class PythonShellTask(Task): """ A task which provides a simple Python Shell to the user. """ - + # Task Interface - - id = 'pyface.tasks.contrib.python_shell' - name = 'Python Shell' - + + id = "pyface.tasks.contrib.python_shell" + name = "Python Shell" + # The list of bindings for the shell bindings = List(Dict) - + # The list of commands to run on shell startup commands = List(Str) - + # the IPythonShell instance that we are interacting with pane = Instance(PythonShellPane) - + # Task Interface - menu_bar = SMenuBar(SMenu(TaskAction(name='Open...', method='open', - accelerator='Ctrl+O'), - id='File', name='&File'), - SMenu(id='View', name='&View')) - + menu_bar = SMenuBar( + SMenu( + TaskAction(name="Open...", method="open", accelerator="Ctrl+O"), + id="File", + name="&File", + ), + SMenu(id="View", name="&View"), + ) + def create_central_pane(self): """ Create a view pane with a Python shell """ logger.debug("Creating Python shell pane in central pane") - self.pane = PythonShellPane(bindings=self.bindings, commands=self.commands) + self.pane = PythonShellPane( + bindings=self.bindings, commands=self.commands + ) return self.pane # PythonShellTask API @@ -98,15 +115,15 @@ def open(self): """ Shows a dialog to open a file. """ - logger.debug('PythonShellTask: opening file') - dialog = FileDialog(parent=self.window.control, wildcard='*.py') + logger.debug("PythonShellTask: opening file") + dialog = FileDialog(parent=self.window.control, wildcard="*.py") if dialog.open() == OK: self._open_file(dialog.path) # Private API - + def _open_file(self, path): """ Execute the selected file in the editor's interpreter """ logger.debug('PythonShellTask: executing file "%s"' % path) - self.pane.editor.execute_file(path) \ No newline at end of file + self.pane.editor.execute_file(path) diff -Nru python-pyface-6.1.2/pyface/tasks/dock_pane.py python-pyface-7.4.0/pyface/tasks/dock_pane.py --- python-pyface-6.1.2/pyface/tasks/dock_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/dock_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,17 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.DockPane` +""" + from pyface.toolkit import toolkit_object -DockPane = toolkit_object('tasks.dock_pane:DockPane') + +DockPane = toolkit_object("tasks.dock_pane:DockPane") diff -Nru python-pyface-6.1.2/pyface/tasks/editor_area_pane.py python-pyface-7.4.0/pyface/tasks/editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/editor_area_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,17 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.EditorAreaPane` +""" + from pyface.toolkit import toolkit_object -EditorAreaPane = toolkit_object('tasks.editor_area_pane:EditorAreaPane') + +EditorAreaPane = toolkit_object("tasks.editor_area_pane:EditorAreaPane") diff -Nru python-pyface-6.1.2/pyface/tasks/editor.py python-pyface-7.4.0/pyface/tasks/editor.py --- python-pyface-6.1.2/pyface/tasks/editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,17 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.Editor` +""" + from pyface.toolkit import toolkit_object -Editor = toolkit_object('tasks.editor:Editor') + +Editor = toolkit_object("tasks.editor:Editor") diff -Nru python-pyface-6.1.2/pyface/tasks/enaml_dock_pane.py python-pyface-7.4.0/pyface/tasks/enaml_dock_pane.py --- python-pyface-6.1.2/pyface/tasks/enaml_dock_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/enaml_dock_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,28 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from traits.api import Instance -# Local imports. + from pyface.tasks.dock_pane import DockPane class EnamlDockPane(DockPane): """ Create a Dock pane for Enaml Components. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'EnamlDockPane' interface - ########################################################################### + # ------------------------------------------------------------------------ #: The Enaml component defining the contents of the DockPane. - component = Instance('enaml.widgets.toolkit_object.ToolkitObject') + component = Instance("enaml.widgets.toolkit_object.ToolkitObject") def create_component(self): """ Return an Enaml component defining the contents of the DockPane. @@ -22,11 +31,11 @@ ------- component : ToolkitObject """ - raise NotImplementedError + raise NotImplementedError() - ########################################################################### + # ------------------------------------------------------------------------ # 'IDockPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Return the toolkit-specific control that represents the pane. """ @@ -45,9 +54,9 @@ return contents - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Destroy the toolkit-specific control that represents the pane. """ diff -Nru python-pyface-6.1.2/pyface/tasks/enaml_editor.py python-pyface-7.4.0/pyface/tasks/enaml_editor.py --- python-pyface-6.1.2/pyface/tasks/enaml_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/enaml_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -# local imports +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.tasks.editor import Editor from pyface.tasks.enaml_pane import EnamlPane diff -Nru python-pyface-6.1.2/pyface/tasks/enaml_pane.py python-pyface-7.4.0/pyface/tasks/enaml_pane.py --- python-pyface-6.1.2/pyface/tasks/enaml_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/enaml_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,27 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Base class defining common code for EnamlTaskPane and EnamlEditor. """ -# Enthought library imports. + from traits.api import HasTraits, Instance class EnamlPane(HasTraits): """ Base class defining common code for EnamlTaskPane and EnamlEditor. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'EnamlPane' interface - ########################################################################### + # ------------------------------------------------------------------------ #: The Enaml component defining the contents of the TaskPane. - component = Instance('enaml.widgets.toolkit_object.ToolkitObject') + component = Instance("enaml.widgets.toolkit_object.ToolkitObject") def create_component(self): """ Return an Enaml component defining the contents of the pane. @@ -21,11 +30,11 @@ ------- component : ToolkitObject """ - raise NotImplementedError + raise NotImplementedError() - ########################################################################### + # ------------------------------------------------------------------------ # 'TaskPane'/'Editor' interface - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create the toolkit-specific control that represents the editor. """ diff -Nru python-pyface-6.1.2/pyface/tasks/enaml_task_pane.py python-pyface-7.4.0/pyface/tasks/enaml_task_pane.py --- python-pyface-6.1.2/pyface/tasks/enaml_task_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/enaml_task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -# Local imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.tasks.task_pane import TaskPane from pyface.tasks.enaml_pane import EnamlPane diff -Nru python-pyface-6.1.2/pyface/tasks/i_advanced_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/i_advanced_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/i_advanced_editor_area_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/i_advanced_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,13 @@ -# Enthought library imports. -from traits.api import Interface +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -# Local imports. from pyface.tasks.i_editor_area_pane import IEditorAreaPane @@ -9,9 +15,9 @@ """ A splitable central pane that contains tabbed editors. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IAdvancedEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_layout(self): """ Returns a LayoutItem that reflects the current state of the editors. diff -Nru python-pyface-6.1.2/pyface/tasks/i_dock_pane.py python-pyface-7.4.0/pyface/tasks/i_dock_pane.py --- python-pyface-6.1.2/pyface/tasks/i_dock_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/i_dock_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ -# Enthought library imports. -from traits.api import Bool, Enum, HasTraits, Str, Tuple +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Bool, Enum, HasTraits, Tuple + -# Local imports. from pyface.tasks.i_task_pane import ITaskPane @@ -12,33 +21,33 @@ general, be moved, resized, and hidden by the user. """ - # If enabled, the pane will have a button to close it, and a visibility - # toggle button will be added to the View menu. Otherwise, the pane's - # visibility will only be adjustable programmatically, though the 'visible' - # attribute. + #: If enabled, the pane will have a button to close it, and a visibility + #: toggle button will be added to the View menu. Otherwise, the pane's + #: visibility will only be adjustable programmatically, though the + #: 'visible' attribute. closable = Bool(True) - # The dock area in which the pane is currently present. - dock_area = Enum('left', 'right', 'top', 'bottom') + #: The dock area in which the pane is currently present. + dock_area = Enum("left", "right", "top", "bottom") - # Whether the pane can be detached from the main window. + #: Whether the pane can be detached from the main window. floatable = Bool(True) - # Whether the pane is currently detached from the main window. + #: Whether the pane is currently detached from the main window. floating = Bool(False) - # Whether the pane can be moved from one dock area to another. + #: Whether the pane can be moved from one dock area to another. movable = Bool(True) - # The size of the dock pane. Note that this value is read-only. - size = Tuple + #: The size of the dock pane. Note that this value is read-only. + size = Tuple() - # Whether the pane is currently visible. + #: Whether the pane is currently visible. visible = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IDockPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. @@ -57,21 +66,21 @@ """ Mixin containing common code for toolkit-specific implementations. """ - #### 'IDockPane' interface ################################################ + # 'IDockPane' interface ------------------------------------------------ closable = Bool(True) - dock_area = Enum('left', 'right', 'top', 'bottom') + dock_area = Enum("left", "right", "top", "bottom") floatable = Bool(True) floating = Bool(False) movable = Bool(True) - size = Tuple + size = Tuple() visible = Bool(False) caption_visible = Bool(True) dock_layer = Bool(0) - ########################################################################### + # ------------------------------------------------------------------------ # 'IDockPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def hide(self): """ Convenience method to hide the dock pane. diff -Nru python-pyface-6.1.2/pyface/tasks/i_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/i_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/i_editor_area_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/i_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,11 +1,29 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import logging -# Enthought library imports. -from traits.api import Bool, Callable, Dict, Event, File, HasTraits, Instance, \ - List, Str -# Local imports. +from traits.api import ( + Bool, + Callable, + Dict, + Event, + File, + HasTraits, + Instance, + List, + Str, +) + + from pyface.tasks.i_editor import IEditor from pyface.tasks.i_task_pane import ITaskPane @@ -22,29 +40,30 @@ can be displayed side-by-side. """ - #### 'IEditorAreaPane' interface ########################################## + # 'IEditorAreaPane' interface -----------------------------------------# - # The currently active editor. + #: The currently active editor. active_editor = Instance(IEditor) - # The list of all the visible editors in the pane. + #: The list of all the visible editors in the pane. editors = List(IEditor) - # A list of extensions for file types to accept via drag and drop. - # Note: This functionality is provided because it is very common, but drag - # and drop support is in general highly toolkit-specific. If more - # sophisticated support is required, subclass an editor area implementation. + #: A list of extensions for file types to accept via drag and drop. + #: Note: This functionality is provided because it is very common, but + #: drag and drop support is in general highly toolkit-specific. If more + #: sophisticated support is required, subclass an editor area + #: implementation. file_drop_extensions = List(Str) - # A file with a supported extension was dropped into the editor area. + #: A file with a supported extension was dropped into the editor area. file_dropped = Event(File) - # Whether to hide the tab bar when there is only a single editor. + #: Whether to hide the tab bar when there is only a single editor. hide_tab_bar = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activates the specified editor in the pane. @@ -92,10 +111,12 @@ The 'factory' parameter is a callabe of form: callable(editor_area=editor_area, obj=obj) -> IEditor + Often, factory will be a class that provides the 'IEditor' interface. The 'filter' parameter is a callable of form: callable(obj) -> bool + that indicates whether the editor factory is suitable for an object. If multiple factories apply to a single object, it is undefined which @@ -115,7 +136,7 @@ class MEditorAreaPane(HasTraits): - #### 'IEditorAreaPane' interface ########################################## + # 'IEditorAreaPane' interface -----------------------------------------# active_editor = Instance(IEditor) editors = List(IEditor) @@ -123,13 +144,13 @@ file_dropped = Event(File) hide_tab_bar = Bool(False) - #### Protected traits ##################################################### + # Protected traits ----------------------------------------------------- _factory_map = Dict(Callable, List(Callable)) - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_editor(self, obj, factory=None): """ Creates an editor for an object. @@ -155,7 +176,7 @@ # If not, create an editor for it. editor = self.create_editor(obj, factory) if editor is None: - logger.warn('Cannot create editor for obj %r', obj) + logger.warning("Cannot create editor for obj %r", obj) else: self.add_editor(editor) diff -Nru python-pyface-6.1.2/pyface/tasks/i_editor.py python-pyface-7.4.0/pyface/tasks/i_editor.py --- python-pyface-6.1.2/pyface/tasks/i_editor.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/i_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,26 @@ -# Enthought library imports. -from traits.api import Any, Bool, Event, HasTraits, Interface, \ - Instance, Property, Unicode, Vetoable, VetoableEvent, cached_property +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import ( + Any, + Bool, + Event, + HasTraits, + Interface, + Instance, + Property, + Str, + Vetoable, + VetoableEvent, + cached_property, +) class IEditor(Interface): @@ -8,39 +28,38 @@ """ #: The editor's user-visible name. - name = Unicode + name = Str() #: The tooltip to show for the editor's tab, if any. - tooltip = Unicode + tooltip = Str() #: The toolkit-specific control that represents the editor. - control = Any + control = Any() #: The object that the editor is editing. - obj = Any + obj = Any() #: Has the editor's object been modified but not saved? - dirty = Bool + dirty = Bool() #: The editor area to which the editor belongs. - editor_area = Instance( - 'pyface.tasks.i_editor_area_pane.IEditorAreaPane') + editor_area = Instance("pyface.tasks.i_editor_area_pane.IEditorAreaPane") #: Is the editor active in the editor area? - is_active = Bool + is_active = Bool() #: Does the editor currently have the focus? - has_focus = Bool + has_focus = Bool() #: Fired when the editor has been requested to close. - closing = VetoableEvent + closing = VetoableEvent() #: Fired when the editor has been closed. - closed = Event + closed = Event() - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): """ Close the editor. @@ -60,25 +79,24 @@ """ Mixin containing common code for toolkit-specific implementations. """ - #### 'IEditor' interface ################################################## + # 'IEditor' interface -------------------------------------------------# - name = Unicode - tooltip = Unicode - control = Any - obj = Any + name = Str() + tooltip = Str() + control = Any() + obj = Any() dirty = Bool(False) - editor_area = Instance( - 'pyface.tasks.i_editor_area_pane.IEditorAreaPane') - is_active = Property(Bool, depends_on='editor_area.active_editor') + editor_area = Instance("pyface.tasks.i_editor_area_pane.IEditorAreaPane") + is_active = Property(Bool, observe="editor_area.active_editor") has_focus = Bool(False) - closing = VetoableEvent - closed = Event + closing = VetoableEvent() + closed = Event() - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): """ Close the editor. @@ -89,9 +107,9 @@ self.editor_area.remove_editor(self) self.closed = True - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ @cached_property def _get_is_active(self): diff -Nru python-pyface-6.1.2/pyface/tasks/i_task_pane.py python-pyface-7.4.0/pyface/tasks/i_task_pane.py --- python-pyface-6.1.2/pyface/tasks/i_task_pane.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/i_task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,16 @@ -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, Interface, Instance, \ - Str, Unicode +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Any, Bool, HasTraits, Interface, Instance, Str + -# Local imports. from pyface.tasks.task import Task @@ -11,23 +19,23 @@ """ #: The pane's identifier, unique within a Task. - id = Str + id = Str() #: The pane's user-visible name. - name = Unicode + name = Str() #: The toolkit-specific control that represents the pane. - control = Any + control = Any() #: Does the pane currently have focus? - has_focus = Bool + has_focus = Bool() #: The task with which the pane is associated. Set by the framework. task = Instance(Task) - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -47,10 +55,10 @@ """ Mixin containing common code for toolkit-specific implementations. """ - #### 'ITaskPane' interface ################################################ + # 'ITaskPane' interface ------------------------------------------------ - id = Str - name = Unicode - control = Any + id = Str() + name = Str() + control = Any() has_focus = Bool(False) task = Instance(Task) diff -Nru python-pyface-6.1.2/pyface/tasks/i_task_window_backend.py python-pyface-7.4.0/pyface/tasks/i_task_window_backend.py --- python-pyface-6.1.2/pyface/tasks/i_task_window_backend.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/i_task_window_backend.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,21 @@ -# Enthought library imports. -from traits.api import Any, DelegatesTo, HasTraits, Instance, Interface, \ - provides +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import ( + Any, + DelegatesTo, + HasTraits, + Instance, + Interface, + provides, +) class ITaskWindowBackend(Interface): @@ -11,14 +26,14 @@ """ #: The root control of the TaskWindow to which the layout belongs. - control = Any + control = Any() #: The TaskWindow to which the layout belongs. - window = Instance('pyface.tasks.task_window.TaskWindow') + window = Instance("pyface.tasks.task_window.TaskWindow") - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskWindowBackend' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the TaskWindow's contents. (See IWindow.) @@ -40,7 +55,7 @@ specified TaskState. """ - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_layout(self): """ Returns a TaskLayout for the current state of the window. @@ -57,30 +72,29 @@ """ Mixin containing common coe for toolkit-specific implementations. """ + # 'ITaskWindowBackend' interface --------------------------------------- - #### 'ITaskWindowBackend' interface ####################################### + control = DelegatesTo("window") + window = Instance("pyface.tasks.task_window.TaskWindow") - control = DelegatesTo('window') - window = Instance('pyface.tasks.task_window.TaskWindow') - - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskWindowBackend' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): - raise NotImplementedError + raise NotImplementedError() def destroy(self): pass def hide_task(self, state): - raise NotImplementedError + raise NotImplementedError() def show_task(self, state): - raise NotImplementedError + raise NotImplementedError() def get_layout(self): - raise NotImplementedError + raise NotImplementedError() def set_layout(self, layout): - raise NotImplementedError + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/tasks/split_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/split_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/split_editor_area_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/split_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,24 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.SplitEditorAreaPane` +- :attr:`~.EditorAreaWidget` +""" + from pyface.toolkit import toolkit_object -SplitEditorAreaPane = toolkit_object('tasks.split_editor_area_pane:' - 'SplitEditorAreaPane') -EditorAreaWidget = toolkit_object('tasks.split_editor_area_pane:' - 'EditorAreaWidget') +SplitEditorAreaPane = toolkit_object( + "tasks.split_editor_area_pane:" "SplitEditorAreaPane" +) + +EditorAreaWidget = toolkit_object( + "tasks.split_editor_area_pane:" "EditorAreaWidget" +) diff -Nru python-pyface-6.1.2/pyface/tasks/task_layout.py python-pyface-7.4.0/pyface/tasks/task_layout.py --- python-pyface-6.1.2/pyface/tasks/task_layout.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/task_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,9 +1,20 @@ -# Standard library imports. -from six.moves import cStringIO as StringIO +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from io import StringIO import sys -# Enthought library imports. -from traits.api import Either, Enum, HasStrictTraits, Int, Instance, List, Str + +from traits.api import ( + Enum, HasStrictTraits, Int, Instance, List, Str, Union, +) class LayoutItem(HasStrictTraits): @@ -34,7 +45,7 @@ def pstream(self, stream, indent=0, multiline=False): """ Pretty-formats the layout item to a stream. """ - call = self.__class__.__name__ + '(' + call = self.__class__.__name__ + "(" indent += len(call) stream.write(call) @@ -52,19 +63,19 @@ arg_indent = indent if name: arg_indent += len(name) + 1 - stream.write(name + '=') + stream.write(name + "=") if isinstance(value, LayoutItem): value.pstream(stream, arg_indent, multiline) else: stream.write(repr(value)) if i < len(args) - 1: - stream.write(',') + stream.write(",") if multiline: - stream.write('\n' + indent * ' ') + stream.write("\n" + indent * " ") else: - stream.write(' ') + stream.write(" ") - stream.write(')') + stream.write(")") class LayoutContainer(LayoutItem): @@ -76,13 +87,13 @@ def __init__(self, *items, **traits): # Items may either be specified as a positional arg or a kwarg. if items: - if 'items' in traits: + if "items" in traits: raise ValueError( "Received 'items' as positional and keyword argument." ) else: - traits['items'] = list(items) - super(LayoutContainer, self).__init__(**traits) + traits["items"] = list(items) + super().__init__(**traits) def iterleaves(self): for item in self.items: @@ -97,20 +108,20 @@ """ A pane in a Task layout. """ - # The ID of the item. If the item refers to a TaskPane, this is the ID of - # that TaskPane. - id = Either(Str, Int, default='', pretty_skip=True) + #: The ID of the item. If the item refers to a TaskPane, this is the ID of + #: that TaskPane. + id = Union(Str, Int, default_value="", pretty_skip=True) - # The width of the pane in pixels. If not specified, the pane will be sized - # according to its size hint. + #: The width of the pane in pixels. If not specified, the pane will be + #: sized according to its size hint. width = Int(-1) - # The height of the pane in pixels. If not specified, the pane will be - # sized according to its size hint. + #: The height of the pane in pixels. If not specified, the pane will be + #: sized according to its size hint. height = Int(-1) - def __init__(self, id='', **traits): - super(PaneItem, self).__init__(**traits) + def __init__(self, id="", **traits): + super().__init__(**traits) self.id = id def pargs(self): @@ -121,40 +132,46 @@ """ A tab area in a Task layout. """ - # A tabbed layout can only contain PaneItems as sub-items. Splitters and - # other Tabbed layouts are not allowed. + #: A tabbed layout can only contain PaneItems as sub-items. Splitters and + #: other Tabbed layouts are not allowed. items = List(PaneItem, pretty_skip=True) - # The ID of the TaskPane which is active in layout. If not specified, the - # first pane is active. - active_tab = Either(Str, Int, default='') + #: The ID of the TaskPane which is active in layout. If not specified, the + #: first pane is active. + active_tab = Union(Str, Int, default_value="") class Splitter(LayoutContainer): """ A split area in a Task layout. """ - # The orientation of the splitter. - orientation = Enum('horizontal', 'vertical') + #: The orientation of the splitter. + orientation = Enum("horizontal", "vertical") - # The sub-items of the splitter, which are PaneItems, Tabbed layouts, and - # other Splitters. - items = List(Either( - PaneItem, - Tabbed, - Instance('pyface.tasks.task_layout.Splitter')), pretty_skip=True) + #: The sub-items of the splitter, which are PaneItems, Tabbed layouts, and + #: other Splitters. + items = List( + Union( + Instance(PaneItem), + Instance(Tabbed), + Instance("pyface.tasks.task_layout.Splitter"), + ), + pretty_skip=True, + ) class HSplitter(Splitter): """ A convenience class for horizontal splitters. """ - orientation = Str('horizontal') + + orientation = Str("horizontal") class VSplitter(Splitter): """ A convenience class for vertical splitters. """ - orientation = Str('vertical') + + orientation = Str("vertical") class DockLayout(LayoutItem): @@ -162,23 +179,23 @@ """ # The layouts for the task's dock panes. - left = Either(PaneItem, Tabbed, Splitter) - right = Either(PaneItem, Tabbed, Splitter) - top = Either(PaneItem, Tabbed, Splitter) - bottom = Either(PaneItem, Tabbed, Splitter) - - # Assignments of dock areas to the window's corners. By default, the top - # and bottom dock areas extend into both of the top and both of the bottom - # corners, respectively. - top_left_corner = Enum('top', 'left') - top_right_corner = Enum('top', 'right') - bottom_left_corner = Enum('bottom', 'left') - bottom_right_corner = Enum('bottom', 'right') + left = Union(Instance(PaneItem), Instance(Tabbed), Instance(Splitter)) + right = Union(Instance(PaneItem), Instance(Tabbed), Instance(Splitter)) + top = Union(Instance(PaneItem), Instance(Tabbed), Instance(Splitter)) + bottom = Union(Instance(PaneItem), Instance(Tabbed), Instance(Splitter)) + + #: Assignments of dock areas to the window's corners. By default, the top + #: and bottom dock areas extend into both of the top and both of the + #: bottom corners, respectively. + top_left_corner = Enum("top", "left") + top_right_corner = Enum("top", "right") + bottom_left_corner = Enum("bottom", "left") + bottom_right_corner = Enum("bottom", "right") class TaskLayout(DockLayout): """ The layout for a Task. """ - # The ID of the task for which this is a layout. - id = Str + #: The ID of the task for which this is a layout. + id = Str() diff -Nru python-pyface-6.1.2/pyface/tasks/task_pane.py python-pyface-7.4.0/pyface/tasks/task_pane.py --- python-pyface-6.1.2/pyface/tasks/task_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,17 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.TaskPane` +""" + from pyface.toolkit import toolkit_object -TaskPane = toolkit_object('tasks.task_pane:TaskPane') + +TaskPane = toolkit_object("tasks.task_pane:TaskPane") diff -Nru python-pyface-6.1.2/pyface/tasks/task.py python-pyface-7.4.0/pyface/tasks/task.py --- python-pyface-6.1.2/pyface/tasks/task.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/task.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,9 +1,17 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.action.api import StatusBarManager -from traits.api import Callable, HasTraits, Instance, List, Str, \ - Unicode +from traits.api import Callable, HasTraits, Instance, List, Str + -# Local imports. from .action.schema import MenuBarSchema, ToolBarSchema from .action.schema_addition import SchemaAddition from pyface.tasks.task_layout import TaskLayout @@ -17,41 +25,42 @@ its view (a TaskWindow) and an application-specific model. """ - # The task's identifier. - id = Str + #: The task's identifier. + id = Str() - # The task's user-visible name. - name = Unicode + #: The task's user-visible name. + name = Str() - # The default layout to use for the task. If not overridden, only the - # central pane is displayed. + #: The default layout to use for the task. If not overridden, only the + #: central pane is displayed. default_layout = Instance(TaskLayout, ()) - # A list of extra IDockPane factories for the task. These dock panes are - # used in conjunction with the dock panes returned by create_dock_panes(). + #: A list of extra IDockPane factories for the task. These dock panes are + #: used in conjunction with the dock panes returned by + #: create_dock_panes(). extra_dock_pane_factories = List(Callable) - # The window to which the task is attached. Set by the framework. - window = Instance('pyface.tasks.task_window.TaskWindow') + #: The window to which the task is attached. Set by the framework. + window = Instance("pyface.tasks.task_window.TaskWindow") - #### Actions ############################################################## + # Actions -------------------------------------------------------------# - # The menu bar for the task. + #: The menu bar for the task. menu_bar = Instance(MenuBarSchema) - # The (optional) status bar for the task. + #: The (optional) status bar for the task. status_bar = Instance(StatusBarManager) - # The list of tool bars for the tasks. + #: The list of tool bars for the tasks. tool_bars = List(ToolBarSchema) - # A list of extra actions, groups, and menus that are inserted into menu - # bars and tool bars constructed from the above schemas. + #: A list of extra actions, groups, and menus that are inserted into menu + #: bars and tool bars constructed from the above schemas. extra_actions = List(SchemaAddition) - ########################################################################### + # ------------------------------------------------------------------------ # 'Task' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activated(self): """ Called after the task has been activated in a TaskWindow. @@ -61,7 +70,7 @@ def create_central_pane(self): """ Create and return the central pane, which must implement ITaskPane. """ - raise NotImplementedError + raise NotImplementedError() def create_dock_panes(self): """ Create and return the task's dock panes (IDockPane instances). diff -Nru python-pyface-6.1.2/pyface/tasks/tasks_application.py python-pyface-7.4.0/pyface/tasks/tasks_application.py --- python-pyface-6.1.2/pyface/tasks/tasks_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tasks_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,30 @@ -# Copyright (c) 2014-2016 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Define a base Task application class to create the event loop, and launch the creation of tasks and corresponding windows. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) -from functools import partial import logging from traits.api import ( - Callable, HasStrictTraits, List, Instance, Property, Str, Unicode, - cached_property, on_trait_change + Callable, + HasStrictTraits, + List, + Instance, + Property, + Str, + cached_property, + observe, ) +from traits.observation.api import trait from pyface.gui_application import GUIApplication @@ -33,14 +37,14 @@ #: The task factory's unique identifier. This ID is assigned to all tasks #: created by the factory. - id = Str + id = Str() #: The task factory's user-visible name. - name = Unicode + name = Str() #: A callable with the following signature: #: - #: callable(**traits) -> Task + #: callable(\**traits) -> Task #: #: Often this attribute will simply be a Task subclass. factory = Callable @@ -67,7 +71,9 @@ tasks = List(Instance("pyface.tasks.task.Task")) #: Currently active Task if any. - active_task = Property(depends_on='active_window.active_task') + active_task = Property( + observe=trait("active_window").trait("active_task", optional=True) + ) #: List of all application task factories. task_factories = List() @@ -75,12 +81,12 @@ #: The default layout for the application. If not specified, a single #: window will be created with the first available task factory. default_layout = List( - Instance('pyface.tasks.task_window_layout.TaskWindowLayout') + Instance("pyface.tasks.task_window_layout.TaskWindowLayout") ) #: Hook to add global schema additions to tasks/windows extra_actions = List( - Instance('pyface.tasks.action.schema_addition.SchemaAddition') + Instance("pyface.action.schema.schema_addition.SchemaAddition") ) #: Hook to add global dock pane additions to tasks/windows @@ -128,7 +134,7 @@ """ from pyface.tasks.task_window_layout import TaskWindowLayout - window = super(TasksApplication, self).create_window(**kwargs) + window = super().create_window(**kwargs) if layout is not None: for task_id in layout.get_tasks(): @@ -136,7 +142,7 @@ if task is not None: window.add_task(task) else: - msg = 'Missing factory for task with ID %r' + msg = "Missing factory for task with ID %r" logger.error(msg, task_id) else: # Create an empty layout to set default size and position only @@ -168,14 +174,14 @@ # Destruction utilities --------------------------------------------------- - @on_trait_change('windows:closed') - def _on_window_closed(self, window, trait, old, new): + @observe("windows:items:closed") + def _on_window_closed(self, event): """ Listener that ensures window handles are released when closed. """ - if getattr(window, 'active_task', None) in self.tasks: + window = event.object + if getattr(window, "active_task", None) in self.tasks: self.tasks.remove(window.active_task) - super(TasksApplication, - self)._on_window_closed(window, trait, old, new) + super()._on_window_closed(event) # Trait initializers and property getters --------------------------------- @@ -186,10 +192,12 @@ from the Task and the TaskWindow is just a shell. """ from pyface.tasks.task_window import TaskWindow + return TaskWindow def _default_layout_default(self): from pyface.tasks.task_window_layout import TaskWindowLayout + window_layout = TaskWindowLayout() if self.task_factories: window_layout.items = [self.task_factories[0].id] @@ -207,57 +215,58 @@ rather than new task windows. """ from pyface.action.api import ( - AboutAction, CloseActiveWindowAction, ExitAction + AboutAction, + CloseActiveWindowAction, + ExitAction, ) + from pyface.action.schema.api import SMenu, SchemaAddition from pyface.tasks.action.api import ( - CreateTaskWindowAction, SchemaAddition, SMenu, - TaskWindowToggleGroup + CreateTaskWindowAction, + TaskWindowToggleGroup, ) return [ SchemaAddition( factory=CreateTaskWindowAction.factory( - application=self, - accelerator='Ctrl+Shift+N', + application=self, accelerator="Ctrl+Shift+N" ), - path='MenuBar/File/new_group', + path="MenuBar/File/new_group", ), SchemaAddition( - id='close_action', + id="close_action", factory=CloseActiveWindowAction.factory( - application=self, - accelerator='Ctrl+Shift+W', + application=self, accelerator="Ctrl+Shift+W" ), - path='MenuBar/File/close_group', + path="MenuBar/File/close_group", ), SchemaAddition( - id='exit_action', + id="exit_action", factory=ExitAction.factory(application=self), - path='MenuBar/File/close_group', - absolute_position='last', + path="MenuBar/File/close_group", + absolute_position="last", ), SchemaAddition( - #id='Window', + # id='Window', factory=lambda: SMenu( TaskWindowToggleGroup(application=self), - id='Window', - name='&Window', + id="Window", + name="&Window", ), - path='MenuBar', + path="MenuBar", after="View", - before="Help" + before="Help", ), SchemaAddition( - id='about_action', + id="about_action", factory=AboutAction.factory(application=self), - path='MenuBar/Help', - absolute_position='first', + path="MenuBar/Help", + absolute_position="first", ), ] @cached_property def _get_active_task(self): if self.active_window is not None: - return getattr(self.active_window, 'active_task', None) + return getattr(self.active_window, "active_task", None) else: return None diff -Nru python-pyface-6.1.2/pyface/tasks/task_window_backend.py python-pyface-7.4.0/pyface/tasks/task_window_backend.py --- python-pyface-6.1.2/pyface/tasks/task_window_backend.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/task_window_backend.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,19 @@ -# Import the toolkit specific version. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +- :attr:`~.TaskWindowBackend` +""" + from pyface.toolkit import toolkit_object + TaskWindowBackend = toolkit_object( - 'tasks.task_window_backend:TaskWindowBackend') + "tasks.task_window_backend:TaskWindowBackend" +) diff -Nru python-pyface-6.1.2/pyface/tasks/task_window_layout.py python-pyface-7.4.0/pyface/tasks/task_window_layout.py --- python-pyface-6.1.2/pyface/tasks/task_window_layout.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/task_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,38 @@ -# Enthought library imports. -from traits.api import Either, List, Str, Tuple, Enum +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import List, Str, Tuple, Enum, Instance, Union -# Local imports. from pyface.tasks.task_layout import LayoutContainer, TaskLayout -import six class TaskWindowLayout(LayoutContainer): """ The layout of a TaskWindow. """ - # The ID of the active task. If unspecified, the first task will be active. - active_task = Str - - # The tasks contained in the window. If an ID is specified, the task will - # use its default layout. Otherwise, it will use the specified TaskLayout. - items = List(Either(Str, TaskLayout), pretty_skip=True) + #: The ID of the active task. If unspecified, the first task will be + #: active. + active_task = Str() + + #: The tasks contained in the window. If an ID is specified, the task will + #: use its default layout. Otherwise, it will use the specified TaskLayout + items = List(Union(Str, Instance(TaskLayout)), pretty_skip=True) - # The position of the window. + #: The position of the window. position = Tuple(-1, -1) - # The size of the window. + #: The size of the window. size = Tuple(800, 600) - size_state = Enum('normal', 'maximized') + #: Whether or not the application is maximized. + size_state = Enum("normal", "maximized") def get_active_task(self): """ Returns the ID of the active task in the layout, or None if there is @@ -33,18 +42,20 @@ return self.active_task elif self.items: first = self.items[0] - return first if isinstance(first, six.string_types) else first.id + return first if isinstance(first, str) else first.id return None def get_tasks(self): """ Returns the IDs of the tasks in the layout. """ - return [ (item if isinstance(item, six.string_types) else item.id) - for item in self.items ] + return [ + (item if isinstance(item, str) else item.id) for item in self.items + ] def is_equivalent_to(self, layout): """ Returns whether two layouts are equivalent, i.e. whether they contain the same tasks. """ - return isinstance(layout, TaskWindowLayout) and \ - set(self.get_tasks()) == set(layout.get_tasks()) + return isinstance(layout, TaskWindowLayout) and set( + self.get_tasks() + ) == set(layout.get_tasks()) diff -Nru python-pyface-6.1.2/pyface/tasks/task_window.py python-pyface-7.4.0/pyface/tasks/task_window.py --- python-pyface-6.1.2/pyface/tasks/task_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/task_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,39 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import logging -# Enthought library imports. + from pyface.action.api import MenuBarManager, StatusBarManager, ToolBarManager from pyface.api import ApplicationWindow -from traits.api import Bool, Callable, HasTraits, HasStrictTraits, Instance, \ - List, Property, Unicode, Vetoable, on_trait_change - -# Local imports. -from pyface.tasks.action.task_action_manager_builder import TaskActionManagerBuilder +from traits.api import ( + Bool, + Callable, + HasStrictTraits, + Instance, + List, + Property, + Str, + Vetoable, + observe, +) + + +from pyface.tasks.action.task_action_manager_builder import ( + TaskActionManagerBuilder, +) from pyface.tasks.i_dock_pane import IDockPane from pyface.tasks.i_task_pane import ITaskPane from pyface.tasks.task import Task, TaskLayout from pyface.tasks.task_window_backend import TaskWindowBackend from pyface.tasks.task_window_layout import TaskWindowLayout -import six # Logging. logger = logging.getLogger(__name__) @@ -27,46 +46,46 @@ its tasks. """ - #### IWindow interface #################################################### + # IWindow interface ---------------------------------------------------- - # Unless a title is specifically assigned, delegate to the active task. - title = Property(Unicode, depends_on=['active_task.name', '_title']) + #: Unless a title is specifically assigned, delegate to the active task. + title = Property(Str, observe=["active_task.name", "_title"]) - #### TaskWindow interface ################################################ + # TaskWindow interface ------------------------------------------------ - # The pane (central or dock) in the active task that currently has focus. + #: The pane (central or dock) in the active task that currently has focus. active_pane = Instance(ITaskPane) - # The active task for this window. + #: The active task for this window. active_task = Instance(Task) - # The list of all tasks currently attached to this window. All panes of the - # inactive tasks are hidden. + #: The list of all tasks currently attached to this window. All panes of + #: the inactive tasks are hidden. tasks = List(Task) - # The central pane of the active task, which is always visible. + #: The central pane of the active task, which is always visible. central_pane = Instance(ITaskPane) - # The list of all dock panes in the active task, which may or may not be - # visible. + #: The list of all dock panes in the active task, which may or may not be + #: visible. dock_panes = List(IDockPane) - # The factory for the window's TaskActionManagerBuilder, which is - # instantiated to translate menu and tool bar schemas into Pyface action - # managers. This attribute can overridden to introduce custom logic into - # the translation process, although this is not usually necessary. + #: The factory for the window's TaskActionManagerBuilder, which is + #: instantiated to translate menu and tool bar schemas into Pyface action + #: managers. This attribute can overridden to introduce custom logic into + #: the translation process, although this is not usually necessary. action_manager_builder_factory = Callable(TaskActionManagerBuilder) - #### Protected traits ##################################################### + # Protected traits ----------------------------------------------------- - _active_state = Instance('pyface.tasks.task_window.TaskState') - _states = List(Instance('pyface.tasks.task_window.TaskState')) - _title = Unicode + _active_state = Instance("pyface.tasks.task_window.TaskState") + _states = List(Instance("pyface.tasks.task_window.TaskState")) + _title = Str() _window_backend = Instance(TaskWindowBackend) - ########################################################################### + # ------------------------------------------------------------------------ # 'Widget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Overridden to ensure that all task panes are cleanly destroyed. @@ -79,11 +98,11 @@ # undesirable animations when the window is being closed. for state in self._states: self._destroy_state(state) - super(TaskWindow, self).destroy() + super().destroy() - ########################################################################### + # ------------------------------------------------------------------------ # 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Opens the window. @@ -107,18 +126,18 @@ return self.control is not None and not event.veto - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Delegate to the TaskWindowBackend. """ return self._window_backend.create_contents(parent) - ########################################################################### + # ------------------------------------------------------------------------ # 'TaskWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_task(self, task): """ Activates a task that has already been added to the window. @@ -142,7 +161,7 @@ self._active_state = state task.activated() elif not state: - logger.warn( + logger.warning( "Cannot activate task %r: task does not belong to the " "window." % task ) @@ -200,7 +219,7 @@ self._destroy_state(state) self._states.remove(state) else: - logger.warn( + logger.warning( "Cannot remove task %r: task does not belong to the " "window." % task ) @@ -257,7 +276,7 @@ state = self._get_state(id) return state.task if state else None - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_layout(self): """ Returns a TaskLayout (for the active task) that reflects the state @@ -306,13 +325,13 @@ # Store layouts for the tasks, including the active task. for layout in window_layout.items: - if isinstance(layout, six.string_types): + if isinstance(layout, str): continue state = self._get_state(layout.id) if state: state.layout = layout else: - logger.warn( + logger.warning( "Cannot apply layout for task %r: task does not " "belong to the window." % layout.id ) @@ -327,9 +346,9 @@ else: self.activate_task(state.task) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'TaskWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _destroy_state(self, state): """ Destroy all controls associated with a Task state. @@ -360,13 +379,15 @@ if self._active_state: layout = self.get_layout() panes.append(self.central_pane) - for area in ('top', 'right', 'bottom', 'left'): + for area in ("top", "right", "bottom", "left"): item = getattr(layout, area) if item: - panes.extend([ - self.get_dock_pane(pane_item.id) - for pane_item in item.iterleaves() - ]) + panes.extend( + [ + self.get_dock_pane(pane_item.id) + for pane_item in item.iterleaves() + ] + ) return panes def _get_state(self, id_or_task): @@ -378,12 +399,12 @@ return state return None - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def __window_backend_default(self): return TaskWindowBackend(window=self) - #### Trait property getters/setters ####################################### + # Trait property getters/setters --------------------------------------- def _get_title(self): if self._title or self.active_task is None: @@ -393,9 +414,11 @@ def _set_title(self, title): self._title = title - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - def __active_state_changed(self, state): + @observe("_active_state") + def _update_traits_given_new_active_state(self, event): + state = event.new if state is None: self.active_task = self.central_pane = None self.dock_panes = [] @@ -409,13 +432,13 @@ self.status_bar_manager = state.status_bar_manager self.tool_bar_managers = state.tool_bar_managers - @on_trait_change('central_pane.has_focus, dock_panes.has_focus') - def _focus_updated(self, obj, name, old, new): - if name == 'has_focus' and new: - self.active_pane = obj + @observe("central_pane:has_focus, dock_panes:items:has_focus") + def _focus_updated(self, event): + if event.new: + self.active_pane = event.object - @on_trait_change('_states[]') - def _states_updated(self): + @observe("_states.items") + def _states_updated(self, event): self.tasks = [state.task for state in self._states] diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_action_manager_builder.py python-pyface-7.4.0/pyface/tasks/tests/test_action_manager_builder.py --- python-pyface-6.1.2/pyface/tasks/tests/test_action_manager_builder.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_action_manager_builder.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,347 +0,0 @@ -# Standard library imports. -import unittest - -# Enthought library imports. -from pyface.action.api import Action, ActionItem, ActionManager, Group, \ - MenuManager, MenuBarManager -from pyface.tasks.action.api import GroupSchema, MenuSchema, MenuBarSchema, \ - SchemaAddition -from pyface.tasks.action.task_action_manager_builder import \ - TaskActionManagerBuilder -from pyface.tasks.api import Task - - -class ActionManagerBuilderTestCase(unittest.TestCase): - - #### 'TestCase' protocol ################################################## - - def setUp(self): - """ Create some dummy actions to use while testing. - """ - for i in range(1, 7): - action_id = 'action%i' % i - setattr(self, action_id, Action(id=action_id, name='Action %i'%i)) - - #### 'ActionManagerBuilderTestCase' protocol ############################## - - def assertActionElementsEqual(self, first, second): - """ Checks that two action managers are (logically) equivalent. - """ - children1 = children2 = [] - self.assertEquals(type(first), type(second)) - self.assertEquals(first.id, second.id) - - if isinstance(first, ActionItem): - self.assertEquals(first.action.name, second.action.name) - - elif isinstance(first, ActionManager): - if not isinstance(first, MenuBarManager): - self.assertEquals(first.name, second.name) - children1, children2 = first.groups, second.groups - - elif isinstance(first, Group): - self.assertEquals(first.separator, second.separator) - children1, children2 = first.items, second.items - - self.assertEquals(len(children1), len(children2)) - for i in range(len(children1)): - self.assertActionElementsEqual(children1[i], children2[i]) - - #### Tests ################################################################ - - def test_simple_menu_bar(self): - """ Does constructing a simple menu with no additions work? - """ - schema = MenuBarSchema( - MenuSchema(self.action1, self.action2, id='File', name='&File'), - MenuSchema(self.action3, self.action4, id='Edit', name='&Edit')) - builder = TaskActionManagerBuilder(task=Task(menu_bar=schema)) - actual = builder.create_menu_bar_manager() - desired = MenuBarManager(MenuManager(self.action1, self.action2, - id='File', name='&File'), - MenuManager(self.action3, self.action4, - id='Edit', name='&Edit'), - id='MenuBar') - self.assertActionElementsEqual(actual, desired) - - #### Tests about schema additions ######################################### - - def test_additions_menu_bar(self): - """ Does constructing a menu with a few additions work? - """ - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, self.action2, id='FileGroup'), - id='File')) - extras = [ SchemaAddition(factory=lambda: self.action3, - before='action1', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action4, - before='action1', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action5, - path='MenuBar/File/FileGroup')] - builder = TaskActionManagerBuilder(task=Task(menu_bar=schema, - extra_actions=extras)) - actual = builder.create_menu_bar_manager() - desired = MenuBarManager(MenuManager(Group(self.action3, self.action4, - self.action1, self.action2, - self.action5, - id='FileGroup'), - id='File'), - id='MenuBar') - self.assertActionElementsEqual(actual, desired) - - def test_extra_menu(self): - """ Test contributing a whole new menu to the menu bar. """ - - # Initial menu. - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, id='FileGroup'), - id='FileMenu') - ) - - # Contributed menu. - extra_menu = MenuSchema( - GroupSchema(self.action2, id='BarGroup'), - id= 'DummyActionsMenu', - ) - - extra_actions = [ - SchemaAddition(path='MenuBar', - factory=lambda : extra_menu, - id='DummyActionsSMenu'), - ] - - # Build the final menu. - builder = TaskActionManagerBuilder( - task=Task(menu_bar=schema, extra_actions=extra_actions) - ) - actual = builder.create_menu_bar_manager() - - desired = MenuBarManager( - MenuManager(Group(self.action1, id='FileGroup'), - id='FileMenu'), - MenuManager(Group(self.action2, id='BarGroup'), - id='DummyActionsMenu'), - id='MenuBar' - ) - - self.assertActionElementsEqual(actual, desired) - - #### Tests about merging schemas ########################################## - - def test_merging_redundant_items(self): - """ Menus and groups with matching path are merged together. """ - - # Initial menu. - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, id='FileGroup'), - name='File menu number one', id='FileMenu') - ) - - # Contributed menus. - extra_menu = MenuSchema( - GroupSchema(self.action2, id='FileGroup'), - name='File menu number two', - id= 'FileMenu', - ) - - extra_actions = [ - SchemaAddition(path='MenuBar', - factory=lambda : extra_menu, - id='DummyActionsSMenu'), - ] - - # Build the final menu. - builder = TaskActionManagerBuilder( - task=Task(menu_bar=schema, extra_actions=extra_actions) - ) - actual = builder.create_menu_bar_manager() - - # Note that we expect the name of the menu to be inherited from - # the menu in the menu bar schema that is defined first. - desired = MenuBarManager( - MenuManager(Group(self.action1, self.action2, id='FileGroup'), - name='File menu number one', id='FileMenu'), - id='MenuBar' - ) - self.assertActionElementsEqual(actual, desired) - - def test_unwanted_merge(self): - """ Test that we don't have automatic merges due to forgetting to set - a schema ID. """ - - # Initial menu. - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, id='FileGroup'), - name='File 1') - ) - - # Contributed menus. - extra_menu = MenuSchema( - GroupSchema(self.action2, id='FileGroup'), - name='File 2' - ) - - extra_actions = [ - SchemaAddition(path='MenuBar', - factory=lambda : extra_menu, - id='DummyActionsSMenu'), - ] - - # Build the final menu. - builder = TaskActionManagerBuilder( - task=Task(menu_bar=schema, extra_actions=extra_actions) - ) - actual = builder.create_menu_bar_manager() - - # Note that we expect the name of the menu to be inherited from - # the menu in the menu bar schema that is defined first. - desired = MenuBarManager( - MenuManager(Group(self.action1, id='FileGroup'), - name='File 1', id='MenuSchema_1'), - MenuManager(Group(self.action2, id='FileGroup'), - name='File 2', id='MenuSchema_2'), - id='MenuBar' - ) - self.assertActionElementsEqual(actual, desired) - - def test_merging_items_with_same_id_but_different_class(self): - """ Schemas with the same path but different types (menus, groups) - are not merged together. - - Having a group and a menu with the same path is of course bad practice, - but we need a predictable outcome. - - """ - - # Initial menu. - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, id='FileGroup'), - id='FileSchema') - ) - - # Contributed menus. - extra_group = GroupSchema(self.action2, id='FileSchema') - - extra_actions = [ - SchemaAddition(path='MenuBar', - factory=(lambda : extra_group), - id='DummyActionsSMenu'), - ] - - # Build the final menu. - builder = TaskActionManagerBuilder( - task=Task(menu_bar=schema, extra_actions=extra_actions) - ) - actual = builder.create_menu_bar_manager() - - desired = MenuBarManager( - MenuManager(Group(self.action1, id='FileGroup'), - id='FileSchema'), - Group(self.action2, id='FileSchema'), - id='MenuBar' - ) - self.assertActionElementsEqual(actual, desired) - - def test_merging_redundant_items_that_are_not_schemas(self): - """ Items that are not schemas cannot be merged, but we should - not crash, either. """ - - # Initial menu. - schema = MenuBarSchema( - # This menu is not a schema... - MenuManager(Group(self.action1, id='FileGroup'), - id='FileMenu') - ) - - # Contributed menus. - extra_menu = MenuSchema( - GroupSchema(self.action2, id='FileGroup'), - id= 'FileMenu', - ) - - extra_actions = [ - SchemaAddition(path='MenuBar', - factory=lambda : extra_menu, - id='DummyActionsSMenu'), - ] - - # Build the final menu. - builder = TaskActionManagerBuilder( - task=Task(menu_bar=schema, extra_actions=extra_actions) - ) - actual = builder.create_menu_bar_manager() - - desired = MenuBarManager( - MenuManager(Group(self.action1, id='FileGroup'), - id='FileMenu'), - MenuManager(Group(self.action2, id='FileGroup'), - id='FileMenu'), - id='MenuBar' - ) - self.assertActionElementsEqual(actual, desired) - - #### Tests about ordering ################################################# - - def test_absolute_ordering(self): - """ Does specifying absolute_position work? - """ - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, self.action2, id='FileGroup'), - id='File')) - extras = [ SchemaAddition(factory=lambda: self.action3, - absolute_position='last', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action4, - absolute_position='first', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action5, - absolute_position='first', - path='MenuBar/File/FileGroup')] - builder = TaskActionManagerBuilder(task=Task(menu_bar=schema, - extra_actions=extras)) - actual = builder.create_menu_bar_manager() - desired = MenuBarManager(MenuManager(Group(self.action4, self.action5, - self.action1, self.action2, - self.action3, - id='FileGroup'), - id='File'), - id='MenuBar') - self.assertActionElementsEqual(actual, desired) - - def test_absolute_and_before_after(self): - """ Does specifying absolute_position along with before, after work? - """ - schema = MenuBarSchema( - MenuSchema(GroupSchema(self.action1, self.action2, id='FileGroup'), - id='File')) - extras = [ SchemaAddition(factory=lambda: self.action3, - id='action3', - after='action2', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action4, - after='action3', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action5, - id='action5', - absolute_position='last', - path='MenuBar/File/FileGroup'), - SchemaAddition(factory=lambda: self.action6, - absolute_position='last', - before='action5', - path='MenuBar/File/FileGroup') - ] - builder = TaskActionManagerBuilder(task=Task(menu_bar=schema, - extra_actions=extras)) - actual = builder.create_menu_bar_manager() - desired = MenuBarManager(MenuManager(Group(self.action1, self.action2, - self.action3, self.action4, - self.action6, self.action5, - id='FileGroup'), - id='File'), - id='MenuBar') - self.assertActionElementsEqual(actual, desired) - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_advanced_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/tests/test_advanced_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/tests/test_advanced_editor_area_pane.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_advanced_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,50 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +import unittest + +from pyface.tasks.api import Editor, AdvancedEditorAreaPane +from pyface.toolkit import toolkit_object + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestAdvancedEditorAreaPane(unittest.TestCase, GuiTestAssistant): + def setUp(self): + GuiTestAssistant.setUp(self) + self.area_pane = AdvancedEditorAreaPane() + + def tearDown(self): + if self.area_pane.control is not None: + with self.delete_widget(self.area_pane.control): + self.area_pane.destroy() + GuiTestAssistant.tearDown(self) + + def test_create_destroy(self): + # test that creating and destroying works as expected + with self.event_loop(): + self.area_pane.create(None) + with self.event_loop(): + self.area_pane.destroy() + + def test_create_destroy_with_editor(self): + # test that creating and destroying works as expected when there are + # editors + with self.event_loop(): + self.area_pane.create(None) + with self.event_loop(): + editor = self.area_pane.create_editor("Hello", Editor) + with self.event_loop(): + self.area_pane.add_editor(editor) + with self.event_loop(): + self.area_pane.activate_editor(editor) + with self.event_loop(): + self.area_pane.destroy() diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_dock_pane_toggle_group.py python-pyface-7.4.0/pyface/tasks/tests/test_dock_pane_toggle_group.py --- python-pyface-6.1.2/pyface/tasks/tests/test_dock_pane_toggle_group.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_dock_pane_toggle_group.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,41 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -# Enthought library imports. -from pyface.tasks.action.api import SMenu, SMenuBar, SGroup, \ - DockPaneToggleGroup + +from pyface.action.schema.api import SMenu, SMenuBar, SGroup +from pyface.tasks.action.api import DockPaneToggleGroup from pyface.tasks.api import DockPane, Task, TaskPane, TaskWindow from pyface.gui import GUI from traits.api import List from traits.etsconfig.api import ETSConfig -USING_WX = ETSConfig.toolkit not in ['', 'qt4'] +USING_WX = ETSConfig.toolkit not in ["", "qt4"] class BogusTask(Task): - id = 'tests.bogus_task' - name = 'Bogus Task' + id = "tests.bogus_task" + name = "Bogus Task" - dock_panes = List + dock_panes = List() def create_central_pane(self): - return TaskPane(id='tests.bogus_task.central_pane') + return TaskPane(id="tests.bogus_task.central_pane") def create_dock_panes(self): self.dock_panes = dock_panes = [ - DockPane(id='tests.bogus_task.dock_pane_2', name='Dock Pane 2'), - DockPane(id='tests.bogus_task.dock_pane_1', name='Dock Pane 1'), + DockPane(id="tests.bogus_task.dock_pane_2", name="Dock Pane 2"), + DockPane(id="tests.bogus_task.dock_pane_1", name="Dock Pane 1"), ] return dock_panes @@ -36,9 +45,10 @@ SMenu( SGroup( group_factory=DockPaneToggleGroup, - id='tests.bogus_task.DockPaneToggleGroup' + id="tests.bogus_task.DockPaneToggleGroup", ), - id= 'View', name='&View' + id="View", + name="&View", ) ) @@ -46,7 +56,6 @@ class DockPaneToggleGroupTestCase(unittest.TestCase): - @unittest.skipIf(USING_WX, "TaskWindowBackend is not implemented in WX") def setUp(self): self.gui = GUI() @@ -61,8 +70,9 @@ # Fish the dock pane toggle group from the menu bar manager. dock_pane_toggle_group = [] + def find_doc_pane_toggle(item): - if item.id == 'tests.bogus_task.DockPaneToggleGroup': + if item.id == "tests.bogus_task.DockPaneToggleGroup": dock_pane_toggle_group.append(item) self.task_state.menu_bar_manager.walk(find_doc_pane_toggle) @@ -80,16 +90,15 @@ del self.window del self.gui - def get_dock_pane_toggle_action_names(self): - names = [ + names = [ action_item.action.name for action_item in self.dock_pane_toggle_group.items ] return names - #### Tests ################################################################ + # Tests ---------------------------------------------------------------- def test_group_content_at_startup(self): # Check that there are 2 dock panes in the group at the beginning. @@ -97,13 +106,13 @@ # Names are sorted by the group. names = self.get_dock_pane_toggle_action_names() - expected_names = ['Dock Pane 1', 'Dock Pane 2'] + expected_names = ["Dock Pane 1", "Dock Pane 2"] self.assertEqual(list(sorted(expected_names)), list(sorted(names))) def test_react_to_dock_pane_added(self): # Add a dock pane to the task. self.task_state.dock_panes.append( - DockPane(id='tests.bogus_task.dock_pane_0', name='Dock Pane 0') + DockPane(id="tests.bogus_task.dock_pane_0", name="Dock Pane 0") ) # Check that there are 3 dock panes in the group. @@ -111,7 +120,7 @@ # Names are sorted by the group. names = self.get_dock_pane_toggle_action_names() - expected_names = ['Dock Pane 0', 'Dock Pane 1', 'Dock Pane 2'] + expected_names = ["Dock Pane 0", "Dock Pane 1", "Dock Pane 2"] self.assertEqual(list(sorted(expected_names)), list(sorted(names))) def test_react_to_dock_pane_removed(self): @@ -122,9 +131,5 @@ self.assertEqual(1, len(self.dock_pane_toggle_group.items)) names = self.get_dock_pane_toggle_action_names() - expected_names = ['Dock Pane 1'] + expected_names = ["Dock Pane 1"] self.assertEqual(list(sorted(expected_names)), list(sorted(names))) - - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/tests/test_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/tests/test_editor_area_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,34 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -# Enthought library imports. + from traits.etsconfig.api import ETSConfig from pyface.tasks.api import Editor, EditorAreaPane +from pyface.toolkit import toolkit_object -USING_WX = ETSConfig.toolkit not in ['', 'qt4'] +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" +USING_WX = ETSConfig.toolkit not in ["", "qt4"] -class EditorAreaPaneTestCase(unittest.TestCase): +class EditorAreaPaneTestCase(unittest.TestCase): @unittest.skipIf(USING_WX, "EditorAreaPane is not implemented in WX") def test_create_editor(self): """ Does creating an editor work? """ area = EditorAreaPane() area.register_factory(Editor, lambda obj: isinstance(obj, int)) - self.assert_(isinstance(area.create_editor(0), Editor)) + self.assertTrue(isinstance(area.create_editor(0), Editor)) @unittest.skipIf(USING_WX, "EditorAreaPane is not implemented in WX") def test_factories(self): @@ -25,11 +37,27 @@ area = EditorAreaPane() area.register_factory(Editor, lambda obj: isinstance(obj, int)) self.assertEqual(area.get_factory(0), Editor) - self.assertEqual(area.get_factory('foo'), None) + self.assertEqual(area.get_factory("foo"), None) area.unregister_factory(Editor) self.assertEqual(area.get_factory(0), None) -if __name__ == '__main__': - unittest.main() +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestEditorAreaPane(unittest.TestCase, GuiTestAssistant): + def setUp(self): + GuiTestAssistant.setUp(self) + self.area_pane = EditorAreaPane() + + def tearDown(self): + if self.area_pane.control is not None: + with self.delete_widget(self.area_pane.control): + self.area_pane.destroy() + GuiTestAssistant.tearDown(self) + + def test_create_destroy(self): + # test that creating and destroying works as expected + with self.event_loop(): + self.area_pane.create(None) + with self.event_loop(): + self.area_pane.destroy() diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_enaml_dock_pane.py python-pyface-7.4.0/pyface/tasks/tests/test_enaml_dock_pane.py --- python-pyface-6.1.2/pyface/tasks/tests/test_enaml_dock_pane.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_enaml_dock_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,44 +1,53 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest from traits.etsconfig.api import ETSConfig # Skip tests if Enaml is not installed or we're using the wx backend. SKIP_REASON = None -if ETSConfig.toolkit not in ['', 'qt4']: +if ETSConfig.toolkit not in ["", "qt4"]: SKIP_REASON = "Enaml does not support WX" else: try: from enaml.widgets.api import Label from traits_enaml.testing.gui_test_assistant import GuiTestAssistant except ImportError: - SKIP_REASON = "Enaml not installed" + SKIP_REASON = "traits_enaml is not installed" if SKIP_REASON is not None: # Dummy class so that the TestEnamlTaskPane class definition below # doesn't fail. - class GuiTestAssistant(object): + class GuiTestAssistant(object): # noqa: F811 pass + from pyface.tasks.api import EnamlDockPane, Task class DummyDockPane(EnamlDockPane): - def create_component(self): - return Label(text='test label') + return Label(text="test label") @unittest.skipIf(SKIP_REASON is not None, SKIP_REASON) class TestEnamlDockPane(GuiTestAssistant, unittest.TestCase): - ########################################################################### + # ------------------------------------------------------------------------ # 'TestCase' interface - ########################################################################### + # ------------------------------------------------------------------------ def setUp(self): GuiTestAssistant.setUp(self) - self.dock_pane = DummyDockPane(task=Task(id='dummy_task')) + self.dock_pane = DummyDockPane(task=Task(id="dummy_task")) with self.event_loop(): self.dock_pane.create(None) @@ -49,9 +58,9 @@ del self.dock_pane GuiTestAssistant.tearDown(self) - ########################################################################### + # ------------------------------------------------------------------------ # Tests - ########################################################################### + # ------------------------------------------------------------------------ def test_creation(self): self.assertIsInstance(self.dock_pane.component, Label) diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_enaml_editor.py python-pyface-7.4.0/pyface/tasks/tests/test_enaml_editor.py --- python-pyface-6.1.2/pyface/tasks/tests/test_enaml_editor.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_enaml_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,32 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest from traits.etsconfig.api import ETSConfig # Skip tests if Enaml is not installed or we're using the wx backend. SKIP_REASON = None -if ETSConfig.toolkit not in ['', 'qt4']: +if ETSConfig.toolkit not in ["", "qt4"]: SKIP_REASON = "Enaml does not support WX" else: try: from enaml.widgets.api import Label from traits_enaml.testing.gui_test_assistant import GuiTestAssistant except ImportError: - SKIP_REASON = "Enaml not installed" + SKIP_REASON = "traits_enaml is not installed" if SKIP_REASON is not None: # Dummy class so that the TestEnamlTaskPane class definition below # doesn't fail. - class GuiTestAssistant(object): + class GuiTestAssistant(object): # noqa: F811 pass @@ -27,21 +36,22 @@ class DummyStrEditor(EnamlEditor): - obj = Str + obj = Str() def create_component(self): return Label(text=self.obj) + @unittest.skipIf(SKIP_REASON is not None, SKIP_REASON) class TestEnamlEditor(GuiTestAssistant, unittest.TestCase): - ########################################################################### + # ------------------------------------------------------------------------ # 'TestCase' interface - ########################################################################### + # ------------------------------------------------------------------------ def setUp(self): GuiTestAssistant.setUp(self) - self.obj = 'test message' + self.obj = "test message" self.editor = DummyStrEditor(obj=self.obj) with self.event_loop(): self.editor.create(None) @@ -53,9 +63,9 @@ del self.editor GuiTestAssistant.tearDown(self) - ########################################################################### + # ------------------------------------------------------------------------ # Tests - ########################################################################### + # ------------------------------------------------------------------------ def test_creation(self): self.assertIsInstance(self.editor.component, Label) diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_enaml_task_pane.py python-pyface-7.4.0/pyface/tasks/tests/test_enaml_task_pane.py --- python-pyface-6.1.2/pyface/tasks/tests/test_enaml_task_pane.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_enaml_task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,40 +1,49 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest from traits.etsconfig.api import ETSConfig # Skip tests if Enaml is not installed or we're using the wx backend. SKIP_REASON = None -if ETSConfig.toolkit not in ['', 'qt4']: +if ETSConfig.toolkit not in ["", "qt4"]: SKIP_REASON = "Enaml does not support WX" else: try: from enaml.widgets.api import Label from traits_enaml.testing.gui_test_assistant import GuiTestAssistant except ImportError: - SKIP_REASON = "Enaml not installed" + SKIP_REASON = "traits_enaml is not installed" if SKIP_REASON is not None: # Dummy class so that the TestEnamlTaskPane class definition below # doesn't fail. - class GuiTestAssistant(object): + class GuiTestAssistant(object): # noqa: F811 pass + from pyface.tasks.api import EnamlTaskPane class DummyTaskPane(EnamlTaskPane): - def create_component(self): - return Label(text='test label') + return Label(text="test label") @unittest.skipIf(SKIP_REASON is not None, SKIP_REASON) class TestEnamlTaskPane(GuiTestAssistant, unittest.TestCase): - ########################################################################### + # ------------------------------------------------------------------------ # 'TestCase' interface - ########################################################################### + # ------------------------------------------------------------------------ def setUp(self): GuiTestAssistant.setUp(self) @@ -49,9 +58,9 @@ del self.task_pane GuiTestAssistant.tearDown(self) - ########################################################################### + # ------------------------------------------------------------------------ # Tests - ########################################################################### + # ------------------------------------------------------------------------ def test_creation(self): self.assertIsInstance(self.task_pane.component, Label) diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_split_editor_area_pane.py python-pyface-7.4.0/pyface/tasks/tests/test_split_editor_area_pane.py --- python-pyface-6.1.2/pyface/tasks/tests/test_split_editor_area_pane.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_split_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,36 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +import unittest + +from pyface.tasks.api import SplitEditorAreaPane +from pyface.toolkit import toolkit_object + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestSplitEditorAreaPane(unittest.TestCase, GuiTestAssistant): + def setUp(self): + GuiTestAssistant.setUp(self) + self.area_pane = SplitEditorAreaPane() + + def tearDown(self): + if self.area_pane.control is not None: + with self.delete_widget(self.area_pane.control): + self.area_pane.destroy() + GuiTestAssistant.tearDown(self) + + def test_create_destroy(self): + # test that creating and destroying works as expected + with self.event_loop(): + self.area_pane.create(None) + with self.event_loop(): + self.area_pane.destroy() diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_task_action_manager_builder.py python-pyface-7.4.0/pyface/tasks/tests/test_task_action_manager_builder.py --- python-pyface-6.1.2/pyface/tasks/tests/test_task_action_manager_builder.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_task_action_manager_builder.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,459 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from contextlib import contextmanager +import unittest + + +from pyface.action.api import ( + Action, + ActionItem, + ActionManager, + Group, + MenuManager, + MenuBarManager, +) +from pyface.action.schema.api import ( + GroupSchema, + MenuSchema, + MenuBarSchema, + SchemaAddition, +) +from pyface.tasks.action.task_action_manager_builder import ( + TaskActionManagerBuilder, +) +from pyface.tasks.api import Task + + +class ActionManagerBuilderTestCase(unittest.TestCase): + + # 'TestCase' protocol -------------------------------------------------# + + def setUp(self): + """ Create some dummy actions to use while testing. + """ + for i in range(1, 7): + action_id = "action%i" % i + setattr( + self, action_id, Action(id=action_id, name="Action %i" % i) + ) + + # 'ActionManagerBuilderTestCase' protocol -----------------------------# + + def assertActionElementsEqual(self, first, second): + """ Checks that two action managers are (logically) equivalent. + """ + children1 = children2 = [] + self.assertEqual(type(first), type(second)) + self.assertEqual(first.id, second.id) + + if isinstance(first, ActionItem): + self.assertEqual(first.action.name, second.action.name) + + elif isinstance(first, ActionManager): + if not isinstance(first, MenuBarManager): + self.assertEqual(first.name, second.name) + children1, children2 = first.groups, second.groups + + elif isinstance(first, Group): + self.assertEqual(first.separator, second.separator) + children1, children2 = first.items, second.items + + self.assertEqual(len(children1), len(children2)) + for i in range(len(children1)): + self.assertActionElementsEqual(children1[i], children2[i]) + + def reset_unique_ids(self): + import pyface.util.id_helper as id_helper + id_helper.object_counter = id_helper._ObjectCounter() + + @contextmanager + def unique_id_context_manager(self): + self.reset_unique_ids() + try: + yield + finally: + self.reset_unique_ids() + + # Tests ---------------------------------------------------------------- + + def test_simple_menu_bar(self): + """ Does constructing a simple menu with no additions work? + """ + schema = MenuBarSchema( + MenuSchema(self.action1, self.action2, id="File", name="&File"), + MenuSchema(self.action3, self.action4, id="Edit", name="&Edit"), + ) + builder = TaskActionManagerBuilder(task=Task(menu_bar=schema)) + actual = builder.create_menu_bar_manager() + desired = MenuBarManager( + MenuManager(self.action1, self.action2, id="File", name="&File"), + MenuManager(self.action3, self.action4, id="Edit", name="&Edit"), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + # Tests about schema additions ----------------------------------------- + + def test_additions_menu_bar(self): + """ Does constructing a menu with a few additions work? + """ + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, self.action2, id="FileGroup"), + id="File", + ) + ) + extras = [ + SchemaAddition( + factory=lambda: self.action3, + before="action1", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action4, + before="action1", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action5, path="MenuBar/File/FileGroup" + ), + ] + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extras) + ) + actual = builder.create_menu_bar_manager() + desired = MenuBarManager( + MenuManager( + Group( + self.action3, + self.action4, + self.action1, + self.action2, + self.action5, + id="FileGroup", + ), + id="File", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_extra_menu(self): + """ Test contributing a whole new menu to the menu bar. """ + + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), id="FileMenu" + ) + ) + + # Contributed menu. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="BarGroup"), id="DummyActionsMenu" + ) + + extra_actions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extra_actions) + ) + actual = builder.create_menu_bar_manager() + + desired = MenuBarManager( + MenuManager(Group(self.action1, id="FileGroup"), id="FileMenu"), + MenuManager( + Group(self.action2, id="BarGroup"), id="DummyActionsMenu" + ), + id="MenuBar", + ) + + self.assertActionElementsEqual(actual, desired) + + # Tests about merging schemas -----------------------------------------# + + def test_merging_redundant_items(self): + """ Menus and groups with matching path are merged together. """ + + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), + name="File menu number one", + id="FileMenu", + ) + ) + + # Contributed menus. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="FileGroup"), + name="File menu number two", + id="FileMenu", + ) + + extra_actions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extra_actions) + ) + actual = builder.create_menu_bar_manager() + + # Note that we expect the name of the menu to be inherited from + # the menu in the menu bar schema that is defined first. + desired = MenuBarManager( + MenuManager( + Group(self.action1, self.action2, id="FileGroup"), + name="File menu number one", + id="FileMenu", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_unwanted_merge(self): + """ Test that we don't have automatic merges due to forgetting to set + a schema ID. """ + with self.unique_id_context_manager(): + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), name="File 1" + ) + ) + + # Contributed menus. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="FileGroup"), name="File 2" + ) + + extra_actions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extra_actions) + ) + actual = builder.create_menu_bar_manager() + + # Note that we expect the name of the menu to be inherited from + # the menu in the menu bar schema that is defined first. + desired = MenuBarManager( + MenuManager( + Group(self.action1, id="FileGroup"), + name="File 1", + id="MenuSchema_1", + ), + MenuManager( + Group(self.action2, id="FileGroup"), + name="File 2", + id="MenuSchema_2", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_merging_items_with_same_id_but_different_class(self): + """ Schemas with the same path but different types (menus, groups) + are not merged together. + + Having a group and a menu with the same path is of course bad practice, + but we need a predictable outcome. + + """ + + # Initial menu. + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, id="FileGroup"), id="FileSchema" + ) + ) + + # Contributed menus. + extra_group = GroupSchema(self.action2, id="FileSchema") + + extra_actions = [ + SchemaAddition( + path="MenuBar", + factory=(lambda: extra_group), + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extra_actions) + ) + actual = builder.create_menu_bar_manager() + + desired = MenuBarManager( + MenuManager(Group(self.action1, id="FileGroup"), id="FileSchema"), + Group(self.action2, id="FileSchema"), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_merging_redundant_items_that_are_not_schemas(self): + """ Items that are not schemas cannot be merged, but we should + not crash, either. """ + + # Initial menu. + schema = MenuBarSchema( + # This menu is not a schema... + MenuManager(Group(self.action1, id="FileGroup"), id="FileMenu") + ) + + # Contributed menus. + extra_menu = MenuSchema( + GroupSchema(self.action2, id="FileGroup"), id="FileMenu" + ) + + extra_actions = [ + SchemaAddition( + path="MenuBar", + factory=lambda: extra_menu, + id="DummyActionsSMenu", + ) + ] + + # Build the final menu. + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extra_actions) + ) + actual = builder.create_menu_bar_manager() + + desired = MenuBarManager( + MenuManager(Group(self.action1, id="FileGroup"), id="FileMenu"), + MenuManager(Group(self.action2, id="FileGroup"), id="FileMenu"), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + # Tests about ordering ------------------------------------------------- + + def test_absolute_ordering(self): + """ Does specifying absolute_position work? + """ + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, self.action2, id="FileGroup"), + id="File", + ) + ) + extras = [ + SchemaAddition( + factory=lambda: self.action3, + absolute_position="last", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action4, + absolute_position="first", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action5, + absolute_position="first", + path="MenuBar/File/FileGroup", + ), + ] + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extras) + ) + actual = builder.create_menu_bar_manager() + desired = MenuBarManager( + MenuManager( + Group( + self.action4, + self.action5, + self.action1, + self.action2, + self.action3, + id="FileGroup", + ), + id="File", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) + + def test_absolute_and_before_after(self): + """ Does specifying absolute_position along with before, after work? + """ + schema = MenuBarSchema( + MenuSchema( + GroupSchema(self.action1, self.action2, id="FileGroup"), + id="File", + ) + ) + extras = [ + SchemaAddition( + factory=lambda: self.action3, + id="action3", + after="action2", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action4, + after="action3", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action5, + id="action5", + absolute_position="last", + path="MenuBar/File/FileGroup", + ), + SchemaAddition( + factory=lambda: self.action6, + absolute_position="last", + before="action5", + path="MenuBar/File/FileGroup", + ), + ] + builder = TaskActionManagerBuilder( + task=Task(menu_bar=schema, extra_actions=extras) + ) + actual = builder.create_menu_bar_manager() + desired = MenuBarManager( + MenuManager( + Group( + self.action1, + self.action2, + self.action3, + self.action4, + self.action6, + self.action5, + id="FileGroup", + ), + id="File", + ), + id="MenuBar", + ) + self.assertActionElementsEqual(actual, desired) diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_task_layout.py python-pyface-7.4.0/pyface/tasks/tests/test_task_layout.py --- python-pyface-6.1.2/pyface/tasks/tests/test_task_layout.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_task_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -# Enthought library imports. + from pyface.tasks.api import HSplitter, PaneItem, Tabbed, VSplitter from ..task_layout import LayoutContainer @@ -45,8 +54,3 @@ items = self.items with self.assertRaises(ValueError): LayoutContainer(*items, items=items) - - - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_tasks_application.py python-pyface-7.4.0/pyface/tasks/tests/test_tasks_application.py --- python-pyface-6.1.2/pyface/tasks/tests/test_tasks_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_tasks_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,32 @@ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from traits.api import Bool +from traits.api import Bool, observe from pyface.application_window import ApplicationWindow from pyface.toolkit import toolkit_object from ..tasks_application import TasksApplication -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" EVENTS = [ - 'starting', 'started', 'application_initialized', 'stopping', 'stopped' + "starting", + "started", + "application_initialized", + "stopping", + "stopped", ] @@ -54,23 +65,26 @@ def start(self): if not self.start_cleanly: return False - super(TestingApp, self).start() + super().start() window = self.windows[0] - window.on_trait_change(self._on_window_closing, 'closing') + window.observe(self._on_window_closing, "closing") return True def stop(self): - super(TestingApp, self).stop() + super().stop() return self.stop_cleanly - def _on_window_closing(self, window, trait, old, new): + def _on_window_closing(self, event): + window = event.new if self.veto_close_window and not self.exit_vetoed: - new.veto = True + window.veto = True self.exit_vetoed = True - def _exiting_fired(self, event): - event.veto = self.veto_exit + @observe('exiting') + def _set_veto_on_exiting_event(self, event): + vetoable_event = event.new + vetoable_event.veto = self.veto_exit self.exit_vetoed = self.veto_exit def _prepare_exit(self): @@ -78,17 +92,18 @@ self.exit_prepared = True if self.exit_prepared_error: raise Exception("Exit preparation failed") - super(TestingApp, self)._prepare_exit() + super()._prepare_exit() -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestApplication(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) self.application_events = [] - if toolkit_object.toolkit == 'wx': + if toolkit_object.toolkit == "wx": import wx + self.event_loop() wx.GetApp().DeletePendingEvents() else: @@ -98,11 +113,12 @@ GuiTestAssistant.tearDown(self) def event_listener(self, event): - self.application_events.append(event) + application_event = event.new + self.application_events.append(application_event) def connect_listeners(self, app): for event in EVENTS: - app.on_trait_change(self.event_listener, event) + app.observe(self.event_listener, event) def test_defaults(self): from traits.etsconfig.api import ETSConfig @@ -118,7 +134,7 @@ app = TasksApplication() self.connect_listeners(app) window = ApplicationWindow() - app.on_trait_change(lambda: app.add_window(window), 'started') + app.observe(lambda _: app.add_window(window), "started") with self.assertMultiTraitChanges([app], EVENTS, []): self.gui.invoke_after(1000, app.exit) diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_task_window.py python-pyface-7.4.0/pyface/tasks/tests/test_task_window.py --- python-pyface-6.1.2/pyface/tasks/tests/test_task_window.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_task_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,80 +1,89 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from pyface.tasks.api import Task from ..task_window import TaskWindow - def _task_window_with_named_tasks(*names, **kwargs): tasks = [Task(name=name) for name in names] - first_active = kwargs.pop('first_active', False) + first_active = kwargs.pop("first_active", False) if first_active: - kwargs['active_task'] = tasks[0] + kwargs["active_task"] = tasks[0] task = TaskWindow(tasks=tasks, **kwargs) return task class TestTaskWindow(unittest.TestCase, UnittestTools): - def test_title_default(self): task_window = TaskWindow() # default is empty - self.assertEqual(task_window.title, '') + self.assertEqual(task_window.title, "") def test_title_no_active_task(self): - task_window = _task_window_with_named_tasks( - 'Test Task', 'Test Task 2') + task_window = _task_window_with_named_tasks("Test Task", "Test Task 2") # should be empty - self.assertEqual(task_window.title, '') + self.assertEqual(task_window.title, "") def test_title_activate_task(self): - task_window = _task_window_with_named_tasks('Test Task') + task_window = _task_window_with_named_tasks("Test Task") task = task_window.tasks[0] # activate task - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.active_task = task - self.assertEqual(task_window.title, 'Test Task') + self.assertEqual(task_window.title, "Test Task") def test_title_change_active_task_name(self): task_window = _task_window_with_named_tasks( - 'Test Task', first_active=True) + "Test Task", first_active=True + ) task_1 = task_window.tasks[0] # change task name - with self.assertTraitChanges(task_window, 'title', count=1): - task_1.name = 'Changed Name' - self.assertEqual(task_window.title, 'Changed Name') + with self.assertTraitChanges(task_window, "title", count=1): + task_1.name = "Changed Name" + self.assertEqual(task_window.title, "Changed Name") def test_title_change_active_task(self): task_window = _task_window_with_named_tasks( - 'Test Task 1', 'Test Task 2', first_active=True) + "Test Task 1", "Test Task 2", first_active=True + ) task = task_window.tasks[1] # change active task - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.active_task = task - self.assertEqual(task_window.title, 'Test Task 2') + self.assertEqual(task_window.title, "Test Task 2") def test_title_change_deactivate_task(self): task_window = _task_window_with_named_tasks( - 'Test Task 1', first_active=True) + "Test Task 1", first_active=True + ) # change active task - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.active_task = None - self.assertEqual(task_window.title, '') + self.assertEqual(task_window.title, "") def test_set_title_no_tasks(self): task_window = _task_window_with_named_tasks() # set window title - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.title = "Window title" self.assertEqual(task_window.title, "Window title") @@ -82,72 +91,77 @@ task_window = _task_window_with_named_tasks(title="Window Title") # set window title - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.title = "New Window title" self.assertEqual(task_window.title, "New Window title") def test_set_title_no_active_task(self): - task_window = _task_window_with_named_tasks('Test Task') + task_window = _task_window_with_named_tasks("Test Task") # set window title - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.title = "Window title" self.assertEqual(task_window.title, "Window title") def test_set_title_active_task(self): task_window = _task_window_with_named_tasks( - 'Test Task', first_active=True) + "Test Task", first_active=True + ) # set window title - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.title = "Window title" self.assertEqual(task_window.title, "Window title") def test_set_title_activate_task(self): task_window = _task_window_with_named_tasks( - 'Test Task', title="Window title") + "Test Task", title="Window title" + ) task = task_window.tasks[0] # change activate task (trait fires, no window title change) - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.active_task = task self.assertEqual(task_window.title, "Window title") def test_set_title_change_active_task_name(self): task_window = _task_window_with_named_tasks( - 'Test Task', title="Window title", first_active=True) + "Test Task", title="Window title", first_active=True + ) task = task_window.tasks[0] # change task name (trait fires, no window title change) - with self.assertTraitChanges(task_window, 'title', count=1): - task.name = 'Changed Name' + with self.assertTraitChanges(task_window, "title", count=1): + task.name = "Changed Name" self.assertEqual(task_window.title, "Window title") def test_set_title_change_active_task(self): task_window = _task_window_with_named_tasks( - 'Test Task', 'Test Task 2', title="Window title", - active_first=True) + "Test Task", "Test Task 2", title="Window title", active_first=True + ) task = task_window.tasks[1] # change task name (trait fires, no window title change) - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.active_task = task self.assertEqual(task_window.title, "Window title") def test_reset_title_active_task(self): task_window = _task_window_with_named_tasks( - 'Test Task', title="Window title", first_active=True) + "Test Task", title="Window title", first_active=True + ) # reset window title - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.title = "" self.assertEqual(task_window.title, "Test Task") def test_reset_title(self): task_window = _task_window_with_named_tasks( - 'Test Task', title="Window title") + "Test Task", title="Window title" + ) # set window title - with self.assertTraitChanges(task_window, 'title', count=1): + with self.assertTraitChanges(task_window, "title", count=1): task_window.title = "" self.assertEqual(task_window.title, "") diff -Nru python-pyface-6.1.2/pyface/tasks/tests/test_topological_sort.py python-pyface-7.4.0/pyface/tasks/tests/test_topological_sort.py --- python-pyface-6.1.2/pyface/tasks/tests/test_topological_sort.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/tests/test_topological_sort.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -# Standard library imports. -import unittest - -# Local imports. -from traits.api import HasTraits, Int -from pyface.tasks.topological_sort import before_after_sort, \ - topological_sort - - -class TestItem(HasTraits): - - id = Int - before = Int - after = Int - - def __init__(self, id, **traits): - super(TestItem, self).__init__(id=id, **traits) - - def __hash__(self): - return hash(self.id) - - def __eq__(self, other): - return self.id == other.id - - def __repr__(self): - return repr(self.id) - - -class TopologicalSortTestCase(unittest.TestCase): - - def test_before_after_sort_1(self): - """ Does the before-after sort work? - """ - items = [ TestItem(1), TestItem(2), TestItem(3, before=2), - TestItem(4, after=1), TestItem(5) ] - actual = before_after_sort(items) - desired = [ TestItem(1), TestItem(3), TestItem(4), - TestItem(2), TestItem(5) ] - self.assertEquals(actual, desired) - - def test_before_after_sort_2(self): - """ Does the before-after sort work when both 'before' and 'after' - are set? - """ - items = [ TestItem(1), TestItem(2), TestItem(3), - TestItem(4, after=2, before=3) ] - actual = before_after_sort(items) - desired = [ TestItem(1), TestItem(2), TestItem(4), TestItem(3) ] - self.assertEquals(actual, desired) - - def test_before_after_sort_3(self): - """ Does the degenerate case for the before-after sort work? - """ - actual = before_after_sort([ TestItem(1) ]) - desired = [ TestItem(1) ] - self.assertEquals(actual, desired) - - def test_topological_sort_1(self): - """ Does a basic topological sort work? - """ - pairs = [ (1,2), (3,5), (4,6), (1,3), (1,4), (1,6), (2,4) ] - result, has_cycles = topological_sort(pairs) - self.assert_(not has_cycles) - self.assertEquals(result, [1, 2, 3, 4, 5, 6]) - - def test_topological_sort_2(self): - """ Does another basic topological sort work? - """ - pairs = [ (1,2), (1,3), (2,4), (3,4), (5,6), (4,5) ] - result, has_cycles = topological_sort(pairs) - self.assert_(not has_cycles) - self.assertEquals(result, [1, 2, 3, 4, 5, 6]) - - def test_topological_sort_3(self): - """ Does cycle detection work? - """ - pairs = [ (1,2), (2,3), (3,1) ] - result, has_cycles = topological_sort(pairs) - self.assert_(has_cycles) - - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/tasks/topological_sort.py python-pyface-7.4.0/pyface/tasks/topological_sort.py --- python-pyface-6.1.2/pyface/tasks/topological_sort.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/topological_sort.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,93 +1,17 @@ -# Standard library imports. -from collections import OrderedDict, defaultdict -import logging - -# Logging. -logger = logging.getLogger(__name__) - - -def before_after_sort(items): - """ Sort a sequence of items with 'before', 'after', and 'id' attributes. - - The sort is topological. If an item does not specify a 'before' or 'after', - it is placed after the preceding item. - - If a cycle is found in the dependencies, a warning is logged and the order - of the items is undefined. - """ - # Handle a degenerate case for which the logic below will fail (because - # prev_item will not be set). - if len(items) < 2: - return items - - # Build a set of pairs representing the graph. - item_map = dict((item.id, item) for item in items if item.id) - pairs = [] - prev_item = None - for item in items: - # Attempt to use 'before' and 'after' to make pairs. - new_pairs = [] - if hasattr(item, 'before') and item.before: - parent, child = item, item_map.get(item.before) - if child: - new_pairs.append((parent, child)) - if hasattr(item, 'after') and item.after: - parent, child = item_map.get(item.after), item - if parent: - new_pairs.append((parent, child)) - - # If we have any pairs, use them. Otherwise, use the previous unmatched - # item as a parent, if possible. - if new_pairs: - pairs.extend(new_pairs) - else: - if prev_item: - pairs.append((prev_item, item)) - prev_item = item - - # Now perform the actual sort. - result, has_cycle = topological_sort(pairs) - if has_cycle: - logger.warning('Cycle in before/after sort for items %r', items) - return result - - -def topological_sort(pairs): - """ Topologically sort a list of (parent, child) pairs. - - Returns a tuple containing the list of elements sorted in dependency order - (parent to child order), if possible, and a boolean indicating whether the - graph contains cycles. - - A simple algorithm due to Kahn, in which vertices are chosen from the graph - in the same order as the eventual topological sort, is used. - - Note that this implementation is stable in the following sense: if we have - the input list [..., (parent, child1), ..., (parent, child2), ...], then - child1 will be before child2 in the output list (if there there is no - additional dependency forcing another ordering). - """ - # Represent the graph in dictionary form. - graph = OrderedDict() - num_parents = defaultdict(int) - for parent, child in pairs: - graph.setdefault(parent, []).append(child) - num_parents[child] += 1 - - # Begin with the parent-less items. - result = [ item for item in graph if num_parents[item] == 0 ] - - # Descend through graph, removing parents as we go. - for parent in result: - if parent in graph: - for child in graph[parent]: - num_parents[child] -= 1 - if num_parents[child] == 0: - result.append(child) - del graph[parent] - - # If there's a cycle, just throw in whatever is left over. - has_cycle = bool(graph) - if has_cycle: - result.extend(list(graph.keys())) - return result, has_cycle +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# Deprecated module - will be removed in a future Pyface release. +# use pyface.action.schema._topological_sort instead + +from pyface.action.schema._topological_sort import ( # noqa: F401 + before_after_sort, + topological_sort, +) diff -Nru python-pyface-6.1.2/pyface/tasks/traits_dock_pane.py python-pyface-7.4.0/pyface/tasks/traits_dock_pane.py --- python-pyface-6.1.2/pyface/tasks/traits_dock_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/traits_dock_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from traits.api import HasTraits, Instance -# Local imports. + from pyface.tasks.dock_pane import DockPane @@ -9,28 +18,29 @@ """ A DockPane that displays a Traits UI View. """ - #### TraitsDockPane interface ############################################# + # TraitsDockPane interface --------------------------------------------- - # The model object to view. If not specified, the pane is used instead. + #: The model object to view. If not specified, the pane is used instead. model = Instance(HasTraits) - # The UI object associated with the Traits view, if it has been constructed. - ui = Instance('traitsui.ui.UI') + #: The UI object associated with the Traits view, if it has been + #: constructed. + ui = Instance("traitsui.ui.UI") - ########################################################################### + # ------------------------------------------------------------------------ # 'HasTraits' interface. - ########################################################################### + # ------------------------------------------------------------------------ def trait_context(self): """ Use the model object for the Traits UI context, if appropriate. """ if self.model: - return { 'object': self.model, 'pane': self } - return super(TraitsDockPane, self).trait_context() + return {"object": self.model, "pane": self} + return super().trait_context() - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Destroy the toolkit-specific control that represents the pane. @@ -40,14 +50,14 @@ self.ui = None # Destroy the dock control. - super(TraitsDockPane, self).destroy() + super().destroy() - ########################################################################### + # ------------------------------------------------------------------------ # 'IDockPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ - self.ui = self.edit_traits(kind='subpanel', parent=parent) + self.ui = self.edit_traits(kind="subpanel", parent=parent) return self.ui.control diff -Nru python-pyface-6.1.2/pyface/tasks/traits_editor.py python-pyface-7.4.0/pyface/tasks/traits_editor.py --- python-pyface-6.1.2/pyface/tasks/traits_editor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/traits_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from traits.api import HasTraits, Instance -# Local imports. + from pyface.tasks.editor import Editor @@ -9,33 +18,34 @@ """ An Editor that displays a Traits UI View. """ - #### TraitsEditor interface ############################################### + # TraitsEditor interface ----------------------------------------------- - # The model object to view. If not specified, the editor is used instead. + #: The model object to view. If not specified, the editor is used instead. model = Instance(HasTraits) - # The UI object associated with the Traits view, if it has been constructed. - ui = Instance('traitsui.ui.UI') + #: The UI object associated with the Traits view, if it has been + #: constructed. + ui = Instance("traitsui.ui.UI") - ########################################################################### + # ------------------------------------------------------------------------ # 'HasTraits' interface. - ########################################################################### + # ------------------------------------------------------------------------ def trait_context(self): """ Use the model object for the Traits UI context, if appropriate. """ if self.model: - return {'object': self.model, 'editor': self} - return super(TraitsEditor, self).trait_context() + return {"object": self.model, "editor": self} + return super().trait_context() - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific contents of the editor. """ - self.ui = self.edit_traits(kind='subpanel', parent=parent) + self.ui = self.edit_traits(kind="subpanel", parent=parent) self.control = self.ui.control def destroy(self): diff -Nru python-pyface-6.1.2/pyface/tasks/traits_task_pane.py python-pyface-7.4.0/pyface/tasks/traits_task_pane.py --- python-pyface-6.1.2/pyface/tasks/traits_task_pane.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tasks/traits_task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from traits.api import HasTraits, Instance -# Local imports. + from .task_pane import TaskPane @@ -9,34 +18,35 @@ """ A TaskPane that displays a Traits UI View. """ - #### TraitsTaskPane interface ############################################# + # TraitsTaskPane interface --------------------------------------------- - # The model object to view. If not specified, the pane is used instead. + #: The model object to view. If not specified, the pane is used instead. model = Instance(HasTraits) - # The UI object associated with the Traits view, if it has been constructed. - ui = Instance('traitsui.ui.UI') + #: The UI object associated with the Traits view, if it has been + #: constructed. + ui = Instance("traitsui.ui.UI") - ########################################################################### + # ------------------------------------------------------------------------ # 'HasTraits' interface. - ########################################################################### + # ------------------------------------------------------------------------ def trait_context(self): """ Use the model object for the Traits UI context, if appropriate. """ if self.model: - return { 'object': self.model, 'pane': self } - return super(TraitsTaskPane, self).trait_context() + return {"object": self.model, "pane": self} + return super().trait_context() - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the pane. """ - self.ui = self.edit_traits(kind='subpanel', parent=parent) + self.ui = self.edit_traits(kind="subpanel", parent=parent) self.control = self.ui.control def destroy(self): diff -Nru python-pyface-6.1.2/pyface/testing/layout_widget_mixin.py python-pyface-7.4.0/pyface/testing/layout_widget_mixin.py --- python-pyface-6.1.2/pyface/testing/layout_widget_mixin.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/testing/layout_widget_mixin.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,78 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from pyface.testing.widget_mixin import WidgetMixin + + +class LayoutWidgetMixin(WidgetMixin): + """ Test mixin for classes which inherit LayoutWidget. """ + + def test_minimum_size(self): + # create a widget with a minimum size + self.widget.minimum_size = (100, 100) + self.widget.create() + minimum_size = self.widget._get_control_minimum_size() + self.gui.process_events() + self.assertEqual(minimum_size, (100, 100)) + + # change the minimum size + self.widget.minimum_size = (50, 50) + self.gui.process_events() + minimum_size = self.widget._get_control_minimum_size() + + self.assertEqual(minimum_size, (50, 50)) + + def test_maximum_size(self): + # create a widget with a maximum size + self.widget.maximum_size = (1000, 1000) + self.widget.create() + maximum_size = self.widget._get_control_maximum_size() + self.gui.process_events() + self.assertEqual(maximum_size, (1000, 1000)) + + # change the maximum size + self.widget.maximum_size = (50, 50) + self.gui.process_events() + maximum_size = self.widget._get_control_maximum_size() + + self.assertEqual(maximum_size, (50, 50)) + + def test_stretch(self): + # create a widget with a maximum size + self.widget.stretch = (2, 3) + self.widget.create() + stretch = self.widget._get_control_stretch() + self.gui.process_events() + self.assertEqual(stretch, (2, 3)) + + # change the maximum size + self.widget.stretch = (5, 0) + self.gui.process_events() + maximum_size = self.widget._get_control_stretch() + + self.assertEqual(maximum_size, (5, 0)) + + def test_size_policy(self): + # create a widget with a maximum size + self.widget.create() + self.assertEqual( + self.widget._get_control_size_policy(), + ("default", "default"), + ) + + for horizontal in ["fixed", "preferred", "expand"]: + for vertical in ["fixed", "preferred", "expand"]: + with self.subTest(horizontal=horizontal, vertical=vertical): + self.widget.size_policy = (horizontal, vertical) + self.gui.process_events() + self.assertEqual( + self.widget._get_control_size_policy(), + (horizontal, vertical), + ) diff -Nru python-pyface-6.1.2/pyface/testing/widget_mixin.py python-pyface-7.4.0/pyface/testing/widget_mixin.py --- python-pyface-6.1.2/pyface/testing/widget_mixin.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/testing/widget_mixin.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,103 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest.mock import patch + +from traits.testing.api import UnittestTools + +from pyface.action.api import Action, MenuManager +from pyface.gui import GUI +from pyface.window import Window + + +class WidgetMixin(UnittestTools): + """ Mixin which provides standard methods for all widgets. """ + + def setUp(self): + self.gui = GUI() + + self.parent = self._create_parent() + self.parent._create() + self.addCleanup(self._destroy_parent) + self.gui.process_events() + + self.widget = self._create_widget() + + self.parent.open() + self.gui.process_events() + + def _create_parent(self): + return Window() + + def _create_widget(self): + raise NotImplementedError() + + def _create_widget_control(self): + self.widget._create() + self.addCleanup(self._destroy_widget) + self.widget.show(True) + self.gui.process_events() + + def _destroy_parent(self): + self.parent.destroy() + self.gui.process_events() + self.parent = None + + def _destroy_widget(self): + self.widget.destroy() + self.gui.process_events() + self.widget = None + + def test_widget_tooltip(self): + self._create_widget_control() + self.widget.tooltip = "New tooltip." + self.gui.process_events() + + self.assertEqual(self.widget._get_control_tooltip(), "New tooltip.") + + def test_widget_tooltip_cleanup(self): + widget = self._create_widget() + with patch.object(widget, '_tooltip_updated', return_value=None) as updated: + widget._create() + try: + widget.show(True) + self.gui.process_events() + finally: + widget.destroy() + self.gui.process_events() + + widget.tooltip = "New tooltip." + + updated.assert_not_called() + + widget = None + + def test_widget_menu(self): + self._create_widget_control() + self.widget.context_menu = MenuManager(Action(name="Test"), name="Test") + + self.gui.process_events() + + def test_widget_context_menu_cleanup(self): + widget = self._create_widget() + with patch.object(widget, '_context_menu_updated', return_value=None) as updated: + widget._create() + try: + widget.show(True) + self.gui.process_events() + finally: + widget.destroy() + self.gui.process_events() + + widget.context_menu = MenuManager(Action(name="Test"), name="Test") + + updated.assert_not_called() + + widget = None diff -Nru python-pyface-6.1.2/pyface/tests/images/image_LICENSE.txt python-pyface-7.4.0/pyface/tests/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/tests/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Nuvola LGPL image_LICENSE_Nuvola.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -pyface/tests/images: - core.png | Nuvola diff -Nru python-pyface-6.1.2/pyface/tests/python_shell_script.py python-pyface-7.4.0/pyface/tests/python_shell_script.py --- python-pyface-6.1.2/pyface/tests/python_shell_script.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/python_shell_script.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # dummy script for testing python shell and python editor widgets # simple import -import sys +import sys # noqa: F401 # set a variable x = 1 diff -Nru python-pyface-6.1.2/pyface/tests/test_about_dialog.py python-pyface-7.4.0/pyface/tests/test_about_dialog.py --- python-pyface-6.1.2/pyface/tests/test_about_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_about_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,31 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest from ..about_dialog import AboutDialog from ..constant import OK, CANCEL -from ..gui import GUI from ..toolkit import toolkit_object from ..window import Window -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestAboutDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -55,7 +63,7 @@ with self.event_loop(): parent.destroy() - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_accept(self): # test that accept works as expected # XXX duplicate of Dialog test, not needed? @@ -64,7 +72,7 @@ self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_close(self): # test that closing works as expected # XXX duplicate of Dialog test, not needed? @@ -73,7 +81,7 @@ self.assertEqual(tester.result, CANCEL) self.assertEqual(self.dialog.return_code, CANCEL) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_parent(self): # test that lifecycle works with a parent parent = Window() @@ -86,3 +94,13 @@ self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) + + def test__create_html(self): + # test that the html content is properly created + self.dialog.additions.extend(["test line 1", "test line 2"]) + self.dialog.copyrights.extend(["copyright", "copyleft"]) + html = self.dialog._create_html() + self.assertIn("test line 1
test line 2
", html) + self.assertIn( + "Copyright © copyright
Copyright © copyleft", html + ) diff -Nru python-pyface-6.1.2/pyface/tests/test_api.py python-pyface-7.4.0/pyface/tests/test_api.py --- python-pyface-6.1.2/pyface/tests/test_api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,170 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Test for pyface.api """ + +import unittest + +from traits.etsconfig.api import ETSConfig + +is_wx = (ETSConfig.toolkit == 'wx') +is_qt = ETSConfig.toolkit.startswith('qt') + + +class TestApi(unittest.TestCase): + """ Test importable items in any environment.""" + + def test_api_importable(self): + # make sure api is importable with the most minimal + # required dependencies, including in the absence of toolkit backends. + from pyface import api # noqa: F401 + + +@unittest.skipIf(not is_qt, "This test is for qt.") +class TestApiQt(unittest.TestCase): + """ Test importable items in a Qt environment.""" + + def test_importable_items_minimal(self): + # Test items should be importable in a minimal Qt environment + # Pygments is excused. Attempt to import PythonEditor or PythonShell + # will fail in an environment without pygments will fail, just as it + # would if these items were imported directly from the corresponding + # subpackages. + from pyface.api import ( # noqa: F401 + AboutDialog, + Alignment, + Application, + ApplicationWindow, + Border, + BaseDropHandler, + CANCEL, + Clipboard, + ConfirmationDialog, + Dialog, + DirectoryDialog, + ExpandablePanel, + FileDialog, + FileDropHandler, + Filter, + GUI, + GUIApplication, + HasBorder, + HasMargin, + HeadingText, + Image, + ImageCache, + ImageResource, + ImageWidget, + KeyPressedEvent, + LayeredPanel, + MDIApplicationWindow, + MDIWindowMenu, + Margin, + MessageDialog, + MultiToolbarWindow, + NO, + OK, + ProgressDialog, + # PythonEditor and PythonShell are omitted + SingleChoiceDialog, + Sorter, + SplashScreen, + SplitApplicationWindow, + SplitDialog, + SplitPanel, + SystemMetrics, + Widget, + Window, + YES, + beep, + choose_one, + clipboard, + confirm, + error, + information, + warning, + ) + + def test_python_editor_python_shell_importable(self): + # If pygments is in the environment, PythonEditor and PythonShell + # should be importable. + try: + import pygments # noqa: F401 + except ImportError: + raise self.skipTest("This test requires pygments.") + + from pyface.api import ( # noqa: F401 + PythonEditor, + PythonShell, + ) + + +@unittest.skipIf(not is_wx, "This test is for wx.") +class TestApiWx(unittest.TestCase): + """ Test importable items in a wx environment.""" + + def test_importable_items(self): + # These items should always be importable for wx environment + from pyface.api import ( # noqa: F401 + AboutDialog, + Alignment, + Application, + ApplicationWindow, + Border, + CANCEL, + Clipboard, + ConfirmationDialog, + BaseDropHandler, + Dialog, + DirectoryDialog, + ExpandablePanel, + FileDialog, + FileDropHandler, + Filter, + GUI, + GUIApplication, + HasBorder, + HasMargin, + HeadingText, + Image, + ImageCache, + ImageResource, + ImageWidget, + KeyPressedEvent, + LayeredPanel, + MDIApplicationWindow, + MDIWindowMenu, + Margin, + MessageDialog, + MultiToolbarWindow, + NO, + OK, + ProgressDialog, + PythonEditor, + PythonShell, + SingleChoiceDialog, + Sorter, + SplashScreen, + SplitApplicationWindow, + SplitDialog, + SplitPanel, + SystemMetrics, + Widget, + Window, + YES, + beep, + choose_one, + clipboard, + confirm, + error, + fix_introspect_bug, + information, + warning, + ) diff -Nru python-pyface-6.1.2/pyface/tests/test_application.py python-pyface-7.4.0/pyface/tests/test_application.py --- python-pyface-6.1.2/pyface/tests/test_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,30 @@ -# Copyright (c) 2014-2018 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import os from shutil import rmtree from tempfile import mkdtemp from unittest import TestCase -from traits.api import Bool -from traits.testing.unittest_tools import UnittestTools +from traits.api import Bool, observe +from traits.testing.api import UnittestTools from ..application import ApplicationExit, Application EVENTS = [ - 'starting', 'started', 'application_initialized', 'stopping', 'stopped' + "starting", + "started", + "application_initialized", + "stopping", + "stopped", ] @@ -56,15 +58,15 @@ exit_prepared_error = Bool(False) def start(self): - super(TestingApp, self).start() + super().start() return self.start_cleanly def stop(self): - super(TestingApp, self).stop() + super().stop() return self.stop_cleanly def _run(self): - super(TestingApp, self)._run() + super()._run() if self.do_exit: if self.error_exit: raise ApplicationExit("error message") @@ -73,8 +75,10 @@ self.exit_vetoed = True return True - def _exiting_fired(self, event): - event.veto = self.veto_exit + @observe('exiting') + def _set_veto_on_exiting_event(self, event): + vetoable_event = event.new + vetoable_event.veto = self.veto_exit def _prepare_exit(self): self.exit_prepared = True @@ -87,11 +91,12 @@ self.application_events = [] def event_listener(self, event): - self.application_events.append(event) + application_event = event.new + self.application_events.append(application_event) def connect_listeners(self, app): for event in EVENTS: - app.on_trait_change(self.event_listener, event) + app.observe(self.event_listener, event) def test_defaults(self): from traits.etsconfig.api import ETSConfig @@ -208,4 +213,4 @@ self.assertFalse(result) event_order = [event.event_type for event in self.application_events] - self.assertEqual(event_order, EVENTS[:-1]) \ No newline at end of file + self.assertEqual(event_order, EVENTS[:-1]) diff -Nru python-pyface-6.1.2/pyface/tests/test_application_window.py python-pyface-7.4.0/pyface/tests/test_application_window.py --- python-pyface-6.1.2/pyface/tests/test_application_window.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,32 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from ..action.api import Action, MenuManager, MenuBarManager, StatusBarManager, ToolBarManager +from ..action.api import ( + Action, + MenuManager, + MenuBarManager, + StatusBarManager, + ToolBarManager, +) from ..application_window import ApplicationWindow from ..toolkit import toolkit_object from ..image_resource import ImageResource -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestApplicationWindow(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -29,12 +44,12 @@ def test_open_close(self): # test that opening and closing works as expected - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() @@ -116,7 +131,33 @@ Action(name="Save"), Action(name="Close"), Action(name="Quit"), - name='File', + name="File", + ) + ) + with self.event_loop(): + self.window._create() + with self.event_loop(): + self.window.show(True) + with self.event_loop(): + self.window.show(False) + with self.event_loop(): + self.window.close() + + def test_menubar_multiple_menus(self): + # test that menubar gets created as expected + self.window.menu_bar_manager = MenuBarManager( + MenuManager( + Action(name="New"), + Action(name="Open"), + Action(name="Save"), + Action(name="Close"), + Action(name="Quit"), + name="File", + ), + MenuManager( + Action(name="Zoom in"), + Action(name="Zoom out"), + name="View", ) ) with self.event_loop(): @@ -132,11 +173,11 @@ # test that toolbar gets created as expected self.window.tool_bar_managers = [ ToolBarManager( - Action(name="New", image=ImageResource('core')), - Action(name="Open", image=ImageResource('core')), - Action(name="Save", image=ImageResource('core')), - Action(name="Close", image=ImageResource('core')), - Action(name="Quit", image=ImageResource('core')), + Action(name="New", image=ImageResource("core")), + Action(name="Open", image=ImageResource("core")), + Action(name="Save", image=ImageResource("core")), + Action(name="Close", image=ImageResource("core")), + Action(name="Quit", image=ImageResource("core")), ) ] with self.event_loop(): @@ -152,11 +193,11 @@ # test that toolbar gets changed as expected self.window.tool_bar_managers = [ ToolBarManager( - Action(name="New", image=ImageResource('core')), - Action(name="Open", image=ImageResource('core')), - Action(name="Save", image=ImageResource('core')), - Action(name="Close", image=ImageResource('core')), - Action(name="Quit", image=ImageResource('core')), + Action(name="New", image=ImageResource("core")), + Action(name="Open", image=ImageResource("core")), + Action(name="Save", image=ImageResource("core")), + Action(name="Close", image=ImageResource("core")), + Action(name="Quit", image=ImageResource("core")), ) ] with self.event_loop(): @@ -166,11 +207,11 @@ with self.event_loop(): self.window.tool_bar_managers = [ ToolBarManager( - Action(name="New", image=ImageResource('core')), - Action(name="Open", image=ImageResource('core')), - Action(name="Save", image=ImageResource('core')), - Action(name="Close", image=ImageResource('core')), - Action(name="Quit", image=ImageResource('core')), + Action(name="New", image=ImageResource("core")), + Action(name="Open", image=ImageResource("core")), + Action(name="Save", image=ImageResource("core")), + Action(name="Close", image=ImageResource("core")), + Action(name="Quit", image=ImageResource("core")), ) ] with self.event_loop(): @@ -181,7 +222,7 @@ def test_statusbar(self): # test that status bar gets created as expected self.window.status_bar_manager = StatusBarManager( - message="hello world", + message="hello world" ) with self.event_loop(): self.window._create() @@ -195,7 +236,7 @@ def test_statusbar_changed(self): # test that status bar gets changed as expected self.window.status_bar_manager = StatusBarManager( - message="hello world", + message="hello world" ) with self.event_loop(): self.window._create() @@ -203,7 +244,7 @@ self.window.show(True) with self.event_loop(): self.window.status_bar_manager = StatusBarManager( - message="goodbye world", + message="goodbye world" ) with self.event_loop(): self.window.show(False) @@ -212,7 +253,7 @@ def test_icon(self): # test that status bar gets created as expected - self.window.icon = ImageResource('core') + self.window.icon = ImageResource("core") with self.event_loop(): self.window._create() with self.event_loop(): diff -Nru python-pyface-6.1.2/pyface/tests/test_array_image.py python-pyface-7.4.0/pyface/tests/test_array_image.py --- python-pyface-6.1.2/pyface/tests/test_array_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_array_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,52 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import unittest + +from traits.testing.optional_dependencies import numpy as np, requires_numpy + +from ..array_image import ArrayImage + + +@requires_numpy +class TestArrayImage(unittest.TestCase): + def setUp(self): + self.data = np.full((32, 64, 4), 0xee, dtype='uint8') + + def test_init(self): + image = ArrayImage(self.data) + + self.assertIs(image.data, self.data) + + def test_init_data_required(self): + with self.assertRaises(TypeError): + ArrayImage() + + def test_create_image(self): + image = ArrayImage(self.data) + + toolkit_image = image.create_image() + + self.assertIsNotNone(toolkit_image) + + def test_create_bitmap(self): + image = ArrayImage(self.data) + + bitmap = image.create_bitmap() + + self.assertIsNotNone(bitmap) + + def test_create_icon(self): + image = ArrayImage(self.data) + + icon = image.create_icon() + + self.assertIsNotNone(icon) diff -Nru python-pyface-6.1.2/pyface/tests/test_base_toolkit.py python-pyface-7.4.0/pyface/tests/test_base_toolkit.py --- python-pyface-6.1.2/pyface/tests/test_base_toolkit.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_base_toolkit.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest from traits.etsconfig.api import ETSConfig @@ -6,23 +15,22 @@ class TestToolkit(unittest.TestCase): - def test_import_null_toolkit(self): - toolkit = import_toolkit('null') - self.assertEqual(toolkit.package, 'pyface') - self.assertEqual(toolkit.toolkit, 'null') + toolkit = import_toolkit("null") + self.assertEqual(toolkit.package, "pyface") + self.assertEqual(toolkit.toolkit, "null") def test_missing_toolkit(self): # test that we get an error with an undefined toolkit with self.assertRaises(RuntimeError): - import_toolkit('nosuchtoolkit') + import_toolkit("nosuchtoolkit") def test_find_current_toolkit_no_etsconfig(self): old_etsconfig_toolkit = ETSConfig._toolkit - ETSConfig._toolkit = '' + ETSConfig._toolkit = "" try: - toolkit = find_toolkit('pyface.toolkits', old_etsconfig_toolkit) - self.assertEqual(toolkit.package, 'pyface') + toolkit = find_toolkit("pyface.toolkits", old_etsconfig_toolkit) + self.assertEqual(toolkit.package, "pyface") self.assertEqual(toolkit.toolkit, old_etsconfig_toolkit) self.assertEqual(ETSConfig.toolkit, old_etsconfig_toolkit) finally: @@ -30,22 +38,22 @@ def test_find_null_toolkit_no_etsconfig(self): old_etsconfig_toolkit = ETSConfig._toolkit - ETSConfig._toolkit = '' + ETSConfig._toolkit = "" try: - toolkit = find_toolkit('pyface.toolkits', 'null') - self.assertEqual(toolkit.package, 'pyface') - self.assertEqual(toolkit.toolkit, 'null') - self.assertEqual(ETSConfig.toolkit, 'null') + toolkit = find_toolkit("pyface.toolkits", "null") + self.assertEqual(toolkit.package, "pyface") + self.assertEqual(toolkit.toolkit, "null") + self.assertEqual(ETSConfig.toolkit, "null") finally: ETSConfig._toolkit = old_etsconfig_toolkit def test_find_nonexistent_toolkit_no_etsconfig(self): old_etsconfig_toolkit = ETSConfig._toolkit - ETSConfig._toolkit = '' + ETSConfig._toolkit = "" try: - toolkit = find_toolkit('pyface.toolkits', 'nonexistent') - self.assertEqual(toolkit.package, 'pyface') - self.assertEqual(toolkit.toolkit, 'null') - self.assertEqual(ETSConfig.toolkit, 'null') + toolkit = find_toolkit("pyface.toolkits", "nonexistent") + self.assertEqual(toolkit.package, "pyface") + self.assertEqual(toolkit.toolkit, "null") + self.assertEqual(ETSConfig.toolkit, "null") finally: ETSConfig._toolkit = old_etsconfig_toolkit diff -Nru python-pyface-6.1.2/pyface/tests/test_beep.py python-pyface-7.4.0/pyface/tests/test_beep.py --- python-pyface-6.1.2/pyface/tests/test_beep.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_beep.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,20 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -import unittest -from traits.testing.unittest_tools import UnittestTools +import unittest from ..beep import beep class TestBeep(unittest.TestCase): - def test_beep(self): # does it call without error - the best we can do beep() diff -Nru python-pyface-6.1.2/pyface/tests/test_clipboard.py python-pyface-7.4.0/pyface/tests/test_clipboard.py --- python-pyface-6.1.2/pyface/tests/test_clipboard.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_clipboard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,9 +1,22 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest +from traits.etsconfig.api import ETSConfig + from ..clipboard import clipboard +is_wx = (ETSConfig.toolkit == 'wx') + class TestObject(object): def __init__(self, **kwargs): @@ -12,54 +25,55 @@ def __eq__(self, other): if isinstance(other, TestObject): - return all(getattr(other, key) == value - for key, value in self.__dict__.items()) + return all( + getattr(other, key) == value + for key, value in self.__dict__.items() + ) class TestClipboard(unittest.TestCase): - def setUp(self): self.clipboard = clipboard def test_set_text_data(self): - self.clipboard.data = 'test' + self.clipboard.data = "test" self.assertTrue(self.clipboard.has_data) - self.assertEquals(self.clipboard.data_type, 'str') - self.assertEquals(self.clipboard.data, 'test') + self.assertEqual(self.clipboard.data_type, "str") + self.assertEqual(self.clipboard.data, "test") self.assertTrue(self.clipboard.has_text_data) - self.assertEquals(self.clipboard.text_data, 'test') + self.assertEqual(self.clipboard.text_data, "test") self.assertFalse(self.clipboard.has_file_data) self.assertFalse(self.clipboard.has_object_data) def test_set_text_data_unicode(self): - self.clipboard.data = u'test' + self.clipboard.data = "test" self.assertTrue(self.clipboard.has_data) - self.assertEquals(self.clipboard.data_type, 'str') - self.assertEquals(self.clipboard.data, u'test') + self.assertEqual(self.clipboard.data_type, "str") + self.assertEqual(self.clipboard.data, "test") self.assertTrue(self.clipboard.has_text_data) - self.assertEquals(self.clipboard.text_data, u'test') + self.assertEqual(self.clipboard.text_data, "test") self.assertFalse(self.clipboard.has_file_data) self.assertFalse(self.clipboard.has_object_data) - @unittest.skip('backends not consistent') + @unittest.skip("backends not consistent") def test_set_file_data(self): - self.clipboard.data = ['file:///images'] + self.clipboard.data = ["file:///images"] self.assertTrue(self.clipboard.has_data) - self.assertEquals(self.clipboard.data_type, 'file') - self.assertEquals(self.clipboard.data, ['/images']) + self.assertEqual(self.clipboard.data_type, "file") + self.assertEqual(self.clipboard.data, ["/images"]) self.assertTrue(self.clipboard.has_file_data) - self.assertEquals(self.clipboard.file_data, ['/images']) + self.assertEqual(self.clipboard.file_data, ["/images"]) self.assertFalse(self.clipboard.has_text_data) self.assertFalse(self.clipboard.has_object_data) def test_set_object_data(self): - data = TestObject(foo='bar', baz=1) + data = TestObject(foo="bar", baz=1) self.clipboard.data = data self.assertTrue(self.clipboard.has_data) - self.assertEquals(self.clipboard.data_type, TestObject) - self.assertEquals(self.clipboard.data, data) + self.assertEqual(self.clipboard.data_type, TestObject) + self.assertEqual(self.clipboard.data, data) self.assertTrue(self.clipboard.has_object_data) - self.assertEquals(self.clipboard.object_type, TestObject) - self.assertEquals(self.clipboard.object_data, data) + self.assertEqual(self.clipboard.object_type, TestObject) + self.assertEqual(self.clipboard.object_data, data) self.assertFalse(self.clipboard.has_text_data) self.assertFalse(self.clipboard.has_file_data) diff -Nru python-pyface-6.1.2/pyface/tests/test_color_dialog.py python-pyface-7.4.0/pyface/tests/test_color_dialog.py --- python-pyface-6.1.2/pyface/tests/test_color_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_color_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,95 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import unittest + +from ..color import Color +from ..color_dialog import ColorDialog, get_color +from ..toolkit import toolkit_object + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + +ModalDialogTester = toolkit_object( + "util.modal_dialog_tester:ModalDialogTester" +) +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestColorDialog(unittest.TestCase, GuiTestAssistant): + + def setUp(self): + GuiTestAssistant.setUp(self) + self.dialog = ColorDialog(color="rebeccapurple") + + def tearDown(self): + if self.dialog.control is not None: + with self.delete_widget(self.dialog.control): + self.dialog.destroy() + del self.dialog + GuiTestAssistant.tearDown(self) + + def test_color(self): + # test that colors are translated as expected + self.dialog.color = "red" + + self.assertEqual(self.dialog.color, Color.from_str("red")) + + def test_create(self): + # test that creation and destruction works as expected + with self.event_loop(): + self.dialog._create() + with self.event_loop(): + self.dialog.destroy() + + def test_destroy(self): + # test that destroy works even when no control + with self.event_loop(): + self.dialog.destroy() + + def test_close(self): + # test that close works + with self.event_loop(): + self.dialog._create() + with self.event_loop(): + self.dialog.close() + + def test_show_alpha(self): + # test that creation and destruction works with show_alpha True + self.dialog.show_alpha = True + + with self.event_loop(): + self.dialog._create() + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestGetColor(unittest.TestCase, GuiTestAssistant): + + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") + def test_close(self): + # test that cancel works as expected + tester = ModalDialogTester( + lambda: get_color(None, "rebeccapurple") + ) + tester.open_and_run(when_opened=lambda x: x.close(accept=False)) + + self.assertEqual(tester.result, None) + + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") + def test_close_show_alpha(self): + # test that cancel works as expected + tester = ModalDialogTester( + lambda: get_color(None, "rebeccapurple", True) + ) + tester.open_and_run(when_opened=lambda x: x.close(accept=False)) + + self.assertEqual(tester.result, None) diff -Nru python-pyface-6.1.2/pyface/tests/test_color.py python-pyface-7.4.0/pyface/tests/test_color.py --- python-pyface-6.1.2/pyface/tests/test_color.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_color.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,210 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from traits.testing.api import UnittestTools + +from pyface.color import Color + + +class TestColor(UnittestTools, TestCase): + + def assert_tuple_almost_equal(self, tuple_1, tuple_2): + self.assertEqual(len(tuple_1), len(tuple_2)) + + for x, y in zip(tuple_1, tuple_2): + self.assertAlmostEqual(x, y) + + def test_init(self): + color = Color() + self.assertEqual(color.rgba, (1.0, 1.0, 1.0, 1.0)) + + def test_init_rgba(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 0.8)) + + def test_init_rgb(self): + color = Color(rgb=(0.4, 0.2, 0.6)) + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 1.0)) + + def test_init_r_g_b_a(self): + color = Color(red=0.4, green=0.2, blue=0.6, alpha=0.8) + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 0.8)) + + def test_init_r_g_b(self): + color = Color(red=0.4, green=0.2, blue=0.6) + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 1.0)) + + def test_init_hsva(self): + color = Color(hsva=(0.4, 0.2, 0.6, 0.8)) + self.assert_tuple_almost_equal(color.rgba, (0.48, 0.6, 0.528, 0.8)) + + def test_init_hsv(self): + color = Color(hsv=(0.4, 0.2, 0.6)) + self.assert_tuple_almost_equal(color.rgba, (0.48, 0.6, 0.528, 1.0)) + + def test_init_hlsa(self): + color = Color(hlsa=(0.4, 0.2, 0.6, 0.8)) + self.assert_tuple_almost_equal(color.rgba, (0.08, 0.32, 0.176, 0.8)) + + def test_init_hls(self): + color = Color(hls=(0.4, 0.2, 0.6)) + self.assert_tuple_almost_equal(color.rgba, (0.08, 0.32, 0.176, 1.0)) + + def test_from_str_name(self): + color = Color.from_str('rebeccapurple') + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 1.0)) + + def test_from_str_hex(self): + color = Color.from_str('#663399ff') + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 1.0)) + + def test_from_str_extra_argument(self): + color = Color.from_str('#663399', alpha=0.5) + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 0.5)) + + def test_from_str_duplicate_argument(self): + with self.assertRaises(TypeError): + Color.from_str('rebeccapurple', rgba=(1.0, 1.0, 1.0, 1.0)) + + def test_toolkit_round_trip(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + toolkit_color = color.to_toolkit() + result = Color.from_toolkit(toolkit_color) + self.assertEqual(result.rgba, (0.4, 0.2, 0.6, 0.8)) + + def test_hex(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + hex_value = color.hex() + self.assertEqual(hex_value, "#663399CC") + + def test_hex_black(self): + color = Color(rgba=(0.0, 0.0, 0.0, 1.0)) + hex_value = color.hex() + self.assertEqual(hex_value, "#000000FF") + + def test_eq(self): + color_1 = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color_2 = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertTrue(color_1 == color_2) + self.assertFalse(color_1 != color_2) + + def test_eq_not_equal(self): + color_1 = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color_2 = Color(rgba=(0.4, 0.4, 0.6, 0.8)) + self.assertTrue(color_1 != color_2) + self.assertFalse(color_1 == color_2) + + def test_eq_other(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertFalse(color == 1) + self.assertTrue(color != 1) + + def test_not_eq(self): + color_1 = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color_2 = Color(rgba=(0.0, 0.0, 0.0, 1.0)) + self.assertTrue(color_1 != color_2) + self.assertFalse(color_1 == color_2) + + def test_str(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + result = str(color) + self.assertEqual(result, "(0.4, 0.2, 0.6, 0.8)") + + def test_repr(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + result = repr(color) + self.assertEqual(result, "Color(rgba=(0.4, 0.2, 0.6, 0.8))") + + def test_get_red(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual(color.red, 0.4) + + def test_set_red(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color.red = 1.0 + self.assertEqual(color.rgba, (1.0, 0.2, 0.6, 0.8)) + + def test_get_green(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual(color.green, 0.2) + + def test_set_green(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color.green = 1.0 + self.assertEqual(color.rgba, (0.4, 1.0, 0.6, 0.8)) + + def test_get_blue(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual(color.blue, 0.6) + + def test_set_blue(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color.blue = 1.0 + self.assertEqual(color.rgba, (0.4, 0.2, 1.0, 0.8)) + + def test_get_alpha(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual(color.alpha, 0.8) + + def test_set_alpha(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color.alpha = 1.0 + self.assertEqual(color.rgba, (0.4, 0.2, 0.6, 1.0)) + + def test_get_rgb(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + self.assertEqual(color.rgb, (0.4, 0.2, 0.6)) + + def test_set_rgb(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + color.rgb = (0.6, 0.8, 0.4) + self.assertEqual(color.rgba, (0.6, 0.8, 0.4, 0.8)) + + def test_get_hsv(self): + color = Color(rgba=(0.48, 0.6, 0.528, 0.8)) + self.assert_tuple_almost_equal(color.hsv, (0.4, 0.2, 0.6)) + + def test_set_hsv(self): + color = Color() + color.hsv = (0.4, 0.2, 0.6) + self.assert_tuple_almost_equal(color.rgba, (0.48, 0.6, 0.528, 1.0)) + + def test_get_hsva(self): + color = Color(rgba=(0.48, 0.6, 0.528, 0.8)) + self.assert_tuple_almost_equal(color.hsva, (0.4, 0.2, 0.6, 0.8)) + + def test_set_hsva(self): + color = Color() + color.hsva = (0.4, 0.2, 0.6, 0.8) + self.assert_tuple_almost_equal(color.rgba, (0.48, 0.6, 0.528, 0.8)) + + def test_get_hls(self): + color = Color(rgba=(0.08, 0.32, 0.176, 0.8)) + self.assert_tuple_almost_equal(color.hls, (0.4, 0.2, 0.6)) + + def test_set_hls(self): + color = Color() + color.hls = (0.4, 0.2, 0.6) + self.assert_tuple_almost_equal(color.rgba, (0.08, 0.32, 0.176, 1)) + + def test_get_hlsa(self): + color = Color(rgba=(0.08, 0.32, 0.176, 0.8)) + self.assert_tuple_almost_equal(color.hlsa, (0.4, 0.2, 0.6, 0.8)) + + def test_set_hlsa(self): + color = Color() + color.hlsa = (0.4, 0.2, 0.6, 0.8) + self.assert_tuple_almost_equal(color.rgba, (0.08, 0.32, 0.176, 0.8)) + + def test_get_is_dark(self): + color = Color(rgba=(0.08, 0.32, 0.176, 0.8)) + self.assertTrue(color.is_dark) diff -Nru python-pyface-6.1.2/pyface/tests/test_confirmation_dialog.py python-pyface-7.4.0/pyface/tests/test_confirmation_dialog.py --- python-pyface-6.1.2/pyface/tests/test_confirmation_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_confirmation_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import platform import unittest @@ -9,23 +18,23 @@ from ..toolkit import toolkit_object from ..window import Window -is_qt = toolkit_object.toolkit == 'qt4' +is_qt = toolkit_object.toolkit == "qt4" if is_qt: from pyface.qt import qt_api -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -is_pyqt5 = (is_qt and qt_api == 'pyqt5') -is_pyqt4_linux = (is_qt and qt_api == 'pyqt' and platform.system() == 'Linux') +is_pyqt5 = is_qt and qt_api == "pyqt5" +is_pyqt4_linux = is_qt and qt_api == "pyqt" and platform.system() == "Linux" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestConfirmationDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -81,7 +90,7 @@ def test_create_yes_renamed(self): # test that creation and destruction works as expected with ok_label - self.dialog.yes_label = u"Sure" + self.dialog.yes_label = "Sure" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -89,7 +98,7 @@ def test_create_no_renamed(self): # test that creation and destruction works as expected with ok_label - self.dialog.no_label = u"No Way" + self.dialog.no_label = "No Way" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -131,13 +140,13 @@ def test_create_image(self): # test that creation and destruction works with a non-standard image - self.dialog.image = ImageResource('core') + self.dialog.image = ImageResource("core") with self.event_loop(): self.dialog._create() with self.event_loop(): self.dialog.destroy() - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_close(self): # test that closing works as expected # XXX duplicate of Dialog test, not needed? @@ -147,7 +156,7 @@ self.assertEqual(tester.result, NO) self.assertEqual(self.dialog.return_code, NO) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_close_with_cancel(self): # test that closing works as expected self.dialog.cancel = True @@ -162,9 +171,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_yes(self): # test that Yes works as expected tester = ModalDialogTester(self.dialog.open) @@ -178,14 +187,14 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_renamed_yes(self): - self.dialog.yes_label = u"Sure" + self.dialog.yes_label = "Sure" # test that Yes works as expected if renamed tester = ModalDialogTester(self.dialog.open) - tester.open_and_wait(when_opened=lambda x: x.click_widget(u"Sure")) + tester.open_and_wait(when_opened=lambda x: x.click_widget("Sure")) self.assertEqual(tester.result, YES) self.assertEqual(self.dialog.return_code, YES) @@ -195,9 +204,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_no(self): # test that No works as expected tester = ModalDialogTester(self.dialog.open) @@ -211,14 +220,14 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_renamed_no(self): - self.dialog.no_label = u"No way" + self.dialog.no_label = "No way" # test that No works as expected if renamed tester = ModalDialogTester(self.dialog.open) - tester.open_and_wait(when_opened=lambda x: x.click_widget(u"No way")) + tester.open_and_wait(when_opened=lambda x: x.click_widget("No way")) self.assertEqual(tester.result, NO) self.assertEqual(self.dialog.return_code, NO) @@ -228,9 +237,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_cancel(self): self.dialog.cancel = True # test that Cancel works as expected @@ -245,20 +254,20 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_cancel_renamed(self): self.dialog.cancel = True - self.dialog.cancel_label = u"Back" + self.dialog.cancel_label = "Back" # test that Cancel works as expected tester = ModalDialogTester(self.dialog.open) - tester.open_and_wait(when_opened=lambda x: x.click_widget(u"Back")) + tester.open_and_wait(when_opened=lambda x: x.click_widget("Back")) self.assertEqual(tester.result, CANCEL) self.assertEqual(self.dialog.return_code, CANCEL) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_parent(self): # test that lifecycle works with a parent parent = Window() @@ -274,7 +283,7 @@ self.assertEqual(self.dialog.return_code, OK) -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestConfirm(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -282,7 +291,7 @@ def tearDown(self): GuiTestAssistant.tearDown(self) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_reject(self): # test that cancel works as expected tester = ModalDialogTester( @@ -297,9 +306,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_yes(self): # test that yes works as expected tester = ModalDialogTester(lambda: confirm(None, "message")) @@ -312,9 +321,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_no(self): # test that yes works as expected tester = ModalDialogTester(lambda: confirm(None, "message")) @@ -327,9 +336,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_cancel(self): # test that cancel works as expected tester = ModalDialogTester( @@ -344,13 +353,13 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_title(self): # test that title works as expected tester = ModalDialogTester( - lambda: confirm(None, "message", title='Title') + lambda: confirm(None, "message", title="Title") ) tester.open_and_run(when_opened=lambda x: x.click_button(NO)) @@ -361,9 +370,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_default_yes(self): # test that default works as expected tester = ModalDialogTester( @@ -378,9 +387,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_default_cancel(self): # test that default works as expected tester = ModalDialogTester( diff -Nru python-pyface-6.1.2/pyface/tests/test_dialog.py python-pyface-7.4.0/pyface/tests/test_dialog.py --- python-pyface-6.1.2/pyface/tests/test_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import platform import unittest @@ -7,23 +16,23 @@ from ..constant import OK, CANCEL from ..toolkit import toolkit_object -is_qt = toolkit_object.toolkit == 'qt4' +is_qt = toolkit_object.toolkit == "qt4" if is_qt: from pyface.qt import qt_api -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -is_pyqt5 = (is_qt and qt_api == 'pyqt5') -is_pyqt4_linux = (is_qt and qt_api == 'pyqt' and platform.system() == 'Linux') +is_pyqt5 = is_qt and qt_api == "pyqt5" +is_pyqt4_linux = is_qt and qt_api == "pyqt" and platform.system() == "Linux" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -66,7 +75,7 @@ def test_create_ok_renamed(self): # test that creation and destruction works as expected with ok_label - self.dialog.ok_label = u"Sure" + self.dialog.ok_label = "Sure" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -74,7 +83,7 @@ def test_create_cancel_renamed(self): # test that creation and destruction works as expected with cancel_label - self.dialog.cancel_label = u"I Don't Think So" + self.dialog.cancel_label = "I Don't Think So" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -91,13 +100,13 @@ def test_create_help_label(self): # test that creation and destruction works as expected with help self.dialog.help_id = "test_help" - self.dialog.help_label = u"Assistance" + self.dialog.help_label = "Assistance" with self.event_loop(): self.dialog._create() with self.event_loop(): self.dialog.destroy() - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_accept(self): # test that accept works as expected tester = ModalDialogTester(self.dialog.open) @@ -106,7 +115,7 @@ self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_reject(self): # test that reject works as expected tester = ModalDialogTester(self.dialog.open) @@ -115,7 +124,7 @@ self.assertEqual(tester.result, CANCEL) self.assertEqual(self.dialog.return_code, CANCEL) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_close(self): # test that closing works as expected tester = ModalDialogTester(self.dialog.open) @@ -129,9 +138,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Dialog click tests don't work reliably on linux. Issue #282." + "Dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_ok(self): # test that OK works as expected tester = ModalDialogTester(self.dialog.open) @@ -145,9 +154,9 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Dialog click tests don't work reliably on linux. Issue #282." + "Dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_cancel(self): # test that cancel works as expected tester = ModalDialogTester(self.dialog.open) @@ -161,14 +170,14 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Dialog click tests don't work reliably on linux. Issue #282." + "Dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_renamed_ok(self): - self.dialog.ok_label = u"Sure" + self.dialog.ok_label = "Sure" # test that OK works as expected if renames tester = ModalDialogTester(self.dialog.open) - tester.open_and_wait(when_opened=lambda x: x.click_widget(u"Sure")) + tester.open_and_wait(when_opened=lambda x: x.click_widget("Sure")) self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) @@ -178,15 +187,15 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Dialog click tests don't work reliably on linux. Issue #282." + "Dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_renamed_cancel(self): - self.dialog.cancel_label = u"I Don't Think So" + self.dialog.cancel_label = "I Don't Think So" # test that OK works as expected if renames tester = ModalDialogTester(self.dialog.open) tester.open_and_wait( - when_opened=lambda x: x.click_widget(u"I Don't Think So") + when_opened=lambda x: x.click_widget("I Don't Think So") ) self.assertEqual(tester.result, CANCEL) @@ -197,12 +206,12 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Dialog click tests don't work reliably on linux. Issue #282." + "Dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_help(self): def click_help_and_close(tester): - tester.click_widget(u"Help") + tester.click_widget("Help") tester.close(accept=True) self.dialog.help_id = "help_test" @@ -218,16 +227,16 @@ ) # noqa @unittest.skipIf( is_pyqt4_linux, - "Dialog click tests don't work reliably on linux. Issue #282." + "Dialog click tests don't work reliably on linux. Issue #282.", ) # noqa - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_renamed_help(self): def click_help_and_close(tester): - tester.click_widget(u"Assistance") + tester.click_widget("Assistance") tester.close(accept=True) self.dialog.help_id = "help_test" - self.dialog.help_label = u"Assistance" + self.dialog.help_label = "Assistance" # test that OK works as expected if renames tester = ModalDialogTester(self.dialog.open) tester.open_and_wait(when_opened=click_help_and_close) @@ -237,7 +246,7 @@ def test_nonmodal_close(self): # test that closing works as expected - self.dialog.style = 'nonmodal' + self.dialog.style = "nonmodal" result = self.dialog.open() with self.event_loop(): @@ -249,7 +258,7 @@ def test_not_resizable(self): # test that a resizable dialog can be created # XXX use nonmodal for better cross-platform coverage - self.dialog.style = 'nonmodal' + self.dialog.style = "nonmodal" self.dialog.resizable = False with self.event_loop(): result = self.dialog.open() diff -Nru python-pyface-6.1.2/pyface/tests/test_directory_dialog.py python-pyface-7.4.0/pyface/tests/test_directory_dialog.py --- python-pyface-6.1.2/pyface/tests/test_directory_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_directory_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,30 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import os import unittest from ..directory_dialog import DirectoryDialog -from ..gui import GUI from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestDirectoryDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -50,7 +58,7 @@ def test_default_path(self): # test that default path works - self.dialog.default_path = os.path.join('images', 'core.png') + self.dialog.default_path = os.path.join("images", "core.png") with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -66,10 +74,10 @@ def test_message(self): # test that message setting works - self.dialog.message = 'Select a directory' + self.dialog.message = "Select a directory" with self.event_loop(): self.dialog._create() with self.event_loop(): self.dialog.close() - #XXX would be nice to actually test with an open dialog, but not right now + # XXX would be nice to actually test with an open dialog, but not right now diff -Nru python-pyface-6.1.2/pyface/tests/test_file_dialog.py python-pyface-7.4.0/pyface/tests/test_file_dialog.py --- python-pyface-6.1.2/pyface/tests/test_file_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_file_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,30 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import os import unittest from ..file_dialog import FileDialog -from ..gui import GUI from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestFileDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -30,12 +38,12 @@ GuiTestAssistant.tearDown(self) def test_create_wildcard(self): - wildcard = FileDialog.create_wildcard('Python', '*.py') + wildcard = FileDialog.create_wildcard("Python", "*.py") self.assertTrue(len(wildcard) != 0) def test_create_wildcard_multiple(self): wildcard = FileDialog.create_wildcard( - 'Python', ['*.py', '*.pyo', '*.pyc', '*.pyd'] + "Python", ["*.py", "*.pyo", "*.pyc", "*.pyd"] ) self.assertTrue(len(wildcard) != 0) @@ -60,7 +68,7 @@ def test_default_path(self): # test that default path works - self.dialog.default_path = os.path.join('images', 'core.png') + self.dialog.default_path = os.path.join("images", "core.png") with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -68,8 +76,8 @@ def test_default_dir_and_file(self): # test that default dir and path works - self.dialog.default_directory = 'images' - self.dialog.default_filename = 'core.png' + self.dialog.default_directory = "images" + self.dialog.default_filename = "core.png" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -77,7 +85,7 @@ def test_open_files(self): # test that open files action works - self.dialog.action = 'open files' + self.dialog.action = "open files" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -85,10 +93,10 @@ def test_save_as(self): # test that open files action works - self.dialog.action = 'save as' + self.dialog.action = "save as" with self.event_loop(): self.dialog._create() with self.event_loop(): self.dialog.close() - #XXX would be nice to actually test with an open dialog, but not right now + # XXX would be nice to actually test with an open dialog, but not right now diff -Nru python-pyface-6.1.2/pyface/tests/test_font_dialog.py python-pyface-7.4.0/pyface/tests/test_font_dialog.py --- python-pyface-6.1.2/pyface/tests/test_font_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_font_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,87 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import unittest + +from ..font import Font +from ..font_dialog import FontDialog, get_font +from ..toolkit import toolkit_object +from ..util.font_parser import simple_parser + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + +ModalDialogTester = toolkit_object( + "util.modal_dialog_tester:ModalDialogTester" +) +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestFontDialog(unittest.TestCase, GuiTestAssistant): + + def setUp(self): + GuiTestAssistant.setUp(self) + self.dialog = FontDialog(font="12 Helvetica sans-serif") + + def tearDown(self): + if self.dialog.control is not None: + with self.delete_widget(self.dialog.control): + self.dialog.destroy() + del self.dialog + GuiTestAssistant.tearDown(self) + + def test_font(self): + # test that fonts are translated as expected + self.dialog.font = "10 bold condensed Helvetica sans-serif" + + self.assertFontEqual( + self.dialog.font, + Font(**simple_parser("10 bold condensed Helvetica sans-serif")), + ) + + def test_create(self): + # test that creation and destruction works as expected + with self.event_loop(): + self.dialog._create() + with self.event_loop(): + self.dialog.destroy() + + def test_destroy(self): + # test that destroy works even when no control + with self.event_loop(): + self.dialog.destroy() + + def test_close(self): + # test that close works + with self.event_loop(): + self.dialog._create() + with self.event_loop(): + self.dialog.close() + + def assertFontEqual(self, font1, font2): + state1 = font1.trait_get(transient=lambda x: not x) + state2 = font2.trait_get(transient=lambda x: not x) + self.assertEqual(state1, state2) + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestGetFont(unittest.TestCase, GuiTestAssistant): + + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") + def test_close(self): + # test that cancel works as expected + tester = ModalDialogTester( + lambda: get_font(None, "12 Helvetica sans-serif") + ) + tester.open_and_run(when_opened=lambda x: x.close(accept=False)) + + self.assertEqual(tester.result, None) diff -Nru python-pyface-6.1.2/pyface/tests/test_font.py python-pyface-7.4.0/pyface/tests/test_font.py --- python-pyface-6.1.2/pyface/tests/test_font.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_font.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,297 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + +from traits.api import HasStrictTraits, TraitError + +from pyface.font import Font, FontSize, FontStretch, SIZES, STRETCHES +from pyface.toolkit import toolkit_object + + +class FontSizeDummy(HasStrictTraits): + + size = FontSize() + size_default_1 = FontSize(14.0) + size_default_2 = FontSize("14.0") + size_default_3 = FontSize("14.0pt") + size_default_4 = FontSize("large") + + +class TestFontSizeTrait(unittest.TestCase): + + def test_font_size_trait_defaults(self): + dummy = FontSizeDummy() + + self.assertEqual(dummy.size, 12.0) + self.assertEqual(dummy.size_default_1, 14.0) + self.assertEqual(dummy.size_default_2, 14.0) + self.assertEqual(dummy.size_default_3, 14.0) + self.assertEqual(dummy.size_default_4, 14.0) + + def test_font_sizes(self): + dummy = FontSizeDummy() + for size, value in SIZES.items(): + with self.subTest(size=size): + dummy.size = size + self.assertEqual(dummy.size, value) + + def test_font_size_trait_invalid_default(self): + for size in ["badvalue", -1.0, "-1.0", "0pt"]: + with self.subTest(size=size): + with self.assertRaises(TraitError): + FontSize(size) + + def test_font_size_trait_validate(self): + dummy = FontSizeDummy() + for size, expected in [ + (14.0, 14.0), + ("15.0", 15.0), + ("16pt", 16.0), + ("17.0px", 17.0), + ]: + with self.subTest(size=size): + dummy.size = size + self.assertEqual(dummy.size, expected) + + def test_font_size_trait_invalid_validate(self): + dummy = FontSizeDummy() + for size in ["badvalue", -1.0, "-1.0", "0pt"]: + with self.subTest(size=size): + with self.assertRaises(TraitError): + dummy.size = size + + +class FontStretchDummy(HasStrictTraits): + + stretch = FontStretch() + stretch_default_1 = FontStretch(150) + stretch_default_2 = FontStretch("150.0") + stretch_default_3 = FontStretch("150.0%") + stretch_default_4 = FontStretch("expanded") + + +class TestFontStretchTrait(unittest.TestCase): + + def test_font_stretch_trait_defaults(self): + dummy = FontStretchDummy() + + self.assertEqual(dummy.stretch, 100.0) + self.assertEqual(dummy.stretch_default_1, 150.0) + self.assertEqual(dummy.stretch_default_2, 150.0) + self.assertEqual(dummy.stretch_default_3, 150.0) + self.assertEqual(dummy.stretch_default_4, 125.0) + + def test_font_stretches(self): + dummy = FontStretchDummy() + for stretch, value in STRETCHES.items(): + with self.subTest(stretch=stretch): + dummy.stretch = stretch + self.assertEqual(dummy.stretch, value) + + def test_font_stretch_trait_invalid_default(self): + for stretch in ["badvalue", 49.5, "49.5", "49.5%", 200.1]: + with self.subTest(stretch=stretch): + with self.assertRaises(TraitError): + FontStretch(stretch) + + def test_font_stretch_trait_validate(self): + dummy = FontStretchDummy() + + for stretch, expected in [ + (150.0, 150.0), + ("125", 125.0), + ("50%", 50.0), + ("ultra-expanded", 200.0) + ]: + with self.subTest(stretch=stretch): + dummy.stretch = stretch + self.assertEqual(dummy.stretch, expected) + + def test_font_stretch_trait_invalid_validate(self): + dummy = FontStretchDummy() + for stretch in ["badvalue", 49.5, "200.1", "49.9%"]: + with self.subTest(stretch=stretch): + with self.assertRaises(TraitError): + dummy.stretch = stretch + + +class TestFont(unittest.TestCase): + + def test_default(self): + font = Font() + + self.assertEqual(font.family, ['default']) + self.assertEqual(font.size, 12.0) + self.assertEqual(font.weight, 'normal') + self.assertEqual(font.stretch, 100) + self.assertEqual(font.style, 'normal') + self.assertEqual(font.variants, set()) + + def test_typical(self): + font = Font( + family=['Helvetica', 'sans-serif'], + size='large', + weight='demi-bold', + stretch='condensed', + style='italic', + variants={'small-caps'}, + decorations={'underline'}, + ) + + self.assertEqual(font.family, ['Helvetica', 'sans-serif']) + self.assertEqual(font.size, 14.0) + self.assertEqual(font.weight, 'demi-bold') + self.assertEqual(font.weight_, 600) + self.assertEqual(font.stretch, 75) + self.assertEqual(font.style, 'italic') + self.assertEqual(font.variants, {'small-caps'}) + self.assertEqual(font.decorations, {'underline'}) + + def test_family_sequence(self): + font = Font(family=('Helvetica', 'sans-serif')) + self.assertEqual(font.family, ['Helvetica', 'sans-serif']) + + def test_variants_frozenset(self): + font = Font(variants=frozenset({'small-caps'})) + self.assertEqual(font.variants, {'small-caps'}) + + def test_decorations_frozenset(self): + font = Font(decorations=frozenset({'underline'})) + self.assertEqual(font.decorations, {'underline'}) + + def test_str(self): + font = Font() + + description = str(font) + + self.assertEqual(description, "12pt default") + + def test_str_typical(self): + font = Font( + family=['Comic Sans', 'decorative'], + size='large', + weight='demi-bold', + stretch='condensed', + style='italic', + variants={'small-caps'}, + decorations={'underline'}, + ) + + description = str(font) + + self.assertEqual( + description, + "italic small-caps underline demi-bold 75% 14pt " + "'Comic Sans', decorative" + ) + + def test_repr(self): + font = Font() + + text = repr(font) + + # this is little more than a smoke check, but good enough + self.assertTrue(text.startswith('Font(')) + + def test_repr_typical(self): + font = Font( + family=['Helvetica', 'sans-serif'], + size='large', + weight='demi-bold', + stretch='condensed', + style='italic', + variants={'small-caps'}, + decorations={'underline'}, + ) + + text = repr(font) + + # this is little more than a smoke check, but good enough + self.assertTrue(text.startswith('Font(')) + + def test_to_toolkit(self): + font = Font() + + # smoke test + toolkit_font = font.to_toolkit() + self.assertIsNotNone(toolkit_font) + + def test_to_toolkit_typical(self): + font = Font( + family=['Helvetica', 'sans-serif'], + size='large', + weight='demi-bold', + stretch='condensed', + style='italic', + variants={'small-caps'}, + decorations={'underline', 'strikethrough', 'overline'}, + ) + + # smoke test + toolkit_font = font.to_toolkit() + self.assertIsNotNone(toolkit_font) + + def test_toolkit_default_roundtrip(self): + font = Font() + + # smoke test + result = Font.from_toolkit(font.to_toolkit()) + + # defaults should round-trip + self.assertTrue(result.family[-1], 'default') + self.assertEqual(result.size, font.size) + self.assertEqual(result.weight, font.weight) + self.assertEqual(result.stretch, font.stretch) + self.assertEqual(result.variants, font.variants) + self.assertEqual(result.decorations, font.decorations) + + def test_from_toolkit_typical(self): + font = Font( + family=['Helvetica', 'sans-serif'], + size='large', + weight='bold', + stretch='condensed', + style='italic', + variants={'small-caps'}, + decorations={'underline', 'strikethrough', 'overline'}, + ) + + # smoke test + result = Font.from_toolkit(font.to_toolkit()) + + # we expect some things should round-trip no matter what system + self.assertEqual(result.size, font.size) + self.assertEqual(result.weight, font.weight) + self.assertEqual(result.style, font.style) + + def test_toolkit_font_to_properties(self): + toolkit_font_to_properties = toolkit_object( + 'font:toolkit_font_to_properties') + + font = Font( + family=['Helvetica', 'sans-serif'], + size='large', + weight='demi-bold', + stretch='condensed', + style='italic', + variants={'small-caps'}, + decorations={'underline', 'strikethrough', 'overline'}, + ) + + properties = toolkit_font_to_properties(font.to_toolkit()) + + self.assertEqual( + set(properties.keys()), + { + 'family', 'size', 'stretch', 'weight', 'style', 'variants', + 'decorations' + } + ) diff -Nru python-pyface-6.1.2/pyface/tests/test_gui_application.py python-pyface-7.4.0/pyface/tests/test_gui_application.py --- python-pyface-6.1.2/pyface/tests/test_gui_application.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_gui_application.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,34 @@ -# Copyright (c) 2014-2018 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import os from shutil import rmtree from tempfile import mkdtemp import unittest -from traits.api import Bool, on_trait_change +from traits.api import Bool, observe from ..application_window import ApplicationWindow from ..gui_application import GUIApplication from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" EVENTS = [ - 'starting', 'started', 'application_initialized', 'stopping', 'stopped' + "starting", + "started", + "application_initialized", + "stopping", + "stopped", ] @@ -58,7 +60,7 @@ #: Whether or not a call to the open a window was vetoed. window_open_vetoed = Bool(False) - + #: Whether or not a call to the exit method was vetoed. exit_vetoed = Bool(False) @@ -71,51 +73,57 @@ def start(self): if not self.start_cleanly: return False - super(TestingApp, self).start() + super().start() window = self.windows[0] - window.on_trait_change(self._on_window_closing, 'closing') + window.observe(self._on_window_closing, "closing") return True def stop(self): - super(TestingApp, self).stop() + super().stop() return self.stop_cleanly - def _on_window_closing(self, window, trait, old, new): + def _on_window_closing(self, event): + window = event.new if self.veto_close_window and not self.exit_vetoed: - new.veto = True + window.veto = True self.exit_vetoed = True - def _application_initialized_fired(self): + @observe("application_initialized") + def _update_window_open_vetoed(self, event): self.window_open_vetoed = ( len(self.windows) > 0 and self.windows[0].control is None ) - def _exiting_fired(self, event): - event.veto = self.veto_exit + @observe('exiting') + def _set_veto_on_exiting_event(self, event): + vetoable_event = event.new + vetoable_event.veto = self.veto_exit self.exit_vetoed = self.veto_exit def _prepare_exit(self): - super(TestingApp, self)._prepare_exit() + super()._prepare_exit() if not self.exit_vetoed: self.exit_prepared = True if self.exit_prepared_error: raise Exception("Exit preparation failed") - @on_trait_change('windows:opening') + @observe("windows:items:opening") def _on_activate_window(self, event): if self.veto_open_window: - event.veto = self.veto_open_window + window = event.new + window.veto = self.veto_open_window -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestGUIApplication(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) self.application_events = [] - if toolkit_object.toolkit == 'wx': + if toolkit_object.toolkit == "wx": import wx + self.event_loop() wx.GetApp().DeletePendingEvents() else: @@ -125,11 +133,12 @@ GuiTestAssistant.tearDown(self) def event_listener(self, event): - self.application_events.append(event) + application_event = event.new + self.application_events.append(application_event) def connect_listeners(self, app): for event in EVENTS: - app.on_trait_change(self.event_listener, event) + app.observe(self.event_listener, event) def test_defaults(self): from traits.etsconfig.api import ETSConfig @@ -156,7 +165,7 @@ app = GUIApplication() self.connect_listeners(app) window = ApplicationWindow() - app.on_trait_change(lambda: app.add_window(window), 'started') + app.observe(lambda _: app.add_window(window), "started") with self.assertMultiTraitChanges([app], EVENTS, []): self.gui.invoke_after(1000, app.exit) @@ -185,7 +194,7 @@ def test_veto_exit(self): app = TestingApp(veto_exit=True) self.connect_listeners(app) - + with self.assertMultiTraitChanges([app], EVENTS, []): self.gui.invoke_after(1000, app.exit) self.gui.invoke_after(2000, app.exit, force=True) diff -Nru python-pyface-6.1.2/pyface/tests/test_heading_text.py python-pyface-7.4.0/pyface/tests/test_heading_text.py --- python-pyface-6.1.2/pyface/tests/test_heading_text.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_heading_text.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -7,11 +16,13 @@ from ..toolkit import toolkit_object from ..window import Window -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + +is_wx = toolkit_object.toolkit == "wx" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestHeadingText(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -34,33 +45,52 @@ def test_lifecycle(self): # test that destroy works with self.event_loop(): - self.widget = HeadingText(self.window.control) + with self.assertWarns(PendingDeprecationWarning): + self.widget = HeadingText(self.window.control) + + self.assertIsNotNone(self.widget.control) + with self.event_loop(): self.widget.destroy() - def test_message(self): - # test that create works with message + def test_two_stage_create(self): + # test that create=False works + self.widget = HeadingText(create=False) + + self.assertIsNone(self.widget.control) + with self.event_loop(): - self.widget = HeadingText(self.window.control, text="Hello") + self.widget.parent = self.window.control + self.widget.create() + + self.assertIsNotNone(self.widget.control) + with self.event_loop(): self.widget.destroy() - def test_image(self): - # test that image works - # XXX this image doesn't make sense here, but that's fine - # XXX this isn't implemented in qt4 backend, but shouldn't fail + def test_text(self): + # test that create works with text with self.event_loop(): self.widget = HeadingText( - self.window.control, image=ImageResource('core.png') + self.window.control, + text="Hello", + create=False, ) + self.widget.create() + + self.assertEqual(self.widget.text, "Hello") + self.assertEqual(self.widget._get_control_text(), "Hello") + with self.event_loop(): self.widget.destroy() - def test_level(self): - # test that create works with level - # XXX this image doesn't make sense here, but that's fine - # XXX this isn't implemented in qt4 backend, but shouldn't fail - with self.event_loop(): - self.widget = HeadingText(self.window.control, level=2) + @unittest.skipUnless(is_wx, "Only Wx supports background images") + def test_image(self): + # test that image raises a deprecation warning with self.event_loop(): - self.widget.destroy() + with self.assertWarns(PendingDeprecationWarning): + self.widget = HeadingText( + self.window.control, + create=False, + image=ImageResource("core.png"), + ) diff -Nru python-pyface-6.1.2/pyface/tests/test_image_cache.py python-pyface-7.4.0/pyface/tests/test_image_cache.py --- python-pyface-6.1.2/pyface/tests/test_image_cache.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_image_cache.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,31 +1,39 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import os import unittest from ..image_cache import ImageCache -IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'images', 'core.png') +IMAGE_PATH = os.path.join(os.path.dirname(__file__), "images", "core.png") class TestPyfaceResourceFactory(unittest.TestCase): - def setUp(self): self.image_cache = ImageCache(32, 32) def test_get_image(self): - image = self.image_cache.get_image(IMAGE_PATH) + self.image_cache.get_image(IMAGE_PATH) def test_get_bitmap(self): - bitmap = self.image_cache.get_bitmap(IMAGE_PATH) + self.image_cache.get_bitmap(IMAGE_PATH) def test_get_image_twice(self): - image1 = self.image_cache.get_image(IMAGE_PATH) - image2 = self.image_cache.get_image(IMAGE_PATH) + self.image_cache.get_image(IMAGE_PATH) + self.image_cache.get_image(IMAGE_PATH) def test_get_bitmap_twice(self): - bitmap1 = self.image_cache.get_bitmap(IMAGE_PATH) - bitmap2 = self.image_cache.get_bitmap(IMAGE_PATH) + self.image_cache.get_bitmap(IMAGE_PATH) + self.image_cache.get_bitmap(IMAGE_PATH) def test_get_image_different_sizes(self): other_image_cache = ImageCache(48, 48) diff -Nru python-pyface-6.1.2/pyface/tests/test_image_resource.py python-pyface-7.4.0/pyface/tests/test_image_resource.py --- python-pyface-6.1.2/pyface/tests/test_image_resource.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_image_resource.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,93 +1,119 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import os import platform -import pkg_resources import unittest +# importlib.resources is new in Python 3.7, and importlib.resources.files is +# new in Python 3.9, so for Python < 3.9 we must rely on the 3rd party +# importlib_resources package. +try: + from importlib.resources import files +except ImportError: + from importlib_resources import files + import pyface import pyface.tests from ..image_resource import ImageResource from ..toolkit import toolkit_object -is_qt = toolkit_object.toolkit == 'qt4' +is_qt = toolkit_object.toolkit == "qt4" if is_qt: from pyface.qt import qt_api -is_pyqt4_windows = (is_qt and qt_api == 'pyqt' and platform.system() == 'Windows') +is_pyqt4_windows = ( + is_qt and qt_api == "pyqt" and platform.system() == "Windows" +) -SEARCH_PATH = pkg_resources.resource_filename('pyface', 'images') -IMAGE_DIR = pkg_resources.resource_filename(__name__, 'images') -IMAGE_PATH = os.path.join(IMAGE_DIR, 'core.png') +SEARCH_PATH = os.fspath(files("pyface") / "images") +IMAGE_PATH = os.fspath(files("pyface.tests") / "images" / "core.png") class TestImageResource(unittest.TestCase): - def setUp(self): # clear cached "not found" image ImageResource._image_not_found = None def test_create_image(self): - image_resource = ImageResource('core') + image_resource = ImageResource("core") image = image_resource.create_image() self.assertIsNotNone(image) self.assertEqual(image_resource.absolute_path, IMAGE_PATH) def test_create_image_again(self): - image_resource = ImageResource('core') + image_resource = ImageResource("core") image = image_resource.create_image() self.assertIsNotNone(image) self.assertEqual(image_resource.absolute_path, IMAGE_PATH) def test_create_image_search_path(self): - image_resource = ImageResource('splash.jpg', [SEARCH_PATH]) - self.assertEqual(image_resource.search_path, - [SEARCH_PATH, pyface.tests]) + image_resource = ImageResource("splash", [SEARCH_PATH]) + self.assertEqual( + image_resource.search_path, [SEARCH_PATH, pyface.tests] + ) image = image_resource.create_image() self.assertIsNotNone(image) - self.assertEqual(image_resource.absolute_path, - os.path.join(SEARCH_PATH, 'splash.jpg')) + self.assertEqual( + image_resource.absolute_path, + os.path.join(SEARCH_PATH, "splash.png"), + ) def test_create_image_search_path_string(self): - image_resource = ImageResource('splash.jpg', SEARCH_PATH) - self.assertEqual(image_resource.search_path, - [SEARCH_PATH, pyface.tests]) + image_resource = ImageResource("splash", SEARCH_PATH) + self.assertEqual( + image_resource.search_path, [SEARCH_PATH, pyface.tests] + ) image = image_resource.create_image() self.assertIsNotNone(image) - self.assertEqual(image_resource.absolute_path, - os.path.join(SEARCH_PATH, 'splash.jpg')) + self.assertEqual( + image_resource.absolute_path, + os.path.join(SEARCH_PATH, "splash.png"), + ) def test_create_image_missing(self): - image_resource = ImageResource('doesnt_exist.png') + image_resource = ImageResource("doesnt_exist.png") image = image_resource.create_image() self.assertIsNotNone(image) self.assertIsNotNone(image_resource._image_not_found) def test_create_bitmap(self): - image_resource = ImageResource('core.png') + image_resource = ImageResource("core.png") image = image_resource.create_bitmap() self.assertIsNotNone(image) self.assertEqual(image_resource.absolute_path, IMAGE_PATH) def test_create_icon(self): - image_resource = ImageResource('core.png') + image_resource = ImageResource("core.png") image = image_resource.create_icon() self.assertIsNotNone(image) self.assertEqual(image_resource.absolute_path, IMAGE_PATH) def test_image_size(self): - image_resource = ImageResource('core') + image_resource = ImageResource("core") image = image_resource.create_image() size = image_resource.image_size(image) self.assertEqual(image_resource._ref.filename, IMAGE_PATH) self.assertEqual(size, (64, 64)) - @unittest.skipIf(is_pyqt4_windows, "QPixmap bug returns (0, 0). Issue #301.") # noqa + @unittest.skipIf( + is_pyqt4_windows, "QPixmap bug returns (0, 0). Issue #301." + ) # noqa def test_image_size_search_path(self): - image_resource = ImageResource('splash.jpg', [SEARCH_PATH]) + image_resource = ImageResource("splash", [SEARCH_PATH]) image = image_resource.create_image() size = image_resource.image_size(image) - self.assertEqual(image_resource.absolute_path, - os.path.join(SEARCH_PATH, 'splash.jpg')) - self.assertEqual(size, (450, 296)) + self.assertEqual( + image_resource.absolute_path, + os.path.join(SEARCH_PATH, "splash.png"), + ) + self.assertEqual(size, (601, 203)) diff -Nru python-pyface-6.1.2/pyface/tests/test_layered_panel.py python-pyface-7.4.0/pyface/tests/test_layered_panel.py --- python-pyface-6.1.2/pyface/tests/test_layered_panel.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_layered_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,143 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import unittest + +from ..heading_text import HeadingText +from ..layered_panel import LayeredPanel +from ..toolkit import toolkit_object +from ..window import Window + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + + +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +class TestLayeredPanel(unittest.TestCase, GuiTestAssistant): + def setUp(self): + GuiTestAssistant.setUp(self) + self.window = Window() + self.window._create() + + def tearDown(self): + if self.widget.control is not None: + with self.delete_widget(self.widget.control): + self.widget.destroy() + + if self.window.control is not None: + with self.delete_widget(self.window.control): + self.window.destroy() + + del self.widget + del self.window + GuiTestAssistant.tearDown(self) + + def test_lifecycle(self): + # test that create and destory work + self.widget = LayeredPanel(create=False) + + self.assertIsNone(self.widget.control) + + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + self.assertIsNotNone(self.widget.control) + + with self.event_loop(): + self.widget.destroy() + + self.assertIsNone(self.widget.control) + + def test_add_layer(self): + # test that a layer can be added + self.widget = LayeredPanel(create=False) + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + layer_widget = HeadingText(parent=self.window.control, create=False) + with self.event_loop(): + layer_widget.create() + + try: + with self.event_loop(): + self.widget.add_layer("test 1", layer_widget.control) + + self.assertIn("test 1", self.widget._layers) + self.assertIs(self.widget._layers["test 1"], layer_widget.control) + finally: + with self.event_loop(): + layer_widget.destroy() + + with self.event_loop(): + self.widget.destroy() + + def test_show_layer(self): + # test that a layer can be shown + self.widget = LayeredPanel(create=False) + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + layer_widget_1 = HeadingText(parent=self.window.control, create=False) + layer_widget_2 = HeadingText(parent=self.window.control, create=False) + with self.event_loop(): + layer_widget_1.create() + layer_widget_2.create() + self.widget.add_layer("test 1", layer_widget_1.control) + self.widget.add_layer("test 2", layer_widget_2.control) + + try: + with self.event_loop(): + self.widget.show_layer("test 1") + + self.assertEqual(self.widget.current_layer_name, "test 1") + self.assertIs(self.widget.current_layer, layer_widget_1.control) + + with self.event_loop(): + self.widget.show_layer("test 2") + + self.assertEqual(self.widget.current_layer_name, "test 2") + self.assertIs(self.widget.current_layer, layer_widget_2.control) + finally: + with self.event_loop(): + layer_widget_1.destroy() + layer_widget_2.destroy() + + with self.event_loop(): + self.widget.destroy() + + def test_has_layer(self): + # test that a has_layer works + self.widget = LayeredPanel(create=False) + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + layer_widget_1 = HeadingText(parent=self.window.control, create=False) + layer_widget_2 = HeadingText(parent=self.window.control, create=False) + with self.event_loop(): + layer_widget_1.create() + layer_widget_2.create() + self.widget.add_layer("test 1", layer_widget_1.control) + self.widget.add_layer("test 2", layer_widget_2.control) + + try: + self.assertTrue(self.widget.has_layer("test 1")) + self.assertTrue(self.widget.has_layer("test 2")) + finally: + with self.event_loop(): + layer_widget_1.destroy() + layer_widget_2.destroy() + + with self.event_loop(): + self.widget.destroy() diff -Nru python-pyface-6.1.2/pyface/tests/test_message_dialog.py python-pyface-7.4.0/pyface/tests/test_message_dialog.py --- python-pyface-6.1.2/pyface/tests/test_message_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_message_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import platform import unittest @@ -8,25 +17,25 @@ from ..toolkit import toolkit_object from ..window import Window -is_qt = toolkit_object.toolkit == 'qt4' +is_qt = toolkit_object.toolkit == "qt4" if is_qt: from pyface.qt import qt_api -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -is_pyqt5 = (is_qt and qt_api == 'pyqt5') -is_pyqt4_linux = (is_qt and qt_api == 'pyqt' and platform.system() == 'Linux') +is_pyqt5 = is_qt and qt_api == "pyqt5" +is_pyqt4_linux = is_qt and qt_api == "pyqt" and platform.system() == "Linux" USING_QT = is_qt -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestMessageDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -80,7 +89,7 @@ def test_create_ok_renamed(self): # test that creation and destruction works as expected with ok_label - self.dialog.ok_label = u"Sure" + self.dialog.ok_label = "Sure" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -88,7 +97,7 @@ def test_message(self): # test that creation and destruction works as expected with message - self.dialog.message = u"This is the message" + self.dialog.message = "This is the message" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -96,8 +105,8 @@ def test_informative(self): # test that creation and destruction works with informative - self.dialog.message = u"This is the message" - self.dialog.informative = u"This is the additional message" + self.dialog.message = "This is the message" + self.dialog.informative = "This is the additional message" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -105,9 +114,9 @@ def test_detail(self): # test that creation and destruction works with detail - self.dialog.message = u"This is the message" - self.dialog.informative = u"This is the additional message" - self.dialog.detail = u"This is the detail" + self.dialog.message = "This is the message" + self.dialog.informative = "This is the additional message" + self.dialog.detail = "This is the detail" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -134,9 +143,9 @@ ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_accept(self): # test that accept works as expected # XXX duplicate of Dialog test, not needed? @@ -150,9 +159,9 @@ ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_close(self): # test that closing works as expected # XXX duplicate of Dialog test, not needed? @@ -166,9 +175,9 @@ ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_ok(self): # test that OK works as expected tester = ModalDialogTester(self.dialog.open) @@ -177,12 +186,12 @@ self.assertEqual(self.dialog.return_code, OK) @unittest.skipIf(USING_QT, "Can't change OK label in Qt") - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_renamed_ok(self): - self.dialog.ok_label = u"Sure" + self.dialog.ok_label = "Sure" # test that OK works as expected if renamed tester = ModalDialogTester(self.dialog.open) - tester.open_and_wait(when_opened=lambda x: x.click_widget(u"Sure")) + tester.open_and_wait(when_opened=lambda x: x.click_widget("Sure")) self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) @@ -191,9 +200,9 @@ ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_parent(self): # test that lifecycle works with a parent parent = Window() @@ -209,8 +218,8 @@ self.assertEqual(self.dialog.return_code, OK) -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') -@unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") +@unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") class TestMessageDialogHelpers(unittest.TestCase, GuiTestAssistant): def test_information(self): self._check_dialog(information) @@ -222,11 +231,11 @@ self._check_dialog(error) def _check_dialog(self, helper): - message = 'message' + message = "message" kwargs = { - 'title': 'Title', - 'detail': 'Detail', - 'informative': 'Informative' + "title": "Title", + "detail": "Detail", + "informative": "Informative", } # smoke test, since dialog helper is opaque diff -Nru python-pyface-6.1.2/pyface/tests/test_new_toolkit/init.py python-pyface-7.4.0/pyface/tests/test_new_toolkit/init.py --- python-pyface-6.1.2/pyface/tests/test_new_toolkit/init.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_new_toolkit/init.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,5 +1,14 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # Dummy toolkit for testing entrypoints from pyface.base_toolkit import Toolkit -toolkit_object = Toolkit('pyface', 'test', 'pyface.tests.test_new_toolkit') +toolkit_object = Toolkit("pyface", "test", "pyface.tests.test_new_toolkit") diff -Nru python-pyface-6.1.2/pyface/tests/test_new_toolkit/widget.py python-pyface-7.4.0/pyface/tests/test_new_toolkit/widget.py --- python-pyface-6.1.2/pyface/tests/test_new_toolkit/widget.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_new_toolkit/widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # Dummy widget module for testing entrypoints from traits.api import provides diff -Nru python-pyface-6.1.2/pyface/tests/test_pil_image.py python-pyface-7.4.0/pyface/tests/test_pil_image.py --- python-pyface-6.1.2/pyface/tests/test_pil_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_pil_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,59 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import os +import logging +import unittest + +# importlib.resources is new in Python 3.7, and importlib.resources.files is +# new in Python 3.9, so for Python < 3.9 we must rely on the 3rd party +# importlib_resources package. +try: + from importlib.resources import files +except ImportError: + from importlib_resources import files + +from pyface.util._optional_dependencies import optional_import + +Image = None + +with optional_import( + "pillow", + msg="PILImage not available due to missing pillow.", + logger=logging.getLogger(__name__)): + + from PIL import Image + from ..pil_image import PILImage + +IMAGE_PATH = os.fspath(files("pyface.tests") / "images" / "core.png") + + +@unittest.skipIf(Image is None, "Pillow not available") +class TestPILImage(unittest.TestCase): + + def setUp(self): + self.pil_image = Image.open(IMAGE_PATH) + + def test_create_image(self): + image = PILImage(self.pil_image) + toolkit_image = image.create_image() + self.assertIsNotNone(toolkit_image) + self.assertEqual(image.image, self.pil_image) + + def test_create_bitmap(self): + image = PILImage(self.pil_image) + bitmap = image.create_bitmap() + self.assertIsNotNone(bitmap) + + def test_create_icon(self): + image = PILImage(self.pil_image) + icon = image.create_icon() + self.assertIsNotNone(icon) diff -Nru python-pyface-6.1.2/pyface/tests/test_progress_dialog.py python-pyface-7.4.0/pyface/tests/test_progress_dialog.py --- python-pyface-6.1.2/pyface/tests/test_progress_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_progress_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,29 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest from ..progress_dialog import ProgressDialog from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestProgressDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -47,6 +56,14 @@ with self.event_loop(): self.dialog.destroy() + def test_can_ok(self): + # test that creation works with can_ok + self.dialog.can_ok = True + with self.event_loop(): + self.dialog._create() + with self.event_loop(): + self.dialog.destroy() + def test_show_time(self): # test that creation works with show_time self.dialog.show_time = True @@ -105,11 +122,11 @@ self.dialog.open() for i in range(11): with self.event_loop(): - self.dialog.change_message('Updating {}'.format(i)) + self.dialog.change_message("Updating {}".format(i)) result = self.dialog.update(i) self.assertEqual(result, (True, False)) - self.assertEqual(self.dialog.message, 'Updating {}'.format(i)) + self.assertEqual(self.dialog.message, "Updating {}".format(i)) self.assertIsNone(self.dialog.control) def test_update_show_time(self): diff -Nru python-pyface-6.1.2/pyface/tests/test_python_editor.py python-pyface-7.4.0/pyface/tests/test_python_editor.py --- python-pyface-6.1.2/pyface/tests/test_python_editor.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,30 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import os -import sys import unittest from ..python_editor import PythonEditor from ..toolkit import toolkit_object from ..window import Window -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" PYTHON_SCRIPT = os.path.join( - os.path.dirname(__file__), 'python_shell_script.py' + os.path.dirname(__file__), "python_shell_script.py" ) -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestPythonEditor(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -37,8 +45,26 @@ def test_lifecycle(self): # test that destroy works with self.event_loop(): - self.widget = PythonEditor(self.window.control) + with self.assertWarns(PendingDeprecationWarning): + self.widget = PythonEditor(self.window.control) + self.assertIsNotNone(self.widget.control) + self.assertFalse(self.widget.dirty) + + with self.event_loop(): + self.widget.destroy() + + def test_two_stage_create(self): + # test that create and destroy work + self.widget = PythonEditor(create=False) + + self.assertIsNone(self.widget.control) + + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + self.assertIsNotNone(self.widget.control) self.assertFalse(self.widget.dirty) with self.event_loop(): @@ -48,21 +74,31 @@ # test that destroy works with self.event_loop(): self.widget = PythonEditor( - self.window.control, show_line_numbers=False + parent=self.window.control, + show_line_numbers=False, + create=False, ) + self.widget.create() + with self.event_loop(): self.widget.show_line_numbers = True + with self.event_loop(): self.widget.show_line_numbers = False + with self.event_loop(): self.widget.destroy() def test_load(self): # test that destroy works with self.event_loop(): - self.widget = PythonEditor(self.window.control) + self.widget = PythonEditor( + parent=self.window.control, + create=False, + ) + self.widget.create() - with self.assertTraitChanges(self.widget, 'changed', count=1): + with self.assertTraitChanges(self.widget, "changed", count=1): with self.event_loop(): self.widget.path = PYTHON_SCRIPT self.assertFalse(self.widget.dirty) @@ -73,7 +109,12 @@ def test_select_line(self): # test that destroy works with self.event_loop(): - self.widget = PythonEditor(self.window.control, path=PYTHON_SCRIPT) + self.widget = PythonEditor( + parent=self.window.control, + path=PYTHON_SCRIPT, + create=False, + ) + self.widget.create() with self.event_loop(): self.widget.select_line(3) diff -Nru python-pyface-6.1.2/pyface/tests/test_python_shell.py python-pyface-7.4.0/pyface/tests/test_python_shell.py --- python-pyface-6.1.2/pyface/tests/test_python_shell.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_python_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import os import sys @@ -8,15 +17,15 @@ from ..toolkit import toolkit_object from ..window import Window -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" PYTHON_SCRIPT = os.path.abspath( - os.path.join(os.path.dirname(__file__), 'python_shell_script.py') + os.path.join(os.path.dirname(__file__), "python_shell_script.py") ) -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestPythonShell(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -37,108 +46,134 @@ def test_lifecycle(self): # test that destroy works with self.event_loop(): - self.widget = PythonShell(self.window.control) + with self.assertWarns(PendingDeprecationWarning): + self.widget = PythonShell(self.window.control) + + self.assertIsNotNone(self.widget.control) + + with self.event_loop(): + self.widget.destroy() + + def test_two_stage_create(self): + # test that create=False works + self.widget = PythonShell(create=False) + + self.assertIsNone(self.widget.control) + + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + self.assertIsNotNone(self.widget.control) + with self.event_loop(): self.widget.destroy() def test_bind(self): # test that binding a variable works + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() with self.event_loop(): - self.widget.bind('x', 1) + self.widget.bind("x", 1) - self.assertEqual(self.widget.interpreter().locals.get('x'), 1) + self.assertEqual(self.widget.interpreter().locals.get("x"), 1) with self.event_loop(): self.widget.destroy() def test_execute_command(self): # test that executing a command works + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() - with self.assertTraitChanges(self.widget, 'command_executed', count=1): + with self.assertTraitChanges(self.widget, "command_executed", count=1): with self.event_loop(): - self.widget.execute_command('x = 1') + self.widget.execute_command("x = 1") - self.assertEqual(self.widget.interpreter().locals.get('x'), 1) + self.assertEqual(self.widget.interpreter().locals.get("x"), 1) with self.event_loop(): self.widget.destroy() def test_execute_command_not_hidden(self): # test that executing a non-hidden command works + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() - with self.assertTraitChanges(self.widget, 'command_executed', count=1): + with self.assertTraitChanges(self.widget, "command_executed", count=1): with self.event_loop(): - self.widget.execute_command('x = 1', hidden=False) + self.widget.execute_command("x = 1", hidden=False) - self.assertEqual(self.widget.interpreter().locals.get('x'), 1) + self.assertEqual(self.widget.interpreter().locals.get("x"), 1) with self.event_loop(): self.widget.destroy() def test_execute_file(self): # test that executing a file works + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() # XXX inconsistent behaviour between backends - #with self.assertTraitChanges(self.widget, 'command_executed', count=1): + # with self.assertTraitChanges(self.widget, 'command_executed', count=1): with self.event_loop(): self.widget.execute_file(PYTHON_SCRIPT) - self.assertEqual(self.widget.interpreter().locals.get('x'), 1) - self.assertEqual(self.widget.interpreter().locals.get('sys'), sys) + self.assertEqual(self.widget.interpreter().locals.get("x"), 1) + self.assertEqual(self.widget.interpreter().locals.get("sys"), sys) with self.event_loop(): self.widget.destroy() def test_execute_file_not_hidden(self): # test that executing a file works + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() # XXX inconsistent behaviour between backends - #with self.assertTraitChanges(self.widget, 'command_executed', count=1): + # with self.assertTraitChanges(self.widget, 'command_executed', count=1): with self.event_loop(): self.widget.execute_file(PYTHON_SCRIPT, hidden=False) - self.assertEqual(self.widget.interpreter().locals.get('x'), 1) - self.assertEqual(self.widget.interpreter().locals.get('sys'), sys) + self.assertEqual(self.widget.interpreter().locals.get("x"), 1) + self.assertEqual(self.widget.interpreter().locals.get("sys"), sys) with self.event_loop(): self.widget.destroy() def test_get_history(self): - # test that executing a command works + # test that command history can be extracted + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() with self.event_loop(): - self.widget.execute_command('x = 1', hidden=False) + self.widget.execute_command("x = 1", hidden=False) history, history_index = self.widget.get_history() - self.assertEqual(history, ['x = 1']) + self.assertEqual(history, ["x = 1"]) self.assertEqual(history_index, 1) with self.event_loop(): self.widget.destroy() def test_set_history(self): - # test that executing a command works + # test that command history can be updated + self.widget = PythonShell(parent=self.window.control, create=False) with self.event_loop(): - self.widget = PythonShell(self.window.control) + self.widget.create() with self.event_loop(): - self.widget.set_history(['x = 1', 'y = x + 1'], 1) + self.widget.set_history(["x = 1", "y = x + 1"], 1) history, history_index = self.widget.get_history() - self.assertEqual(history, ['x = 1', 'y = x + 1']) + self.assertEqual(history, ["x = 1", "y = x + 1"]) self.assertEqual(history_index, 1) with self.event_loop(): diff -Nru python-pyface-6.1.2/pyface/tests/test_resource_manager.py python-pyface-7.4.0/pyface/tests/test_resource_manager.py --- python-pyface-6.1.2/pyface/tests/test_resource_manager.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_resource_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,92 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from collections.abc import Sequence +import importlib.util import os +import shutil +import tempfile import unittest +import pyface # a package with images as package resources from ..resource_manager import PyfaceResourceFactory +from ..resource_manager import ResourceManager -IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'images', 'core.png') +IMAGE_PATH = os.path.join(os.path.dirname(__file__), "images", "core.png") class TestPyfaceResourceFactory(unittest.TestCase): - def setUp(self): self.resource_factory = PyfaceResourceFactory() def test_image_from_file(self): - image = self.resource_factory.image_from_file(IMAGE_PATH) + self.resource_factory.image_from_file(IMAGE_PATH) def test_image_from_data(self): - with open(IMAGE_PATH, 'rb') as fp: + with open(IMAGE_PATH, "rb") as fp: data = fp.read() - image = self.resource_factory.image_from_data(data) + self.resource_factory.image_from_data(data) + + def test_locate_image(self): + class ASequence(Sequence): + def __init__(self, data): + self.data = data + + def __getitem__(self, i): + return self.data[i] + + def __len__(self): + return len(self.data) + + sequence = ASequence([os.path.dirname(IMAGE_PATH)]) + + resource_manager = ResourceManager() + img_ref = resource_manager.locate_image("core.png", sequence) + self.assertEqual(IMAGE_PATH, img_ref.filename) + + def test_locate_image_with_module(self): + # ResourceManager should be able to find the images/close.png which + # is included in pyface package data. + resource_manager = ResourceManager() + image_ref = resource_manager.locate_image("close.png", [pyface]) + self.assertGreater(len(image_ref.data), 0) + + def test_locate_image_with_module_missing_file(self): + # The required image is not found, locate_image should return None. + resource_manager = ResourceManager() + image_ref = resource_manager.locate_image( + "does_not_exist.png", [pyface] + ) + self.assertIsNone(image_ref) + + def test_locate_image_with_name_being_dunder_main(self): + # When a module is not a package, we will fall back to use __file__ + + # given a module from which there is an image in the same folder + with tempfile.TemporaryDirectory() as tmp_dir: + shutil.copyfile(IMAGE_PATH, os.path.join(tmp_dir, "random.png")) + # create an empty file for creating a module. + py_filepath = os.path.join(tmp_dir, "tmp.py") + with open(py_filepath, "w", encoding="utf-8"): + pass + spec = importlib.util.spec_from_file_location( + "__main__", py_filepath + ) + module = importlib.util.module_from_spec(spec) + + resource_manager = ResourceManager( + resource_factory=PyfaceResourceFactory() + ) + # when + image_ref = resource_manager.load_image("random.png", [module]) + + # then + self.assertIsNotNone(image_ref) diff -Nru python-pyface-6.1.2/pyface/tests/test_single_choice_dialog.py python-pyface-7.4.0/pyface/tests/test_single_choice_dialog.py --- python-pyface-6.1.2/pyface/tests/test_single_choice_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_single_choice_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,13 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2016, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -from __future__ import absolute_import import unittest @@ -22,26 +15,25 @@ from ..single_choice_dialog import SingleChoiceDialog from ..constant import OK, CANCEL -from ..gui import GUI from ..toolkit import toolkit_object from ..window import Window -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) # noqa: E501 -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -USING_QT = ETSConfig.toolkit not in ['', 'wx'] +USING_QT = ETSConfig.toolkit not in ["", "wx"] -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestSingleChoiceDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) - self.dialog = SingleChoiceDialog(choices=['red', 'blue', 'green']) + self.dialog = SingleChoiceDialog(choices=["red", "blue", "green"]) def tearDown(self): if self.dialog.control is not None: @@ -85,7 +77,7 @@ def test_message(self): # test that creation and destruction works as expected with message - self.dialog.message = u"This is the message" + self.dialog.message = "This is the message" with self.event_loop(): self.dialog._create() with self.event_loop(): @@ -94,13 +86,13 @@ def test_choice_strings(self): # test that choice strings work using simple strings self.assertEqual( - self.dialog._choice_strings(), ['red', 'blue', 'green'] + self.dialog._choice_strings(), ["red", "blue", "green"] ) def test_choice_strings_convert(self): # test that choice strings work using simple strings self.dialog.choices = [1, 2, 3] - self.assertEqual(self.dialog._choice_strings(), ['1', '2', '3']) + self.assertEqual(self.dialog._choice_strings(), ["1", "2", "3"]) def test_choice_strings_name_attribute(self): # test that choice strings work using attribute name of objects @@ -108,10 +100,10 @@ def __init__(self, description): self.description = description - self.dialog.choices = [Item(name) for name in ['red', 'blue', 'green']] - self.dialog.name_attribute = 'description' + self.dialog.choices = [Item(name) for name in ["red", "blue", "green"]] + self.dialog.name_attribute = "description" self.assertEqual( - self.dialog._choice_strings(), ['red', 'blue', 'green'] + self.dialog._choice_strings(), ["red", "blue", "green"] ) def test_choice_strings_name_attribute_convert(self): @@ -121,8 +113,8 @@ self.description = description self.dialog.choices = [Item(name) for name in [1, 2, 3]] - self.dialog.name_attribute = 'description' - self.assertEqual(self.dialog._choice_strings(), ['1', '2', '3']) + self.dialog.name_attribute = "description" + self.assertEqual(self.dialog._choice_strings(), ["1", "2", "3"]) def test_choice_strings_empty(self): # test that choice strings work using simple strings @@ -132,20 +124,20 @@ def test_choice_strings_duplicated(self): # test that choice strings work using simple strings - self.dialog.choices = ['red', 'green', 'blue', 'green'] + self.dialog.choices = ["red", "green", "blue", "green"] with self.assertRaises(ValueError): self.dialog._choice_strings() - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_accept(self): # test that accept works as expected tester = ModalDialogTester(self.dialog.open) tester.open_and_run(when_opened=lambda x: x.close(accept=True)) self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) - self.assertEqual(self.dialog.choice, 'red') + self.assertEqual(self.dialog.choice, "red") - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_reject(self): # test that accept works as expected tester = ModalDialogTester(self.dialog.open) @@ -155,7 +147,7 @@ self.assertEqual(self.dialog.return_code, CANCEL) self.assertIsNone(self.dialog.choice) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_close(self): # test that closing works as expected tester = ModalDialogTester(self.dialog.open) @@ -167,7 +159,7 @@ self.assertEqual(self.dialog.return_code, CANCEL) self.assertIsNone(self.dialog.choice) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_parent(self): # test that lifecycle works with a parent parent = Window() @@ -181,7 +173,7 @@ self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_change_choice_accept(self): # test that if we change choice it's reflected in result def select_green_and_ok(tester): @@ -194,9 +186,9 @@ self.assertEqual(tester.result, OK) self.assertEqual(self.dialog.return_code, OK) - self.assertEqual(self.dialog.choice, 'green') + self.assertEqual(self.dialog.choice, "green") - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_change_choice_with_reject(self): # test that lifecycle works with a parent def select_green_and_cancel(tester): @@ -211,7 +203,7 @@ self.assertEqual(self.dialog.return_code, CANCEL) self.assertIsNone(self.dialog.choice) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_change_choice_with_close(self): # test that lifecycle works with a parent def select_green_and_close(tester): diff -Nru python-pyface-6.1.2/pyface/tests/test_splash_screen_log_handler.py python-pyface-7.4.0/pyface/tests/test_splash_screen_log_handler.py --- python-pyface-6.1.2/pyface/tests/test_splash_screen_log_handler.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_splash_screen_log_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,23 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from traits.api import Any, HasTraits, Unicode +from traits.api import HasTraits, Str from ..splash_screen_log_handler import SplashScreenLogHandler class DummySplashScreen(HasTraits): - text = Unicode(u'original') + text = Str("original") class DummyRecord(object): @@ -20,18 +29,17 @@ class TestSplashScreenLogHandler(unittest.TestCase): - def setUp(self): self.ss = DummySplashScreen() self.sslh = SplashScreenLogHandler(self.ss) def test_unicode_message(self): - self.assertEqual(self.ss.text, u'original') - message = u'G\u00f6khan' + self.assertEqual(self.ss.text, "original") + message = "G\u00f6khan" self.sslh.emit(DummyRecord(message)) - self.assertEqual(self.ss.text, message + u'...') + self.assertEqual(self.ss.text, message + "...") def test_ascii_message(self): - message = 'Goekhan' + message = "Goekhan" self.sslh.emit(DummyRecord(message)) - self.assertEqual(self.ss.text, message + u'...') + self.assertEqual(self.ss.text, message + "...") diff -Nru python-pyface-6.1.2/pyface/tests/test_splash_screen.py python-pyface-7.4.0/pyface/tests/test_splash_screen.py --- python-pyface-6.1.2/pyface/tests/test_splash_screen.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_splash_screen.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,25 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest -from ..gui import GUI from ..image_resource import ImageResource from ..splash_screen import SplashScreen from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestWindow(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -32,12 +40,12 @@ def test_open_close(self): # test that opening and closing works as expected - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() @@ -54,27 +62,27 @@ def test_image(self): # test that images work - self.window.image = ImageResource('core') - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + self.window.image = ImageResource("core") + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() def test_text(self): # test that images work self.window.text = "Splash screen" - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() @@ -82,15 +90,15 @@ # test that images work # XXX this throws a non-failing exception on wx # - probably the way the test is written. - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() with self.event_loop(): self.window.text = "Splash screen" - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() diff -Nru python-pyface-6.1.2/pyface/tests/test_split_application_window.py python-pyface-7.4.0/pyface/tests/test_split_application_window.py --- python-pyface-6.1.2/pyface/tests/test_split_application_window.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_split_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -6,11 +15,11 @@ from ..split_application_window import SplitApplicationWindow from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestSplitApplicationWindow(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -30,26 +39,26 @@ def test_open_close(self): # test that opening and closing works as expected - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() def test_horizontal_split(self): # test that horizontal split works - self.window.direction = 'horizontal' - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + self.window.direction = "horizontal" + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() @@ -57,25 +66,25 @@ # test that contents works self.window.lhs = HeadingText self.window.rhs = HeadingText - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() def test_ratio(self): # test that ratio split works self.window.ratio = 0.25 - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() diff -Nru python-pyface-6.1.2/pyface/tests/test_split_dialog.py python-pyface-7.4.0/pyface/tests/test_split_dialog.py --- python-pyface-6.1.2/pyface/tests/test_split_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_split_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -6,11 +15,11 @@ from ..split_dialog import SplitDialog from ..toolkit import toolkit_object -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestDialog(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -37,7 +46,7 @@ def test_horizontal(self): # test that horizontal split works - self.dialog.direction = 'horizontal' + self.dialog.direction = "horizontal" with self.event_loop(): self.dialog._create() with self.event_loop(): diff -Nru python-pyface-6.1.2/pyface/tests/test_split_panel.py python-pyface-7.4.0/pyface/tests/test_split_panel.py --- python-pyface-6.1.2/pyface/tests/test_split_panel.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_split_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -7,11 +16,11 @@ from ..toolkit import toolkit_object from ..window import Window -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestHeadingText(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -34,7 +43,26 @@ def test_lifecycle(self): # test that destroy works with self.event_loop(): - self.widget = SplitPanel(self.window.control) + with self.assertWarns(PendingDeprecationWarning): + self.widget = SplitPanel(self.window.control) + + self.assertIsNotNone(self.widget.control) + + with self.event_loop(): + self.widget.destroy() + + def test_two_stage_create(self): + # test that create=False works + self.widget = SplitPanel(create=False) + + self.assertIsNone(self.widget.control) + + with self.event_loop(): + self.widget.parent = self.window.control + self.widget.create() + + self.assertIsNotNone(self.widget.control) + with self.event_loop(): self.widget.destroy() @@ -42,7 +70,7 @@ # test that horizontal split works with self.event_loop(): self.widget = SplitPanel( - self.window.control, direction='horizontal' + self.window.control, direction="horizontal" ) with self.event_loop(): self.widget.destroy() diff -Nru python-pyface-6.1.2/pyface/tests/test_system_metrics.py python-pyface-7.4.0/pyface/tests/test_system_metrics.py --- python-pyface-6.1.2/pyface/tests/test_system_metrics.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_system_metrics.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -6,7 +15,6 @@ class TestSystemMetrics(unittest.TestCase): - def setUp(self): self.metrics = SystemMetrics() diff -Nru python-pyface-6.1.2/pyface/tests/test_toolkit.py python-pyface-7.4.0/pyface/tests/test_toolkit.py --- python-pyface-6.1.2/pyface/tests/test_toolkit.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_toolkit.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,53 @@ -import pkg_resources +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest +try: + # Starting Python 3.8, importlib.metadata is available in the Python + # standard library and starting Python 3.10, the "select" interface is + # available on EntryPoints. + from importlib.metadata import entry_points +except ImportError: + from importlib_metadata import entry_points + import pyface.toolkit class TestToolkit(unittest.TestCase): - def test_missing_import(self): # test that we get an undefined object if no toolkit implementation - cls = pyface.toolkit.toolkit_object('tests:Missing') + cls = pyface.toolkit.toolkit_object("tests:Missing") with self.assertRaises(NotImplementedError): - obj = cls() + cls() def test_bad_import(self): # test that we don't filter unrelated import errors with self.assertRaises(ImportError): - cls = pyface.toolkit.toolkit_object('tests.bad_import:Missing') + pyface.toolkit.toolkit_object("tests.bad_import:Missing") def test_core_plugins(self): # test that we can see appropriate core entrypoints - plugins = set(entry_point.name for entry_point in - pkg_resources.iter_entry_points('pyface.toolkits')) - self.assertLessEqual({'qt4', 'wx', 'qt', 'null'}, plugins) + # This compatibility layer can be removed when we drop support for + # Python < 3.10. Ref https://github.com/enthought/pyface/issues/999. + all_entry_points = entry_points() + if hasattr(all_entry_points, "select"): + plugins = { + ep.name + for ep in entry_points().select(group='pyface.toolkits') + } + else: + plugins = { + ep.name for ep in entry_points()['pyface.toolkits'] + } + self.assertLessEqual({"qt4", "wx", "qt", "null"}, plugins) def test_toolkit_object(self): # test that the Toolkit class works as expected @@ -30,7 +55,7 @@ from pyface.tests.test_new_toolkit.init import toolkit_object from pyface.tests.test_new_toolkit.widget import Widget as TestWidget - Widget = toolkit_object('widget:Widget') + Widget = toolkit_object("widget:Widget") self.assertEqual(Widget, TestWidget) @@ -41,9 +66,11 @@ toolkit_object = pyface.toolkit.toolkit_object old_packages = toolkit_object.packages - toolkit_object.packages = ['pyface.tests.test_new_toolkit'] + old_packages + toolkit_object.packages = [ + "pyface.tests.test_new_toolkit" + ] + old_packages try: - Widget = toolkit_object('widget:Widget') + Widget = toolkit_object("widget:Widget") self.assertEqual(Widget, TestWidget) finally: toolkit_object.packages = old_packages @@ -51,12 +78,14 @@ def test_toolkit_object_not_overriden(self): # test that the Toolkit class works when object not overridden toolkit_object = pyface.toolkit.toolkit_object - TestWindow = toolkit_object('window:Window') + TestWindow = toolkit_object("window:Window") old_packages = toolkit_object.packages - toolkit_object.packages = ['pyface.tests.test_new_toolkit'] + old_packages + toolkit_object.packages = [ + "pyface.tests.test_new_toolkit" + ] + old_packages try: - Window = toolkit_object('window:Window') + Window = toolkit_object("window:Window") self.assertEqual(Window, TestWindow) finally: toolkit_object.packages = old_packages diff -Nru python-pyface-6.1.2/pyface/tests/test_ui_traits.py python-pyface-7.4.0/pyface/tests/test_ui_traits.py --- python-pyface-6.1.2/pyface/tests/test_ui_traits.py 2019-06-18 11:33:39.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_ui_traits.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,40 +1,51 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2016, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought Developers -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import os import unittest -from traits.api import HasTraits, TraitError -from traits.testing.unittest_tools import UnittestTools +# importlib.resources is new in Python 3.7, and importlib.resources.files is +# new in Python 3.9, so for Python < 3.9 we must rely on the 3rd party +# importlib_resources package. try: - from traits.trait_handlers import ( - CALLABLE_AND_ARGS_DEFAULT_VALUE, - UNSPECIFIED_DEFAULT_VALUE - ) + from importlib.resources import files except ImportError: - UNSPECIFIED_DEFAULT_VALUE = -1 - CALLABLE_AND_ARGS_DEFAULT_VALUE = 7 + from importlib_resources import files + +try: + import PIL.Image +except ImportError: + PIL = None + +from traits.api import DefaultValue, HasTraits, TraitError +from traits.testing.optional_dependencies import numpy as np, requires_numpy +from traits.testing.api import UnittestTools -from ..i_image_resource import IImageResource +from ..color import Color +from ..font import Font from ..image_resource import ImageResource -from ..ui_traits import (Border, HasBorder, HasMargin, Image, Margin, - image_resource_cache, image_bitmap_cache) +from ..ui_traits import ( + Border, + HasBorder, + HasMargin, + Image, + Margin, + PyfaceColor, + PyfaceFont, + image_resource_cache, + image_bitmap_cache, +) -IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'images', 'core.png') +IMAGE_PATH = os.fspath(files("pyface.tests") / "images" / "core.png") class ImageClass(HasTraits): @@ -42,6 +53,16 @@ image = Image +class ColorClass(HasTraits): + + color = PyfaceColor() + + +class FontClass(HasTraits): + + font = PyfaceFont() + + class HasMarginClass(HasTraits): margin = HasMargin @@ -53,7 +74,6 @@ class TestImageTrait(unittest.TestCase, UnittestTools): - def setUp(self): # clear all cached images image_resource_cache.clear() @@ -69,38 +89,61 @@ def test_init_local_image(self): from pyface.image_resource import ImageResource - image_class = ImageClass(image=ImageResource('core.png')) + image_class = ImageClass(image=ImageResource("core.png")) self.assertIsInstance(image_class.image, ImageResource) - self.assertEqual(image_class.image.name, 'core.png') - self.assertEqual(image_class.image.absolute_path, - os.path.abspath(IMAGE_PATH)) + self.assertEqual(image_class.image.name, "core.png") + self.assertEqual( + image_class.image.absolute_path, os.path.abspath(IMAGE_PATH) + ) def test_init_pyface_image(self): from pyface.image_resource import ImageResource - image_class = ImageClass(image='about') - im = image_class.image.create_image() + image_class = ImageClass(image="about") + image_class.image.create_image() self.assertIsInstance(image_class.image, ImageResource) - self.assertEqual(image_class.image.name, 'about') + self.assertEqual(image_class.image.name, "about") self.assertIsNone(image_class.image._image_not_found) self.assertIsNotNone(image_class.image._ref.data) def test_init_pyface_image_library(self): from pyface.image_resource import ImageResource - image_class = ImageClass(image='@icons:dialog-warning') + image_class = ImageClass(image="@icons:dialog-warning") self.assertIsInstance(image_class.image, ImageResource) - self.assertEqual(image_class.image.name, 'dialog-warning.png') + self.assertEqual(image_class.image.name, "dialog-warning.png") self.assertIsNone(image_class.image._image_not_found) - self.assertEqual(image_class.image._ref.file_name, 'dialog-warning.png') - self.assertEqual(image_class.image._ref.volume_name, 'icons') + self.assertEqual( + image_class.image._ref.file_name, "dialog-warning.png" + ) + self.assertEqual(image_class.image._ref.volume_name, "icons") + @requires_numpy + def test_init_array_image(self): + from pyface.array_image import ArrayImage -class TestMargin(unittest.TestCase): + data = np.full((32, 64, 4), 0xee, dtype='uint8') + image = ArrayImage(data) + image_class = ImageClass(image=image) + + self.assertIsInstance(image_class.image, ArrayImage) + self.assertTrue((image_class.image.data == data).all()) + + @unittest.skipIf(PIL is None, "PIL/Pillow is not available") + def test_init_pil_image(self): + from pyface.pil_image import PILImage + + pil_image = PIL.Image.open(IMAGE_PATH) + image = PILImage(pil_image) + image_class = ImageClass(image=image) + + self.assertIsInstance(image_class.image, PILImage) + +class TestMargin(unittest.TestCase): def test_defaults(self): margin = Margin() self.assertEqual(margin.top, 0) @@ -130,8 +173,323 @@ self.assertEqual(margin.right, 2) -class TestHasMargin(unittest.TestCase, UnittestTools): +class TestPyfaceColor(unittest.TestCase): + + def test_init(self): + trait = PyfaceColor() + self.assertEqual(trait.default_value, (Color, (), {})) + self.assertEqual( + trait.default_value_type, + DefaultValue.callable_and_args, + ) + + def test_init_name(self): + trait = PyfaceColor("rebeccapurple") + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + + def test_init_hex(self): + trait = PyfaceColor("#663399ff") + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + + def test_init_color(self): + trait = PyfaceColor(Color(rgba=(0.4, 0.2, 0.6, 1.0))) + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + + def test_init_tuple(self): + trait = PyfaceColor((0.4, 0.2, 0.6, 1.0)) + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + + def test_init_list(self): + trait = PyfaceColor([0.4, 0.2, 0.6, 1.0]) + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + + @requires_numpy + def test_init_array(self): + trait = PyfaceColor(np.array([0.4, 0.2, 0.6, 1.0])) + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + @requires_numpy + def test_init_array_structured_dtype(self): + """ Test if "typical" RGBA structured array value works. """ + arr = np.array( + [(0.4, 0.2, 0.6, 1.0)], + dtype=np.dtype([ + ('red', float), + ('green', float), + ('blue', float), + ('alpha', float), + ]), + ) + trait = PyfaceColor(arr[0]) + self.assertEqual( + trait.default_value, + (Color, (), {'rgba': (0.4, 0.2, 0.6, 1.0)}) + ) + + def test_init_invalid(self): + with self.assertRaises(TraitError): + PyfaceColor((0.4, 0.2)) + + def test_validate_color(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + trait = PyfaceColor() + validated = trait.validate(None, None, color) + self.assertIs( + validated, color + ) + + def test_validate_name(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + trait = PyfaceColor() + validated = trait.validate(None, None, "rebeccapurple") + self.assertEqual( + validated, color + ) + + def test_validate_hex(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + trait = PyfaceColor() + validated = trait.validate(None, None, "#663399ff") + self.assertEqual( + validated, color + ) + + def test_validate_tuple(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + trait = PyfaceColor() + validated = trait.validate(None, None, (0.4, 0.2, 0.6, 0.8)) + self.assertEqual( + validated, color + ) + + def test_validate_list(self): + color = Color(rgba=(0.4, 0.2, 0.6, 0.8)) + trait = PyfaceColor() + validated = trait.validate(None, None, [0.4, 0.2, 0.6, 0.8]) + self.assertEqual( + validated, color + ) + + def test_validate_rgb_list(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + trait = PyfaceColor() + validated = trait.validate(None, None, [0.4, 0.2, 0.6]) + self.assertEqual( + validated, color + ) + + def test_validate_bad_string(self): + trait = PyfaceColor() + with self.assertRaises(TraitError): + trait.validate(None, None, "not a color") + + def test_validate_bad_object(self): + trait = PyfaceColor() + with self.assertRaises(TraitError): + trait.validate(None, None, object()) + + def test_info(self): + trait = PyfaceColor() + self.assertIsInstance(trait.info(), str) + + def test_default_trait(self): + color_class = ColorClass() + self.assertEqual(color_class.color, Color()) + + def test_set_color(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + color_class = ColorClass(color=color) + self.assertIs(color_class.color, color) + + def test_set_name(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + color_class = ColorClass(color="rebeccapurple") + self.assertEqual(color_class.color, color) + + def test_set_hex(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + color_class = ColorClass(color="#663399ff") + self.assertEqual(color_class.color, color) + + def test_set_tuple(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + color_class = ColorClass(color=(0.4, 0.2, 0.6, 1.0)) + self.assertEqual(color_class.color, color) + + def test_set_list(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + color_class = ColorClass(color=[0.4, 0.2, 0.6, 1.0]) + self.assertEqual(color_class.color, color) + + @requires_numpy + def test_set_array(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + color_class = ColorClass(color=np.array([0.4, 0.2, 0.6, 1.0])) + self.assertEqual(color_class.color, color) + + @requires_numpy + def test_set_structured_dtype(self): + color = Color(rgba=(0.4, 0.2, 0.6, 1.0)) + arr = np.array( + [(0.4, 0.2, 0.6, 1.0)], + dtype=np.dtype([ + ('red', float), + ('green', float), + ('blue', float), + ('alpha', float), + ]), + ) + color_class = ColorClass(color=arr[0]) + self.assertEqual(color_class.color, color) + + +class TestPyfaceFont(unittest.TestCase): + + def test_init(self): + trait = PyfaceFont() + self.assertEqual(trait.default_value, (Font, (), {})) + self.assertEqual( + trait.default_value_type, + DefaultValue.callable_and_args, + ) + + def test_init_empty_string(self): + trait = PyfaceFont("") + self.assertEqual( + trait.default_value, + ( + Font, + (), + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': 100, + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + ) + + def test_init_typical_string(self): + trait = PyfaceFont( + "10 pt bold condensed italic underline Helvetica sans-serif") + self.assertEqual( + trait.default_value, + ( + Font, + (), + { + 'family': ["helvetica", "sans-serif"], + 'size': 10.0, + 'weight': "bold", + 'stretch': 75.0, + 'style': "italic", + 'variants': set(), + 'decorations': {"underline"}, + }, + ) + ) + + def test_init_font(self): + font = Font( + family=["helvetica", "sans-serif"], + size=10.0, + weight="bold", + stretch=75.0, + style="italic", + variants=set(), + decorations={"underline"}, + ) + trait = PyfaceFont(font) + self.assertEqual( + trait.default_value, + ( + Font, + (), + { + 'family': ["helvetica", "sans-serif"], + 'size': 10.0, + 'weight': "bold", + 'stretch': 75.0, + 'style': "italic", + 'variants': set(), + 'decorations': {"underline"}, + }, + ) + ) + + def test_init_invalid(self): + with self.assertRaises(ValueError): + PyfaceFont(0) + + def test_set_empty_string(self): + font_class = FontClass() + font_class.font = "" + self.assertFontEqual(font_class.font, Font()) + + def test_set_typical_string(self): + font_class = FontClass() + font_class.font = "10 pt bold condensed italic underline Helvetica sans-serif" # noqa: E501 + self.assertFontEqual( + font_class.font, + Font( + family=["helvetica", "sans-serif"], + size=10.0, + weight="bold", + stretch=75.0, + style="italic", + variants=set(), + decorations={"underline"}, + ), + ) + + def test_set_font(self): + font_class = FontClass() + font = Font( + family=["helvetica", "sans-serif"], + size=10.0, + weight="bold", + stretch=75.0, + style="italic", + variants=set(), + decorations={"underline"}, + ) + font_class.font = font + self.assertIs(font_class.font, font) + + def test_set_failure(self): + font_class = FontClass() + + with self.assertRaises(TraitError): + font_class.font = None + + def assertFontEqual(self, font1, font2): + state1 = font1.trait_get(transient=lambda x: not x) + state2 = font2.trait_get(transient=lambda x: not x) + self.assertEqual(state1, state2) + + +class TestHasMargin(unittest.TestCase, UnittestTools): def test_defaults(self): has_margin = HasMarginClass() margin = has_margin.margin @@ -143,22 +501,16 @@ def test_unspecified_default(self): trait = HasMargin() - trait.default_value_type = UNSPECIFIED_DEFAULT_VALUE + trait.default_value_type = DefaultValue.unspecified (dvt, dv) = trait.get_default_value() - self.assertEqual(dvt, CALLABLE_AND_ARGS_DEFAULT_VALUE) + self.assertEqual(dvt, DefaultValue.callable_and_args) self.assertEqual( - dv, - ( - Margin, - (), - {'top': 0, 'bottom': 0, 'left': 0, 'right': 0}, - ) + dv, (Margin, (), {"top": 0, "bottom": 0, "left": 0, "right": 0}) ) def test_default_int(self): - class HasMarginClass(HasTraits): margin = HasMargin(4) @@ -172,7 +524,6 @@ self.assertEqual(margin.right, 4) def test_default_one_tuple(self): - class HasMarginClass(HasTraits): margin = HasMargin((4,)) @@ -186,7 +537,6 @@ self.assertEqual(margin.right, 4) def test_default_two_tuple(self): - class HasMarginClass(HasTraits): margin = HasMargin((4, 2)) @@ -200,7 +550,6 @@ self.assertEqual(margin.right, 4) def test_default_four_tuple(self): - class HasMarginClass(HasTraits): margin = HasMargin((4, 2, 3, 1)) @@ -272,7 +621,7 @@ def test_set_int(self): has_margin = HasMarginClass() - with self.assertTraitChanges(has_margin, 'margin', 1): + with self.assertTraitChanges(has_margin, "margin", 1): has_margin.margin = 4 margin = has_margin.margin @@ -283,7 +632,7 @@ def test_set_one_tuple(self): has_margin = HasMarginClass() - with self.assertTraitChanges(has_margin, 'margin', 1): + with self.assertTraitChanges(has_margin, "margin", 1): has_margin.margin = (4,) margin = has_margin.margin @@ -295,7 +644,7 @@ def test_set_two_tuple(self): has_margin = HasMarginClass() - with self.assertTraitChanges(has_margin, 'margin', 1): + with self.assertTraitChanges(has_margin, "margin", 1): has_margin.margin = (4, 2) margin = has_margin.margin @@ -307,7 +656,7 @@ def test_set_four_tuple(self): has_margin = HasMarginClass() - with self.assertTraitChanges(has_margin, 'margin', 1): + with self.assertTraitChanges(has_margin, "margin", 1): has_margin.margin = (4, 2, 3, 1) margin = has_margin.margin @@ -319,7 +668,7 @@ def test_set_margin(self): margin = Margin() has_margin = HasMarginClass() - with self.assertTraitChanges(has_margin, 'margin', 1): + with self.assertTraitChanges(has_margin, "margin", 1): has_margin.margin = margin self.assertEqual(has_margin.margin, margin) @@ -331,7 +680,6 @@ class TestBorder(unittest.TestCase): - def test_defaults(self): border = Border() self.assertEqual(border.top, 0) @@ -362,7 +710,6 @@ class TestHasBorder(unittest.TestCase, UnittestTools): - def test_defaults(self): has_border = HasBorderClass() border = has_border.border @@ -374,22 +721,16 @@ def test_unspecified_default(self): trait = HasBorder() - trait.default_value_type = UNSPECIFIED_DEFAULT_VALUE + trait.default_value_type = DefaultValue.unspecified (dvt, dv) = trait.get_default_value() - self.assertEqual(dvt, CALLABLE_AND_ARGS_DEFAULT_VALUE) + self.assertEqual(dvt, DefaultValue.callable_and_args) self.assertEqual( - dv, - ( - Border, - (), - {'top': 0, 'bottom': 0, 'left': 0, 'right': 0}, - ) + dv, (Border, (), {"top": 0, "bottom": 0, "left": 0, "right": 0}) ) def test_default_int(self): - class HasBorderClass(HasTraits): border = HasBorder(4) @@ -403,7 +744,6 @@ self.assertEqual(border.right, 4) def test_default_one_tuple(self): - class HasBorderClass(HasTraits): border = HasBorder((4,)) @@ -417,7 +757,6 @@ self.assertEqual(border.right, 4) def test_default_two_tuple(self): - class HasBorderClass(HasTraits): border = HasBorder((4, 2)) @@ -431,7 +770,6 @@ self.assertEqual(border.right, 4) def test_default_four_tuple(self): - class HasBorderClass(HasTraits): border = HasBorder((4, 2, 3, 1)) @@ -503,7 +841,7 @@ def test_set_int(self): has_border = HasBorderClass() - with self.assertTraitChanges(has_border, 'border', 1): + with self.assertTraitChanges(has_border, "border", 1): has_border.border = 4 border = has_border.border @@ -514,7 +852,7 @@ def test_set_one_tuple(self): has_border = HasBorderClass() - with self.assertTraitChanges(has_border, 'border', 1): + with self.assertTraitChanges(has_border, "border", 1): has_border.border = (4,) border = has_border.border @@ -526,7 +864,7 @@ def test_set_two_tuple(self): has_border = HasBorderClass() - with self.assertTraitChanges(has_border, 'border', 1): + with self.assertTraitChanges(has_border, "border", 1): has_border.border = (4, 2) border = has_border.border @@ -538,7 +876,7 @@ def test_set_four_tuple(self): has_border = HasBorderClass() - with self.assertTraitChanges(has_border, 'border', 1): + with self.assertTraitChanges(has_border, "border", 1): has_border.border = (4, 2, 3, 1) border = has_border.border @@ -550,7 +888,7 @@ def test_set_border(self): border = Border() has_border = HasBorderClass() - with self.assertTraitChanges(has_border, 'border', 1): + with self.assertTraitChanges(has_border, "border", 1): has_border.border = border self.assertEqual(has_border.border, border) diff -Nru python-pyface-6.1.2/pyface/tests/test_widget.py python-pyface-7.4.0/pyface/tests/test_widget.py --- python-pyface-6.1.2/pyface/tests/test_widget.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,33 +1,76 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import sys import unittest -from traits.testing.unittest_tools import UnittestTools +from traits.api import Instance +from traits.testing.api import UnittestTools +from pyface.testing.widget_mixin import WidgetMixin +from ..application_window import ApplicationWindow from ..toolkit import toolkit_object from ..widget import Widget -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" + +is_qt = (toolkit_object.toolkit in {"qt4", "qt"}) +is_linux = (sys.platform == "linux") +is_mac = (sys.platform == "darwin") class ConcreteWidget(Widget): def _create_control(self, parent): - if toolkit_object.toolkit == 'wx': + if toolkit_object.toolkit == "wx": import wx + control = wx.Window(parent) control.Enable(self.enabled) control.Show(self.visible) - elif toolkit_object.toolkit == 'qt4': + elif toolkit_object.toolkit in {"qt4", "qt"}: from pyface.qt import QtGui + from pyface.qt.QtCore import Qt + control = QtGui.QWidget(parent) control.setEnabled(self.enabled) control.setVisible(self.visible) + control.setFocusPolicy(Qt.FocusPolicy.StrongFocus) else: control = None return control +class MainWindow(ApplicationWindow): + + widget = Instance(ConcreteWidget) + + def _create_contents(self, parent): + """ Create and return the window's contents. + Parameters + ---------- + parent : toolkit control + The window's toolkit control to be used as the parent for + widgets in the contents. + + Returns + ------- + control : toolkit control + A control to be used for contents of the window. + """ + self.widget = ConcreteWidget(parent=parent) + self.widget._create() + return self.widget.control + + class TestWidget(unittest.TestCase, UnittestTools): def setUp(self): self.widget = Widget() @@ -42,6 +85,11 @@ def test_create(self): # create is not Implemented with self.assertRaises(NotImplementedError): + self.widget.create() + + def test__create(self): + # _create is not Implemented + with self.assertRaises(NotImplementedError): self.widget._create() def test_destroy(self): @@ -49,31 +97,31 @@ self.widget.destroy() def test_show(self): - with self.assertTraitChanges(self.widget, 'visible', count=1): + with self.assertTraitChanges(self.widget, "visible", count=1): self.widget.show(False) self.assertFalse(self.widget.visible) def test_visible(self): - with self.assertTraitChanges(self.widget, 'visible', count=1): + with self.assertTraitChanges(self.widget, "visible", count=1): self.widget.visible = False self.assertFalse(self.widget.visible) def test_enable(self): - with self.assertTraitChanges(self.widget, 'enabled', count=1): + with self.assertTraitChanges(self.widget, "enabled", count=1): self.widget.enable(False) self.assertFalse(self.widget.enabled) def test_enabled(self): - with self.assertTraitChanges(self.widget, 'enabled', count=1): + with self.assertTraitChanges(self.widget, "enabled", count=1): self.widget.enabled = False self.assertFalse(self.widget.enabled) -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestConcreteWidget(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -88,7 +136,7 @@ def test_lifecycle(self): with self.event_loop(): - self.widget._create() + self.widget.create() with self.event_loop(): self.widget.destroy() @@ -96,16 +144,16 @@ self.widget.visible = False self.widget.enabled = False with self.event_loop(): - self.widget._create() + self.widget.create() self.assertFalse(self.widget.control.isVisible()) self.assertFalse(self.widget.control.isEnabled()) def test_show(self): with self.event_loop(): - self.widget._create() + self.widget.create() - with self.assertTraitChanges(self.widget, 'visible', count=1): + with self.assertTraitChanges(self.widget, "visible", count=1): with self.event_loop(): self.widget.show(False) @@ -113,19 +161,152 @@ def test_visible(self): with self.event_loop(): - self.widget._create() + self.widget.create() - with self.assertTraitChanges(self.widget, 'visible', count=1): + with self.assertTraitChanges(self.widget, "visible", count=1): with self.event_loop(): self.widget.visible = False self.assertFalse(self.widget.control.isVisible()) + def test_contents_visible(self): + window = MainWindow() + window._create() + + try: + with self.event_loop(): + window.open() + + # widget visible trait stays True when parent is hidden + with self.assertTraitDoesNotChange(window.widget, "visible"): + with self.event_loop(): + window.visible = False + + # widget visible trait stays True when parent is shown + with self.assertTraitDoesNotChange(window.widget, "visible"): + with self.event_loop(): + window.visible = True + finally: + window.destroy() + + def test_contents_hidden(self): + window = MainWindow() + window._create() + + try: + with self.event_loop(): + window.open() + window.widget.visible = False + + # widget visible trait stays False when parent is hidden + with self.assertTraitDoesNotChange(window.widget, "visible"): + with self.event_loop(): + window.visible = False + + # widget visible trait stays False when parent is shown + with self.assertTraitDoesNotChange(window.widget, "visible"): + with self.event_loop(): + window.visible = True + finally: + window.destroy() + + @unittest.skipUnless(is_qt, "Qt-specific test of hidden state") + def test_contents_hide_external_change(self): + window = MainWindow() + window._create() + + try: + with self.event_loop(): + window.open() + + # widget visibile trait stays True when parent is hidden + with self.assertTraitDoesNotChange(window.widget, "visible"): + with self.event_loop(): + window.visible = False + + self.assertFalse(window.widget.control.isVisible()) + self.assertFalse(window.widget.control.isHidden()) + + # widget visibile trait becomes False when widget is hidden + with self.assertTraitChanges(window.widget, "visible"): + with self.event_loop(): + window.widget.control.hide() + + self.assertFalse(window.widget.visible) + self.assertFalse(window.widget.control.isVisible()) + self.assertTrue(window.widget.control.isHidden()) + + # widget visibile trait stays False when parent is shown + with self.assertTraitDoesNotChange(window.widget, "visible"): + with self.event_loop(): + window.visible = True + + self.assertFalse(window.widget.control.isVisible()) + self.assertTrue(window.widget.control.isHidden()) + finally: + window.destroy() + + @unittest.skipUnless(is_qt, "Qt-specific test of hidden state") + def test_show_widget_with_parent_is_invisible_qt(self): + # Test setting the widget visible to true when its parent visibility + # is false. + window = MainWindow() + window._create() + + try: + # given + with self.event_loop(): + window.open() + window.widget.visible = False + + with self.event_loop(): + window.visible = False + + # when + with self.event_loop(): + window.widget.visible = True + + # then + self.assertTrue(window.widget.visible) + self.assertFalse(window.widget.control.isVisible()) + self.assertFalse(window.widget.control.isHidden()) + + finally: + window.destroy() + + @unittest.skipUnless(is_qt, "Qt-specific test of hidden state") + def test_show_widget_then_parent_is_invisible_qt(self): + # Test showing the widget when the parent is also visible, and then + # make the parent invisible + window = MainWindow() + window._create() + + try: + # given + with self.event_loop(): + window.open() + window.visible = True + + with self.event_loop(): + window.widget.visible = True + + # when + with self.event_loop(): + window.visible = False + + # then + self.assertTrue(window.widget.visible) + self.assertFalse(window.widget.control.isVisible()) + self.assertFalse(window.widget.control.isHidden()) + + finally: + window.destroy() + def test_enable(self): with self.event_loop(): - self.widget._create() + self.widget.create() - with self.assertTraitChanges(self.widget, 'enabled', count=1): + with self.assertTraitChanges(self.widget, "enabled", count=1): with self.event_loop(): self.widget.enable(False) @@ -133,10 +314,31 @@ def test_enabled(self): with self.event_loop(): - self.widget._create() + self.widget.create() - with self.assertTraitChanges(self.widget, 'enabled', count=1): + with self.assertTraitChanges(self.widget, "enabled", count=1): with self.event_loop(): self.widget.enabled = False self.assertFalse(self.widget.control.isEnabled()) + + @unittest.skipUnless( + is_mac, + "Broken on Linux and Windows", + ) + def test_focus(self): + with self.event_loop(): + self.widget.create() + + self.assertFalse(self.widget.has_focus()) + + with self.event_loop(): + self.widget.focus() + + self.assertTrue(self.widget.has_focus()) + + +class TestWidgetCommon(WidgetMixin, unittest.TestCase): + + def _create_widget(self): + return ConcreteWidget(parent=self.parent.control, tooltip='Dummy') diff -Nru python-pyface-6.1.2/pyface/tests/test_window.py python-pyface-7.4.0/pyface/tests/test_window.py --- python-pyface-6.1.2/pyface/tests/test_window.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/tests/test_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import platform import unittest @@ -7,23 +16,23 @@ from ..toolkit import toolkit_object from ..window import Window -is_qt = toolkit_object.toolkit == 'qt4' +is_qt = toolkit_object.toolkit == "qt4" if is_qt: from pyface.qt import qt_api -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" ModalDialogTester = toolkit_object( - 'util.modal_dialog_tester:ModalDialogTester' + "util.modal_dialog_tester:ModalDialogTester" ) -no_modal_dialog_tester = (ModalDialogTester.__name__ == 'Unimplemented') +no_modal_dialog_tester = ModalDialogTester.__name__ == "Unimplemented" -is_pyqt5 = (is_qt and qt_api == 'pyqt5') -is_pyqt4_linux = (is_qt and qt_api == 'pyqt' and platform.system() == 'Linux') +is_pyqt5 = is_qt and qt_api == "pyqt5" +is_pyqt4_linux = is_qt and qt_api == "pyqt" and platform.system() == "Linux" -@unittest.skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@unittest.skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestWindow(unittest.TestCase, GuiTestAssistant): def setUp(self): GuiTestAssistant.setUp(self) @@ -43,13 +52,13 @@ def test_open_close(self): # test that opening and closing works as expected - with self.assertTraitChanges(self.window, 'opening', count=1): - with self.assertTraitChanges(self.window, 'opened', count=1): + with self.assertTraitChanges(self.window, "opening", count=1): + with self.assertTraitChanges(self.window, "opened", count=1): with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'closing', count=1): - with self.assertTraitChanges(self.window, 'closed', count=1): + with self.assertTraitChanges(self.window, "closing", count=1): + with self.assertTraitChanges(self.window, "closed", count=1): with self.event_loop(): self.window.close() @@ -130,7 +139,7 @@ with self.event_loop(): self.window.visible = False - with self.assertTraitChanges(self.window, 'visible', count=1): + with self.assertTraitChanges(self.window, "visible", count=1): with self.event_loop(): self.window.control.show() @@ -140,19 +149,19 @@ with self.event_loop(): self.window.open() - with self.assertTraitChanges(self.window, 'visible', count=1): + with self.assertTraitChanges(self.window, "visible", count=1): with self.event_loop(): self.window.control.hide() self.assertFalse(self.window.visible) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Confirmation dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) def test_confirm_reject(self): # test that cancel works as expected @@ -163,13 +172,13 @@ self.assertEqual(tester.result, CANCEL) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Confirmation dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) def test_confirm_yes(self): # test that yes works as expected @@ -178,13 +187,13 @@ self.assertEqual(tester.result, YES) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Confirmation dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) def test_confirm_no(self): # test that no works as expected @@ -193,13 +202,13 @@ self.assertEqual(tester.result, NO) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Confirmation dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Confirmation dialog click tests don't work reliably on linux. Issue #282." + "Confirmation dialog click tests don't work reliably on linux. Issue #282.", ) def test_confirm_cancel(self): # test that cncel works as expected @@ -210,47 +219,47 @@ self.assertEqual(tester.result, CANCEL) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_information_accept(self): self._check_message_dialog_accept(self.window.information) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Message dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) def test_information_ok(self): self._check_message_dialog_ok(self.window.information) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_warning_accept(self): self._check_message_dialog_accept(self.window.warning) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Message dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) def test_warning_ok(self): self._check_message_dialog_ok(self.window.warning) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") def test_error_accept(self): self._check_message_dialog_accept(self.window.error) - @unittest.skipIf(no_modal_dialog_tester, 'ModalDialogTester unavailable') + @unittest.skipIf(no_modal_dialog_tester, "ModalDialogTester unavailable") @unittest.skipIf( is_pyqt5, "Message dialog click tests don't work on pyqt5." ) @unittest.skipIf( is_pyqt4_linux, - "Message dialog click tests don't work reliably on linux. Issue #282." + "Message dialog click tests don't work reliably on linux. Issue #282.", ) def test_error_ok(self): self._check_message_dialog_ok(self.window.error) @@ -269,9 +278,9 @@ def _setup_tester(self, method): kwargs = { - 'title': 'Title', - 'detail': 'Detail', - 'informative': 'Informative' + "title": "Title", + "detail": "Detail", + "informative": "Informative", } tester = ModalDialogTester(lambda: method("message", **kwargs)) return tester diff -Nru python-pyface-6.1.2/pyface/timer/api.py python-pyface-7.4.0/pyface/timer/api.py --- python-pyface-6.1.2/pyface/timer/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/timer/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,31 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.timer`` subpackage. + +- :func:`~.do_later` +- :func:`~.do_after` +- :class:`~.DoLaterTimer` +- :class:`~.CallbackTimer` +- :class:`~.EventTimer` +- :class:`~.Timer` + +Interfaces +---------- +- :class:`~.ICallbackTimer` +- :class:`~.IEventTimer` +- :class:`~.ITimer` -from __future__ import absolute_import +""" from .do_later import do_later, do_after, DoLaterTimer from .i_timer import ICallbackTimer, IEventTimer, ITimer diff -Nru python-pyface-6.1.2/pyface/timer/do_later.py python-pyface-7.4.0/pyface/timer/do_later.py --- python-pyface-6.1.2/pyface/timer/do_later.py 2019-06-03 10:58:47.000000000 +0000 +++ python-pyface-7.4.0/pyface/timer/do_later.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2005-18, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. @@ -25,7 +26,7 @@ def __init__(self, interval, callable, args, kw_args): # Adapt the old DoLaterTimer initializer to the Timer initializer. - super(DoLaterTimer, self).__init__(interval, callable, *args, **kw_args) + super().__init__(interval, callable, *args, **kw_args) def do_later(callable, *args, **kwargs): @@ -35,14 +36,11 @@ ---------- callable : callable The callable to call in 50ms time. - *args, **kwargs : + args, kwargs : tuple, dict Arguments to be passed through to the callable. """ return CallbackTimer.single_shot( - interval=0.05, - callback=callable, - args=args, - kwargs=kwargs, + interval=0.05, callback=callable, args=args, kwargs=kwargs ) @@ -55,12 +53,12 @@ The time interval in milliseconds to wait before calling. callable : callable The callable to call. - *args, **kwargs : + args + Positional arguments to be passed through to the callable. + kwargs + Keyword arguments to be passed through to the callable. Arguments to be passed through to the callable. """ return CallbackTimer.single_shot( - interval=interval / 1000.0, - callback=callable, - args=args, - kwargs=kwargs, + interval=interval / 1000.0, callback=callable, args=args, kwargs=kwargs ) diff -Nru python-pyface-6.1.2/pyface/timer/i_timer.py python-pyface-7.4.0/pyface/timer/i_timer.py --- python-pyface-6.1.2/pyface/timer/i_timer.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/timer/i_timer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,12 @@ -# Copyright (c) 2018, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! -# -# Author: Corran Webster +# Thanks for using Enthought open source! """ Interfaces and base classes for cross-toolkit timers @@ -17,19 +15,26 @@ back-end, and mixins that provide additional capabilities. """ from abc import abstractmethod -import sys import time from traits.api import ( - ABCHasTraits, Bool, Callable, Dict, Either, Event, Float, HasTraits, Int, - Interface, Property, Range, Tuple, provides + ABCHasTraits, + Bool, + Callable, + Dict, + Event, + Float, + HasTraits, + Int, + Interface, + Property, + Range, + Tuple, + provides, + Union, ) -if sys.version_info[:2] < (3, 3): - import timeit - perf_counter = timeit.default_timer -else: - perf_counter = time.perf_counter +perf_counter = time.perf_counter class ITimer(Interface): @@ -45,13 +50,13 @@ interval = Range(low=0.0) #: The number of times to repeat the callback, or None if no limit. - repeat = Either(None, Int) + repeat = Union(None, Int) #: The maximum length of time to run in seconds, or None if no limit. - expire = Either(None, Float) + expire = Union(None, Float) #: Whether or not the timer is currently running. - active = Bool + active = Bool() # ------------------------------------------------------------------------- # ITimer interface @@ -88,7 +93,7 @@ # IEventTimer interface -------------------------------------------------- #: A traits Event to fire when the callback happens. - timeout = Event + timeout = Event() class ICallbackTimer(ITimer): @@ -100,10 +105,10 @@ callback = Callable #: Positional arguments to give the callback. - args = Tuple + args = Tuple() #: Keyword arguments to give the callback. - kwargs = Dict + kwargs = Dict() @provides(ITimer) @@ -126,21 +131,21 @@ interval = Range(low=0.0, value=0.05) #: The number of times to repeat the callback, or None if no limit. - repeat = Either(None, Int) + repeat = Union(None, Int) #: The maximum length of time to run in seconds, or None if no limit. - expire = Either(None, Float) + expire = Union(None, Float) #: Property that controls the state of the timer. - active = Property(Bool, depends_on='_active') + active = Property(Bool, observe="_active") # Private interface ------------------------------------------------------ #: Whether or not the timer is currently running. - _active = Bool + _active = Bool() #: The most recent start time. - _start_time = Float + _start_time = Float() # ------------------------------------------------------------------------- # ITimer interface @@ -254,7 +259,7 @@ # IEventTimer interface -------------------------------------------------- #: A traits Event to fire when the callback happens. - timeout = Event + timeout = Event() # ------------------------------------------------------------------------- # ITimer interface @@ -278,10 +283,10 @@ callback = Callable #: Positional arguments to give the callback. - args = Tuple + args = Tuple() #: Keyword arguments to give the callback. - kwargs = Dict + kwargs = Dict() # ------------------------------------------------------------------------- # ITimer interface diff -Nru python-pyface-6.1.2/pyface/timer/tests/test_do_later.py python-pyface-7.4.0/pyface/timer/tests/test_do_later.py --- python-pyface-6.1.2/pyface/timer/tests/test_do_later.py 2019-06-20 08:48:12.000000000 +0000 +++ python-pyface-7.4.0/pyface/timer/tests/test_do_later.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,22 @@ -from __future__ import print_function +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + -import time from unittest import TestCase, skipIf from pyface.toolkit import toolkit_object from ..i_timer import perf_counter from ..do_later import DoLaterTimer, do_after, do_later -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" class ConditionHandler(object): @@ -21,7 +29,7 @@ self.count += 1 -@skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestDoLaterTimer(TestCase, GuiTestAssistant): """ Test the DoLaterTimer. """ @@ -56,13 +64,12 @@ expected_time, handler.times[0], "Expected call after {} seconds, took {} seconds)".format( - expected_length, - handler.times[0] - start_time - ) + expected_length, handler.times[0] - start_time + ), ) -@skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestDoLater(TestCase, GuiTestAssistant): """ Test do_later. """ @@ -88,7 +95,7 @@ self.assertEqual(handler.count, 1) -@skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestDoAfter(TestCase, GuiTestAssistant): """ Test do_after. """ @@ -123,7 +130,6 @@ expected_time, handler.times[0], "Expected call after {} seconds, took {} seconds)".format( - expected_length, - handler.times[0] - start_time - ) + expected_length, handler.times[0] - start_time + ), ) diff -Nru python-pyface-6.1.2/pyface/timer/tests/test_timer.py python-pyface-7.4.0/pyface/timer/tests/test_timer.py --- python-pyface-6.1.2/pyface/timer/tests/test_timer.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/timer/tests/test_timer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import print_function +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import time from unittest import TestCase, skipIf @@ -6,8 +15,8 @@ from ..i_timer import perf_counter from ..timer import CallbackTimer, EventTimer, Timer -GuiTestAssistant = toolkit_object('util.gui_test_assistant:GuiTestAssistant') -no_gui_test_assistant = (GuiTestAssistant.__name__ == 'Unimplemented') +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") +no_gui_test_assistant = GuiTestAssistant.__name__ == "Unimplemented" class ConditionHandler(object): @@ -16,7 +25,7 @@ self.times = [] self.called = False - def callback(self): + def callback(self, event=None): self.times.append(perf_counter()) self.count += 1 self.called = True @@ -28,7 +37,7 @@ return lambda: self.count >= repeat -@skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestEventTimer(TestCase, GuiTestAssistant): """ Test the EventTimer. """ @@ -65,7 +74,7 @@ def test_single_shot_method(self): timer = EventTimer.single_shot() handler = ConditionHandler() - timer.on_trait_change(handler.callback, 'timeout') + timer.observe(handler.callback, "timeout") try: self.assertTrue(timer.active) self.event_loop_helper.event_loop_until_condition( @@ -94,7 +103,7 @@ def test_timeout_event(self): timer = EventTimer() handler = ConditionHandler() - timer.on_trait_change(handler.callback, 'timeout') + timer.observe(handler.callback, "timeout") timer.start() try: @@ -107,7 +116,7 @@ def test_repeat(self): timer = EventTimer(repeat=4) handler = ConditionHandler() - timer.on_trait_change(handler.callback, 'timeout') + timer.observe(handler.callback, "timeout") timer.start() try: @@ -122,7 +131,7 @@ def test_interval(self): timer = EventTimer(repeat=4, interval=0.1) handler = ConditionHandler() - timer.on_trait_change(handler.callback, 'timeout') + timer.observe(handler.callback, "timeout") timer.start() try: @@ -153,7 +162,7 @@ def test_expire(self): timer = EventTimer(expire=1.0, interval=0.1) handler = ConditionHandler() - timer.on_trait_change(handler.callback, 'timeout') + timer.observe(handler.callback, "timeout") timer.start() try: @@ -178,7 +187,7 @@ ) -@skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestCallbackTimer(TestCase, GuiTestAssistant): """ Test the CallbackTimer. """ @@ -347,7 +356,7 @@ timer.stop() -@skipIf(no_gui_test_assistant, 'No GuiTestAssistant') +@skipIf(no_gui_test_assistant, "No GuiTestAssistant") class TestTimer(TestCase, GuiTestAssistant): """ Test the CallbackTimer. """ @@ -413,9 +422,14 @@ self.assertFalse(timer.IsRunning()) - self.assertEqual(handler.count, 4) - - expected_times = [start_time + 0.2 * i + 0.2 for i in range(4)] + # The callback may be called more than 4 times depending + # on the order when condition timer in event_loop_unit_condition + # is called relative to the Timer here. Timer accuracy also depends + # on whether the event loop is interrupted by the system. + # The objective is that the timer should not be called too frequently. + expected_times = [ + start_time + 0.2 * i + 0.2 for i in range(handler.count) + ] self.assertTrue( all( @@ -423,7 +437,6 @@ for actual, expected in zip(handler.times, expected_times) ), "Expected calls after {} times, got {})".format( - expected_times, - handler.times - ) + expected_times, handler.times + ), ) diff -Nru python-pyface-6.1.2/pyface/timer/timer.py python-pyface-7.4.0/pyface/timer/timer.py --- python-pyface-6.1.2/pyface/timer/timer.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/timer/timer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,12 @@ -# Author: Prabhu Ramachandran -# Copyright (c) 2006-2018, Enthought, Inc. -# License: BSD Style. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Event-loop based timers that perform actions periodically. @@ -13,7 +19,7 @@ from pyface.toolkit import toolkit_object from pyface.timer.i_timer import MCallbackTimer, MEventTimer -PyfaceTimer = toolkit_object('timer.timer:PyfaceTimer') +PyfaceTimer = toolkit_object("timer.timer:PyfaceTimer") class EventTimer(MEventTimer, PyfaceTimer): @@ -34,11 +40,8 @@ arguments and keyword args after every `millisecs` (milliseconds). """ interval = millisecs / 1000.0 - super(Timer, self).__init__( - interval=interval, - callback=callable, - args=args, - kwargs=kwargs, + super().__init__( + interval=interval, callback=callable, args=args, kwargs=kwargs ) self.start() diff -Nru python-pyface-6.1.2/pyface/toolkit.py python-pyface-7.4.0/pyface/toolkit.py --- python-pyface-6.1.2/pyface/toolkit.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/toolkit.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited -# All rights reserved. -# -# Copyright (c) 2015-2017, Enthought, Inc. +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! -# -#------------------------------------------------------------------------------ + """ This module provides the toolkit object for the current backend toolkit -See :py_func:`pyface.base_toolkit.find_toolkit` for details on the loading +See :py:func:`pyface.base_toolkit.find_toolkit` for details on the loading algorithm. """ @@ -24,4 +20,4 @@ # The toolkit function. -toolkit = toolkit_object = find_toolkit('pyface.toolkits') +toolkit = toolkit_object = find_toolkit("pyface.toolkits") diff -Nru python-pyface-6.1.2/pyface/tree/api.py python-pyface-7.4.0/pyface/tree/api.py --- python-pyface-6.1.2/pyface/tree/api.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,34 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + + +""" + +API for the ``pyface.tree`` subpackage. + +- :class:`~.NodeEvent` +- :class:`~.NodeMonitor` +- :class:`~.NodeManager` +- :class:`~.NodeTree` +- :class:`~.NodeTreeModel` +- :class:`~.NodeType` +- :class:`~.TraitDictNodeType` +- :class:`~.TraitListNodeType` +- :class:`~.TreeModel` + +Note that the following classes are only available in the Wx toolkit at the +moment. + +- :class:`~.Tree`. -from __future__ import absolute_import +""" from .node_event import NodeEvent from .node_monitor import NodeMonitor diff -Nru python-pyface-6.1.2/pyface/tree/images/image_LICENSE.txt python-pyface-7.4.0/pyface/tree/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/tree/images/image_LICENSE.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -GV (Gael Varoquaux) Public Domain N/A -Nuvola LGPL image_LICENSE_Nuvola.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/tree/images: - closed_folder.png | Nuvola - document.png | GV - open_folder.png | Nuvola diff -Nru python-pyface-6.1.2/pyface/tree/node_event.py python-pyface-7.4.0/pyface/tree/node_event.py --- python-pyface-6.1.2/pyface/tree/node_event.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/node_event.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The event fired by the tree models/node monitors etc. """ -# Enthought library imports. from traits.api import Any, HasTraits, Int, List @@ -23,15 +19,13 @@ """ The event fired by the tree models/node monitors etc. """ # The node that has changed. - node = Any + node = Any() # The nodes (if any) that have been inserted/removed/changed. - children = List + children = List() # The nodes (if any) that have been replaced. - old_children = List + old_children = List() # The starting index for nodes that have been inserted. - index = Int - -#### EOF ###################################################################### + index = Int() diff -Nru python-pyface-6.1.2/pyface/tree/node_manager.py python-pyface-7.4.0/pyface/tree/node_manager.py --- python-pyface-6.1.2/pyface/tree/node_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/node_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The node manager looks after a collection of node types. """ -# Standard library imports. + import logging -# Enthought library imports. -from traits.api import HasPrivateTraits, List -# Local imports +from traits.api import HasPrivateTraits, List, observe + + from .node_type import NodeType @@ -30,36 +27,36 @@ class NodeManager(HasPrivateTraits): """ The node manager looks after a collection of node types. """ - #### 'NodeManager' interface ########################################## + # 'NodeManager' interface -----------------------------------------# # All registered node types. node_types = List(NodeType) # fixme: Where should the system actions go? The node tree, the node # tree model, here?!? - system_actions = List + system_actions = List() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Creates a new tree model. """ # Base class constructor. - super(NodeManager, self).__init__(**traits) + super().__init__(**traits) # This saves looking up a node's type every time. If we ever have # nodes that change type dynamically then we will obviously have to # re-think this (although we should probably re-think dynamic type # changes first ;^). - self._node_to_type_map = {} # { Any node : NodeType node_type } + self._node_to_type_map = {} # { Any node : NodeType node_type } return - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ # fixme: This is the only API call that we currently have that manipulates # the manager's node types. Should we make the 'node_types' list @@ -70,8 +67,6 @@ node_type.node_manager = self self.node_types.append(node_type) - return - def get_node_type(self, node): """ Returns the node's type. @@ -101,7 +96,7 @@ node_type = None if node_type is None: - logger.warn('no node type for %s' % str(node)) + logger.warning("no node type for %s" % str(node)) return node_type @@ -123,16 +118,15 @@ return key - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - def _node_types_changed(self, new): + @observe("node_types") + def _update_node_manager_on_new_node_types(self, event): """ Called when the entire list of node types has been changed. """ - + new = event.new for node_type in new: node_type.node_manager = self return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/node_monitor.py python-pyface-7.4.0/pyface/tree/node_monitor.py --- python-pyface-6.1.2/pyface/tree/node_monitor.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/node_monitor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A monitor for appearance and structural changes to a node. """ -# Standard library imports. + import logging -# Enthought library imports. + from traits.api import Any, Event, HasTraits -# Local imports. + from .node_event import NodeEvent @@ -30,12 +27,12 @@ class NodeMonitor(HasTraits): """ A monitor for appearance and structural changes to a node. """ - #### 'NodeMonitor' interface ############################################## + # 'NodeMonitor' interface ---------------------------------------------# # The node that we are monitoring. - node = Any + node = Any() - #### Events #### + # Events ---- # Fired when child nodes in the node that we are monitoring have changed in # some way that affects their appearance but NOT their structure. @@ -58,12 +55,11 @@ # changes/inserts/removals). structure_changed = Event(NodeEvent) - - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeMonitor' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### public methods ####################################################### + # public methods ------------------------------------------------------- def start(self): """ Start listening to changes to the node. """ @@ -71,24 +67,17 @@ if self.node.obj is not None: self._setup_trait_change_handlers(self.node.obj) - return - - def stop(self): """ Stop listening to changes to the node. """ if self.node.obj is not None: self._setup_trait_change_handlers(self.node.obj, remove=True) - return - def fire_nodes_changed(self, children=[]): """ Fires the nodes changed event. """ self.nodes_changed = NodeEvent(node=self.node, children=children) - return - def fire_nodes_inserted(self, children, index=-1): """ Fires the nodes inserted event. @@ -102,15 +91,11 @@ node=self.node, children=children, index=index ) - return - def fire_nodes_removed(self, children): """ Fires the nodes removed event. """ self.nodes_removed = NodeEvent(node=self.node, children=children) - return - def fire_nodes_replaced(self, old_children, new_children): """ Fires the nodes replaced event. """ @@ -118,8 +103,6 @@ node=self.node, old_children=old_children, children=new_children ) - return - def fire_structure_changed(self): """ Fires the structure changed event. """ @@ -127,18 +110,18 @@ return - - #### protected methods #################################################### + # protected methods ---------------------------------------------------- def _setup_trait_change_handlers(self, obj, remove=False): """ Add or remove trait change handlers to/from a node. """ - logger.debug('%s trait listeners on (%s) in NodeMonitor (%s)', - (remove and 'Removing' or 'Adding'), obj, self) + logger.debug( + "%s trait listeners on (%s) in NodeMonitor (%s)", + (remove and "Removing" or "Adding"), + obj, + self, + ) - pass # derived classes should do something here! + pass # derived classes should do something here! return - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/node_tree_model.py python-pyface-7.4.0/pyface/tree/node_tree_model.py --- python-pyface-6.1.2/pyface/tree/node_tree_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/node_tree_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The model for a tree control with extensible node types. """ -# Enthought library imports. from traits.api import Dict, Instance -# Local imports. + from .node_manager import NodeManager from .tree_model import TreeModel @@ -25,19 +21,19 @@ class NodeTreeModel(TreeModel): """ The model for a tree control with extensible node types. """ - #### 'NodeTreeModel' interface ############################################ + # 'NodeTreeModel' interface -------------------------------------------- # The node manager looks after all node types. node_manager = Instance(NodeManager, ()) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Node monitors. - _monitors = Dict + _monitors = Dict() - ########################################################################### + # ------------------------------------------------------------------------ # 'TreeModel' interface. - ########################################################################### + # ------------------------------------------------------------------------ def has_children(self, node): """ Returns True if a node has children, otherwise False. @@ -112,8 +108,6 @@ node_type.drop(node, data) - return - def get_image(self, node, selected, expanded): """ Returns the label image for a node. @@ -219,8 +213,6 @@ self._start_monitor(monitor) self._monitors[self.node_manager.get_key(node)] = monitor - return - def remove_listener(self, node): """ Removes a listener for changes to a node. """ @@ -233,9 +225,9 @@ return - ######################################################################### + # ------------------------------------------------------------------------ # 'NodeTreeModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_context_menu(self, node): """ Returns the context menu for a node. """ @@ -245,119 +237,88 @@ return node_type.get_context_menu(node) - ########################################################################### + # ------------------------------------------------------------------------ # 'Private' interface - ########################################################################### + # ------------------------------------------------------------------------ def _start_monitor(self, monitor): """ Starts a monitor. """ - monitor.on_trait_change( - self._on_nodes_changed, 'nodes_changed' - ) + monitor.observe(self._on_nodes_changed, "nodes_changed") - monitor.on_trait_change( - self._on_nodes_inserted, 'nodes_inserted' - ) + monitor.observe(self._on_nodes_inserted, "nodes_inserted") - monitor.on_trait_change( - self._on_nodes_removed, 'nodes_removed' - ) + monitor.observe(self._on_nodes_removed, "nodes_removed") - monitor.on_trait_change( - self._on_nodes_replaced, 'nodes_replaced' - ) + monitor.observe(self._on_nodes_replaced, "nodes_replaced") - monitor.on_trait_change( - self._on_structure_changed, 'structure_changed' - ) + monitor.observe(self._on_structure_changed, "structure_changed") monitor.start() - return - def _stop_monitor(self, monitor): """ Stops a monitor. """ - monitor.on_trait_change( - self._on_nodes_changed, 'nodes_changed', remove=True - ) + monitor.observe(self._on_nodes_changed, "nodes_changed", remove=True) - monitor.on_trait_change( - self._on_nodes_inserted, 'nodes_inserted', remove=True - ) + monitor.observe(self._on_nodes_inserted, "nodes_inserted", remove=True) - monitor.on_trait_change( - self._on_nodes_removed, 'nodes_removed', remove=True - ) + monitor.observe(self._on_nodes_removed, "nodes_removed", remove=True) - monitor.on_trait_change( - self._on_nodes_replaced, 'nodes_replaced', remove=True - ) + monitor.observe(self._on_nodes_replaced, "nodes_replaced", remove=True) - - monitor.on_trait_change( - self._on_structure_changed, 'structure_changed', remove=True + monitor.observe( + self._on_structure_changed, "structure_changed", remove=True ) monitor.stop() return - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - #### Static #### + # Static ---- # fixme: Commented this out as listeners are added and removed by the tree. # This caused duplicate monitors to be created for the root node. -## def _root_changed(self, old, new): -## """ Called when the root of the model has been changed. """ + ## def _root_changed(self, old, new): + ## """ Called when the root of the model has been changed. """ -## if old is not None: -## # Remove a listener for structure/appearance changes -## self.remove_listener(old) + ## if old is not None: + ## # Remove a listener for structure/appearance changes + ## self.remove_listener(old) -## if new is not None: -## # Wire up a listener for structure/appearance changes -## self.add_listener(new) + ## if new is not None: + ## # Wire up a listener for structure/appearance changes + ## self.add_listener(new) -## return + ## return - #### Dynamic #### + # Dynamic ---- - def _on_nodes_changed(self, monitor, trait_name, event): + def _on_nodes_changed(self, event): """ Called when nodes have changed. """ - self.nodes_changed = event - - return + self.nodes_changed = event.new - def _on_nodes_inserted(self, monitor, trait_name, event): + def _on_nodes_inserted(self, event): """ Called when nodes have been inserted. """ - self.nodes_inserted = event + self.nodes_inserted = event.new - return - - def _on_nodes_removed(self, monitor, trait_name, event): + def _on_nodes_removed(self, event): """ Called when nodes have been removed. """ - self.nodes_removed = event - - return + self.nodes_removed = event.new - def _on_nodes_replaced(self, monitor, trait_name, event): + def _on_nodes_replaced(self, event): """ Called when nodes have been replaced. """ - self.nodes_replaced = event + self.nodes_replaced = event.new - return - - def _on_structure_changed(self, monitor, trait_name, event): + def _on_structure_changed(self, event): """ Called when the structure of a node has changed drastically. """ - self.structure_changed = event + self.structure_changed = event.new return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/node_tree.py python-pyface-7.4.0/pyface/tree/node_tree.py --- python-pyface-6.1.2/pyface/tree/node_tree.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/node_tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A tree control with extensible node types. """ -# Standard library imports. -import six -if six.PY2: - from inspect import getargspec -else: - # avoid deprecation warning - from inspect import getfullargspec as getargspec +# avoid deprecation warning +from inspect import getfullargspec + -# Enthought library imports. from pyface.action.api import ActionEvent -from traits.api import Instance, List, Property +from traits.api import Instance, List, observe, Property + -# Local imports. from .node_manager import NodeManager from .node_type import NodeType from .node_tree_model import NodeTreeModel @@ -36,12 +28,12 @@ class NodeTree(Tree): """ A tree control with extensible node types. """ - #### 'Tree' interface ##################################################### + # 'Tree' interface ----------------------------------------------------- # The model that provides the data for the tree. model = Instance(NodeTreeModel, ()) - #### 'NodeTree' interface ################################################# + # 'NodeTree' interface ------------------------------------------------- # The node manager looks after all node types. node_manager = Property(Instance(NodeManager)) @@ -49,11 +41,11 @@ # The node types in the tree. node_types = Property(List(NodeType)) - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeTree' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Properties ########################################################### + # Properties ----------------------------------------------------------- # node_manager def _get_node_manager(self): @@ -81,24 +73,25 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'Tree' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _node_activated_changed(self, obj): + @observe("node_activated") + def _perform_default_action_on_activated_node(self, event): """ Called when a node has been activated (i.e., double-clicked). """ - + obj = event.new default_action = self.model.get_default_action(obj) if default_action is not None: self._perform_default_action(default_action, obj) - return - - def _node_right_clicked_changed(self, obj, point): + @observe("node_right_clicked") + def _show_menu_on_right_clicked_object(self, event): """ Called when the right mouse button is clicked on the tree. """ - + obj_point = event.new + obj, point = obj_point # Add the node that the right-click occurred on to the selection. self.select(obj) @@ -113,9 +106,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_to_menu(self, menu_item): """ Adds a menu item to a menu bar. """ @@ -136,11 +129,11 @@ """ Perform an action. """ # fixme: We need a more formal event structure! - event.widget = self + event.widget = self event.context = self._context # fixme: the 'perform' method without taking an event is deprecated! - args, varargs, varkw, defaults = getargspec(action.perform) + args, varargs, varkw, defaults = getfullargspec(action.perform) # If the only argument is 'self' then this is the DEPRECATED # interface. @@ -152,9 +145,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_action_event(self, obj): """ Return a new action event for the specified object. """ @@ -166,8 +159,6 @@ action.perform(self._create_action_event(obj)) - return - def _popup_menu(self, menu_manager, obj, point): """ Popup the menu described by the menu manager. """ @@ -183,5 +174,3 @@ menu_manager.destroy() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/node_type.py python-pyface-7.4.0/pyface/tree/node_type.py --- python-pyface-6.1.2/pyface/tree/node_type.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/node_type.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The base class for all node types. """ -# Enthought library imports. -from traits.api import Any, HasPrivateTraits, Instance, List -from pyface.api import ImageResource -from pyface.action.api import Action, ActionManagerItem, Group +from traits.api import Any, HasPrivateTraits, Instance + +from pyface.api import Image, ImageResource +from pyface.action.api import Action, Group from pyface.action.api import MenuManager @@ -25,34 +22,34 @@ """ The base class for all node types. """ # The default image used to represent nodes that DO NOT allow children. - DOCUMENT = ImageResource('document') + DOCUMENT = ImageResource("document") # The default image used to represent nodes that allow children and are NOT # expanded. - CLOSED_FOLDER = ImageResource('closed_folder') + CLOSED_FOLDER = ImageResource("closed_folder") # The default image used to represent nodes that allow children and ARE # expanded. - OPEN_FOLDER = ImageResource('open_folder') + OPEN_FOLDER = ImageResource("open_folder") - #### 'NodeType' interface ################################################# + # 'NodeType' interface ------------------------------------------------- # The node manager that the type belongs to. - node_manager = Instance('pyface.tree.node_manager.NodeManager') + node_manager = Instance("pyface.tree.node_manager.NodeManager") # The image used to represent nodes that DO NOT allow children. - image = Instance(ImageResource) + image = Image(DOCUMENT) # The image used to represent nodes that allow children and are NOT # expanded. - closed_image = Instance(ImageResource) + closed_image = Image(CLOSED_FOLDER) # The image used to represent nodes that allow children and ARE expanded. - open_image = Instance(ImageResource) + open_image = Image(OPEN_FOLDER) # The default actions/groups/menus available on nodes of this type (shown # on the context menu). - actions = Any#List + actions = Any # List # The default action for nodes of this type. The default action is # performed when a node is activated (i.e., double-clicked). @@ -60,18 +57,18 @@ # The default actions/groups/menus for creating new children within nodes # of this type (shown in the 'New' menu of the context menu). - new_actions = Any#List + new_actions = Any # List - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeType' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### These methods are specific to the 'NodeType' interface ############### + # These methods are specific to the 'NodeType' interface --------------- def is_type_for(self, node): """ Returns True if a node is deemed to be of this type. """ - raise NotImplementedError + raise NotImplementedError() def allows_children(self, node): """ Does the node allow children (ie. a folder vs a file). """ @@ -86,19 +83,14 @@ def get_context_menu(self, node): """ Returns the context menu for a node. """ - sat = Group(id='SystemActionsTop') - nsa = Group(id='NodeSpecificActions') - sab = Group(id='SystemActionsBottom') + sat = Group(id="SystemActionsTop") + nsa = Group(id="NodeSpecificActions") + sab = Group(id="SystemActionsBottom") # The 'New' menu. new_actions = self.get_new_actions(node) if new_actions is not None and len(new_actions) > 0: - sat.append( - MenuManager( - name = 'New', - *new_actions - ), - ) + sat.append(MenuManager(name="New", *new_actions)) # Node-specific actions. actions = self.get_actions(node) @@ -155,7 +147,7 @@ return None - #### These methods are exactly the same as the 'TreeModel' interface ###### + # These methods are exactly the same as the 'TreeModel' interface -----# def has_children(self, node): """ Returns True if a node has children, otherwise False. @@ -175,7 +167,7 @@ """ - raise NotImplementedError + raise NotImplementedError() def get_drag_value(self, node): """ Get the value that is dragged for a node. @@ -194,22 +186,22 @@ def drop(self, obj, data): """ Drops an object onto a node. """ - raise NotImplementedError + raise NotImplementedError() def get_image(self, node, selected, expanded): """ Returns the label image for a node. """ if self.allows_children(node): if expanded: - order = ['open_image', 'closed_image', 'image'] + order = ["open_image", "closed_image", "image"] default = self.OPEN_FOLDER else: - order = ['closed_image', 'open_image', 'image'] + order = ["closed_image", "open_image", "image"] default = self.CLOSED_FOLDER else: - order = ['image', 'open_image', 'closed_image'] + order = ["image", "open_image", "closed_image"] default = self.DOCUMENT # Use the search order to look for a trait that is NOT None. @@ -278,5 +270,3 @@ """ Returns True if the node is expandanble, otherwise False. """ return True - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/trait_dict_node_type.py python-pyface-7.4.0/pyface/tree/trait_dict_node_type.py --- python-pyface-6.1.2/pyface/tree/trait_dict_node_type.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/trait_dict_node_type.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,48 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The node type for a trait dictionary. """ -# Enthought library imports. from traits.api import Any, Str -# Local imports. + from .node_type import NodeType class TraitDictNodeType(NodeType): """ The node type for a trait dictionary. """ - #### 'TraitDictNodeType' interface ######################################## + # 'TraitDictNodeType' interface ---------------------------------------- # The type of object that provides the trait dictionary. - klass = Any + klass = Any() # The label text. - text = Str + text = Str() # The trait name. - trait_name = Str + trait_name = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeType' interface. - ########################################################################### + # ------------------------------------------------------------------------ def is_type_for(self, node): """ Returns True if this node type recognizes a node. """ - is_type_for = isinstance(node, dict) \ - and hasattr(node, 'object') \ - and isinstance(node.object, self.klass) \ - and node.name == self.trait_name + is_type_for = ( + isinstance(node, dict) + and hasattr(node, "object") + and isinstance(node.object, self.klass) + and node.name == self.trait_name + ) return is_type_for @@ -55,5 +65,3 @@ """ Returns the label text for a node. """ return self.text - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/trait_list_node_type.py python-pyface-7.4.0/pyface/tree/trait_list_node_type.py --- python-pyface-6.1.2/pyface/tree/trait_list_node_type.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/trait_list_node_type.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,48 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The node type for a trait list. """ -# Enthought library imports. from traits.api import Any, Str -# Local imports. + from .node_type import NodeType class TraitListNodeType(NodeType): """ The node type for a trait list. """ - #### 'TraitListNodeType' interface ######################################## + # 'TraitListNodeType' interface ---------------------------------------- # The type of object that provides the trait list. - klass = Any + klass = Any() # The label text. - text = Str + text = Str() # The name of the trait. - trait_name = Str + trait_name = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'NodeType' interface. - ########################################################################### + # ------------------------------------------------------------------------ def is_type_for(self, node): """ Returns True if this node type recognizes a node. """ - is_type_for = isinstance(node, list) \ - and hasattr(node, 'object') \ - and isinstance(node.object, self.klass) \ - and node.name == self.trait_name + is_type_for = ( + isinstance(node, list) + and hasattr(node, "object") + and isinstance(node.object, self.klass) + and node.name == self.trait_name + ) return is_type_for @@ -55,5 +65,3 @@ """ Returns the label text for a node. """ return self.text - -##### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/tree_model.py python-pyface-7.4.0/pyface/tree/tree_model.py --- python-pyface-6.1.2/pyface/tree/tree_model.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/tree_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,29 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Model for tree views. """ -# Enthought library imports. from traits.api import Any, HasTraits, Event -# Local imports. + from .node_event import NodeEvent class TreeModel(HasTraits): """ Model for tree views. """ - #### 'TreeModel' interface ################################################ + # 'TreeModel' interface ------------------------------------------------ # The root of the model. - root = Any + root = Any() # Fired when nodes in the tree have changed in some way that affects their # appearance but NOT their structure or position in the tree. @@ -46,9 +42,9 @@ # node down. structure_changed = Event(NodeEvent) - ######################################################################### + # ------------------------------------------------------------------------ # 'TreeModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def has_children(self, node): """ Returns True if a node has children, otherwise False. @@ -59,12 +55,12 @@ """ - raise NotImplementedError + raise NotImplementedError() def get_children(self, node): """ Returns the children of a node. """ - raise NotImplementedError + raise NotImplementedError() def get_drag_value(self, node): """ Get the value that is dragged for a node. @@ -83,7 +79,7 @@ def drop(self, node, obj): """ Drops an object onto a node. """ - raise NotImplementedError + raise NotImplementedError() def get_image(self, node, selected, expanded): """ Returns the label image for a node. @@ -172,22 +168,16 @@ self.nodes_changed = NodeEvent(node=node, children=children) - return - def fire_nodes_inserted(self, node, children): """ Fires the nodes inserted event. """ self.nodes_inserted = NodeEvent(node=node, children=children) - return - - def fire_nodes_removed(self, parent, children): + def fire_nodes_removed(self, node, children): """ Fires the nodes removed event. """ self.nodes_removed = NodeEvent(node=node, children=children) - return - def fire_nodes_replaced(self, node, old_children, new_children): """ Fires the nodes removed event. """ @@ -195,13 +185,9 @@ node=node, old_children=old_children, children=new_children ) - return - def fire_structure_changed(self, node): """ Fires the structure changed event. """ self.structure_changed = NodeEvent(node=node) return - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/tree/tree.py python-pyface-7.4.0/pyface/tree/tree.py --- python-pyface-6.1.2/pyface/tree/tree.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/tree/tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ The implementation of a tree control with a model/ui architecture. """ # Import the toolkit specific version. -from __future__ import absolute_import + from pyface.toolkit import toolkit_object -Tree = toolkit_object('tree.tree:Tree') + +Tree = toolkit_object("tree.tree:Tree") diff -Nru python-pyface-6.1.2/pyface/ui/null/action/action_item.py python-pyface-7.4.0/pyface/ui/null/action/action_item.py --- python-pyface-6.1.2/pyface/ui/null/action/action_item.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/action_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,34 +1,30 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enth373ought.com/licenses/BSD.txt -# Thanks for using Enthought open source! +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The 'null' specific implementations of the action manager internal classes. """ -# Enthought library imports. from traits.api import Any, Bool, HasTraits class _MenuItem(HasTraits): """ A menu item representation of an action item. """ - #### '_MenuItem' interface ################################################ + # '_MenuItem' interface ------------------------------------------------ # Is the item checked? checked = Bool(False) # A controller object we delegate taking actions through (if any). - controller = Any + controller = Any() # Is the item enabled? enabled = Bool(True) @@ -38,11 +34,11 @@ # The radio group we are part of (None if the menu item is not part of such # a group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, menu, item, controller): """ Creates a new menu item for an action item. """ @@ -56,19 +52,17 @@ self.controller = controller controller.add_to_menu(self) - return - class _Tool(HasTraits): """ A tool bar tool representation of an action item. """ - #### '_Tool' interface #################################################### + # '_Tool' interface ---------------------------------------------------- # Is the item checked? checked = Bool(False) # A controller object we delegate taking actions through (if any). - controller = Any + controller = Any() # Is the item enabled? enabled = Bool(True) @@ -78,61 +72,42 @@ # The radio group we are part of (None if the tool is not part of such a # group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, tool_bar, image_cache, item, controller, - show_labels): + def __init__( + self, parent, tool_bar, image_cache, item, controller, show_labels + ): """ Creates a new tool bar tool for an action item. """ self.item = item self.tool_bar = tool_bar - # Create an appropriate tool depending on the style of the action. - action = self.item.action - - # If the action has an image then convert it to a bitmap (as required - # by the toolbar). - if action.image is not None: - image = action.image.create_image() - path = action.image.absolute_path - bmp = image_cache.get_bitmap(path) - - else: - from pyface.api import ImageResource - image = ImageResource('foo') - bmp = image.create_bitmap() - self.control_id = 1 self.control = None if controller is not None: self.controller = controller controller.add_to_toolbar(self) - return - class _PaletteTool(HasTraits): """ A tool palette representation of an action item. """ - #### '_PaletteTool' interface ############################################# + # '_PaletteTool' interface --------------------------------------------- # The radio group we are part of (None if the tool is not part of such a # group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, tool_palette, image_cache, item, show_labels): """ Creates a new tool palette tool for an action item. """ self.item = item self.tool_palette = tool_palette - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/action/__init__.py python-pyface-7.4.0/pyface/ui/null/action/__init__.py --- python-pyface-6.1.2/pyface/ui/null/action/__init__.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. -#------------------------------------------------------------------------------ +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/null/action/menu_bar_manager.py python-pyface-7.4.0/pyface/ui/null/action/menu_bar_manager.py --- python-pyface-6.1.2/pyface/ui/null/action/menu_bar_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/menu_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The 'null' backend specific implementation of a menu bar manager. """ -# Local imports. from pyface.action.action_manager import ActionManager class MenuBarManager(ActionManager): """ A menu bar manager realizes itself in errr, a menu bar control. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'MenuBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu_bar(self, parent, controller=None): """ Creates a menu bar representation of the manager. """ @@ -37,5 +33,3 @@ controller = self.controller return None - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/action/menu_manager.py python-pyface-7.4.0/pyface/ui/null/action/menu_manager.py --- python-pyface-6.1.2/pyface/ui/null/action/menu_manager.py 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The 'null' backend specific implementation of a menu manager. """ -# Enthought library imports. -from traits.api import Unicode +from traits.api import Str + -# Local imports. from pyface.action.action_manager import ActionManager from pyface.action.action_manager_item import ActionManagerItem -from pyface.action.group import Group class MenuManager(ActionManager, ActionManagerItem): @@ -30,15 +25,15 @@ This could be a sub-menu or a context (popup) menu. """ - #### 'MenuManager' interface ############################################## + # 'MenuManager' interface ---------------------------------------------# # The menu manager's name (if the manager is a sub-menu, this is what its # label will be). - name = Unicode + name = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'MenuManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu(self, parent, controller=None): """ Creates a menu representation of the manager. """ @@ -52,19 +47,14 @@ return None - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManagerItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_to_menu(self, parent, menu, controller): """ Adds the item to a menu. """ - return def add_to_toolbar(self, parent, tool_bar, image_cache, controller): """ Adds the item to a tool bar. """ raise ValueError("Cannot add a menu manager to a toolbar.") - - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/action/status_bar_manager.py python-pyface-7.4.0/pyface/ui/null/action/status_bar_manager.py --- python-pyface-6.1.2/pyface/ui/null/action/status_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/status_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,57 +1,53 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A status bar manager realizes itself in a status bar control. """ -# Enthought library imports. -from traits.api import Any, HasTraits, List, Property, Str, Unicode +from traits.api import Any, HasTraits, List, Property, Str class StatusBarManager(HasTraits): """ A status bar manager realizes itself in a status bar control. """ # The manager's unique identifier (if it has one). - id = Str + id = Str() # The message displayed in the first field of the status bar. message = Property # The messages to be displayed in the status bar fields. - messages = List(Unicode) + messages = List(Str) # The toolkit-specific control that represents the status bar. - status_bar = Any + status_bar = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'StatusBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_status_bar(self, parent): """ Creates a status bar. """ return self.status_bar - ########################################################################### + # ------------------------------------------------------------------------ # Property handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _get_message(self): if len(self.messages) > 0: message = self.messages[0] else: - message = '' + message = "" return message @@ -61,23 +57,20 @@ old = self.messages[0] self.messages[0] = value else: - old = '' + old = "" self.messages.append(old) - self.trait_property_changed('message', old, value) + self.trait_property_changed("message", old, value) return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _messages_changed(self): """ Sets the text displayed on the status bar. """ - return def _messages_items_changed(self): """ Sets the text displayed on the status bar. """ return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/action/tool_bar_manager.py python-pyface-7.4.0/pyface/ui/null/action/tool_bar_manager.py --- python-pyface-6.1.2/pyface/ui/null/action/tool_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/tool_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The 'null' backend specific implementation of the tool bar manager. """ -# Enthought library imports. from traits.api import Bool, Enum, Instance, Tuple -# Local imports. + from pyface.image_cache import ImageCache from pyface.action.action_manager import ActionManager @@ -26,13 +22,13 @@ class ToolBarManager(ActionManager): """ A tool bar manager realizes itself in errr, a tool bar control. """ - #### 'ToolBarManager' interface ########################################### + # 'ToolBarManager' interface ------------------------------------------- # The size of tool images (width, height). image_size = Tuple((16, 16)) # The orientation of the toolbar. - orientation = Enum('horizontal', 'vertical') + orientation = Enum("horizontal", "vertical") # Should we display the name of each tool bar tool under its image? show_tool_names = Bool(True) @@ -40,20 +36,20 @@ # Should we display the horizontal divider? show_divider = Bool(True) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Cache of tool images (scaled to the appropriate size). _image_cache = Instance(ImageCache) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *args, **traits): """ Creates a new tool bar manager. """ # Base class contructor. - super(ToolBarManager, self).__init__(*args, **traits) + super().__init__(*args, **traits) # An image cache to make sure that we only load each image used in the # tool bar exactly once. @@ -61,9 +57,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ToolBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_tool_bar(self, parent, controller=None): """ Creates a tool bar. """ @@ -76,5 +72,3 @@ controller = self.controller return None - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/action/tool_palette_manager.py python-pyface-7.4.0/pyface/ui/null/action/tool_palette_manager.py --- python-pyface-6.1.2/pyface/ui/null/action/tool_palette_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/tool_palette_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A tool bar manager realizes itself in a tool palette control. """ -# Enthought library imports. -from traits.api import Any, Bool, Enum, Instance, Tuple +from traits.api import Bool, Instance, Tuple + -# Local imports. from pyface.image_cache import ImageCache from pyface.action.action_manager import ActionManager -from .tool_palette import ToolPalette class ToolPaletteManager(ActionManager): """ A tool bar manager realizes itself in a tool palette bar control. """ - #### 'ToolPaletteManager' interface ####################################### + # 'ToolPaletteManager' interface --------------------------------------- # The size of tool images (width, height). image_size = Tuple((16, 16)) @@ -34,20 +29,20 @@ # Should we display the name of each tool bar tool under its image? show_tool_names = Bool(True) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Cache of tool images (scaled to the appropriate size). _image_cache = Instance(ImageCache) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *args, **traits): """ Creates a new tool bar manager. """ # Base class contructor. - super(ToolPaletteManager, self).__init__(*args, **traits) + super().__init__(*args, **traits) # An image cache to make sure that we only load each image used in the # tool bar exactly once. @@ -55,13 +50,10 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ToolPaletteManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_tool_palette(self, parent, controller=None): """ Creates a tool bar. """ return None - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/action/tool_palette.py python-pyface-7.4.0/pyface/ui/null/action/tool_palette.py --- python-pyface-6.1.2/pyface/ui/null/action/tool_palette.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/action/tool_palette.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ View of an ActionManager drawn as a rectangle of buttons. """ from pyface.widget import Widget @@ -20,39 +17,39 @@ class ToolPalette(Widget): - tools = List + tools = List() - id_tool_map = Dict + id_tool_map = Dict() - tool_id_to_button_map = Dict + tool_id_to_button_map = Dict() button_size = Tuple((25, 25), Int, Int) is_realized = Bool(False) - tool_listeners = Dict + tool_listeners = Dict() # Maps a button id to its tool id. - button_tool_map = Dict + button_tool_map = Dict() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, **traits): """ Creates a new tool palette. """ # Base class constructor. - super(ToolPalette, self).__init__(**traits) + super().__init__(**traits) # Create the toolkit-specific control that represents the widget. self.control = self._create_control(parent) return - ########################################################################### + # ------------------------------------------------------------------------ # ToolPalette interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_tool(self, label, bmp, kind, tooltip, longtip): """ Add a tool with the specified properties to the palette. @@ -69,19 +66,15 @@ if the 'checked' parameter is True; unchecked otherwise. If the button is a standard button, this method is a NOP. """ - return def enable_tool(self, id, enabled): """ Enable or disable the tool identified by 'id'. """ - return def on_tool_event(self, id, callback): """ Register a callback for events on the tool identified by 'id'. """ - return def realize(self): """ Realize the control so that it can be displayed. """ - return def get_tool_state(self, id): """ Get the toggle state of the tool identified by 'id'. """ @@ -89,13 +82,9 @@ return state - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): return None - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/clipboard.py python-pyface-7.4.0/pyface/ui/null/clipboard.py --- python-pyface-6.1.2/pyface/ui/null/clipboard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/clipboard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2009, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Evan Patterson # Date: 06/29/09 -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + -# ETS imports from traits.api import provides from pyface.i_clipboard import IClipboard, BaseClipboard @@ -22,17 +22,16 @@ """ A dummy clipboard implementationf for the null backend. """ - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_has_data(self): return False - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'object_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_object_data(self): pass @@ -44,11 +43,11 @@ return False def _get_object_type(self): - return '' + return "" - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'text_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_text_data(self): return False @@ -59,9 +58,9 @@ def _get_has_text_data(self): pass - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'file_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_file_data(self): pass @@ -69,5 +68,5 @@ def _set_file_data(self, data): pass - def _get_has_file_data (self): + def _get_has_file_data(self): return False diff -Nru python-pyface-6.1.2/pyface/ui/null/color.py python-pyface-7.4.0/pyface/ui/null/color.py --- python-pyface-6.1.2/pyface/ui/null/color.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/color.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,51 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Color conversion routines for the null toolkit. + +This module provides a couple of utility methods to support the +pyface.color.Color class to_toolkit and from_toolkit methods. + +For definiteness, the null toolkit uses tuples of RGBA values from 0 to 255 +to represent colors. +""" + +from pyface.color import channels_to_ints, ints_to_channels + + +def toolkit_color_to_rgba(color): + """ Convert a hex tuple to an RGBA tuple. + + Parameters + ---------- + color : tuple + A tuple of integer values from 0 to 255 inclusive. + + Returns + ------- + rgba : tuple + A tuple of 4 floating point values between 0.0 and 1.0 inclusive. + """ + return ints_to_channels(color) + + +def rgba_to_toolkit_color(rgba): + """ Convert an RGBA tuple to a hex tuple. + + Parameters + ---------- + color : tuple + A tuple of integer values from 0 to 255 inclusive. + + Returns + ------- + rgba : tuple + A tuple of 4 floating point values between 0.0 and 1.0 inclusive. + """ + return channels_to_ints(rgba) diff -Nru python-pyface-6.1.2/pyface/ui/null/image_resource.py python-pyface-7.4.0/pyface/ui/null/image_resource.py --- python-pyface-6.1.2/pyface/ui/null/image_resource.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/image_resource.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Standard library imports. import os -# Enthought library imports. + from traits.api import Any, HasTraits, List, Property, provides -from traits.api import Unicode +from traits.api import Str + -# Local imports. from pyface.i_image_resource import IImageResource, MImageResource @@ -30,23 +25,22 @@ IImageResource interface for the API documentation. """ - - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # The resource manager reference for the image. - _ref = Any + _ref = Any() - #### 'ImageResource' interface ############################################ + # 'ImageResource' interface -------------------------------------------- - absolute_path = Property(Unicode) + absolute_path = Property(Str) - name = Unicode + name = Str() - search_path = List + search_path = List() - ########################################################################### - # 'ImageResource' interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'IImage' interface. + # ------------------------------------------------------------------------ def create_bitmap(self, size=None): return self.create_image(size) @@ -54,9 +48,9 @@ def create_icon(self, size=None): return self.create_image(size) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_absolute_path(self): # FIXME: This doesn't quite work with the new notion of image size. We @@ -70,5 +64,3 @@ absolute_path = self._get_image_not_found().absolute_path return absolute_path - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/init.py python-pyface-7.4.0/pyface/ui/null/init.py --- python-pyface-6.1.2/pyface/ui/null/init.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/init.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -#------------------------------------------------------------------------------ +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Initialize this backend. """ from pyface.base_toolkit import Toolkit -toolkit_object = Toolkit('pyface', 'null', 'pyface.ui.null') +toolkit_object = Toolkit("pyface", "null", "pyface.ui.null") diff -Nru python-pyface-6.1.2/pyface/ui/null/resource_manager.py python-pyface-7.4.0/pyface/ui/null/resource_manager.py --- python-pyface-6.1.2/pyface/ui/null/resource_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/resource_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Enthought library imports. from pyface.resource.api import ResourceFactory class PyfaceResourceFactory(ResourceFactory): """ The implementation of a shared resource manager. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceFactory' toolkit interface. - ########################################################################### + # ------------------------------------------------------------------------ def image_from_file(self, filename): """ Creates an image from the data in the specified filename. """ # Just return the data as a string for now. - f = open(filename, 'rb') + f = open(filename, "rb") data = f.read() f.close() @@ -36,5 +31,3 @@ def image_from_data(self, data): """ Creates an image from the specified data. """ return data - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/tests/bad_import.py python-pyface-7.4.0/pyface/ui/null/tests/bad_import.py --- python-pyface-6.1.2/pyface/ui/null/tests/bad_import.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/tests/bad_import.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # This is used to test what happens when there is an unrelated import error # when importing a toolkit object -raise ImportError('No module named nonexistent') +raise ImportError("No module named nonexistent") diff -Nru python-pyface-6.1.2/pyface/ui/null/widget.py python-pyface-7.4.0/pyface/ui/null/widget.py --- python-pyface-6.1.2/pyface/ui/null/widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Enthought library imports. from traits.api import Any, HasTraits, provides -# Local imports. + from pyface.i_widget import IWidget, MWidget @@ -26,18 +21,15 @@ interface for the API documentation. """ + # 'IWidget' interface -------------------------------------------------# - #### 'IWidget' interface ################################################## + control = Any() - control = Any + parent = Any() - parent = Any - - ########################################################################### + # ------------------------------------------------------------------------ # 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): self.control = None - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/null/window.py python-pyface-7.4.0/pyface/ui/null/window.py --- python-pyface-6.1.2/pyface/ui/null/window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/null/window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Enthought library imports. -from traits.api import Any, Event, Property, provides, Unicode +from traits.api import Event, Property, provides, Str from traits.api import Tuple -# Local imports. + from pyface.i_window import IWindow, MWindow from pyface.key_pressed_event import KeyPressedEvent from .widget import Widget @@ -29,32 +24,31 @@ interface for the API documentation. """ - - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------# position = Property(Tuple) size = Property(Tuple) - title = Unicode + title = Str() - #### Events ##### + # Events ----- - activated = Event + activated = Event() - closed = Event + closed = Event() - closing = Event + closing = Event() - deactivated = Event + deactivated = Event() key_pressed = Event(KeyPressedEvent) - opened = Event + opened = Event() - opening = Event + opening = Event() - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Shadow trait for position. _position = Tuple((-1, -1)) @@ -62,23 +56,16 @@ # Shadow trait for size. _size = Tuple((-1, -1)) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def show(self, visible): pass - ########################################################################### - # Protected 'IWindow' interface. - ########################################################################### - - def _add_event_listeners(self): - pass - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_position(self): """ Property getter for position. """ @@ -91,7 +78,7 @@ old = self._position self._position = position - self.trait_property_changed('position', old, position) + self.trait_property_changed("position", old, position) def _get_size(self): """ Property getter for size. """ @@ -104,6 +91,4 @@ old = self._size self._size = size - self.trait_property_changed('size', old, size) - -#### EOF ###################################################################### + self.trait_property_changed("size", old, size) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/about_dialog.py python-pyface-7.4.0/pyface/ui/qt4/about_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/about_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/about_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,32 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! -# Standard library imports. -import platform -import sys -# Major package imports. -from pyface.qt import QtCore, QtGui +import platform -# Enthought library imports. -from traits.api import Instance, List, provides, Unicode +from traits.api import Any, Callable, List, provides, Str, Tuple -# Local imports. +from pyface.qt import QtCore, QtGui from pyface.i_about_dialog import IAboutDialog, MAboutDialog -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from .dialog import Dialog +from .image_resource import ImageResource + # The HTML displayed in the QLabel. -_DIALOG_TEXT = ''' +_DIALOG_TEXT = """
@@ -46,13 +42,16 @@ Qt %s

- Copyright © 2003-2010 Enthought, Inc.
+ %s +

+

+ Copyright © 2003-2022 Enthought, Inc.
Copyright © 2007 Riverbank Computing Limited

-''' +""" @provides(IAboutDialog) @@ -61,15 +60,35 @@ IAboutDialog interface for the API documentation. """ - #### 'IAboutDialog' interface ############################################# + # 'IAboutDialog' interface --------------------------------------------- + + additions = List(Str) - additions = List(Unicode) + copyrights = List(Str) - image = Instance(ImageResource, ImageResource('about')) + image = Image(ImageResource("about")) - ########################################################################### + # Private interface ---------------------------------------------------# + + #: A list of connected Qt signals to be removed before destruction. + #: First item in the tuple is the Qt signal. The second item is the event + #: handler. + _connections_to_remove = List(Tuple(Any, Callable)) + + # ------------------------------------------------------------------------- + # 'IWidget' interface. + # ------------------------------------------------------------------------- + + def destroy(self): + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) + + super().destroy() + + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): label = QtGui.QLabel() @@ -83,32 +102,46 @@ # Set the title. self.title = "About %s" % title - # Load the image to be displayed in the about box. - image = self.image.create_image() - path = self.image.absolute_path - - # The additional strings. - additions = '
'.join(self.additions) - - # Get the version numbers. - py_version = platform.python_version() - qt_version = QtCore.__version__ - # Set the page contents. - label.setText(_DIALOG_TEXT % (path, additions, py_version, qt_version)) + label.setText(self._create_html()) # Create the button. buttons = QtGui.QDialogButtonBox() if self.ok_label: - buttons.addButton(self.ok_label, QtGui.QDialogButtonBox.AcceptRole) + buttons.addButton(self.ok_label, QtGui.QDialogButtonBox.ButtonRole.AcceptRole) else: - buttons.addButton(QtGui.QDialogButtonBox.Ok) + buttons.addButton(QtGui.QDialogButtonBox.StandardButton.Ok) buttons.accepted.connect(parent.accept) + self._connections_to_remove.append((buttons.accepted, parent.accept)) lay = QtGui.QVBoxLayout() lay.addWidget(label) lay.addWidget(buttons) parent.setLayout(lay) + + def _create_html(self): + # Load the image to be displayed in the about box. + path = self.image.absolute_path + + # The additional strings. + additions = "
".join(self.additions) + + # Get the version numbers. + py_version = platform.python_version() + qt_version = QtCore.__version__ + + # The additional copyright strings. + copyrights = "
".join( + ["Copyright © %s" % line for line in self.copyrights] + ) + + return _DIALOG_TEXT % ( + path, + additions, + py_version, + qt_version, + copyrights, + ) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/action/action_item.py python-pyface-7.4.0/pyface/ui/qt4/action/action_item.py --- python-pyface-6.1.2/pyface/ui/qt4/action/action_item.py 2019-06-18 10:52:41.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/action/action_item.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,39 +1,32 @@ -# Copyright (c) 2007, Riverbank Computing Limited -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described -# in the PyQt GPL exception also apply. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: +# Thanks for using Enthought open source! """ The PyQt specific implementations the action manager internal classes. """ -# Standard library imports. -import six -if six.PY2: - from inspect import getargspec -else: - # avoid deprecation warning - from inspect import getfullargspec as getargspec +from inspect import getfullargspec + + +from pyface.qt import QtGui -# Major package imports. -from pyface.qt import QtGui, QtCore -# Enthought library imports. from traits.api import Any, Bool, HasTraits -# Local imports. + from pyface.action.action_event import ActionEvent class PyfaceWidgetAction(QtGui.QWidgetAction): - def __init__(self, parent, action): - super(PyfaceWidgetAction, self).__init__(parent) + super().__init__(parent) self.action = action def createWidget(self, parent): @@ -45,13 +38,13 @@ class _MenuItem(HasTraits): """ A menu item representation of an action item. """ - #### '_MenuItem' interface ################################################ + # '_MenuItem' interface ------------------------------------------------ # Is the item checked? checked = Bool(False) # A controller object we delegate taking actions through (if any). - controller = Any + controller = Any() # Is the item enabled? enabled = Bool(True) @@ -61,7 +54,7 @@ # The radio group we are part of (None if the menu item is not part of such # a group). - group = Any + group = Any() # The toolkit control. control = Any() @@ -69,9 +62,9 @@ # The toolkit control id. control_id = None - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, menu, item, controller): """ Creates a new menu item for an action item. """ @@ -82,15 +75,20 @@ # FIXME v3: This is a wx'ism and should be hidden in the toolkit code. self.control_id = None - if action.style == 'widget': + if action.style == "widget": self.control = PyfaceWidgetAction(parent, action) menu.addAction(self.control) elif action.image is None: - self.control = menu.addAction(action.name, self._qt4_on_triggered, - action.accelerator) + self.control = menu.addAction( + action.name, self._qt4_on_triggered, action.accelerator + ) else: - self.control = menu.addAction(action.image.create_icon(), - action.name, self._qt4_on_triggered, action.accelerator) + self.control = menu.addAction( + action.image.create_icon(), + action.name, + self._qt4_on_triggered, + action.accelerator, + ) menu.menu_items.append(self) self.control.setToolTip(action.tooltip) @@ -98,16 +96,16 @@ self.control.setEnabled(action.enabled) self.control.setVisible(action.visible) - if getattr(action, 'menu_role', False): + if getattr(action, "menu_role", False): if action.menu_role == "About": - self.control.setMenuRole(QtGui.QAction.AboutRole) + self.control.setMenuRole(QtGui.QAction.MenuRole.AboutRole) elif action.menu_role == "Preferences": - self.control.setMenuRole(QtGui.QAction.PreferencesRole) + self.control.setMenuRole(QtGui.QAction.MenuRole.PreferencesRole) - if action.style == 'toggle': + if action.style == "toggle": self.control.setCheckable(True) self.control.setChecked(action.checked) - elif action.style == 'radio': + elif action.style == "radio": # Create an action group if it hasn't already been done. try: ag = item.parent._qt4_ag @@ -121,12 +119,13 @@ # Listen for trait changes on the action (so that we can update its # enabled/disabled/checked state etc). - action.on_trait_change(self._on_action_enabled_changed, 'enabled') - action.on_trait_change(self._on_action_visible_changed, 'visible') - action.on_trait_change(self._on_action_checked_changed, 'checked') - action.on_trait_change(self._on_action_name_changed, 'name') - action.on_trait_change(self._on_action_accelerator_changed, - 'accelerator') + action.observe(self._on_action_enabled_changed, "enabled") + action.observe(self._on_action_visible_changed, "visible") + action.observe(self._on_action_checked_changed, "checked") + action.observe(self._on_action_name_changed, "name") + action.observe(self._on_action_accelerator_changed, "accelerator") + action.observe(self._on_action_image_changed, "image") + action.observe(self._on_action_tooltip_changed, "tooltip") # Detect if the control is destroyed. self.control.destroyed.connect(self._qt4_on_destroyed) @@ -137,20 +136,19 @@ def dispose(self): action = self.item.action - action.on_trait_change(self._on_action_enabled_changed, 'enabled', - remove=True) - action.on_trait_change(self._on_action_visible_changed, 'visible', - remove=True) - action.on_trait_change(self._on_action_checked_changed, 'checked', - remove=True) - action.on_trait_change(self._on_action_name_changed, 'name', - remove=True) - action.on_trait_change(self._on_action_accelerator_changed, - 'accelerator', remove=True) + action.observe(self._on_action_enabled_changed, "enabled", remove=True) + action.observe(self._on_action_visible_changed, "visible", remove=True) + action.observe(self._on_action_checked_changed, "checked", remove=True) + action.observe(self._on_action_name_changed, "name", remove=True) + action.observe( + self._on_action_accelerator_changed, "accelerator", remove=True + ) + action.observe(self._on_action_image_changed, "image", remove=True) + action.observe(self._on_action_tooltip_changed, "tooltip", remove=True) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _qt4_on_destroyed(self, control=None): """ Delete the reference to the control to avoid attempting to talk to @@ -164,7 +162,7 @@ action = self.item.action action_event = ActionEvent() - is_checkable = action.style in ['radio', 'toggle'] + is_checkable = action.style in ["radio", "toggle"] # Perform the action! if self.controller is not None: @@ -175,7 +173,7 @@ # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. This is also # useful as Traits UI controllers *never* require the event. - argspec = getargspec(self.controller.perform) + argspec = getfullargspec(self.controller.perform) # If the only arguments are 'self' and 'action' then don't pass # the event! @@ -192,7 +190,7 @@ # Most of the time, action's do no care about the event (it # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. - argspec = getargspec(action.perform) + argspec = getfullargspec(action.perform) # If the only argument is 'self' then don't pass the event! if len(argspec.args) == 1: @@ -201,7 +199,7 @@ else: action.perform(action_event) - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- def _enabled_changed(self): """ Called when our 'enabled' trait is changed. """ @@ -218,42 +216,59 @@ if self.control is not None: self.control.setChecked(self.checked) - def _on_action_enabled_changed(self, action, trait_name, old, new): + def _on_action_enabled_changed(self, event): """ Called when the enabled trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setEnabled(action.enabled) - def _on_action_visible_changed(self, action, trait_name, old, new): + def _on_action_visible_changed(self, event): """ Called when the visible trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setVisible(action.visible) - def _on_action_checked_changed(self, action, trait_name, old, new): + def _on_action_checked_changed(self, event): """ Called when the checked trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setChecked(action.checked) - def _on_action_name_changed(self, action, trait_name, old, new): + def _on_action_name_changed(self, event): """ Called when the name trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setText(action.name) - def _on_action_accelerator_changed(self, action, trait_name, old, new): + def _on_action_accelerator_changed(self, event): """ Called when the accelerator trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setShortcut(action.accelerator) + def _on_action_image_changed(self, event): + """ Called when the accelerator trait is changed on an action. """ + action = event.object + if self.control is not None: + self.control.setIcon(action.image.create_icon()) + + def _on_action_tooltip_changed(self, event): + """ Called when the accelerator trait is changed on an action. """ + action = event.object + if self.control is not None: + self.control.setToolTip(action.tooltip) + class _Tool(HasTraits): """ A tool bar tool representation of an action item. """ - #### '_Tool' interface #################################################### + # '_Tool' interface ---------------------------------------------------- # Is the item checked? checked = Bool(False) # A controller object we delegate taking actions through (if any). - controller = Any + controller = Any() # Is the item enabled? enabled = Bool(True) @@ -263,7 +278,7 @@ # The radio group we are part of (None if the tool is not part of such a # group). - group = Any + group = Any() # The toolkit control. control = Any() @@ -271,19 +286,20 @@ # The toolkit control id. control_id = None - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, tool_bar, image_cache, item, controller, - show_labels): + def __init__( + self, parent, tool_bar, image_cache, item, controller, show_labels + ): """ Creates a new tool bar tool for an action item. """ self.item = item self.tool_bar = tool_bar action = item.action - if action.style == 'widget': + if action.style == "widget": widget = action.create_control(tool_bar) self.control = tool_bar.addWidget(widget) elif action.image is None: @@ -292,6 +308,7 @@ size = tool_bar.iconSize() image = action.image.create_icon((size.width(), size.height())) self.control = tool_bar.addAction(image, action.name) + tool_bar.tools.append(self) self.control.triggered.connect(self._qt4_on_triggered) @@ -300,10 +317,10 @@ self.control.setEnabled(action.enabled) self.control.setVisible(action.visible) - if action.style == 'toggle': + if action.style == "toggle": self.control.setCheckable(True) self.control.setChecked(action.checked) - elif action.style == 'radio': + elif action.style == "radio": # Create an action group if it hasn't already been done. try: ag = item.parent._qt4_ag @@ -322,12 +339,13 @@ # Listen for trait changes on the action (so that we can update its # enabled/disabled/checked state etc). - action.on_trait_change(self._on_action_enabled_changed, 'enabled') - action.on_trait_change(self._on_action_visible_changed, 'visible') - action.on_trait_change(self._on_action_checked_changed, 'checked') - action.on_trait_change(self._on_action_name_changed, 'name') - action.on_trait_change(self._on_action_accelerator_changed, - 'accelerator') + action.observe(self._on_action_enabled_changed, "enabled") + action.observe(self._on_action_visible_changed, "visible") + action.observe(self._on_action_checked_changed, "checked") + action.observe(self._on_action_name_changed, "name") + action.observe(self._on_action_accelerator_changed, "accelerator") + action.observe(self._on_action_image_changed, "image") + action.observe(self._on_action_tooltip_changed, "tooltip") # Detect if the control is destroyed. self.control.destroyed.connect(self._qt4_on_destroyed) @@ -336,9 +354,21 @@ self.controller = controller controller.add_to_toolbar(self) - ########################################################################### + def dispose(self): + action = self.item.action + action.observe(self._on_action_enabled_changed, "enabled", remove=True) + action.observe(self._on_action_visible_changed, "visible", remove=True) + action.observe(self._on_action_checked_changed, "checked", remove=True) + action.observe(self._on_action_name_changed, "name", remove=True) + action.observe( + self._on_action_accelerator_changed, "accelerator", remove=True + ) + action.observe(self._on_action_image_changed, "image", remove=True) + action.observe(self._on_action_tooltip_changed, "tooltip", remove=True) + + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _qt4_on_destroyed(self, control=None): """ Delete the reference to the control to avoid attempting to talk to @@ -360,7 +390,7 @@ # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. This is also # useful as Traits UI controllers *never* require the event. - argspec = getargspec(self.controller.perform) + argspec = getfullargspec(self.controller.perform) # If the only arguments are 'self' and 'action' then don't pass # the event! @@ -376,7 +406,7 @@ # Most of the time, action's do no care about the event (it # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. - argspec = getargspec(action.perform) + argspec = getfullargspec(action.perform) # If the only argument is 'self' then don't pass the event! if len(argspec.args) == 1: @@ -385,7 +415,7 @@ else: action.perform(action_event) - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- def _enabled_changed(self): """ Called when our 'enabled' trait is changed. """ @@ -402,44 +432,64 @@ if self.control is not None: self.control.setChecked(self.checked) - def _on_action_enabled_changed(self, action, trait_name, old, new): + def _on_action_enabled_changed(self, event): """ Called when the enabled trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setEnabled(action.enabled) - def _on_action_visible_changed(self, action, trait_name, old, new): + def _on_action_visible_changed(self, event): """ Called when the visible trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setVisible(action.visible) - def _on_action_checked_changed(self, action, trait_name, old, new): + def _on_action_checked_changed(self, event): """ Called when the checked trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setChecked(action.checked) - def _on_action_name_changed(self, action, trait_name, old, new): + def _on_action_name_changed(self, event): """ Called when the name trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setText(action.name) - def _on_action_accelerator_changed(self, action, trait_name, old, new): + def _on_action_accelerator_changed(self, event): """ Called when the accelerator trait is changed on an action. """ + action = event.object if self.control is not None: self.control.setShortcut(action.accelerator) + def _on_action_image_changed(self, event): + """ Called when the accelerator trait is changed on an action. """ + action = event.object + if self.control is not None: + size = self.tool_bar.iconSize() + self.control.setIcon( + action.image.create_icon((size.width(), size.height())) + ) + + def _on_action_tooltip_changed(self, event): + """ Called when the accelerator trait is changed on an action. """ + action = event.object + if self.control is not None: + self.control.setToolTip(action.tooltip) + class _PaletteTool(HasTraits): """ A tool palette representation of an action item. """ - #### '_PaletteTool' interface ############################################# + # '_PaletteTool' interface --------------------------------------------- # The radio group we are part of (None if the tool is not part of such a # group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, tool_palette, image_cache, item, show_labels): """ Creates a new tool palette tool for an action item. """ @@ -452,70 +502,70 @@ if action.style == "widget": raise NotImplementedError( - "Qt does not support widgets in palettes") + "Qt does not support widgets in palettes" + ) # Tool palette tools never have '...' at the end. - if label.endswith('...'): + if label.endswith("..."): label = label[:-3] # And they never contain shortcuts. - label = label.replace('&', '') + label = label.replace("&", "") - image = action.image.create_image() path = action.image.absolute_path bmp = image_cache.get_bitmap(path) - kind = action.style + kind = action.style tooltip = action.tooltip longtip = action.description if not show_labels: - label = '' + label = "" # Add the tool to the tool palette. - self.tool_id = tool_palette.add_tool(label, bmp, kind, tooltip,longtip) + self.tool_id = tool_palette.add_tool( + label, bmp, kind, tooltip, longtip + ) tool_palette.toggle_tool(self.tool_id, action.checked) tool_palette.enable_tool(self.tool_id, action.enabled) tool_palette.on_tool_event(self.tool_id, self._on_tool) # Listen to the trait changes on the action (so that we can update its # enabled/disabled/checked state etc). - action.on_trait_change(self._on_action_enabled_changed, 'enabled') - action.on_trait_change(self._on_action_checked_changed, 'checked') + action.observe(self._on_action_enabled_changed, "enabled") + action.observe(self._on_action_checked_changed, "checked") return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_action_enabled_changed(self, action, trait_name, old, new): + def _on_action_enabled_changed(self, event): """ Called when the enabled trait is changed on an action. """ - + action = event.object self.tool_palette.enable_tool(self.tool_id, action.enabled) - return - - def _on_action_checked_changed(self, action, trait_name, old, new): + def _on_action_checked_changed(self, event): """ Called when the checked trait is changed on an action. """ - - if action.style == 'radio': + action = event.object + if action.style == "radio": # If we're turning this one on, then we need to turn all the others # off. But if we're turning this one off, don't worry about the # others. - if new: + if event.new: for item in self.item.parent.items: if item is not self.item: item.action.checked = False # This will *not* emit a tool event. - self.tool_palette.toggle_tool(self.tool_id, new) + self.tool_palette.toggle_tool(self.tool_id, event.new) return - #### Tool palette event handlers ########################################## + # Tool palette event handlers -----------------------------------------# def _on_tool(self, event): """ Called when the tool palette button is clicked. """ @@ -523,10 +573,13 @@ action = self.item.action action_event = ActionEvent() - is_checkable = (action.style == 'radio' or action.style == 'check') - # Perform the action! action.checked = self.tool_palette.get_tool_state(self.tool_id) == 1 action.perform(action_event) return + + def dispose(self): + action = self.item.action + action.observe(self._on_action_enabled_changed, "enabled", remove=True) + action.observe(self._on_action_checked_changed, "checked", remove=True) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/action/__init__.py python-pyface-7.4.0/pyface/ui/qt4/action/__init__.py --- python-pyface-6.1.2/pyface/ui/qt4/action/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/action/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/qt4/action/menu_bar_manager.py python-pyface-7.4.0/pyface/ui/qt4/action/menu_bar_manager.py --- python-pyface-6.1.2/pyface/ui/qt4/action/menu_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/action/menu_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,35 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ + """ The PyQt specific implementation of a menu bar manager. """ -# Standard library imports. + import sys -# Major package imports. + from pyface.qt import QtGui -# Local imports. + from pyface.action.action_manager import ActionManager class MenuBarManager(ActionManager): """ A menu bar manager realizes itself in errr, a menu bar control. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'MenuBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu_bar(self, parent, controller=None): """ Creates a menu bar representation of the manager. """ @@ -40,7 +43,7 @@ # Create the menu bar. Work around disappearing menu bars on OS X # (particulary on PySide but also under certain circumstances on PyQt4). - if isinstance(parent, QtGui.QMainWindow) and sys.platform == 'darwin': + if isinstance(parent, QtGui.QMainWindow) and sys.platform == "darwin": parent.menuBar().setParent(None) menu_bar = parent.menuBar() else: @@ -54,5 +57,3 @@ menu_bar.addMenu(menu) return menu_bar - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/action/menu_manager.py python-pyface-7.4.0/pyface/ui/qt4/action/menu_manager.py --- python-pyface-6.1.2/pyface/ui/qt4/action/menu_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/action/menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ + """ The PyQt specific implementation of a menu manager. """ -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Instance, Unicode -# Local imports. +from traits.api import Instance, List, Str + + from pyface.action.action_manager import ActionManager from pyface.action.action_manager_item import ActionManagerItem from pyface.action.action_item import _Tool, Action @@ -31,18 +33,23 @@ This could be a sub-menu or a context (popup) menu. """ - #### 'MenuManager' interface ############################################## + # 'MenuManager' interface ---------------------------------------------# # The menu manager's name (if the manager is a sub-menu, this is what its # label will be). - name = Unicode + name = Str() # The default action for tool button when shown in a toolbar (Qt only) action = Instance(Action) - ########################################################################### + # Private interface ---------------------------------------------------# + + #: Keep track of all created menus in order to properly dispose of them + _menus = List() + + # ------------------------------------------------------------------------ # 'MenuManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu(self, parent, controller=None): """ Creates a menu representation of the manager. """ @@ -54,11 +61,25 @@ if controller is None: controller = self.controller - return _Menu(self, parent, controller) + menu = _Menu(self, parent, controller) + self._menus.append(menu) + + return menu + + # ------------------------------------------------------------------------ + # 'ActionManager' interface. + # ------------------------------------------------------------------------ + + def destroy(self): + while self._menus: + menu = self._menus.pop() + menu.dispose() - ########################################################################### + super().destroy() + + # ------------------------------------------------------------------------ # 'ActionManagerItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_to_menu(self, parent, menu, controller): """ Adds the item to a menu. """ @@ -67,13 +88,15 @@ submenu.menuAction().setText(self.name) menu.addMenu(submenu) - def add_to_toolbar(self, parent, tool_bar, image_cache, controller, - show_labels=True): + def add_to_toolbar( + self, parent, tool_bar, image_cache, controller, show_labels=True + ): """ Adds the item to a tool bar. """ menu = self.create_menu(parent, controller) if self.action: tool_action = _Tool( - parent, tool_bar, image_cache, self, controller, show_labels).control + parent, tool_bar, image_cache, self, controller, show_labels + ).control tool_action.setMenu(menu) else: tool_action = menu.menuAction() @@ -81,16 +104,19 @@ tool_action.setText(self.name) tool_button = tool_bar.widgetForAction(tool_action) - tool_button.setPopupMode(tool_button.MenuButtonPopup if self.action - else tool_button.InstantPopup) + tool_button.setPopupMode( + tool_button.MenuButtonPopup + if self.action + else tool_button.InstantPopup + ) class _Menu(QtGui.QMenu): """ The toolkit-specific menu control. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, manager, parent, controller): """ Creates a new tree. """ @@ -114,46 +140,57 @@ self.refresh() # Listen to the manager being updated. - self._manager.on_trait_change(self.refresh, 'changed') - self._manager.on_trait_change(self._on_enabled_changed, 'enabled') - self._manager.on_trait_change(self._on_visible_changed, 'visible') - self._manager.on_trait_change(self._on_name_changed, 'name') + self._manager.observe(self.refresh, "changed") + self._manager.observe(self._on_enabled_changed, "enabled") + self._manager.observe(self._on_visible_changed, "visible") + self._manager.observe(self._on_name_changed, "name") + self._manager.observe(self._on_image_changed, "action:image") self.setEnabled(self._manager.enabled) self.menuAction().setVisible(self._manager.visible) return - ########################################################################### + def dispose(self): + self._manager.observe(self.refresh, "changed", remove=True) + self._manager.observe(self._on_enabled_changed, "enabled", remove=True) + self._manager.observe(self._on_visible_changed, "visible", remove=True) + self._manager.observe(self._on_name_changed, "name", remove=True) + self._manager.observe(self._on_image_changed, "action:image", remove=True) + # Removes event listeners from downstream menu items + self.clear() + + # ------------------------------------------------------------------------ # '_Menu' interface. - ########################################################################### + # ------------------------------------------------------------------------ def clear(self): """ Clears the items from the menu. """ for item in self.menu_items: item.dispose() - + self.menu_items = [] - super(_Menu, self).clear() + super().clear() def is_empty(self): """ Is the menu empty? """ return self.isEmpty() - def refresh(self): + def refresh(self, event=None): """ Ensures that the menu reflects the state of the manager. """ self.clear() manager = self._manager - parent = self._parent + parent = self._parent previous_non_empty_group = None for group in manager.groups: - previous_non_empty_group = self._add_group(parent, group, - previous_non_empty_group) + previous_non_empty_group = self._add_group( + parent, group, previous_non_empty_group + ) self.setEnabled(manager.enabled) @@ -166,28 +203,29 @@ point = QtCore.QPoint(x, y) self.popup(point) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_enabled_changed(self, obj, trait_name, old, new): + def _on_enabled_changed(self, event): """ Dynamic trait change handler. """ - self.setEnabled(new) + self.setEnabled(event.new) - def _on_visible_changed(self, obj, trait_name, old, new): + def _on_visible_changed(self, event): """ Dynamic trait change handler. """ - self.menuAction().setVisible(new) - - return + self.menuAction().setVisible(event.new) - def _on_name_changed(self, obj, trait_name, old, new): + def _on_name_changed(self, event): """ Dynamic trait change handler. """ - self.menuAction().setText(new) + self.menuAction().setText(event.new) - return + def _on_image_changed(self, event): + """ Dynamic trait change handler. """ + + self.menuAction().setIcon(event.new.create_icon()) def _add_group(self, parent, group, previous_non_empty_group=None): """ Adds a group to a menu. """ @@ -204,9 +242,11 @@ if len(item.items) > 0: self._add_group(parent, item, previous_non_empty_group) - if previous_non_empty_group is not None \ - and previous_non_empty_group.separator \ - and item.separator: + if ( + previous_non_empty_group is not None + and previous_non_empty_group.separator + and item.separator + ): self.addSeparator() previous_non_empty_group = item @@ -217,5 +257,3 @@ previous_non_empty_group = group return previous_non_empty_group - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/action/status_bar_manager.py python-pyface-7.4.0/pyface/ui/qt4/action/status_bar_manager.py --- python-pyface-6.1.2/pyface/ui/qt4/action/status_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/action/status_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + -# Major package imports. from pyface.qt import QtGui -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, List, Property, Str, \ - Unicode + +from traits.api import Any, Bool, HasTraits, List, Property, Str class StatusBarManager(HasTraits): @@ -22,10 +27,10 @@ message = Property # The messages to be displayed in the status bar fields. - messages = List(Unicode) + messages = List(Str) # The toolkit-specific control that represents the status bar. - status_bar = Any + status_bar = Any() # Whether to show a size grip on the status bar. size_grip = Bool(False) @@ -33,9 +38,9 @@ # Whether the status bar is visible. visible = Bool(True) - ########################################################################### + # ------------------------------------------------------------------------ # 'StatusBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_status_bar(self, parent): """ Creates a status bar. """ @@ -58,16 +63,16 @@ self.status_bar.deleteLater() self.status_bar = None - ########################################################################### + # ------------------------------------------------------------------------ # Property handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _get_message(self): if len(self.messages) > 0: message = self.messages[0] else: - message = '' + message = "" return message @@ -77,14 +82,14 @@ old = self.messages[0] self.messages[0] = value else: - old = '' + old = "" self.messages.append(old) - self.trait_property_changed('message', old, value) + self.trait_property_changed("message", old, value) - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _messages_changed(self): """ Sets the text displayed on the status bar. """ @@ -110,9 +115,9 @@ if self.status_bar is not None: self.status_bar.setVisible(self.visible) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _show_messages(self): """ Display the list of messages. """ @@ -122,6 +127,3 @@ # probably also need to extend the API to allow a "message" to be a # widget - depends on what wx is capable of. self.status_bar.showMessage(" ".join(self.messages)) - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/action/tool_bar_manager.py python-pyface-7.4.0/pyface/ui/qt4/action/tool_bar_manager.py --- python-pyface-6.1.2/pyface/ui/qt4/action/tool_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/action/tool_bar_manager.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,19 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, Enum, Instance, Str, Tuple -# Local imports. +from traits.api import Bool, Enum, Instance, List, Str, Tuple + + from pyface.image_cache import ImageCache from pyface.action.action_manager import ActionManager @@ -21,7 +27,7 @@ class ToolBarManager(ActionManager): """ A tool bar manager realizes itself in errr, a tool bar control. """ - #### 'ToolBarManager' interface ########################################### + # 'ToolBarManager' interface ------------------------------------------- # Is the tool bar enabled? enabled = Bool(True) @@ -33,10 +39,10 @@ image_size = Tuple((16, 16)) # The toolbar name (used to distinguish multiple toolbars). - name = Str('ToolBar') + name = Str("ToolBar") # The orientation of the toolbar. - orientation = Enum('horizontal', 'vertical') + orientation = Enum("horizontal", "vertical") # Should we display the name of each tool bar tool under its image? show_tool_names = Bool(True) @@ -44,20 +50,23 @@ # Should we display the horizontal divider? show_divider = Bool(True) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Cache of tool images (scaled to the appropriate size). _image_cache = Instance(ImageCache) - ########################################################################### + #: Keep track of all created toolbars in order to properly dispose of them + _toolbars = List() + + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *args, **traits): """ Creates a new tool bar manager. """ # Base class constructor. - super(ToolBarManager, self).__init__(*args, **traits) + super().__init__(*args, **traits) # An image cache to make sure that we only load each image used in the # tool bar exactly once. @@ -65,9 +74,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ToolBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_tool_bar(self, parent, controller=None): """ Creates a tool bar. """ @@ -81,16 +90,17 @@ # Create the control. tool_bar = _ToolBar(self, parent) + self._toolbars.append(tool_bar) tool_bar.setObjectName(self.id) tool_bar.setWindowTitle(self.name) if self.show_tool_names: - tool_bar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon) + tool_bar.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextUnderIcon) - if self.orientation == 'horizontal': - tool_bar.setOrientation(QtCore.Qt.Horizontal) + if self.orientation == "horizontal": + tool_bar.setOrientation(QtCore.Qt.Orientation.Horizontal) else: - tool_bar.setOrientation(QtCore.Qt.Vertical) + tool_bar.setOrientation(QtCore.Qt.Orientation.Vertical) # We would normally leave it to the current style to determine the icon # size. @@ -102,9 +112,20 @@ return tool_bar - ########################################################################### + # ------------------------------------------------------------------------ + # 'ActionManager' interface. + # ------------------------------------------------------------------------ + + def destroy(self): + while self._toolbars: + toolbar = self._toolbars.pop() + toolbar.dispose() + + super().destroy() + + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _qt4_add_tools(self, parent, tool_bar, controller): """ Adds tools for all items in the list of groups. """ @@ -115,8 +136,9 @@ # Is a separator required? if previous_non_empty_group is not None and group.separator: separator = tool_bar.addSeparator() - group.on_trait_change(self._separator_visibility_method(separator), - 'visible') + group.observe( + self._separator_visibility_method(separator), "visible" + ) previous_non_empty_group = group @@ -127,58 +149,69 @@ tool_bar, self._image_cache, controller, - self.show_tool_names + self.show_tool_names, ) - return - def _separator_visibility_method(self, separator): """ Method to return closure to set visibility of group separators. """ - return lambda visible: separator.setVisible(visible) + return lambda event: separator.setVisible(event.new) class _ToolBar(QtGui.QToolBar): """ The toolkit-specific tool bar implementation. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, tool_bar_manager, parent): """ Constructor. """ QtGui.QToolBar.__init__(self, parent) + # List of tools + self.tools = [] + # Listen for changes to the tool bar manager's enablement and # visibility. self.tool_bar_manager = tool_bar_manager - self.tool_bar_manager.on_trait_change( - self._on_tool_bar_manager_enabled_changed, 'enabled' + self.tool_bar_manager.observe( + self._on_tool_bar_manager_enabled_changed, "enabled" ) - self.tool_bar_manager.on_trait_change( - self._on_tool_bar_manager_visible_changed, 'visible' + self.tool_bar_manager.observe( + self._on_tool_bar_manager_visible_changed, "visible" ) return - ########################################################################### + def dispose(self): + self.tool_bar_manager.observe( + self._on_tool_bar_manager_enabled_changed, "enabled", remove=True + ) + self.tool_bar_manager.observe( + self._on_tool_bar_manager_visible_changed, "visible", remove=True + ) + # Removes event listeners from downstream tools and clears their + # references + for item in self.tools: + item.dispose() + + self.tools = [] + + # ------------------------------------------------------------------------ # Trait change handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_tool_bar_manager_enabled_changed(self, obj, trait_name, old, new): + def _on_tool_bar_manager_enabled_changed(self, event): """ Dynamic trait change handler. """ - self.setEnabled(new) - - return + self.setEnabled(event.new) - def _on_tool_bar_manager_visible_changed(self, obj, trait_name, old, new): + def _on_tool_bar_manager_visible_changed(self, event): """ Dynamic trait change handler. """ - self.setVisible(new) + self.setVisible(event.new) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/application_window.py python-pyface-7.4.0/pyface/ui/qt4/application_window.py --- python-pyface-6.1.2/pyface/ui/qt4/application_window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/application_window.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,30 +1,35 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described -# in the PyQt GPL exception also apply. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import sys -# Major package imports. + from pyface.qt import QtGui -# Enthought library imports. + from pyface.action.api import MenuBarManager, StatusBarManager from pyface.action.api import ToolBarManager -from traits.api import Instance, List, on_trait_change, provides, Unicode +from traits.api import Instance, List, observe, provides, Str + -# Local imports. -from pyface.i_application_window import ( - IApplicationWindow, MApplicationWindow -) -from pyface.image_resource import ImageResource +from pyface.i_application_window import IApplicationWindow, MApplicationWindow +from pyface.ui_traits import Image +from .image_resource import ImageResource from .window import Window @@ -34,10 +39,10 @@ IApplicationWindow interface for the API documentation. """ - #### 'IApplicationWindow' interface ####################################### + # 'IApplicationWindow' interface --------------------------------------- #: The icon to display in the application window title bar. - icon = Instance(ImageResource) + icon = Image() #: The menu bar manager for the window. menu_bar_manager = Instance(MenuBarManager) @@ -51,20 +56,20 @@ #: The collection of tool bar managers for the window. tool_bar_managers = List(ToolBarManager) - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------# #: The window title. - title = Unicode("Pyface") + title = Str("Pyface") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): panel = QtGui.QWidget(parent) palette = QtGui.QPalette(panel.palette()) - palette.setColor(QtGui.QPalette.Window, QtGui.QColor('blue')) + palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("blue")) panel.setPalette(palette) panel.setAutoFillBackground(True) @@ -97,7 +102,7 @@ if len(tool_bar.objectName()) == 0: tool_bar.setObjectName(tool_bar_manager.name) - if sys.platform == 'darwin': + if sys.platform == "darwin": # Work around bug in Qt on OS X where creating a tool bar with a # QMainWindow parent hides the window. See # http://bugreports.qt.nokia.com/browse/QTBUG-5069 for more info. @@ -105,27 +110,27 @@ def _set_window_icon(self): if self.icon is None: - icon = ImageResource('application.png') + icon = ImageResource("application.png") else: icon = self.icon if self.control is not None: self.control.setWindowIcon(icon.create_icon()) - ########################################################################### + # ------------------------------------------------------------------------ # 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _size_default(self): """ Trait initialiser. """ return (800, 600) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create(self): - super(ApplicationWindow, self)._create() + super()._create() contents = self._create_contents(self.control) self.control.setCentralWidget(contents) @@ -133,17 +138,17 @@ self._create_trim_widgets(self.control) def _create_control(self, parent): - control = super(ApplicationWindow, self)._create_control(parent) - control.setObjectName('ApplicationWindow') + control = super()._create_control(parent) + control.setObjectName("ApplicationWindow") control.setAnimated(False) control.setDockNestingEnabled(True) return control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_tool_bar_managers(self): """ Return all tool bar managers specified for the window. """ @@ -157,24 +162,26 @@ return tool_bar_managers - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ # QMainWindow takes ownership of the menu bar and the status bar upon # assignment. For this reason, it is unnecessary to delete the old controls # in the following two handlers. - def _menu_bar_manager_changed(self): + @observe("menu_bar_manager") + def _menu_bar_manager_updated(self, event): if self.control is not None: self._create_menu_bar(self.control) - def _status_bar_manager_changed(self, old, new): + @observe("status_bar_manager") + def _status_bar_manager_updated(self, event): if self.control is not None: - if old is not None: - old.destroy_status_bar() + if event.old is not None: + event.old.destroy_status_bar() self._create_status_bar(self.control) - @on_trait_change('tool_bar_manager, tool_bar_managers') - def _update_tool_bar_managers(self): + @observe("tool_bar_manager, tool_bar_managers.items") + def _update_tool_bar_managers(self, event): if self.control is not None: # Remove the old toolbars. for child in self.control.children(): @@ -185,5 +192,6 @@ # Add the new toolbars. self._create_tool_bar(self.control) - def _icon_changed(self): + @observe("icon") + def _icon_updated(self, event): self._set_window_icon() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/beep.py python-pyface-7.4.0/pyface/ui/qt4/beep.py --- python-pyface-6.1.2/pyface/ui/qt4/beep.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/beep.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # Copyright 2012 Philip Chimento """Sound the system bell, Qt implementation.""" diff -Nru python-pyface-6.1.2/pyface/ui/qt4/clipboard.py python-pyface-7.4.0/pyface/ui/qt4/clipboard.py --- python-pyface-6.1.2/pyface/ui/qt4/clipboard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/clipboard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2009, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Evan Patterson # Date: 06/26/09 -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + -# Standard library imports from io import BytesIO -from six.moves.cPickle import dumps, load, loads +from pickle import dumps, load, loads + -# System library imports from pyface.qt import QtCore, QtGui -# ETS imports + from traits.api import provides from pyface.i_clipboard import IClipboard, BaseClipboard -import six # Shortcuts cb = QtGui.QApplication.clipboard() @@ -34,23 +33,25 @@ @provides(IClipboard) class Clipboard(BaseClipboard): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_has_data(self): return self.has_object_data or self.has_text_data or self.has_file_data - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'object_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_object_data(self): obj = None mime_data = cb.mimeData() if mime_data.hasFormat(PYTHON_TYPE): serialized_data = BytesIO(mime_data.data(PYTHON_TYPE).data()) - klass = load(serialized_data) + # Loading the serialized data the first time returns the klass + _ = load(serialized_data) + # Loading it a second time returns the actual object obj = load(serialized_data) return obj @@ -64,7 +65,7 @@ return cb.mimeData().hasFormat(PYTHON_TYPE) def _get_object_type(self): - result = '' + result = "" mime_data = cb.mimeData() if mime_data.hasFormat(PYTHON_TYPE): try: @@ -74,9 +75,9 @@ pass return result - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'text_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_text_data(self): return cb.text() @@ -87,9 +88,9 @@ def _get_has_text_data(self): return cb.mimeData().hasText() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'file_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_file_data(self): mime_data = cb.mimeData() @@ -99,11 +100,11 @@ return [] def _set_file_data(self, data): - if isinstance(data, six.string_types): + if isinstance(data, str): data = [data] mime_data = QtCore.QMimeData() mime_data.setUrls([QtCore.QUrl(path) for path in data]) cb.setMimeData(mime_data) - def _get_has_file_data (self): + def _get_has_file_data(self): return cb.mimeData().hasUrls() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/code_widget.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/code_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/code_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/code_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,41 +1,39 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + -# Standard library imports -import math import sys -# System library imports + from pyface.qt import QtCore, QtGui -# Local imports + from .find_widget import FindWidget from .gutters import LineNumberWidget, StatusGutterWidget from .replace_widget import ReplaceWidget from .pygments_highlighter import PygmentsHighlighter -import six class CodeWidget(QtGui.QPlainTextEdit): """ A widget for viewing and editing code. """ - ########################################################################### + # ------------------------------------------------------------------------ # CodeWidget interface - ########################################################################### + # ------------------------------------------------------------------------ focus_lost = QtCore.Signal() - def __init__(self, parent, should_highlight_current_line=True, font=None, - lexer=None): - super(CodeWidget, self).__init__(parent) + def __init__( + self, parent, should_highlight_current_line=False, font=None, lexer=None + ): + super().__init__(parent) self.highlighter = PygmentsHighlighter(self.document(), lexer) self.line_number_widget = LineNumberWidget(self) @@ -44,15 +42,15 @@ if font is None: # Set a decent fixed width font for this platform. font = QtGui.QFont() - if sys.platform == 'win32': + if sys.platform == "win32": # Prefer Consolas, but fall back to Courier if necessary. - font.setFamily('Consolas') + font.setFamily("Consolas") if not font.exactMatch(): - font.setFamily('Courier') - elif sys.platform == 'darwin': - font.setFamily('Monaco') + font.setFamily("Courier") + elif sys.platform == "darwin": + font.setFamily("Monaco") else: - font.setFamily('Monospace') + font.setFamily("Monospace") font.setStyleHint(QtGui.QFont.TypeWriter) self.set_font(font) @@ -60,7 +58,7 @@ self.should_highlight_current_line = should_highlight_current_line # What that highlight color should be. - self.line_highlight_color = QtGui.QColor(QtCore.Qt.yellow).lighter(160) + self.line_highlight_color = self.palette().alternateBase() # Auto-indentation behavior self.auto_indent = True @@ -70,8 +68,8 @@ self.tabs_as_spaces = True self.tab_width = 4 - self.indent_character = ':' - self.comment_character = '#' + self.indent_character = ":" + self.comment_character = "#" # Set up gutter widget and current line highlighting self.blockCountChanged.connect(self.update_line_number_width) @@ -82,13 +80,22 @@ self.highlight_current_line() # Don't wrap text - self.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) + self.setLineWrapMode(QtGui.QPlainTextEdit.LineWrapMode.NoWrap) # Key bindings - self.indent_key = QtGui.QKeySequence(QtCore.Qt.Key_Tab) - self.unindent_key = QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Backtab) - self.comment_key = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Slash) - self.backspace_key = QtGui.QKeySequence(QtCore.Qt.Key_Backspace) + self.indent_key = QtGui.QKeySequence(QtCore.Qt.Key.Key_Tab) + self.unindent_key = QtGui.QKeySequence( + QtCore.Qt.Modifier.SHIFT + QtCore.Qt.Key.Key_Backtab + ) + self.comment_key = QtGui.QKeySequence( + QtCore.Qt.Modifier.CTRL + QtCore.Qt.Key.Key_Slash + ) + self.backspace_key = QtGui.QKeySequence(QtCore.Qt.Key.Key_Backspace) + + def _remove_event_listeners(self): + self.blockCountChanged.disconnect(self.update_line_number_width) + self.updateRequest.disconnect(self.update_line_numbers) + self.cursorPositionChanged.disconnect(self.highlight_current_line) def lines(self): """ Return the number of lines. @@ -130,7 +137,7 @@ def get_selected_text(self): """ Return the currently selected text. """ - return six.text_type(self.textCursor().selectedText()) + return str(self.textCursor().selectedText()) def set_font(self, font): """ Set the new QFont. @@ -153,7 +160,8 @@ if dy: self.line_number_widget.scroll(0, dy) self.line_number_widget.update( - 0, rect.y(), self.line_number_widget.width(), rect.height()) + 0, rect.y(), self.line_number_widget.width(), rect.height() + ) if rect.contains(self.viewport().rect()): self.update_line_number_width() @@ -176,15 +184,16 @@ selection = QtGui.QTextEdit.ExtraSelection() selection.format.setBackground(self.line_highlight_color) selection.format.setProperty( - QtGui.QTextFormat.FullWidthSelection, True) + QtGui.QTextFormat.Property.FullWidthSelection, True + ) selection.cursor = self.textCursor() selection.cursor.clearSelection() self.setExtraSelections([selection]) def autoindent_newline(self): - tab = '\t' + tab = "\t" if self.tabs_as_spaces: - tab = ' '*self.tab_width + tab = " " * self.tab_width cursor = self.textCursor() text = cursor.block().text() @@ -245,7 +254,7 @@ cursor.beginEditBlock() removed = self.line_unindent(cursor) - position = max(position-removed, 0) + position = max(position - removed, 0) cursor.endEditBlock() cursor.setPosition(position) @@ -293,8 +302,10 @@ comment = True for block in sel_blocks: text = block.text() - if len(text) > indent_pos and \ - text[indent_pos] == self.comment_character: + if ( + len(text) > indent_pos + and text[indent_pos] == self.comment_character + ): # Already commented. comment = False break @@ -306,7 +317,7 @@ cursor.setPosition(block.position()) if comment: if block.length() < indent_pos: - cursor.insertText(' ' * indent_pos) + cursor.insertText(" " * indent_pos) self.line_comment(cursor, indent_pos) else: self.line_uncomment(cursor, indent_pos) @@ -314,24 +325,26 @@ self._show_selected_blocks(sel_blocks) def line_comment(self, cursor, position): - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) - cursor.movePosition(QtGui.QTextCursor.Right, - QtGui.QTextCursor.MoveAnchor, position) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Right, QtGui.QTextCursor.MoveMode.MoveAnchor, position + ) cursor.insertText(self.comment_character) def line_uncomment(self, cursor, position=0): - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) text = cursor.block().text() - new_text = text[:position] + text[position+1:] - cursor.movePosition(QtGui.QTextCursor.EndOfBlock, - QtGui.QTextCursor.KeepAnchor) + new_text = text[:position] + text[position + 1:] + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.EndOfBlock, QtGui.QTextCursor.MoveMode.KeepAnchor + ) cursor.removeSelectedText() cursor.insertText(new_text) def line_indent(self, cursor): - tab = '\t' + tab = "\t" if self.tabs_as_spaces: - tab = ' ' + tab = " " cursor.insertText(tab) @@ -339,15 +352,16 @@ """ Unindents the cursor's line. Returns the number of characters removed. """ - tab = '\t' + tab = "\t" if self.tabs_as_spaces: - tab = ' ' + tab = " " - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) if cursor.block().text().startswith(tab): new_text = cursor.block().text()[len(tab):] - cursor.movePosition(QtGui.QTextCursor.EndOfBlock, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.EndOfBlock, QtGui.QTextCursor.MoveMode.KeepAnchor + ) cursor.removeSelectedText() cursor.insertText(new_text) return len(tab) @@ -358,12 +372,12 @@ """ Return the word under the cursor. """ cursor = self.textCursor() - cursor.select(QtGui.QTextCursor.WordUnderCursor) - return six.text_type(cursor.selectedText()) + cursor.select(QtGui.QTextCursor.SelectionType.WordUnderCursor) + return str(cursor.selectedText()) - ########################################################################### + # ------------------------------------------------------------------------ # QWidget interface - ########################################################################### + # ------------------------------------------------------------------------ # FIXME: This is a quick hack to be able to access the keyPressEvent # from the rest editor. This should be changed to work within the traits @@ -373,30 +387,31 @@ def keyPressEvent(self, event): if self.isReadOnly(): - return super(CodeWidget, self).keyPressEvent(event) + return super().keyPressEvent(event) key_sequence = QtGui.QKeySequence(event.key() + int(event.modifiers())) - self.keyPressEvent_action(event) # FIXME: see above + self.keyPressEvent_action(event) # FIXME: see above # If the cursor is in the middle of the first line, pressing the "up" # key causes the cursor to go to the start of the first line, i.e. the # beginning of the document. Likewise, if the cursor is somewhere in the # last line, the "down" key causes it to go to the end. cursor = self.textCursor() - if key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key_Up)): - cursor.movePosition(QtGui.QTextCursor.StartOfLine) + if key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key.Key_Up)): + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfLine) if cursor.atStart(): self.setTextCursor(cursor) event.accept() - elif key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key_Down)): - cursor.movePosition(QtGui.QTextCursor.EndOfLine) + elif key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key.Key_Down)): + cursor.movePosition(QtGui.QTextCursor.MoveOperation.EndOfLine) if cursor.atEnd(): self.setTextCursor(cursor) event.accept() - elif self.auto_indent and \ - key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key_Return)): + elif self.auto_indent and key_sequence.matches( + QtGui.QKeySequence(QtCore.Qt.Key.Key_Return) + ): event.accept() return self.autoindent_newline() elif key_sequence.matches(self.indent_key): @@ -408,49 +423,70 @@ elif key_sequence.matches(self.comment_key): event.accept() return self.block_comment() - elif self.auto_indent and self.smart_backspace and \ - key_sequence.matches(self.backspace_key) and \ - self._backspace_should_unindent(): + elif ( + self.auto_indent + and self.smart_backspace + and key_sequence.matches(self.backspace_key) + and self._backspace_should_unindent() + ): event.accept() return self.block_unindent() - return super(CodeWidget, self).keyPressEvent(event) + return super().keyPressEvent(event) def resizeEvent(self, event): QtGui.QPlainTextEdit.resizeEvent(self, event) contents = self.contentsRect() - self.line_number_widget.setGeometry(QtCore.QRect(contents.left(), - contents.top(), self.line_number_widget.digits_width(), - contents.height())) + self.line_number_widget.setGeometry( + QtCore.QRect( + contents.left(), + contents.top(), + self.line_number_widget.digits_width(), + contents.height(), + ) + ) # use the viewport width to determine the right edge. This allows for # the propper placement w/ and w/o the scrollbar - right_pos = self.viewport().width() + self.line_number_widget.width() + 1\ - - self.status_widget.sizeHint().width() - self.status_widget.setGeometry(QtCore.QRect(right_pos, - contents.top(), self.status_widget.sizeHint().width(), - contents.height())) + right_pos = ( + self.viewport().width() + + self.line_number_widget.width() + + 1 + - self.status_widget.sizeHint().width() + ) + self.status_widget.setGeometry( + QtCore.QRect( + right_pos, + contents.top(), + self.status_widget.sizeHint().width(), + contents.height(), + ) + ) def focusOutEvent(self, event): QtGui.QPlainTextEdit.focusOutEvent(self, event) self.focus_lost.emit() - def sizeHint(self): # Suggest a size that is 80 characters wide and 40 lines tall. style = self.style() opt = QtGui.QStyleOptionHeader() font_metrics = QtGui.QFontMetrics(self.document().defaultFont()) - width = font_metrics.width(' ') * 80 + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + width = font_metrics.horizontalAdvance(" ") * 80 + else: + width = font_metrics.width(" ") * 80 width += self.line_number_widget.sizeHint().width() width += self.status_widget.sizeHint().width() - width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent, opt, self) + width += style.pixelMetric(QtGui.QStyle.PixelMetric.PM_ScrollBarExtent, opt, self) height = font_metrics.height() * 40 return QtCore.QSize(width, height) - ########################################################################### + # ------------------------------------------------------------------------ # Private methods - ########################################################################### + # ------------------------------------------------------------------------ def _get_indent_position(self, line): trimmed = line.rstrip() @@ -466,30 +502,32 @@ cursor = self.textCursor() cursor.clearSelection() cursor.setPosition(selected_blocks[0].position()) - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) - cursor.movePosition(QtGui.QTextCursor.NextBlock, - QtGui.QTextCursor.KeepAnchor, len(selected_blocks)) - cursor.movePosition(QtGui.QTextCursor.EndOfBlock, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.NextBlock, + QtGui.QTextCursor.MoveMode.KeepAnchor, + len(selected_blocks), + ) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.EndOfBlock, QtGui.QTextCursor.MoveMode.KeepAnchor + ) self.setTextCursor(cursor) def _get_selected_blocks(self): cursor = self.textCursor() if cursor.position() > cursor.anchor(): - move_op = QtGui.QTextCursor.PreviousBlock start_pos = cursor.anchor() end_pos = cursor.position() else: - move_op = QtGui.QTextCursor.NextBlock start_pos = cursor.position() end_pos = cursor.anchor() cursor.setPosition(start_pos) - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) blocks = [cursor.block()] - while cursor.movePosition(QtGui.QTextCursor.NextBlock): + while cursor.movePosition(QtGui.QTextCursor.MoveOperation.NextBlock): block = cursor.block() if block.position() < end_pos: blocks.append(block) @@ -515,12 +553,12 @@ for search & replace """ - ########################################################################### + # ------------------------------------------------------------------------ # AdvancedCodeWidget interface - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, font=None, lexer=None): - super(AdvancedCodeWidget, self).__init__(parent) + super().__init__(parent) self.code = CodeWidget(self, font=font, lexer=lexer) self.find = FindWidget(self) @@ -541,7 +579,8 @@ self.replace.line_edit.returnPressed.connect(self.find_next) self.replace.line_edit.textChanged.connect( - self._update_replace_all_enabled) + self._update_replace_all_enabled + ) self.replace.next_button.clicked.connect(self.find_next) self.replace.prev_button.clicked.connect(self.find_prev) self.replace.replace_button.clicked.connect(self.replace_next) @@ -556,6 +595,24 @@ self.setLayout(layout) + def _remove_event_listeners(self): + self.code.selectionChanged.disconnect(self._update_replace_enabled) + + self.find.line_edit.returnPressed.disconnect(self.find_next) + self.find.next_button.clicked.disconnect(self.find_next) + self.find.prev_button.clicked.disconnect(self.find_prev) + + self.replace.line_edit.returnPressed.disconnect(self.find_next) + self.replace.line_edit.textChanged.disconnect( + self._update_replace_all_enabled + ) + self.replace.next_button.clicked.disconnect(self.find_next) + self.replace.prev_button.clicked.disconnect(self.find_prev) + self.replace.replace_button.clicked.disconnect(self.replace_next) + self.replace.replace_all_button.clicked.disconnect(self.replace_all) + + self.code._remove_event_listeners() + def lines(self): """ Return the number of lines. """ @@ -589,9 +646,10 @@ self.replace.hide() self.find.show() self.find.setFocus() - if (self.active_find_widget == self.replace or - (not self.active_find_widget and - self.previous_find_widget == self.replace)): + if self.active_find_widget == self.replace or ( + not self.active_find_widget + and self.previous_find_widget == self.replace + ): self.find.line_edit.setText(self.replace.line_edit.text()) self.find.line_edit.selectAll() self.active_find_widget = self.find @@ -600,14 +658,15 @@ self.find.hide() self.replace.show() self.replace.setFocus() - if (self.active_find_widget == self.find or - (not self.active_find_widget and - self.previous_find_widget == self.find)): + if self.active_find_widget == self.find or ( + not self.active_find_widget + and self.previous_find_widget == self.find + ): self.replace.line_edit.setText(self.find.line_edit.text()) self.replace.line_edit.selectAll() self.active_find_widget = self.replace - def find_in_document(self, search_text, direction='forward', replace=None): + def find_in_document(self, search_text, direction="forward", replace=None): """ Finds the next occurance of the desired text and optionally replaces it. If 'replace' is None, a regular search will be executed, otherwise it will replace the occurance with @@ -625,16 +684,18 @@ flags = QtGui.QTextDocument.FindFlags(0) if self.active_find_widget.case_action.isChecked(): - flags |= QtGui.QTextDocument.FindCaseSensitively + flags |= QtGui.QTextDocument.FindFlag.FindCaseSensitively if self.active_find_widget.word_action.isChecked(): - flags |= QtGui.QTextDocument.FindWholeWords - if direction == 'backward': - flags |= QtGui.QTextDocument.FindBackward + flags |= QtGui.QTextDocument.FindFlag.FindWholeWords + if direction == "backward": + flags |= QtGui.QTextDocument.FindFlag.FindBackward find_cursor = document.find(search_text, self.code.textCursor(), flags) if find_cursor.isNull() and wrap: - if direction == 'backward': - find_cursor = document.find(search_text, document.characterCount()-1, flags) + if direction == "backward": + find_cursor = document.find( + search_text, document.characterCount() - 1, flags + ) else: find_cursor = document.find(search_text, 0, flags) @@ -645,21 +706,27 @@ find_cursor.insertText(replace) find_cursor.endEditBlock() find_cursor.movePosition( - QtGui.QTextCursor.Left, QtGui.QTextCursor.MoveAnchor,len(replace)) + QtGui.QTextCursor.MoveOperation.Left, + QtGui.QTextCursor.MoveMode.MoveAnchor, + len(replace), + ) find_cursor.movePosition( - QtGui.QTextCursor.Right, QtGui.QTextCursor.KeepAnchor,len(replace)) + QtGui.QTextCursor.MoveOperation.Right, + QtGui.QTextCursor.MoveMode.KeepAnchor, + len(replace), + ) self.code.setTextCursor(find_cursor) else: self.code.setTextCursor(find_cursor) return find_cursor else: - #else not found: beep or indicate? + # else not found: beep or indicate? return None def find_next(self): if not self.active_find_widget: self.enable_find() - search_text = six.text_type(self.active_find_widget.line_edit.text()) + search_text = str(self.active_find_widget.line_edit.text()) cursor = self.find_in_document(search_text=search_text) if cursor: @@ -669,9 +736,10 @@ def find_prev(self): if not self.active_find_widget: self.enable_find() - search_text = six.text_type(self.active_find_widget.line_edit.text()) - cursor = self.find_in_document(search_text=search_text, - direction='backward') + search_text = str(self.active_find_widget.line_edit.text()) + cursor = self.find_in_document( + search_text=search_text, direction="backward" + ) if cursor: return 1 return 0 @@ -690,14 +758,17 @@ return 0 def replace_all(self): - search_text = six.text_type(self.replace.line_edit.text()) - replace_text = six.text_type(self.replace.replace_edit.text()) + search_text = str(self.replace.line_edit.text()) + replace_text = str(self.replace.replace_edit.text()) count = 0 cursor = self.code.textCursor() cursor.beginEditBlock() - while self.find_in_document(search_text=search_text, - replace=replace_text) != None: + while ( + self.find_in_document( + search_text=search_text, replace=replace_text + ) is not None + ): count += 1 cursor.endEditBlock() return count @@ -713,18 +784,18 @@ def centerCursor(self): self.code.centerCursor() - ########################################################################### + # ------------------------------------------------------------------------ # QWidget interface - ########################################################################### + # ------------------------------------------------------------------------ def keyPressEvent(self, event): key_sequence = QtGui.QKeySequence(event.key() + int(event.modifiers())) - if key_sequence.matches(QtGui.QKeySequence.Find): + if key_sequence.matches(QtGui.QKeySequence.StandardKey.Find): self.enable_find() - elif key_sequence.matches(QtGui.QKeySequence.Replace): + elif key_sequence.matches(QtGui.QKeySequence.StandardKey.Replace): if not self.code.isReadOnly(): self.enable_replace() - elif key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key_Escape)): + elif key_sequence.matches(QtGui.QKeySequence(QtCore.Qt.Key.Key_Escape)): if self.active_find_widget: self.find.hide() self.replace.hide() @@ -732,11 +803,11 @@ self.previous_find_widget = self.active_find_widget self.active_find_widget = None - return super(AdvancedCodeWidget, self).keyPressEvent(event) + return super().keyPressEvent(event) - ########################################################################### + # ------------------------------------------------------------------------ # Private methods - ########################################################################### + # ------------------------------------------------------------------------ def _update_replace_enabled(self): selection = self.code.textCursor().selectedText() @@ -747,24 +818,24 @@ self.replace.replace_all_button.setEnabled(len(text)) -if __name__ == '__main__': +if __name__ == "__main__": def set_trace(): from PyQt4.QtCore import pyqtRemoveInputHook + pyqtRemoveInputHook() import pdb - pdb.Pdb().set_trace(sys._getframe().f_back) - import sys + pdb.Pdb().set_trace(sys._getframe().f_back) app = QtGui.QApplication(sys.argv) window = AdvancedCodeWidget(None) if len(sys.argv) > 1: - f = open(sys.argv[1], 'r') + f = open(sys.argv[1], "r") window.code.setPlainText(f.read()) - window.code.set_info_lines([3,4,8]) + window.code.set_info_lines([3, 4, 8]) window.resize(640, 640) window.show() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/find_widget.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/find_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/find_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/find_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,13 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import weakref @@ -15,21 +15,25 @@ class FindWidget(QtGui.QWidget): - def __init__(self, parent): - super(FindWidget, self).__init__(parent) + super().__init__(parent) self.adv_code_widget = weakref.ref(parent) - self.button_size = self.fontMetrics().width(u'Replace All') + 20 + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + self.button_size = self.fontMetrics().horizontalAdvance("Replace All") + 20 + else: + self.button_size = self.fontMetrics().width("Replace All") + 20 form_layout = QtGui.QFormLayout() - form_layout.addRow('Fin&d', self._create_find_control()) + form_layout.addRow("Fin&d", self._create_find_control()) layout = QtGui.QHBoxLayout() layout.addLayout(form_layout) - close_button = QtGui.QPushButton('Close') - layout.addWidget(close_button, 1, QtCore.Qt.AlignRight) + close_button = QtGui.QPushButton("Close") + layout.addWidget(close_button, 1, QtCore.Qt.AlignmentFlag.AlignRight) close_button.clicked.connect(self.hide) self.setLayout(layout) @@ -41,19 +45,19 @@ control = QtGui.QWidget(self) self.line_edit = QtGui.QLineEdit() - self.next_button = QtGui.QPushButton('&Next') + self.next_button = QtGui.QPushButton("&Next") self.next_button.setFixedWidth(self.button_size) - self.prev_button = QtGui.QPushButton('&Prev') + self.prev_button = QtGui.QPushButton("&Prev") self.prev_button.setFixedWidth(self.button_size) - self.options_button = QtGui.QPushButton('&Options') + self.options_button = QtGui.QPushButton("&Options") self.options_button.setFixedWidth(self.button_size) options_menu = QtGui.QMenu(self) - self.case_action = QtGui.QAction('Match &case', options_menu) + self.case_action = QtGui.QAction("Match &case", options_menu) self.case_action.setCheckable(True) - self.word_action = QtGui.QAction('Match words', options_menu) + self.word_action = QtGui.QAction("Match words", options_menu) self.word_action.setCheckable(True) - self.wrap_action = QtGui.QAction('Wrap search', options_menu) + self.wrap_action = QtGui.QAction("Wrap search", options_menu) self.wrap_action.setCheckable(True) self.wrap_action.setChecked(True) options_menu.addAction(self.case_action) @@ -71,4 +75,3 @@ control.setLayout(layout) return control - diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/gutters.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/gutters.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/gutters.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/gutters.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,13 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import math @@ -17,7 +17,6 @@ class GutterWidget(QtGui.QWidget): min_width = 5 - background_color = QtGui.QColor(220, 220, 220) def sizeHint(self): return QtCore.QSize(self.min_width, 0) @@ -26,19 +25,20 @@ """ Paint the line numbers. """ painter = QtGui.QPainter(self) - painter.fillRect(event.rect(), QtCore.Qt.lightGray) + painter.fillRect(event.rect(), self.pallette().window()) def wheelEvent(self, event): """ Delegate mouse wheel events to parent for seamless scrolling. """ self.parent().wheelEvent(event) + class StatusGutterWidget(GutterWidget): """ Draws status markers """ def __init__(self, *args, **kw): - super(StatusGutterWidget, self).__init__(*args, **kw) + super().__init__(*args, **kw) self.error_lines = [] self.warn_lines = [] @@ -51,23 +51,30 @@ """ Paint the line numbers. """ painter = QtGui.QPainter(self) - painter.fillRect(event.rect(), self.background_color) + painter.fillRect(event.rect(), self.palette().window()) cw = self.parent() - pixels_per_block = self.height()/float(cw.blockCount()) + pixels_per_block = self.height() / float(cw.blockCount()) for line in self.info_lines: - painter.fillRect(QtCore.QRect(0, line*pixels_per_block, self.width(), 3), - QtCore.Qt.green) + painter.fillRect( + QtCore.QRect(0, line * pixels_per_block, self.width(), 3), + QtCore.Qt.GlobalColor.green, + ) for line in self.warn_lines: - painter.fillRect(QtCore.QRect(0, line*pixels_per_block, self.width(), 3), - QtCore.Qt.yellow) + painter.fillRect( + QtCore.QRect(0, line * pixels_per_block, self.width(), 3), + QtCore.Qt.GlobalColor.yellow, + ) for line in self.error_lines: - painter.fillRect(QtCore.QRect(0, line*pixels_per_block, self.width(), 3), - QtCore.Qt.red) + painter.fillRect( + QtCore.QRect(0, line * pixels_per_block, self.width(), 3), + QtCore.Qt.GlobalColor.red, + ) + class LineNumberWidget(GutterWidget): """ Draw line numbers. @@ -85,10 +92,20 @@ def digits_width(self): nlines = max(1, self.parent().blockCount()) - ndigits = max(self.min_char_width, - int(math.floor(math.log10(nlines) + 1))) - width = max(self.fontMetrics().width(u'0' * ndigits) + 3, - self.min_width) + ndigits = max( + self.min_char_width, int(math.floor(math.log10(nlines) + 1)) + ) + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + width = max( + self.fontMetrics().horizontalAdvance("0" * ndigits) + 3, + self.min_width + ) + else: + width = max( + self.fontMetrics().width("0" * ndigits) + 3, self.min_width + ) return width def sizeHint(self): @@ -99,23 +116,30 @@ """ painter = QtGui.QPainter(self) painter.setFont(self.font) - painter.fillRect(event.rect(), self.background_color) + painter.fillRect(event.rect(), self.palette().window()) cw = self.parent() block = cw.firstVisibleBlock() blocknum = block.blockNumber() - top = cw.blockBoundingGeometry(block).translated( - cw.contentOffset()).top() + top = ( + cw.blockBoundingGeometry(block) + .translated(cw.contentOffset()) + .top() + ) bottom = top + int(cw.blockBoundingRect(block).height()) + painter.setBrush(self.palette().windowText()) while block.isValid() and top <= event.rect().bottom(): if block.isVisible() and bottom >= event.rect().top(): - painter.setPen(QtCore.Qt.black) - painter.drawText(0, top, self.width() - 2, - self.fontMetrics().height(), - QtCore.Qt.AlignRight, str(blocknum + 1)) + painter.drawText( + 0, + top, + self.width() - 2, + self.fontMetrics().height(), + QtCore.Qt.AlignmentFlag.AlignRight, + str(blocknum + 1), + ) block = block.next() top = bottom bottom = top + int(cw.blockBoundingRect(block).height()) blocknum += 1 - diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/__init__.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/__init__.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/pygments_highlighter.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/pygments_highlighter.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/pygments_highlighter.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/pygments_highlighter.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,13 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + from pyface.qt import QtGui @@ -15,10 +15,9 @@ from pygments.lexers import CLexer, CppLexer, PythonLexer, get_lexer_by_name from pygments.styles.default import DefaultStyle from pygments.token import Comment -import six -def get_tokens_unprocessed(self, text, stack=('root',)): +def get_tokens_unprocessed(self, text, stack=("root",)): """ Split ``text`` into (tokentype, text) pairs. Monkeypatched to store the final stack on the object itself. @@ -30,7 +29,7 @@ """ pos = 0 tokendefs = self._tokens - if hasattr(self, '_saved_state_stack'): + if hasattr(self, "_saved_state_stack"): statestack = list(self._saved_state_stack) else: statestack = list(stack) @@ -50,16 +49,16 @@ # state transition if isinstance(new_state, tuple): for state in new_state: - if state == '#pop': + if state == "#pop": statestack.pop() - elif state == '#push': + elif state == "#push": statestack.append(statestack[-1]) else: statestack.append(state) elif isinstance(new_state, int): # pop del statestack[new_state:] - elif new_state == '#push': + elif new_state == "#push": statestack.append(statestack[-1]) else: assert False, "wrong state def: %r" % new_state @@ -67,18 +66,19 @@ break else: try: - if text[pos] == '\n': + if text[pos] == "\n": # at EOL, reset state to "root" pos += 1 - statestack = ['root'] - statetokens = tokendefs['root'] - yield pos, Text, u'\n' + statestack = ["root"] + statetokens = tokendefs["root"] + yield pos, Text, "\n" continue yield pos, Error, text[pos] pos += 1 except IndexError: break - self._saved_state_stack = list(statestack) + self._saved_state_stack = list(statestack) + # Monkeypatch! RegexLexer.get_tokens_unprocessed = get_tokens_unprocessed @@ -90,6 +90,7 @@ # to the C and C++ lexers. This means that nested multiline comments will appear # to be valid C/C++, but this is better than the alternative for now. + def replace_pattern(tokens, new_pattern): """ Given a RegexLexer token dictionary 'tokens', replace all patterns that match the token specified in 'new_pattern' with 'new_pattern'. @@ -99,23 +100,26 @@ if isinstance(pattern, tuple) and pattern[1] == new_pattern[1]: state[index] = new_pattern + # More monkeypatching! -comment_start = (r'/\*', Comment.Multiline, 'comment') -comment_state = [ (r'[^*/]', Comment.Multiline), - (r'/\*', Comment.Multiline, '#push'), - (r'\*/', Comment.Multiline, '#pop'), - (r'[*/]', Comment.Multiline) ] +comment_start = (r"/\*", Comment.Multiline, "comment") +comment_state = [ + (r"[^*/]", Comment.Multiline), + (r"/\*", Comment.Multiline, "#push"), + (r"\*/", Comment.Multiline, "#pop"), + (r"[*/]", Comment.Multiline), +] replace_pattern(CLexer.tokens, comment_start) replace_pattern(CppLexer.tokens, comment_start) -CLexer.tokens['comment'] = comment_state -CppLexer.tokens['comment'] = comment_state +CLexer.tokens["comment"] = comment_state +CppLexer.tokens["comment"] = comment_state class BlockUserData(QtGui.QTextBlockUserData): """ Storage for the user data associated with each line. """ - syntax_stack = ('root',) + syntax_stack = ("root",) def __init__(self, **kwds): QtGui.QTextBlockUserData.__init__(self) @@ -123,17 +127,18 @@ setattr(self, key, value) def __repr__(self): - attrs = ['syntax_stack'] - kwds = ', '.join([ '%s=%r' % (attr, getattr(self, attr)) - for attr in attrs ]) - return 'BlockUserData(%s)' % kwds + attrs = ["syntax_stack"] + kwds = ", ".join( + ["%s=%r" % (attr, getattr(self, attr)) for attr in attrs] + ) + return "BlockUserData(%s)" % kwds class PygmentsHighlighter(QtGui.QSyntaxHighlighter): """ Syntax highlighter that uses Pygments for parsing. """ def __init__(self, parent, lexer=None): - super(PygmentsHighlighter, self).__init__(parent) + super().__init__(parent) try: self._lexer = get_lexer_by_name(lexer) @@ -148,12 +153,12 @@ def highlightBlock(self, qstring): """ Highlight a block of text. """ - qstring = six.text_type(qstring) + qstring = str(qstring) prev_data = self.previous_block_data() if prev_data is not None: self._lexer._saved_state_stack = prev_data.syntax_stack - elif hasattr(self._lexer, '_saved_state_stack'): + elif hasattr(self._lexer, "_saved_state_stack"): del self._lexer._saved_state_stack index = 0 @@ -165,7 +170,7 @@ self.setFormat(index, l, format) index += l - if hasattr(self._lexer, '_saved_state_stack'): + if hasattr(self._lexer, "_saved_state_stack"): data = BlockUserData(syntax_stack=self._lexer._saved_state_stack) self.currentBlock().setUserData(data) @@ -187,32 +192,36 @@ if token in self._formats: return self._formats[token] result = None + while not self._style.styles_token(token): + token = token.parent for key, value in self._style.style_for_token(token).items(): if value: if result is None: result = QtGui.QTextCharFormat() - if key == 'color': + if key == "color": result.setForeground(self._get_brush(value)) - elif key == 'bgcolor': + elif key == "bgcolor": result.setBackground(self._get_brush(value)) - elif key == 'bold': - result.setFontWeight(QtGui.QFont.Bold) - elif key == 'italic': + elif key == "bold": + result.setFontWeight(QtGui.QFont.Weight.Bold) + elif key == "italic": result.setFontItalic(True) - elif key == 'underline': + elif key == "underline": result.setUnderlineStyle( - QtGui.QTextCharFormat.SingleUnderline) - elif key == 'sans': + QtGui.QTextCharFormat.UnderlineStyle.SingleUnderline + ) + elif key == "sans": result.setFontStyleHint(QtGui.QFont.SansSerif) - elif key == 'roman': - result.setFontStyleHint(QtGui.QFont.Times) - elif key == 'mono': + elif key == "roman": + result.setFontStyleHint(QtGui.QFont.StyleHint.Times) + elif key == "mono": result.setFontStyleHint(QtGui.QFont.TypeWriter) - elif key == 'border': + elif key == "border": # Borders are normally used for errors. We can't do a border # so instead we do a wavy underline result.setUnderlineStyle( - QtGui.QTextCharFormat.WaveUnderline) + QtGui.QTextCharFormat.UnderlineStyle.WaveUnderline + ) result.setUnderlineColor(self._get_color(value)) self._formats[token] = result return result @@ -230,8 +239,9 @@ def _get_color(self, color): qcolor = QtGui.QColor() - qcolor.setRgb(int(color[:2],base=16), - int(color[2:4], base=16), - int(color[4:6], base=16)) + qcolor.setRgb( + int(color[:2], base=16), + int(color[2:4], base=16), + int(color[4:6], base=16), + ) return qcolor - diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/replace_widget.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/replace_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/replace_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/replace_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,13 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2010, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import weakref @@ -15,23 +15,30 @@ from .find_widget import FindWidget -class ReplaceWidget(FindWidget): +class ReplaceWidget(FindWidget): def __init__(self, parent): + # We explicitly call __init__ on the classes which FindWidget inherits from + # instead of calling FindWidget.__init__. super(FindWidget, self).__init__(parent) self.adv_code_widget = weakref.ref(parent) - self.button_size = self.fontMetrics().width(u'Replace All') + 20 + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + self.button_size = self.fontMetrics().horizontalAdvance("Replace All") + 20 + else: + self.button_size = self.fontMetrics().width("Replace All") + 20 form_layout = QtGui.QFormLayout() - form_layout.addRow('Fin&d', self._create_find_control()) - form_layout.addRow('Rep&lace', self._create_replace_control()) + form_layout.addRow("Fin&d", self._create_find_control()) + form_layout.addRow("Rep&lace", self._create_replace_control()) layout = QtGui.QHBoxLayout() layout.addLayout(form_layout) - close_button = QtGui.QPushButton('Close') - layout.addWidget(close_button, 1, QtCore.Qt.AlignRight) + close_button = QtGui.QPushButton("Close") + layout.addWidget(close_button, 1, QtCore.Qt.AlignmentFlag.AlignRight) close_button.clicked.connect(self.hide) self.setLayout(layout) @@ -40,9 +47,9 @@ control = QtGui.QWidget(self) self.replace_edit = QtGui.QLineEdit() - self.replace_button = QtGui.QPushButton('&Replace') + self.replace_button = QtGui.QPushButton("&Replace") self.replace_button.setFixedWidth(self.button_size) - self.replace_all_button = QtGui.QPushButton('Replace &All') + self.replace_all_button = QtGui.QPushButton("Replace &All") self.replace_all_button.setFixedWidth(self.button_size) layout = QtGui.QHBoxLayout() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/code_editor/tests/test_code_widget.py python-pyface-7.4.0/pyface/ui/qt4/code_editor/tests/test_code_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/code_editor/tests/test_code_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/code_editor/tests/test_code_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,26 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2016, Enthought Inc +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought Inc -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + -# Standard library imports. import unittest -import mock +from unittest import mock + -# System library imports. from pyface.qt import QtCore, QtGui from pyface.qt.QtTest import QTest -# Enthought library imports. - -# Local imports. -from pyface.ui.qt4.code_editor.code_widget import CodeWidget, AdvancedCodeWidget +from pyface.ui.qt4.code_editor.code_widget import ( + CodeWidget, + AdvancedCodeWidget, +) class TestCodeWidget(unittest.TestCase): @@ -31,9 +31,16 @@ def tearDown(self): self.qapp.processEvents() + def test_different_lexer(self): + # Setting a different lexer should not fail. + # See enthought/traitsui#982 + cw = CodeWidget(None, lexer="yaml") + text = "number: 1" + cw.setPlainText(text) + def test_readonly_editor(self): cw = CodeWidget(None) - text = 'Some\nText' + text = "Some\nText" cw.setPlainText(text) def check(typed, expected): @@ -44,23 +51,24 @@ self.assertEqual(cw.toPlainText(), expected) cw.setReadOnly(True) - check('More', text) + check("More", text) cw.setReadOnly(False) - check('Extra', 'Extra' + text) + check("Extra", "Extra" + text) def test_readonly_replace_widget(self): acw = AdvancedCodeWidget(None) - text = 'Some\nText' + text = "Some\nText" acw.code.setPlainText(text) acw.show() # On some platforms, Find/Replace do not have default keybindings - FindKey = QtGui.QKeySequence('Ctrl+F') - ReplaceKey = QtGui.QKeySequence('Ctrl+H') - patcher_find = mock.patch('pyface.qt.QtGui.QKeySequence.Find', FindKey) - patcher_replace = mock.patch('pyface.qt.QtGui.QKeySequence.Replace', - ReplaceKey) + FindKey = QtGui.QKeySequence("Ctrl+F") + ReplaceKey = QtGui.QKeySequence("Ctrl+H") + patcher_find = mock.patch("pyface.qt.QtGui.QKeySequence.StandardKey.Find", FindKey) + patcher_replace = mock.patch( + "pyface.qt.QtGui.QKeySequence.StandardKey.Replace", ReplaceKey + ) patcher_find.start() patcher_replace.start() self.addCleanup(patcher_find.stop) @@ -74,9 +82,10 @@ first_key = key_seq[0] except IndexError: return False - key = QtCore.Qt.Key(first_key & ~QtCore.Qt.KeyboardModifierMask) + key = QtCore.Qt.Key(first_key & ~QtCore.Qt.KeyboardModifier.KeyboardModifierMask) modifier = QtCore.Qt.KeyboardModifier( - first_key & QtCore.Qt.KeyboardModifierMask) + first_key & QtCore.Qt.KeyboardModifier.KeyboardModifierMask + ) QTest.keyClick(widget, key, modifier) return True @@ -99,7 +108,3 @@ self.assertTrue(acw.replace.isVisible()) acw.replace.hide() self.assertFalse(acw.replace.isVisible()) - - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/color_dialog.py python-pyface-7.4.0/pyface/ui/qt4/color_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/color_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/color_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,63 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" A dialog that allows the user to select a color. """ + +from pyface.qt import QtGui + +from traits.api import Bool, provides + +from pyface.color import Color +from pyface.ui_traits import PyfaceColor +from pyface.i_color_dialog import IColorDialog +from .dialog import Dialog + + +@provides(IColorDialog) +class ColorDialog(Dialog): + """ A dialog that allows the user to choose a color. + """ + + # 'IColorDialog' interface ---------------------------------------------- + + #: The color in the dialog. + color = PyfaceColor() + + #: Whether or not to allow the user to chose an alpha value. + show_alpha = Bool(False) + + # ------------------------------------------------------------------------ + # 'IDialog' interface. + # ------------------------------------------------------------------------ + + def _create_contents(self, parent): + # In PyQt this is a canned dialog so there are no contents. + pass + + # ------------------------------------------------------------------------ + # 'IWindow' interface. + # ------------------------------------------------------------------------ + + def close(self): + if self.control.result() == QtGui.QDialog.DialogCode.Accepted: + qcolor = self.control.selectedColor() + self.color = Color.from_toolkit(qcolor) + return super(ColorDialog, self).close() + + # ------------------------------------------------------------------------ + # 'IWindow' interface. + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + qcolor = self.color.to_toolkit() + dialog = QtGui.QColorDialog(qcolor, parent) + if self.show_alpha: + dialog.setOptions(QtGui.QColorDialog.ColorDialogOption.ShowAlphaChannel) + return dialog diff -Nru python-pyface-6.1.2/pyface/ui/qt4/color.py python-pyface-7.4.0/pyface/ui/qt4/color.py --- python-pyface-6.1.2/pyface/ui/qt4/color.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/color.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,57 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Color conversion routines for the Qt toolkit. + +This module provides a couple of utility methods to support the +pyface.color.Color class to_toolkit and from_toolkit methods. +""" + +from pyface.qt.QtGui import QColor + +from pyface.color import channels_to_ints, ints_to_channels + + +def toolkit_color_to_rgba(qcolor): + """ Convert a QColor to an RGBA tuple. + + Parameters + ---------- + qcolor : QColor + A QColor object. + + Returns + ------- + rgba_tuple : tuple + A tuple of 4 floating point values between 0.0 and 1.0 inclusive. + """ + values = ( + qcolor.red(), + qcolor.green(), + qcolor.blue(), + qcolor.alpha(), + ) + return ints_to_channels(values) + + +def rgba_to_toolkit_color(rgba): + """ Convert an RGBA tuple to a QColor. + + Parameters + ---------- + rgba_tuple : tuple + A tuple of 4 floating point values between 0.0 and 1.0 inclusive. + + Returns + ------- + qcolor : QColor + A QColor object. + """ + values = channels_to_ints(rgba) + return QColor(*values) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/confirmation_dialog.py python-pyface-7.4.0/pyface/ui/qt4/confirmation_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/confirmation_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/confirmation_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,26 +1,29 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, Dict, Enum, Instance, provides, Unicode -# Local imports. -from pyface.i_confirmation_dialog import IConfirmationDialog, MConfirmationDialog +from traits.api import Bool, Dict, Enum, provides, Str + + +from pyface.i_confirmation_dialog import ( + IConfirmationDialog, + MConfirmationDialog, +) from pyface.constant import CANCEL, YES, NO -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from .dialog import Dialog, _RESULT_MAP @@ -30,43 +33,42 @@ IConfirmationDialog interface for the API documentation. """ - - #### 'IConfirmationDialog' interface ###################################### + # 'IConfirmationDialog' interface -------------------------------------# cancel = Bool(False) default = Enum(NO, YES, CANCEL) - image = Instance(ImageResource) + image = Image() - message = Unicode + message = Str() - informative = Unicode + informative = Str() - detail = Unicode + detail = Str() - no_label = Unicode + no_label = Str() - yes_label = Unicode + yes_label = Str() # If we create custom buttons with the various roles, then we need to # keep track of the buttons so we can see what the user clicked. It's # not correct nor sufficient to check the return result from QMessageBox.exec_(). # (As of Qt 4.5.1, even clicking a button with the YesRole would lead to - # exec_() returning QDialog.Rejected. + # exec_() returning QDialog.DialogCode.Rejected. _button_result_map = Dict() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In PyQt this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): dlg = QtGui.QMessageBox(parent) @@ -83,15 +85,15 @@ dlg.move(*self.position) if self.image is None: - dlg.setIcon(QtGui.QMessageBox.Warning) + dlg.setIcon(QtGui.QMessageBox.Icon.Warning) else: dlg.setIconPixmap(self.image.create_image()) # The 'Yes' button. if self.yes_label: - btn = dlg.addButton(self.yes_label, QtGui.QMessageBox.YesRole) + btn = dlg.addButton(self.yes_label, QtGui.QMessageBox.ButtonRole.YesRole) else: - btn = dlg.addButton(QtGui.QMessageBox.Yes) + btn = dlg.addButton(QtGui.QMessageBox.StandardButton.Yes) self._button_result_map[btn] = YES if self.default == YES: @@ -99,9 +101,9 @@ # The 'No' button. if self.no_label: - btn = dlg.addButton(self.no_label, QtGui.QMessageBox.NoRole) + btn = dlg.addButton(self.no_label, QtGui.QMessageBox.ButtonRole.NoRole) else: - btn = dlg.addButton(QtGui.QMessageBox.No) + btn = dlg.addButton(QtGui.QMessageBox.StandardButton.No) self._button_result_map[btn] = NO if self.default == NO: @@ -110,9 +112,11 @@ # The 'Cancel' button. if self.cancel: if self.cancel_label: - btn = dlg.addButton(self.cancel_label, QtGui.QMessageBox.RejectRole) + btn = dlg.addButton( + self.cancel_label, QtGui.QMessageBox.ButtonRole.RejectRole + ) else: - btn = dlg.addButton(QtGui.QMessageBox.Cancel) + btn = dlg.addButton(QtGui.QMessageBox.StandardButton.Cancel) self._button_result_map[btn] = CANCEL @@ -122,7 +126,7 @@ return dlg def _show_modal(self): - self.control.setWindowModality(QtCore.Qt.ApplicationModal) + self.control.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) retval = self.control.exec_() if self.control is None: # dialog window closed @@ -136,5 +140,3 @@ else: retval = _RESULT_MAP[retval] return retval - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/console/api.py python-pyface-7.4.0/pyface/ui/qt4/console/api.py --- python-pyface-6.1.2/pyface/ui/qt4/console/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/console/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from .bracket_matcher import BracketMatcher from .call_tip_widget import CallTipWidget from .completion_lexer import CompletionLexer diff -Nru python-pyface-6.1.2/pyface/ui/qt4/console/bracket_matcher.py python-pyface-7.4.0/pyface/ui/qt4/console/bracket_matcher.py --- python-pyface-6.1.2/pyface/ui/qt4/console/bracket_matcher.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/console/bracket_matcher.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,7 +1,16 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Provides bracket matching for Q[Plain]TextEdit widgets. """ -# System library imports + from pyface.qt import QtCore, QtGui @@ -9,32 +18,37 @@ """ Matches square brackets, braces, and parentheses based on cursor position. """ - + # Protected class variables. - _opening_map = { '(':')', '{':'}', '[':']' } - _closing_map = { ')':'(', '}':'{', ']':'[' } + _opening_map = {"(": ")", "{": "}", "[": "]"} + _closing_map = {")": "(", "}": "{", "]": "["} - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'QObject' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def __init__(self, text_edit): """ Create a call tip manager that is attached to the specified Qt text edit widget. """ assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit)) - super(BracketMatcher, self).__init__() + super().__init__() # The format to apply to matching brackets. self.format = QtGui.QTextCharFormat() - self.format.setBackground(QtGui.QColor('silver')) + self.format.setBackground(QtGui.QColor("silver")) self._text_edit = text_edit text_edit.cursorPositionChanged.connect(self._cursor_position_changed) - #-------------------------------------------------------------------------- + def _remove_event_listeners(self): + self._text_edit.cursorPositionChanged.disconnect( + self._cursor_position_changed + ) + + # -------------------------------------------------------------------------- # Protected interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def _find_match(self, position): """ Given a valid position in the text document, try to find the @@ -75,13 +89,14 @@ selection = QtGui.QTextEdit.ExtraSelection() cursor = self._text_edit.textCursor() cursor.setPosition(position) - cursor.movePosition(QtGui.QTextCursor.NextCharacter, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.NextCharacter, QtGui.QTextCursor.MoveMode.KeepAnchor + ) selection.cursor = cursor selection.format = self.format return selection - #------ Signal handlers ---------------------------------------------------- + # Signal handlers ---------------------------------------------------- def _cursor_position_changed(self): """ Updates the document formatting based on the new cursor position. @@ -95,6 +110,8 @@ position = cursor.position() - 1 match_position = self._find_match(position) if match_position != -1: - extra_selections = [ self._selection_for_character(pos) - for pos in (position, match_position) ] + extra_selections = [ + self._selection_for_character(pos) + for pos in (position, match_position) + ] self._text_edit.setExtraSelections(extra_selections) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/console/call_tip_widget.py python-pyface-7.4.0/pyface/ui/qt4/console/call_tip_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/console/call_tip_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/console/call_tip_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,9 +1,17 @@ -# Standard library imports +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import re -from textwrap import dedent from unicodedata import category -# System library imports + from pyface.qt import QtCore, QtGui @@ -11,32 +19,40 @@ """ Shows call tips by parsing the current text of Q[Plain]TextEdit. """ - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'QObject' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def __init__(self, text_edit): """ Create a call tip manager that is attached to the specified Qt text edit widget. """ assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit)) - super(CallTipWidget, self).__init__(None, QtCore.Qt.ToolTip) + super().__init__(None, QtCore.Qt.WindowType.ToolTip) self._hide_timer = QtCore.QBasicTimer() self._text_edit = text_edit self.setFont(text_edit.document().defaultFont()) - self.setForegroundRole(QtGui.QPalette.ToolTipText) - self.setBackgroundRole(QtGui.QPalette.ToolTipBase) + self.setForegroundRole(QtGui.QPalette.ColorRole.ToolTipText) + self.setBackgroundRole(QtGui.QPalette.ColorRole.ToolTipBase) self.setPalette(QtGui.QToolTip.palette()) - self.setAlignment(QtCore.Qt.AlignLeft) + self.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft) self.setIndent(1) - self.setFrameStyle(QtGui.QFrame.NoFrame) - self.setMargin(1 + self.style().pixelMetric( - QtGui.QStyle.PM_ToolTipLabelFrameWidth, None, self)) - self.setWindowOpacity(self.style().styleHint( - QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self, None) / 255.0) + self.setFrameStyle(QtGui.QFrame.Shape.NoFrame) + self.setMargin( + 1 + + self.style().pixelMetric( + QtGui.QStyle.PixelMetric.PM_ToolTipLabelFrameWidth, None, self + ) + ) + self.setWindowOpacity( + self.style().styleHint( + QtGui.QStyle.StyleHint.SH_ToolTipLabel_Opacity, None, self, None + ) + / 255.0 + ) def eventFilter(self, obj, event): """ Reimplemented to hide on certain key presses and on text edit focus @@ -45,24 +61,24 @@ if obj == self._text_edit: etype = event.type() - if etype == QtCore.QEvent.KeyPress: + if etype == QtCore.QEvent.Type.KeyPress: key = event.key() - if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): + if key in (QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Return): self.hide() - elif key == QtCore.Qt.Key_Escape: + elif key == QtCore.Qt.Key.Key_Escape: self.hide() return True - elif etype == QtCore.QEvent.FocusOut: + elif etype == QtCore.QEvent.Type.FocusOut: self.hide() - elif etype == QtCore.QEvent.Enter: + elif etype == QtCore.QEvent.Type.Enter: self._hide_timer.stop() - elif etype == QtCore.QEvent.Leave: + elif etype == QtCore.QEvent.Type.Leave: self._leave_event_hide() - return super(CallTipWidget, self).eventFilter(obj, event) + return super().eventFilter(obj, event) def timerEvent(self, event): """ Reimplemented to hide the widget when the hide timer fires. @@ -71,28 +87,29 @@ self._hide_timer.stop() self.hide() - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'QWidget' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def enterEvent(self, event): """ Reimplemented to cancel the hide timer. """ - super(CallTipWidget, self).enterEvent(event) + super().enterEvent(event) self._hide_timer.stop() def hideEvent(self, event): """ Reimplemented to disconnect signal handlers and event filter. """ - super(CallTipWidget, self).hideEvent(event) + super().hideEvent(event) self._text_edit.cursorPositionChanged.disconnect( - self._cursor_position_changed) + self._cursor_position_changed + ) self._text_edit.removeEventFilter(self) def leaveEvent(self, event): """ Reimplemented to start the hide timer. """ - super(CallTipWidget, self).leaveEvent(event) + super().leaveEvent(event) self._leave_event_hide() def paintEvent(self, event): @@ -101,27 +118,28 @@ painter = QtGui.QStylePainter(self) option = QtGui.QStyleOptionFrame() option.initFrom(self) - painter.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, option) + painter.drawPrimitive(QtGui.QStyle.PrimitiveElement.PE_PanelTipLabel, option) painter.end() - super(CallTipWidget, self).paintEvent(event) + super().paintEvent(event) def setFont(self, font): """ Reimplemented to allow use of this method as a slot. """ - super(CallTipWidget, self).setFont(font) + super().setFont(font) def showEvent(self, event): """ Reimplemented to connect signal handlers and event filter. """ - super(CallTipWidget, self).showEvent(event) + super().showEvent(event) self._text_edit.cursorPositionChanged.connect( - self._cursor_position_changed) + self._cursor_position_changed + ) self._text_edit.installEventFilter(self) - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'CallTipWidget' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def show_call_info(self, call_line=None, doc=None, maxlines=20): """ Attempts to show the specified call line and docstring at the @@ -131,12 +149,12 @@ if doc: match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc) if match: - doc = doc[:match.end()] + '\n[Documentation continues...]' + doc = doc[: match.end()] + "\n[Documentation continues...]" else: - doc = '' - + doc = "" + if call_line: - doc = '\n\n'.join([call_line, doc]) + doc = "\n\n".join([call_line, doc]) return self.show_tip(doc) def show_tip(self, tip): @@ -144,24 +162,28 @@ """ # Attempt to find the cursor position at which to show the call tip. text_edit = self._text_edit - document = text_edit.document() cursor = text_edit.textCursor() search_pos = cursor.position() - 1 - self._start_position, _ = self._find_parenthesis(search_pos, - forward=False) + self._start_position, _ = self._find_parenthesis( + search_pos, forward=False + ) if self._start_position == -1: return False # Set the text and resize the widget accordingly. self.setText(tip) self.resize(self.sizeHint()) - + # Locate and show the widget. Place the tip below the current line # unless it would be off the screen. In that case, place it above # the current line. - padding = 3 # Distance in pixels between cursor bounds and tip box. + padding = 3 # Distance in pixels between cursor bounds and tip box. cursor_rect = text_edit.cursorRect(cursor) - screen_rect = QtGui.QApplication.desktop().screenGeometry(text_edit) + if QtCore.__version_info__ >= (5, 10): + screen = text_edit.window().windowHandle().screen() + screen_rect = screen.availableGeometry() + else: + screen_rect = QtGui.QApplication.desktop().screenGeometry(text_edit) point = text_edit.mapToGlobal(cursor_rect.bottomRight()) point.setY(point.y() + padding) tip_height = self.size().height() @@ -171,10 +193,10 @@ self.move(point) self.show() return True - - #-------------------------------------------------------------------------- + + # -------------------------------------------------------------------------- # Protected interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def _find_parenthesis(self, position, forward=True): """ If 'forward' is True (resp. False), proceed forwards @@ -188,14 +210,14 @@ char = document.characterAt(position) # Search until a match is found or a non-printable character is # encountered. - while category(char) != 'Cc' and position > 0: - if char == ',' and depth == 0: + while category(char) != "Cc" and position > 0: + if char == "," and depth == 0: commas += 1 - elif char == ')': + elif char == ")": if forward and depth == 0: break depth += 1 - elif char == '(': + elif char == "(": if not forward and depth == 0: break depth -= 1 @@ -209,14 +231,17 @@ """ Hides the tooltip after some time has passed (assuming the cursor is not over the tooltip). """ - if (not self._hide_timer.isActive() and + if ( + not self._hide_timer.isActive() + and # If Enter events always came after Leave events, we wouldn't need # this check. But on Mac OS, it sometimes happens the other way # around when the tooltip is created. - QtGui.QApplication.topLevelAt(QtGui.QCursor.pos()) != self): + QtGui.QApplication.topLevelAt(QtGui.QCursor.pos()) != self + ): self._hide_timer.start(300, self) - #------ Signal handlers ---------------------------------------------------- + # Signal handlers ---------------------------------------------------- def _cursor_position_changed(self): """ Updates the tip based on user cursor movement. diff -Nru python-pyface-6.1.2/pyface/ui/qt4/console/completion_lexer.py python-pyface-7.4.0/pyface/ui/qt4/console/completion_lexer.py --- python-pyface-6.1.2/pyface/ui/qt4/console/completion_lexer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/console/completion_lexer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,27 @@ -# System library imports +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pygments.token import Token, is_token_subtype class CompletionLexer(object): - """ Uses Pygments and some auxillary information to lex code snippets for + """ Uses Pygments and some auxillary information to lex code snippets for symbol contexts. """ # Maps Lexer names to a list of possible name separators - separator_map = { 'C' : [ '.', '->' ], - 'C++' : [ '.', '->', '::' ], - 'Python' : [ '.' ] } + separator_map = { + "C": [".", "->"], + "C++": [".", "->", "::"], + "Python": ["."], + } def __init__(self, lexer): """ Create a CompletionLexer using the specified Pygments lexer. @@ -27,11 +38,14 @@ # Pygments often tacks on a newline when none is specified in the input. # Remove this newline. - if reversed_tokens and reversed_tokens[0][1].endswith('\n') and \ - not string.endswith('\n'): + if ( + reversed_tokens + and reversed_tokens[0][1].endswith("\n") + and not string.endswith("\n") + ): reversed_tokens.pop(0) - - current_op = '' + + current_op = "" for token, text in reversed_tokens: if is_token_subtype(token, Token.Name): @@ -39,14 +53,14 @@ # Handle a trailing separator, e.g 'foo.bar.' if current_op in self._name_separators: if not context: - context.insert(0, '') + context.insert(0, "") # Handle non-separator operators and punction. elif current_op: break context.insert(0, text) - current_op = '' + current_op = "" # Pygments doesn't understand that, e.g., '->' is a single operator # in C++. This is why we have to build up an operator from @@ -66,9 +80,8 @@ def set_lexer(self, lexer, name_separators=None): self._lexer = lexer if name_separators is None: - self._name_separators = self.separator_map.get(lexer.name, ['.']) + self._name_separators = self.separator_map.get(lexer.name, ["."]) else: self._name_separators = list(name_separators) lexer = property(get_lexer, set_lexer) - diff -Nru python-pyface-6.1.2/pyface/ui/qt4/console/console_widget.py python-pyface-7.4.0/pyface/ui/qt4/console/console_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/console/console_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/console/console_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,14 +1,23 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An abstract base class for console-type widgets. """ # FIXME: This file and the others in this directory have been ripped, more or # less intact, out of IPython. At some point we should figure out a more # maintainable solution. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + -# Standard library imports import os from os.path import commonprefix import re @@ -16,22 +25,25 @@ from textwrap import dedent from unicodedata import category -# System library imports + from pyface.qt import QtCore, QtGui -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Functions -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def is_letter_or_number(char): """ Returns whether the specified unicode character is a letter or a number. """ cat = category(char) - return cat.startswith('L') or cat.startswith('N') + return cat.startswith("L") or cat.startswith("N") + -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class ConsoleWidget(QtGui.QWidget): """ An abstract base class for console-type widgets. This class has @@ -46,7 +58,7 @@ convenient to implementors of a console-style widget. """ - #------ Configuration ------------------------------------------------------ + # Configuration ------------------------------------------------------ # The maximum number of lines of text before truncation. Specifying a # non-positive number disables text truncation (not recommended). @@ -55,7 +67,7 @@ # The type of underlying text widget to use. Valid values are 'plain', which # specifies a QPlainTextEdit, and 'rich', which specifies a QTextEdit. # NOTE: this value can only be specified during initialization. - kind = 'plain' + kind = "plain" # The type of paging to use. Valid values are: # 'inside' : The widget pages like a traditional terminal. @@ -67,14 +79,14 @@ # 'custom_page_requested(str)' signal. # 'none' : The text is written directly to the console. # NOTE: this value can only be specified during initialization. - paging = 'inside' + paging = "inside" # Whether to override ShortcutEvents for the keybindings defined by this # widget (Ctrl+n, Ctrl+a, etc). Enable this if you want this widget to take # priority (when it has focus) over, e.g., window-level menu shortcuts. override_shortcuts = False - #------ Signals ------------------------------------------------------------ + # Signals ------------------------------------------------------------ # Signals that indicate ConsoleWidget state. copy_available = QtCore.Signal(bool) @@ -88,28 +100,31 @@ # Signal emitted when the font is changed. font_changed = QtCore.Signal(QtGui.QFont) - #------ Protected class variables ------------------------------------------ + # Protected class variables ------------------------------------------ # When the control key is down, these keys are mapped. - _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left, - QtCore.Qt.Key_F : QtCore.Qt.Key_Right, - QtCore.Qt.Key_A : QtCore.Qt.Key_Home, - QtCore.Qt.Key_P : QtCore.Qt.Key_Up, - QtCore.Qt.Key_N : QtCore.Qt.Key_Down, - QtCore.Qt.Key_D : QtCore.Qt.Key_Delete, } - if not sys.platform == 'darwin': + _ctrl_down_remap = { + QtCore.Qt.Key.Key_B: QtCore.Qt.Key.Key_Left, + QtCore.Qt.Key.Key_F: QtCore.Qt.Key.Key_Right, + QtCore.Qt.Key.Key_A: QtCore.Qt.Key.Key_Home, + QtCore.Qt.Key.Key_P: QtCore.Qt.Key.Key_Up, + QtCore.Qt.Key.Key_N: QtCore.Qt.Key.Key_Down, + QtCore.Qt.Key.Key_D: QtCore.Qt.Key.Key_Delete, + } + if not sys.platform == "darwin": # On OS X, Ctrl-E already does the right thing, whereas End moves the # cursor to the bottom of the buffer. - _ctrl_down_remap[QtCore.Qt.Key_E] = QtCore.Qt.Key_End + _ctrl_down_remap[QtCore.Qt.Key.Key_E] = QtCore.Qt.Key.Key_End # The shortcuts defined by this widget. We need to keep track of these to # support 'override_shortcuts' above. - _shortcuts = set(_ctrl_down_remap.keys()) | set([ - QtCore.Qt.Key_C, QtCore.Qt.Key_G, QtCore.Qt.Key_O, QtCore.Qt.Key_V]) + _shortcuts = set(_ctrl_down_remap.keys()) | set( + [QtCore.Qt.Key.Key_C, QtCore.Qt.Key.Key_G, QtCore.Qt.Key.Key_O, QtCore.Qt.Key.Key_V] + ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'QObject' interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def __init__(self, parent=None): """ Create a ConsoleWidget. @@ -119,7 +134,12 @@ parent : QWidget, optional [default None] The parent for this widget. """ - super(ConsoleWidget, self).__init__(parent) + super().__init__(parent) + + # A list of connected Qt signals to be removed before destruction. + # First item in the tuple is the Qt signal. The second item is the + # event handler. + self._connections_to_remove = [] # Create the layout and underlying text widget. layout = QtGui.QStackedLayout(self) @@ -127,19 +147,19 @@ self._control = self._create_control() self._page_control = None self._splitter = None - if self.paging in ('hsplit', 'vsplit'): + if self.paging in ("hsplit", "vsplit"): self._splitter = QtGui.QSplitter() - if self.paging == 'hsplit': - self._splitter.setOrientation(QtCore.Qt.Horizontal) + if self.paging == "hsplit": + self._splitter.setOrientation(QtCore.Qt.Orientation.Horizontal) else: - self._splitter.setOrientation(QtCore.Qt.Vertical) + self._splitter.setOrientation(QtCore.Qt.Orientation.Vertical) self._splitter.addWidget(self._control) layout.addWidget(self._splitter) else: layout.addWidget(self._control) # Create the paging widget, if necessary. - if self.paging in ('inside', 'hsplit', 'vsplit'): + if self.paging in ("inside", "hsplit", "vsplit"): self._page_control = self._create_page_control() if self._splitter: self._page_control.hide() @@ -149,49 +169,52 @@ # Initialize protected variables. Some variables contain useful state # information for subclasses; they should be considered read-only. - self._continuation_prompt = '> ' + self._continuation_prompt = "> " self._continuation_prompt_html = None self._executing = False self._filter_drag = False self._filter_resize = False - self._prompt = '' + self._prompt = "" self._prompt_html = None self._prompt_pos = 0 - self._prompt_sep = '' + self._prompt_sep = "" self._reading = False self._reading_callback = None self._tab_width = 8 self._text_completing_pos = 0 - self._filename = 'python.html' - self._png_mode=None + self._filename = "python.html" + self._png_mode = None # Set a monospaced font. self.reset_font() # Configure actions. - action = QtGui.QAction('Print', None) + action = QtGui.QAction("Print", None) action.setEnabled(True) - printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print) - if printkey.matches("Ctrl+P") and sys.platform != 'darwin': + printkey = QtGui.QKeySequence(QtGui.QKeySequence.StandardKey.Print) + if printkey.matches("Ctrl+P") and sys.platform != "darwin": # Only override the default if there is a collision. # Qt ctrl = cmd on OSX, so that the match gets a false positive. printkey = "Ctrl+Shift+P" action.setShortcut(printkey) action.triggered.connect(self.print_) + self._connections_to_remove.append((action.triggered, self.print_)) self.addAction(action) self._print_action = action - action = QtGui.QAction('Save as HTML/XML', None) + action = QtGui.QAction("Save as HTML/XML", None) action.setEnabled(self.can_export()) - action.setShortcut(QtGui.QKeySequence.Save) + action.setShortcut(QtGui.QKeySequence.StandardKey.Save) action.triggered.connect(self.export) + self._connections_to_remove.append((action.triggered, self.export)) self.addAction(action) self._export_action = action - action = QtGui.QAction('Select All', None) + action = QtGui.QAction("Select All", None) action.setEnabled(True) - action.setShortcut(QtGui.QKeySequence.SelectAll) + action.setShortcut(QtGui.QKeySequence.StandardKey.SelectAll) action.triggered.connect(self.select_all) + self._connections_to_remove.append((action.triggered, self.select_all)) self.addAction(action) self._select_all_action = action @@ -200,15 +223,19 @@ text widgets. """ etype = event.type() - if etype == QtCore.QEvent.KeyPress: + if etype == QtCore.QEvent.Type.KeyPress: # Re-map keys for all filtered widgets. key = event.key() - if self._control_key_down(event.modifiers()) and \ - key in self._ctrl_down_remap: - new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, - self._ctrl_down_remap[key], - QtCore.Qt.NoModifier) + if ( + self._control_key_down(event.modifiers()) + and key in self._ctrl_down_remap + ): + new_event = QtGui.QKeyEvent( + QtCore.QEvent.Type.KeyPress, + self._ctrl_down_remap[key], + QtCore.Qt.KeyboardModifier.NoModifier, + ) QtGui.QApplication.sendEvent(obj, new_event) return True @@ -219,16 +246,18 @@ return self._event_filter_page_keypress(event) # Make middle-click paste safe. - elif etype == QtCore.QEvent.MouseButtonRelease and \ - event.button() == QtCore.Qt.MidButton and \ - obj == self._control.viewport(): + elif ( + etype == QtCore.QEvent.Type.MouseButtonRelease + and event.button() == QtCore.Qt.MouseButton.MiddleButton + and obj == self._control.viewport() + ): cursor = self._control.cursorForPosition(event.pos()) self._control.setTextCursor(cursor) - self.paste(QtGui.QClipboard.Selection) + self.paste(QtGui.QClipboard.Mode.Selection) return True # Manually adjust the scrollbars *after* a resize event is dispatched. - elif etype == QtCore.QEvent.Resize and not self._filter_resize: + elif etype == QtCore.QEvent.Type.Resize and not self._filter_resize: self._filter_resize = True QtGui.QApplication.sendEvent(obj, event) self._adjust_scrollbars() @@ -236,10 +265,12 @@ return True # Override shortcuts for all filtered widgets. - elif etype == QtCore.QEvent.ShortcutOverride and \ - self.override_shortcuts and \ - self._control_key_down(event.modifiers()) and \ - event.key() in self._shortcuts: + elif ( + etype == QtCore.QEvent.Type.ShortcutOverride + and self.override_shortcuts + and self._control_key_down(event.modifiers()) + and event.key() in self._shortcuts + ): event.accept() # Ensure that drags are safe. The problem is that the drag starting @@ -251,20 +282,24 @@ # The fact that we have to clear the user's selection is unfortunate, # but the alternative--trying to prevent Qt from using its hardwired # drag logic and writing our own--is worse. - elif etype == QtCore.QEvent.DragEnter and \ - obj == self._control.viewport() and \ - event.source() == self._control.viewport(): + elif ( + etype == QtCore.QEvent.Type.DragEnter + and obj == self._control.viewport() + and event.source() == self._control.viewport() + ): self._filter_drag = True - elif etype == QtCore.QEvent.DragLeave and \ - obj == self._control.viewport() and \ - self._filter_drag: + elif ( + etype == QtCore.QEvent.Type.DragLeave + and obj == self._control.viewport() + and self._filter_drag + ): cursor = self._control.textCursor() cursor.clearSelection() self._control.setTextCursor(cursor) self._filter_drag = False # Ensure that drops are safe. - elif etype == QtCore.QEvent.Drop and obj == self._control.viewport(): + elif etype == QtCore.QEvent.Type.Drop and obj == self._control.viewport(): cursor = self._control.cursorForPosition(event.pos()) if self._in_buffer(cursor.position()): text = event.mimeData().text() @@ -275,41 +310,54 @@ QtGui.QApplication.sendEvent(obj, QtGui.QDragLeaveEvent()) return True - return super(ConsoleWidget, self).eventFilter(obj, event) + return super().eventFilter(obj, event) - #--------------------------------------------------------------------------- + def _remove_event_listeners(self): + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) + + # --------------------------------------------------------------------------- # 'QWidget' interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def sizeHint(self): """ Reimplemented to suggest a size that is 80 characters wide and 25 lines high. """ font_metrics = QtGui.QFontMetrics(self.font) - margin = (self._control.frameWidth() + - self._control.document().documentMargin()) * 2 + margin = ( + self._control.frameWidth() + + self._control.document().documentMargin() + ) * 2 style = self.style() - splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth) + splitwidth = style.pixelMetric(QtGui.QStyle.PixelMetric.PM_SplitterWidth) # Note 1: Despite my best efforts to take the various margins into # account, the width is still coming out a bit too small, so we include # a fudge factor of one character here. # Note 2: QFontMetrics.maxWidth is not used here or anywhere else due # to a Qt bug on certain Mac OS systems where it returns 0. - width = font_metrics.width(' ') * 81 + margin - width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent) - if self.paging == 'hsplit': + + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + width = font_metrics.horizontalAdvance(" ") * 81 + margin + else: + width = font_metrics.width(" ") * 81 + margin + width += style.pixelMetric(QtGui.QStyle.PixelMetric.PM_ScrollBarExtent) + if self.paging == "hsplit": width = width * 2 + splitwidth height = font_metrics.height() * 25 + margin - if self.paging == 'vsplit': + if self.paging == "vsplit": height = height * 2 + splitwidth - return QtCore.QSize(width, height) + return QtCore.QSize(int(width), int(height)) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'ConsoleWidget' public interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def can_copy(self): """ Returns whether text can be copied to the clipboard. @@ -320,14 +368,16 @@ """ Returns whether text can be cut to the clipboard. """ cursor = self._control.textCursor() - return (cursor.hasSelection() and - self._in_buffer(cursor.anchor()) and - self._in_buffer(cursor.position())) + return ( + cursor.hasSelection() + and self._in_buffer(cursor.anchor()) + and self._in_buffer(cursor.position()) + ) def can_paste(self): """ Returns whether text can be pasted from the clipboard. """ - if self._control.textInteractionFlags() & QtCore.Qt.TextEditable: + if self._control.textInteractionFlags() & QtCore.Qt.TextInteractionFlag.TextEditable: return bool(QtGui.QApplication.clipboard().text()) return False @@ -412,7 +462,7 @@ if not hidden: # A newline is appended later, but it should be considered part # of the input buffer. - source += '\n' + source += "\n" elif not hidden: self.input_buffer = source @@ -426,7 +476,7 @@ raise RuntimeError(error % source) else: if complete: - self._append_plain_text('\n') + self._append_plain_text("\n") self._executing_input_buffer = self.input_buffer self._executing = True self._prompt_finished() @@ -448,14 +498,14 @@ # removed seamlessly via undo/redo. cursor = self._get_end_cursor() cursor.beginEditBlock() - cursor.insertText('\n') + cursor.insertText("\n") self._insert_continuation_prompt(cursor) cursor.endEditBlock() # Do not do this inside the edit block. It works as expected # when using a QPlainTextEdit control, but does not have an # effect when using a QTextEdit. I believe this is a Qt bug. - self._control.moveCursor(QtGui.QTextCursor.End) + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) return complete @@ -468,11 +518,11 @@ return self._executing_input_buffer cursor = self._get_end_cursor() - cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.MoveMode.KeepAnchor) input_buffer = cursor.selection().toPlainText() # Strip out continuation prompts. - return input_buffer.replace('\n' + self._continuation_prompt, '\n') + return input_buffer.replace("\n" + self._continuation_prompt, "\n") def _set_input_buffer(self, string): """ Replaces the text in the input buffer with 'string'. @@ -484,13 +534,13 @@ # Remove old text. cursor = self._get_end_cursor() cursor.beginEditBlock() - cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.MoveMode.KeepAnchor) cursor.removeSelectedText() # Insert new text with continuation prompts. self._insert_plain_text_into_buffer(self._get_prompt_cursor(), string) cursor.endEditBlock() - self._control.moveCursor(QtGui.QTextCursor.End) + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) input_buffer = property(_get_input_buffer, _set_input_buffer) @@ -503,7 +553,15 @@ """ Sets the base font for the ConsoleWidget to the specified QFont. """ font_metrics = QtGui.QFontMetrics(font) - self._control.setTabStopWidth(self.tab_width * font_metrics.width(' ')) + + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + width = font_metrics.horizontalAdvance(" ") + else: + width = font_metrics.width(" ") + + self._control.setTabStopDistance(self.tab_width * width) self._control.document().setDefaultFont(font) if self._page_control: @@ -513,7 +571,7 @@ font = property(_get_font, _set_font) - def paste(self, mode=QtGui.QClipboard.Clipboard): + def paste(self, mode=QtGui.QClipboard.Mode.Clipboard): """ Paste the contents of the clipboard into the input region. Parameters: @@ -524,7 +582,7 @@ used to access the selection clipboard in X11 and the Find buffer in Mac OS. By default, the regular clipboard is used. """ - if self._control.textInteractionFlags() & QtCore.Qt.TextEditable: + if self._control.textInteractionFlags() & QtCore.Qt.TextInteractionFlag.TextEditable: # Make sure the paste is safe. self._keep_cursor_in_buffer() cursor = self._control.textCursor() @@ -534,36 +592,36 @@ text = QtGui.QApplication.clipboard().text(mode).rstrip() self._insert_plain_text_into_buffer(cursor, dedent(text)) - def print_(self, printer = None): + def print_(self, printer=None): """ Print the contents of the ConsoleWidget to the specified QPrinter. """ - if (not printer): + if not printer: printer = QtGui.QPrinter() - if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted): + if QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.DialogCode.Accepted: return self._control.print_(printer) - def export(self, parent = None): + def export(self, parent=None): """Export HTML/XML in various modes from one Dialog.""" - parent = parent or None # sometimes parent is False - dialog = QtGui.QFileDialog(parent, 'Save Console as...') - dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) + parent = parent or None # sometimes parent is False + dialog = QtGui.QFileDialog(parent, "Save Console as...") + dialog.setAcceptMode(QtGui.QFileDialog.AcceptMode.AcceptSave) filters = [ - 'HTML with PNG figures (*.html *.htm)', - 'XHTML with inline SVG figures (*.xhtml *.xml)' + "HTML with PNG figures (*.html *.htm)", + "XHTML with inline SVG figures (*.xhtml *.xml)", ] dialog.setNameFilters(filters) if self._filename: dialog.selectFile(self._filename) - root,ext = os.path.splitext(self._filename) - if ext.lower() in ('.xml', '.xhtml'): + root, ext = os.path.splitext(self._filename) + if ext.lower() in (".xml", ".xhtml"): dialog.selectNameFilter(filters[-1]) if dialog.exec_(): filename = str(dialog.selectedFiles()[0]) self._filename = filename choice = str(dialog.selectedNameFilter()) - if choice.startswith('XHTML'): + if choice.startswith("XHTML"): exporter = self.export_xhtml else: exporter = self.export_html @@ -572,9 +630,14 @@ return exporter(filename) except Exception as e: title = self.window().windowTitle() - msg = "Error while saving to: %s\n"%filename+str(e) - QtGui.QMessageBox.warning(self, title, msg, - QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok) + msg = "Error while saving to: %s\n" % filename + str(e) + QtGui.QMessageBox.warning( + self, + title, + msg, + QtGui.QMessageBox.StandardButton.Ok, + QtGui.QMessageBox.StandardButton.Ok, + ) return None def export_html(self, filename): @@ -592,12 +655,11 @@ # N.B. this is overly restrictive, but Qt's output is # predictable... img_re = re.compile(r'') - html = self.fix_html_encoding( - str(self._control.toHtml().toUtf8())) + html = self.fix_html_encoding(str(self._control.toHtml().toUtf8())) if self._png_mode: # preference saved, don't ask again if img_re.search(html): - inline = (self._png_mode == 'inline') + inline = self._png_mode == "inline" else: inline = True elif img_re.search(html): @@ -606,18 +668,20 @@ layout = QtGui.QVBoxLayout(widget) title = self.window().windowTitle() msg = "Exporting HTML with PNGs" - info = "Would you like inline PNGs (single large html file) or "+\ - "external image files?" + info = ( + "Would you like inline PNGs (single large html file) or " + + "external image files?" + ) checkbox = QtGui.QCheckBox("&Don't ask again") - checkbox.setShortcut('D') + checkbox.setShortcut("D") ib = QtGui.QPushButton("&Inline", self) - ib.setShortcut('I') + ib.setShortcut("I") eb = QtGui.QPushButton("&External", self) - eb.setShortcut('E') - box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg) + eb.setShortcut("E") + box = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Question, title, msg) box.setInformativeText(info) - box.addButton(ib,QtGui.QMessageBox.NoRole) - box.addButton(eb,QtGui.QMessageBox.YesRole) + box.addButton(ib, QtGui.QMessageBox.ButtonRole.NoRole) + box.addButton(eb, QtGui.QMessageBox.ButtonRole.YesRole) box.setDefaultButton(ib) layout.setSpacing(0) layout.addWidget(box) @@ -625,13 +689,13 @@ widget.setLayout(layout) widget.show() reply = box.exec_() - inline = (reply == 0) + inline = reply == 0 if checkbox.checkState(): # don't ask anymore, always use this choice if inline: - self._png_mode='inline' + self._png_mode = "inline" else: - self._png_mode='external' + self._png_mode = "external" else: # no images inline = True @@ -639,16 +703,18 @@ if inline: path = None else: - root,ext = os.path.splitext(filename) - path = root+"_files" + root, ext = os.path.splitext(filename) + path = root + "_files" if os.path.isfile(path): - raise OSError("%s exists, but is not a directory."%path) + raise OSError("%s exists, but is not a directory." % path) - f = open(filename, 'w') + f = open(filename, "w") try: - f.write(img_re.sub( - lambda x: self.image_tag(x, path = path, format = "png"), - html)) + f.write( + img_re.sub( + lambda x: self.image_tag(x, path=path, format="png"), html + ) + ) except Exception as e: f.close() raise e @@ -656,11 +722,10 @@ f.close() return filename - def export_xhtml(self, filename): """ Export the contents of the ConsoleWidget as XHTML with inline SVGs. """ - f = open(filename, 'w') + f = open(filename, "w") try: # N.B. this is overly restrictive, but Qt's output is # predictable... @@ -669,14 +734,18 @@ # Hack to make xhtml header -- note that we are not doing # any check for valid xml offset = html.find("") - assert(offset > -1) - html = ('\n'+ - html[offset+6:]) + assert offset > -1 + html = ( + '\n' + + html[offset + 6:] + ) # And now declare UTF-8 encoding html = self.fix_html_encoding(html) - f.write(img_re.sub( - lambda x: self.image_tag(x, path = None, format = "svg"), - html)) + f.write( + img_re.sub( + lambda x: self.image_tag(x, path=None, format="svg"), html + ) + ) except Exception as e: f.close() raise e @@ -699,15 +768,17 @@ C.f. http://www.w3.org/International/O-charset for details. """ offset = html.find("") - if(offset > -1): - html = (html[:offset+6]+ - '\n\n'+ - html[offset+6:]) + if offset > -1: + html = ( + html[: offset + 6] + + '\n\n' + + html[offset + 6:] + ) return html - def image_tag(self, match, path = None, format = "png"): + def image_tag(self, match, path=None, format="png"): """ Return (X)HTML mark-up for the image-tag given by match. Parameters @@ -749,16 +820,16 @@ def reset_font(self): """ Sets the font to the default fixed-width font for this platform. """ - if sys.platform == 'win32': + if sys.platform == "win32": # Consolas ships with Vista/Win7, fallback to Courier if needed - family, fallback = 'Consolas', 'Courier' - elif sys.platform == 'darwin': + family, fallback = "Consolas", "Courier" + elif sys.platform == "darwin": # OSX always has Monaco, no need for a fallback - family, fallback = 'Monaco', None + family, fallback = "Monaco", None else: # FIXME: remove Consolas as a default on Linux once our font # selections are configurable by the user. - family, fallback = 'Consolas', 'Monospace' + family, fallback = "Consolas", "Monospace" # Check whether we got what we wanted using QFontInfo, since # exactMatch() is overly strict and returns false in too many cases. @@ -792,7 +863,15 @@ """ Sets the width (in terms of space characters) for tab characters. """ font_metrics = QtGui.QFontMetrics(self.font) - self._control.setTabStopWidth(tab_width * font_metrics.width(' ')) + + # QFontMetrics.width() is deprecated and Qt docs suggest using + # horizontalAdvance() instead, but is only available since Qt 5.11 + if QtCore.__version_info__ >= (5, 11): + width = font_metrics.horizontalAdvance(" ") + else: + width = font_metrics.width(" ") + + self._control.setTabStopDistance(tab_width * width) self._tab_width = tab_width @@ -804,21 +883,21 @@ """ self._control.undo() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'ConsoleWidget' abstract interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _is_complete(self, source, interactive): """ Returns whether 'source' can be executed. When triggered by an Enter/Return key press, 'interactive' is True; otherwise, it is False. """ - raise NotImplementedError + raise NotImplementedError() def _execute(self, source, hidden): """ Execute 'source'. If 'hidden', do not show any output. """ - raise NotImplementedError + raise NotImplementedError() def _prompt_started_hook(self): """ Called immediately after a new prompt is displayed. @@ -849,9 +928,9 @@ """ return False - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'ConsoleWidget' protected interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def _append_html(self, html): """ Appends html at the end of the console buffer. @@ -877,7 +956,7 @@ with its old input buffer. """ input_buffer = self.input_buffer - self._append_plain_text('\n') + self._append_plain_text("\n") self._prompt_finished() self._append_plain_text(text) @@ -898,18 +977,19 @@ # Select and remove all text below the input buffer. cursor = self._get_prompt_cursor() prompt = self._continuation_prompt.lstrip() - while cursor.movePosition(QtGui.QTextCursor.NextBlock): + while cursor.movePosition(QtGui.QTextCursor.MoveOperation.NextBlock): temp_cursor = QtGui.QTextCursor(cursor) - temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor) + temp_cursor.select(QtGui.QTextCursor.SelectionType.BlockUnderCursor) text = temp_cursor.selection().toPlainText().lstrip() if not text.startswith(prompt): break else: # We've reached the end of the input buffer and no text follows. return - cursor.movePosition(QtGui.QTextCursor.Left) # Grab the newline. - cursor.movePosition(QtGui.QTextCursor.End, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.Left) # Grab the newline. + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.End, QtGui.QTextCursor.MoveMode.KeepAnchor + ) cursor.removeSelectedText() # After doing this, we have no choice but to clear the undo/redo @@ -926,25 +1006,27 @@ self._cancel_text_completion() if len(items) == 1: - cursor.setPosition(self._control.textCursor().position(), - QtGui.QTextCursor.KeepAnchor) + cursor.setPosition( + self._control.textCursor().position(), + QtGui.QTextCursor.MoveMode.KeepAnchor, + ) cursor.insertText(items[0]) elif len(items) > 1: current_pos = self._control.textCursor().position() prefix = commonprefix(items) if prefix: - cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(current_pos, QtGui.QTextCursor.MoveMode.KeepAnchor) cursor.insertText(prefix) current_pos = cursor.position() cursor.beginEditBlock() - self._append_plain_text('\n') + self._append_plain_text("\n") self._page(self._format_as_columns(items)) cursor.endEditBlock() cursor.setPosition(current_pos) - self._control.moveCursor(QtGui.QTextCursor.End) + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) self._control.setTextCursor(cursor) self._text_completing_pos = current_pos @@ -953,17 +1035,17 @@ """ menu = QtGui.QMenu(self) - cut_action = menu.addAction('Cut', self.cut) + cut_action = menu.addAction("Cut", self.cut) cut_action.setEnabled(self.can_cut()) - cut_action.setShortcut(QtGui.QKeySequence.Cut) + cut_action.setShortcut(QtGui.QKeySequence.StandardKey.Cut) - copy_action = menu.addAction('Copy', self.copy) + copy_action = menu.addAction("Copy", self.copy) copy_action.setEnabled(self.can_copy()) - copy_action.setShortcut(QtGui.QKeySequence.Copy) + copy_action.setShortcut(QtGui.QKeySequence.StandardKey.Copy) - paste_action = menu.addAction('Paste', self.paste) + paste_action = menu.addAction("Paste", self.paste) paste_action.setEnabled(self.can_paste()) - paste_action.setShortcut(QtGui.QKeySequence.Paste) + paste_action.setShortcut(QtGui.QKeySequence.StandardKey.Paste) menu.addSeparator() menu.addAction(self._select_all_action) @@ -986,19 +1068,19 @@ """ # Note that on Mac OS, ControlModifier corresponds to the Command key # while MetaModifier corresponds to the Control key. - if sys.platform == 'darwin': - down = include_command and (modifiers & QtCore.Qt.ControlModifier) - return bool(down) ^ bool(modifiers & QtCore.Qt.MetaModifier) + if sys.platform == "darwin": + down = include_command and (modifiers & QtCore.Qt.KeyboardModifier.ControlModifier) + return bool(down) ^ bool(modifiers & QtCore.Qt.KeyboardModifier.MetaModifier) else: - return bool(modifiers & QtCore.Qt.ControlModifier) + return bool(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier) def _create_control(self): """ Creates and connects the underlying text widget. """ # Create the underlying control. - if self.kind == 'plain': + if self.kind == "plain": control = QtGui.QPlainTextEdit() - elif self.kind == 'rich': + elif self.kind == "rich": control = QtGui.QTextEdit() control.setAcceptRichText(False) @@ -1009,11 +1091,28 @@ # Connect signals. control.cursorPositionChanged.connect(self._cursor_position_changed) + self._connections_to_remove.append( + (control.cursorPositionChanged, self._cursor_position_changed) + ) control.customContextMenuRequested.connect( - self._custom_context_menu_requested) + self._custom_context_menu_requested + ) + self._connections_to_remove.append( + (control.customContextMenuRequested, + self._custom_context_menu_requested) + ) control.copyAvailable.connect(self.copy_available) + self._connections_to_remove.append( + (control.copyAvailable, self.copy_available) + ) control.redoAvailable.connect(self.redo_available) + self._connections_to_remove.append( + (control.redoAvailable, self.redo_available) + ) control.undoAvailable.connect(self.undo_available) + self._connections_to_remove.append( + (control.undoAvailable, self.undo_available) + ) # Hijack the document size change signal to prevent Qt from adjusting # the viewport's scrollbar. We are relying on an implementation detail @@ -1022,25 +1121,27 @@ layout = control.document().documentLayout() layout.documentSizeChanged.disconnect() layout.documentSizeChanged.connect(self._adjust_scrollbars) + # The document layout doesn't stay the same therefore its signal is + # not explicitly disconnected on destruction # Configure the control. - control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + control.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) control.setReadOnly(True) control.setUndoRedoEnabled(False) - control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn) return control def _create_page_control(self): """ Creates and connects the underlying paging widget. """ - if self.kind == 'plain': + if self.kind == "plain": control = QtGui.QPlainTextEdit() - elif self.kind == 'rich': + elif self.kind == "rich": control = QtGui.QTextEdit() control.installEventFilter(self) control.setReadOnly(True) control.setUndoRedoEnabled(False) - control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn) return control def _event_filter_console_keypress(self, event): @@ -1052,26 +1153,26 @@ position = cursor.position() key = event.key() ctrl_down = self._control_key_down(event.modifiers()) - alt_down = event.modifiers() & QtCore.Qt.AltModifier - shift_down = event.modifiers() & QtCore.Qt.ShiftModifier + alt_down = event.modifiers() & QtCore.Qt.KeyboardModifier.AltModifier + shift_down = event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier - #------ Special sequences ---------------------------------------------- + # Special sequences ---------------------------------------------- - if event.matches(QtGui.QKeySequence.Copy): + if event.matches(QtGui.QKeySequence.StandardKey.Copy): self.copy() intercepted = True - elif event.matches(QtGui.QKeySequence.Cut): + elif event.matches(QtGui.QKeySequence.StandardKey.Cut): self.cut() intercepted = True - elif event.matches(QtGui.QKeySequence.Paste): + elif event.matches(QtGui.QKeySequence.StandardKey.Paste): self.paste() intercepted = True - #------ Special modifier logic ----------------------------------------- + # Special modifier logic ----------------------------------------- - elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter): + elif key in (QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter): intercepted = True # Special handling when tab completing in text mode. @@ -1079,7 +1180,7 @@ if self._in_buffer(position): if self._reading: - self._append_plain_text('\n') + self._append_plain_text("\n") self._reading = False if self._reading_callback: self._reading_callback() @@ -1088,190 +1189,209 @@ # whitespace after the cursor, execute. Otherwise, split the # line with a continuation prompt. elif not self._executing: - cursor.movePosition(QtGui.QTextCursor.End, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.End, QtGui.QTextCursor.MoveMode.KeepAnchor + ) at_end = len(cursor.selectedText().strip()) == 0 - single_line = (self._get_end_cursor().blockNumber() == - self._get_prompt_cursor().blockNumber()) + single_line = ( + self._get_end_cursor().blockNumber() + == self._get_prompt_cursor().blockNumber() + ) if (at_end or shift_down or single_line) and not ctrl_down: - self.execute(interactive = not shift_down) + self.execute(interactive=not shift_down) else: # Do this inside an edit block for clean undo/redo. cursor.beginEditBlock() cursor.setPosition(position) - cursor.insertText('\n') + cursor.insertText("\n") self._insert_continuation_prompt(cursor) cursor.endEditBlock() # Ensure that the whole input buffer is visible. # FIXME: This will not be usable if the input buffer is # taller than the console widget. - self._control.moveCursor(QtGui.QTextCursor.End) + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) self._control.setTextCursor(cursor) - #------ Control/Cmd modifier ------------------------------------------- + # Control/Cmd modifier ------------------------------------------- elif ctrl_down: - if key == QtCore.Qt.Key_G: + if key == QtCore.Qt.Key.Key_G: self._keyboard_quit() intercepted = True - elif key == QtCore.Qt.Key_K: + elif key == QtCore.Qt.Key.Key_K: if self._in_buffer(position): - cursor.movePosition(QtGui.QTextCursor.EndOfLine, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.EndOfLine, + QtGui.QTextCursor.MoveMode.KeepAnchor, + ) if not cursor.hasSelection(): # Line deletion (remove continuation prompt) - cursor.movePosition(QtGui.QTextCursor.NextBlock, - QtGui.QTextCursor.KeepAnchor) - cursor.movePosition(QtGui.QTextCursor.Right, - QtGui.QTextCursor.KeepAnchor, - len(self._continuation_prompt)) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.NextBlock, + QtGui.QTextCursor.MoveMode.KeepAnchor, + ) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Right, + QtGui.QTextCursor.MoveMode.KeepAnchor, + len(self._continuation_prompt), + ) cursor.removeSelectedText() intercepted = True - elif key == QtCore.Qt.Key_L: + elif key == QtCore.Qt.Key.Key_L: self.prompt_to_top() intercepted = True - elif key == QtCore.Qt.Key_O: + elif key == QtCore.Qt.Key.Key_O: if self._page_control and self._page_control.isVisible(): self._page_control.setFocus() intercepted = True - elif key == QtCore.Qt.Key_Y: + elif key == QtCore.Qt.Key.Key_Y: self.paste() intercepted = True - elif key in (QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete): + elif key in (QtCore.Qt.Key.Key_Backspace, QtCore.Qt.Key.Key_Delete): intercepted = True - elif key == QtCore.Qt.Key_Plus: + elif key == QtCore.Qt.Key.Key_Plus: self.change_font_size(1) intercepted = True - elif key == QtCore.Qt.Key_Minus: + elif key == QtCore.Qt.Key.Key_Minus: self.change_font_size(-1) intercepted = True - #------ Alt modifier --------------------------------------------------- + # Alt modifier --------------------------------------------------- elif alt_down: - if key == QtCore.Qt.Key_B: + if key == QtCore.Qt.Key.Key_B: self._set_cursor(self._get_word_start_cursor(position)) intercepted = True - elif key == QtCore.Qt.Key_F: + elif key == QtCore.Qt.Key.Key_F: self._set_cursor(self._get_word_end_cursor(position)) intercepted = True - elif key == QtCore.Qt.Key_Backspace: + elif key == QtCore.Qt.Key.Key_Backspace: cursor = self._get_word_start_cursor(position) - cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(position, QtGui.QTextCursor.MoveMode.KeepAnchor) cursor.removeSelectedText() intercepted = True - elif key == QtCore.Qt.Key_D: + elif key == QtCore.Qt.Key.Key_D: cursor = self._get_word_end_cursor(position) - cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(position, QtGui.QTextCursor.MoveMode.KeepAnchor) cursor.removeSelectedText() intercepted = True - elif key == QtCore.Qt.Key_Delete: + elif key == QtCore.Qt.Key.Key_Delete: intercepted = True - elif key == QtCore.Qt.Key_Greater: - self._control.moveCursor(QtGui.QTextCursor.End) + elif key == QtCore.Qt.Key.Key_Greater: + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) intercepted = True - elif key == QtCore.Qt.Key_Less: + elif key == QtCore.Qt.Key.Key_Less: self._control.setTextCursor(self._get_prompt_cursor()) intercepted = True - #------ No modifiers --------------------------------------------------- + # No modifiers --------------------------------------------------- else: if shift_down: - anchormode=QtGui.QTextCursor.KeepAnchor + anchormode = QtGui.QTextCursor.MoveMode.KeepAnchor else: - anchormode=QtGui.QTextCursor.MoveAnchor + anchormode = QtGui.QTextCursor.MoveMode.MoveAnchor - if key == QtCore.Qt.Key_Escape: + if key == QtCore.Qt.Key.Key_Escape: self._keyboard_quit() intercepted = True - elif key == QtCore.Qt.Key_Up: + elif key == QtCore.Qt.Key.Key_Up: if self._reading or not self._up_pressed(): intercepted = True else: prompt_line = self._get_prompt_cursor().blockNumber() intercepted = cursor.blockNumber() <= prompt_line - elif key == QtCore.Qt.Key_Down: + elif key == QtCore.Qt.Key.Key_Down: if self._reading or not self._down_pressed(): intercepted = True else: end_line = self._get_end_cursor().blockNumber() intercepted = cursor.blockNumber() == end_line - elif key == QtCore.Qt.Key_Tab: + elif key == QtCore.Qt.Key.Key_Tab: if not self._reading: intercepted = not self._tab_pressed() - elif key == QtCore.Qt.Key_Left: + elif key == QtCore.Qt.Key.Key_Left: # Move to the previous line line, col = cursor.blockNumber(), cursor.columnNumber() - if line > self._get_prompt_cursor().blockNumber() and \ - col == len(self._continuation_prompt): - self._control.moveCursor(QtGui.QTextCursor.PreviousBlock, - mode=anchormode) - self._control.moveCursor(QtGui.QTextCursor.EndOfBlock, - mode=anchormode) + if line > self._get_prompt_cursor().blockNumber() and col == len( + self._continuation_prompt + ): + self._control.moveCursor( + QtGui.QTextCursor.MoveOperation.PreviousBlock, mode=anchormode + ) + self._control.moveCursor( + QtGui.QTextCursor.MoveOperation.EndOfBlock, mode=anchormode + ) intercepted = True # Regular left movement else: intercepted = not self._in_buffer(position - 1) - elif key == QtCore.Qt.Key_Right: + elif key == QtCore.Qt.Key.Key_Right: original_block_number = cursor.blockNumber() - cursor.movePosition(QtGui.QTextCursor.Right, - mode=anchormode) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.Right, mode=anchormode) if cursor.blockNumber() != original_block_number: - cursor.movePosition(QtGui.QTextCursor.Right, - n=len(self._continuation_prompt), - mode=anchormode) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Right, + n=len(self._continuation_prompt), + mode=anchormode, + ) self._set_cursor(cursor) intercepted = True - elif key == QtCore.Qt.Key_Home: + elif key == QtCore.Qt.Key.Key_Home: start_line = cursor.blockNumber() if start_line == self._get_prompt_cursor().blockNumber(): start_pos = self._prompt_pos else: - cursor.movePosition(QtGui.QTextCursor.StartOfBlock, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.StartOfBlock, + QtGui.QTextCursor.MoveMode.KeepAnchor, + ) start_pos = cursor.position() start_pos += len(self._continuation_prompt) cursor.setPosition(position) if shift_down and self._in_buffer(position): - cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(start_pos, QtGui.QTextCursor.MoveMode.KeepAnchor) else: cursor.setPosition(start_pos) self._set_cursor(cursor) intercepted = True - elif key == QtCore.Qt.Key_Backspace: + elif key == QtCore.Qt.Key.Key_Backspace: # Line deletion (remove continuation prompt) line, col = cursor.blockNumber(), cursor.columnNumber() - if not self._reading and \ - col == len(self._continuation_prompt) and \ - line > self._get_prompt_cursor().blockNumber(): + if ( + not self._reading + and col == len(self._continuation_prompt) + and line > self._get_prompt_cursor().blockNumber() + ): cursor.beginEditBlock() - cursor.movePosition(QtGui.QTextCursor.StartOfBlock, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.StartOfBlock, + QtGui.QTextCursor.MoveMode.KeepAnchor, + ) cursor.removeSelectedText() cursor.deletePreviousChar() cursor.endEditBlock() @@ -1283,26 +1403,37 @@ if anchor == position: intercepted = not self._in_buffer(position - 1) else: - intercepted = not self._in_buffer(min(anchor, position)) + intercepted = not self._in_buffer( + min(anchor, position) + ) - elif key == QtCore.Qt.Key_Delete: + elif key == QtCore.Qt.Key.Key_Delete: # Line deletion (remove continuation prompt) - if not self._reading and self._in_buffer(position) and \ - cursor.atBlockEnd() and not cursor.hasSelection(): - cursor.movePosition(QtGui.QTextCursor.NextBlock, - QtGui.QTextCursor.KeepAnchor) - cursor.movePosition(QtGui.QTextCursor.Right, - QtGui.QTextCursor.KeepAnchor, - len(self._continuation_prompt)) + if ( + not self._reading + and self._in_buffer(position) + and cursor.atBlockEnd() + and not cursor.hasSelection() + ): + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.NextBlock, + QtGui.QTextCursor.MoveMode.KeepAnchor, + ) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Right, + QtGui.QTextCursor.MoveMode.KeepAnchor, + len(self._continuation_prompt), + ) cursor.removeSelectedText() intercepted = True # Regular forwards deletion: else: anchor = cursor.anchor() - intercepted = (not self._in_buffer(anchor) or - not self._in_buffer(position)) + intercepted = not self._in_buffer( + anchor + ) or not self._in_buffer(position) # Don't move the cursor if Control/Cmd is pressed to allow copy-paste # using the keyboard in any part of the buffer. @@ -1317,46 +1448,50 @@ """ key = event.key() ctrl_down = self._control_key_down(event.modifiers()) - alt_down = event.modifiers() & QtCore.Qt.AltModifier + alt_down = event.modifiers() & QtCore.Qt.KeyboardModifier.AltModifier if ctrl_down: - if key == QtCore.Qt.Key_O: + if key == QtCore.Qt.Key.Key_O: self._control.setFocus() return True elif alt_down: - if key == QtCore.Qt.Key_Greater: - self._page_control.moveCursor(QtGui.QTextCursor.End) + if key == QtCore.Qt.Key.Key_Greater: + self._page_control.moveCursor(QtGui.QTextCursor.MoveOperation.End) return True - elif key == QtCore.Qt.Key_Less: - self._page_control.moveCursor(QtGui.QTextCursor.Start) + elif key == QtCore.Qt.Key.Key_Less: + self._page_control.moveCursor(QtGui.QTextCursor.MoveOperation.Start) return True - elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape): + elif key in (QtCore.Qt.Key.Key_Q, QtCore.Qt.Key.Key_Escape): if self._splitter: self._page_control.hide() else: self.layout().setCurrentWidget(self._control) return True - elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): - new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, - QtCore.Qt.Key_PageDown, - QtCore.Qt.NoModifier) + elif key in (QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Return): + new_event = QtGui.QKeyEvent( + QtCore.QEvent.Type.KeyPress, + QtCore.Qt.Key.Key_PageDown, + QtCore.Qt.KeyboardModifier.NoModifier, + ) QtGui.QApplication.sendEvent(self._page_control, new_event) return True - elif key == QtCore.Qt.Key_Backspace: - new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, - QtCore.Qt.Key_PageUp, - QtCore.Qt.NoModifier) + elif key == QtCore.Qt.Key.Key_Backspace: + new_event = QtGui.QKeyEvent( + QtCore.QEvent.Type.KeyPress, + QtCore.Qt.Key.Key_PageUp, + QtCore.Qt.KeyboardModifier.NoModifier, + ) QtGui.QApplication.sendEvent(self._page_control, new_event) return True return False - def _format_as_columns(self, items, separator=' '): + def _format_as_columns(self, items, separator=" "): """ Transform a list of strings into a single string with columns. Parameters @@ -1376,18 +1511,18 @@ # Calculate the number of characters available. width = self._control.viewport().width() - char_width = QtGui.QFontMetrics(self.font).width(' ') + char_width = QtGui.QFontMetrics(self.font).width(" ") displaywidth = max(10, (width / char_width) - 1) # Some degenerate cases. size = len(items) if size == 0: - return '\n' + return "\n" elif size == 1: - return '%s\n' % items[0] + return "%s\n" % items[0] # Try every row count from 1 upwards - array_index = lambda nrows, row, col: nrows*col + row + array_index = lambda nrows, row, col: nrows * col + row for nrows in range(1, size): ncols = (size + nrows - 1) // nrows colwidths = [] @@ -1397,7 +1532,8 @@ colwidth = 0 for row in range(nrows): i = array_index(nrows, row, col) - if i >= size: break + if i >= size: + break x = items[i] colwidth = max(colwidth, len(x)) colwidths.append(colwidth) @@ -1409,29 +1545,30 @@ # The smallest number of rows computed and the max widths for each # column has been obtained. Now we just have to format each of the rows. - string = '' + string = "" for row in range(nrows): texts = [] for col in range(ncols): - i = row + nrows*col + i = row + nrows * col if i >= size: - texts.append('') + texts.append("") else: texts.append(items[i]) while texts and not texts[-1]: del texts[-1] for col in range(len(texts)): texts[col] = texts[col].ljust(colwidths[col]) - string += '%s\n' % separator.join(texts) + string += "%s\n" % separator.join(texts) return string def _get_block_plain_text(self, block): """ Given a QTextBlock, return its unformatted text. """ cursor = QtGui.QTextCursor(block) - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) - cursor.movePosition(QtGui.QTextCursor.EndOfBlock, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.EndOfBlock, QtGui.QTextCursor.MoveMode.KeepAnchor + ) return cursor.selection().toPlainText() def _get_cursor(self): @@ -1443,7 +1580,7 @@ """ Convenience method that returns a cursor for the last character. """ cursor = self._control.textCursor() - cursor.movePosition(QtGui.QTextCursor.End) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.End) return cursor def _get_input_buffer_cursor_column(self): @@ -1497,7 +1634,7 @@ """ cursor = self._control.textCursor() cursor.setPosition(start) - cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(end, QtGui.QTextCursor.MoveMode.KeepAnchor) return cursor def _get_word_start_cursor(self, position): @@ -1507,11 +1644,13 @@ """ document = self._control.document() position -= 1 - while position >= self._prompt_pos and \ - not is_letter_or_number(document.characterAt(position)): + while position >= self._prompt_pos and not is_letter_or_number( + document.characterAt(position) + ): position -= 1 - while position >= self._prompt_pos and \ - is_letter_or_number(document.characterAt(position)): + while position >= self._prompt_pos and is_letter_or_number( + document.characterAt(position) + ): position -= 1 cursor = self._control.textCursor() cursor.setPosition(position + 1) @@ -1524,11 +1663,13 @@ """ document = self._control.document() end = self._get_end_cursor().position() - while position < end and \ - not is_letter_or_number(document.characterAt(position)): + while position < end and not is_letter_or_number( + document.characterAt(position) + ): position += 1 - while position < end and \ - is_letter_or_number(document.characterAt(position)): + while position < end and is_letter_or_number( + document.characterAt(position) + ): position += 1 cursor = self._control.textCursor() cursor.setPosition(position) @@ -1541,7 +1682,8 @@ self._insert_plain_text(cursor, self._continuation_prompt) else: self._continuation_prompt = self._insert_html_fetching_plain_text( - cursor, self._continuation_prompt_html) + cursor, self._continuation_prompt_html + ) def _insert_html(self, cursor, html): """ Inserts HTML using the specified cursor in such a way that future @@ -1555,13 +1697,14 @@ # in unwanted formatting, lost tab characters, etc. The following code # hacks around this behavior, which I consider to be a bug in Qt, by # (crudely) resetting the document's style state. - cursor.movePosition(QtGui.QTextCursor.Left, - QtGui.QTextCursor.KeepAnchor) - if cursor.selection().toPlainText() == ' ': + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Left, QtGui.QTextCursor.MoveMode.KeepAnchor + ) + if cursor.selection().toPlainText() == " ": cursor.removeSelectedText() else: - cursor.movePosition(QtGui.QTextCursor.Right) - cursor.insertText(' ', QtGui.QTextCharFormat()) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.Right) + cursor.insertText(" ", QtGui.QTextCharFormat()) cursor.endEditBlock() def _insert_html_fetching_plain_text(self, cursor, html): @@ -1574,7 +1717,7 @@ start = cursor.position() self._insert_html(cursor, html) end = cursor.position() - cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor) + cursor.setPosition(start, QtGui.QTextCursor.MoveMode.KeepAnchor) text = cursor.selection().toPlainText() cursor.setPosition(end) @@ -1600,9 +1743,9 @@ if self._continuation_prompt_html is None: cursor.insertText(self._continuation_prompt) else: - self._continuation_prompt = \ - self._insert_html_fetching_plain_text( - cursor, self._continuation_prompt_html) + self._continuation_prompt = self._insert_html_fetching_plain_text( + cursor, self._continuation_prompt_html + ) cursor.insertText(line) cursor.endEditBlock() @@ -1620,7 +1763,7 @@ if line == prompt_line: return position >= self._prompt_pos elif line > prompt_line: - cursor.movePosition(QtGui.QTextCursor.StartOfBlock) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.StartOfBlock) prompt_pos = cursor.position() + len(self._continuation_prompt) return position >= prompt_pos return False @@ -1632,7 +1775,7 @@ moved = not self._in_buffer() if moved: cursor = self._control.textCursor() - cursor.movePosition(QtGui.QTextCursor.End) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.End) self._control.setTextCursor(cursor) return moved @@ -1642,7 +1785,7 @@ if self._text_completing_pos: self._cancel_text_completion() else: - self.input_buffer = '' + self.input_buffer = "" def _page(self, text, html=False): """ Displays text using the pager if it exceeds the height of the @@ -1655,9 +1798,10 @@ """ line_height = QtGui.QFontMetrics(self.font).height() minlines = self._control.viewport().height() / line_height - if self.paging != 'none' and \ - re.match("(?:[^\n]*\n){%i}" % minlines, text): - if self.paging == 'custom': + if self.paging != "none" and re.match( + "(?:[^\n]*\n){%i}" % minlines, text + ): + if self.paging == "custom": self.custom_page_requested.emit(text) else: self._page_control.clear() @@ -1666,7 +1810,7 @@ self._insert_html(cursor, text) else: self._insert_plain_text(cursor, text) - self._page_control.moveCursor(QtGui.QTextCursor.Start) + self._page_control.moveCursor(QtGui.QTextCursor.MoveOperation.Start) self._page_control.viewport().resize(self._control.size()) if self._splitter: @@ -1695,11 +1839,11 @@ self._control.setUndoRedoEnabled(True) self._control.setReadOnly(False) - self._control.moveCursor(QtGui.QTextCursor.End) + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) self._executing = False self._prompt_started_hook() - def _readline(self, prompt='', callback=None): + def _readline(self, prompt="", callback=None): """ Reads one line of input from the user. Parameters @@ -1718,12 +1862,16 @@ input string with the trailing newline stripped. """ if self._reading: - raise RuntimeError('Cannot read a line. Widget is already reading.') + raise RuntimeError( + "Cannot read a line. Widget is already reading." + ) if not callback and not self.isVisible(): # If the user cannot see the widget, this function cannot return. - raise RuntimeError('Cannot synchronously read a line if the widget ' - 'is not visible!') + raise RuntimeError( + "Cannot synchronously read a line if the widget " + "is not visible!" + ) self._reading = True self._show_prompt(prompt, newline=False) @@ -1732,11 +1880,12 @@ self._reading_callback = None while self._reading: QtCore.QCoreApplication.processEvents() - return self.input_buffer.rstrip('\n') + return self.input_buffer.rstrip("\n") else: - self._reading_callback = lambda: \ - callback(self.input_buffer.rstrip('\n')) + self._reading_callback = lambda: callback( + self.input_buffer.rstrip("\n") + ) def _set_continuation_prompt(self, prompt, html=False): """ Sets the continuation prompt. @@ -1793,10 +1942,11 @@ if newline: cursor = self._get_end_cursor() if cursor.position() > 0: - cursor.movePosition(QtGui.QTextCursor.Left, - QtGui.QTextCursor.KeepAnchor) - if cursor.selection().toPlainText() != '\n': - self._append_plain_text('\n') + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Left, QtGui.QTextCursor.MoveMode.KeepAnchor + ) + if cursor.selection().toPlainText() != "\n": + self._append_plain_text("\n") # Write the prompt. self._append_plain_text(self._prompt_sep) @@ -1817,7 +1967,7 @@ self._prompt_pos = self._get_end_cursor().position() self._prompt_started() - #------ Signal handlers ---------------------------------------------------- + # Signal handlers ---------------------------------------------------- def _adjust_scrollbars(self): """ Expands the vertical scrollbar beyond the range set by Qt. @@ -1838,7 +1988,7 @@ step = viewport_height diff = maximum - scrollbar.maximum() scrollbar.setRange(0, maximum) - scrollbar.setPageStep(step) + scrollbar.setPageStep(int(step)) # Compensate for undesirable scrolling that occurs automatically due to # maximumBlockCount() text truncation. if diff < 0 and document.blockCount() == document.maximumBlockCount(): @@ -1854,8 +2004,10 @@ pos = cursor.position() text_cursor = self._control.textCursor() text_cursor.setPosition(self._text_completing_pos) - if pos < self._text_completing_pos or \ - cursor.blockNumber() > text_cursor.blockNumber(): + if ( + pos < self._text_completing_pos + or cursor.blockNumber() > text_cursor.blockNumber() + ): self._clear_temporary_buffer() self._text_completing_pos = 0 else: diff -Nru python-pyface-6.1.2/pyface/ui/qt4/console/history_console_widget.py python-pyface-7.4.0/pyface/ui/qt4/console/history_console_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/console/history_console_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/console/history_console_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,19 +1,17 @@ -# Copyright (c) 2011-18, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# System library imports +# Thanks for using Enthought open source! + from pyface.qt import QtGui -# Local imports + from .console_widget import ConsoleWidget @@ -22,21 +20,21 @@ executed and provides a readline-esque interface to this history. """ - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'object' interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def __init__(self, *args, **kw): - super(HistoryConsoleWidget, self).__init__(*args, **kw) + super().__init__(*args, **kw) # HistoryConsoleWidget protected variables. self._history = [] self._history_index = 0 - self._history_prefix = '' + self._history_prefix = "" - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'ConsoleWidget' public interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def execute(self, source=None, hidden=False, interactive=False): """ Reimplemented to the store history. @@ -44,8 +42,7 @@ if not hidden: history = self.input_buffer if source is None else source - executed = super(HistoryConsoleWidget, self).execute( - source, hidden, interactive) + executed = super().execute(source, hidden, interactive) if executed and not hidden: # Save the command unless it was an empty string or was identical @@ -59,9 +56,9 @@ return executed - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'ConsoleWidget' abstract interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _up_pressed(self): """ Called when the up key is pressed. Returns whether to continue @@ -73,8 +70,9 @@ # Set a search prefix based on the cursor position. col = self._get_input_buffer_cursor_column() input_buffer = self.input_buffer - if self._history_index == len(self._history) or \ - (self._history_prefix and col != len(self._history_prefix)): + if self._history_index == len(self._history) or ( + self._history_prefix and col != len(self._history_prefix) + ): self._history_index = len(self._history) self._history_prefix = input_buffer[:col] @@ -86,10 +84,11 @@ # search. cursor = self._get_prompt_cursor() if self._history_prefix: - cursor.movePosition(QtGui.QTextCursor.Right, - n=len(self._history_prefix)) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Right, n=len(self._history_prefix) + ) else: - cursor.movePosition(QtGui.QTextCursor.EndOfLine) + cursor.movePosition(QtGui.QTextCursor.MoveOperation.EndOfLine) self._set_cursor(cursor) return False @@ -112,19 +111,20 @@ # input buffer is set.) if self._history_prefix: cursor = self._get_prompt_cursor() - cursor.movePosition(QtGui.QTextCursor.Right, - n=len(self._history_prefix)) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Right, n=len(self._history_prefix) + ) self._set_cursor(cursor) return False return True - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'HistoryConsoleWidget' public interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def history_previous(self, prefix=''): + def history_previous(self, prefix=""): """ If possible, set the input buffer to a previous item in the history. Parameters: @@ -145,7 +145,7 @@ self._history_index = index self.input_buffer = history - def history_next(self, prefix=''): + def history_next(self, prefix=""): """ Set the input buffer to a subsequent item in the history, or to the original search prefix if there is no such item. @@ -164,9 +164,9 @@ history = prefix self.input_buffer = history - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'HistoryConsoleWidget' protected interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _set_history(self, history, history_index=None): """ Replace the current history with a sequence of history items. diff -Nru python-pyface-6.1.2/pyface/ui/qt4/data_view/data_view_item_model.py python-pyface-7.4.0/pyface/ui/qt4/data_view/data_view_item_model.py --- python-pyface-6.1.2/pyface/ui/qt4/data_view/data_view_item_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/data_view/data_view_item_model.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,409 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import logging + +from pyface.qt import is_qt4 +from pyface.qt.QtCore import QAbstractItemModel, QMimeData, QModelIndex, Qt +from pyface.qt.QtGui import QColor +from pyface.data_view.abstract_data_model import AbstractDataModel +from pyface.data_view.abstract_value_type import CheckState +from pyface.data_view.data_view_errors import ( + DataViewGetError, DataViewSetError +) +from pyface.data_view.index_manager import Root +from .data_wrapper import DataWrapper + + +logger = logging.getLogger(__name__) + +# XXX This file is scaffolding and may need to be rewritten + +WHITE = QColor(255, 255, 255) +BLACK = QColor(0, 0, 0) + +set_check_state_map = { + Qt.CheckState.Checked: CheckState.CHECKED, + Qt.CheckState.Unchecked: CheckState.UNCHECKED, +} +get_check_state_map = { + CheckState.CHECKED: Qt.CheckState.Checked, + CheckState.UNCHECKED: Qt.CheckState.Unchecked, +} + + +class DataViewItemModel(QAbstractItemModel): + """ A QAbstractItemModel that understands AbstractDataModels. """ + + def __init__(self, model, selection_type, exporters, parent=None): + super().__init__(parent) + self.model = model + self.selectionType = selection_type + self.exporters = exporters + self.destroyed.connect(self._on_destroyed) + + @property + def model(self): + return self._model + + @model.setter + def model(self, model: AbstractDataModel): + self._disconnect_model_observers() + if hasattr(self, '_model'): + self.beginResetModel() + self._model = model + self.endResetModel() + else: + # model is being initialized + self._model = model + self._connect_model_observers() + + # model event listeners + + def on_structure_changed(self, event): + self.beginResetModel() + self.endResetModel() + + def on_values_changed(self, event): + top, left, bottom, right = event.new + if top == () and bottom == (): + # this is a column header change + self.headerDataChanged.emit(Qt.Orientation.Horizontal, left[0], right[0]) + elif left == () and right == (): + # this is a row header change + # XXX this is currently not supported and not needed + pass + else: + for i, (top_row, bottom_row) in enumerate(zip(top, bottom)): + if top_row != bottom_row: + break + top = top[:i+1] + bottom = bottom[:i+1] + + top_left = self._to_model_index(top, left) + bottom_right = self._to_model_index(bottom, right) + self.dataChanged.emit(top_left, bottom_right) + + # Structure methods + + def parent(self, index): + if not index.isValid(): + return QModelIndex() + parent = index.internalPointer() + if parent == Root: + return QModelIndex() + + grandparent, row = self.model.index_manager.get_parent_and_row(parent) + return self.createIndex(row, 0, grandparent) + + def index(self, row, column, parent): + if parent.isValid(): + parent_index = self.model.index_manager.create_index( + parent.internalPointer(), + parent.row(), + ) + else: + parent_index = Root + index = self.createIndex(row, column, parent_index) + return index + + def rowCount(self, index=QModelIndex()): + row_index = self._to_row_index(index) + try: + if self.model.can_have_children(row_index): + return self.model.get_row_count(row_index) + except Exception: + logger.exception("Error in rowCount") + + return 0 + + def columnCount(self, index=QModelIndex()): + row_index = self._to_row_index(index) + try: + # the number of columns is constant; leaf rows return 0 + if self.model.can_have_children(row_index): + return self.model.get_column_count() + 1 + except Exception: + logger.exception("Error in columnCount") + + return 0 + + # Data methods + + def flags(self, index): + row = self._to_row_index(index) + column = self._to_column_index(index) + value_type = self.model.get_value_type(row, column) + if row == () and column == (): + return Qt.ItemFlag.ItemIsEnabled + + flags = Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsDragEnabled + if not is_qt4 and not self.model.can_have_children(row): + flags |= Qt.ItemFlag.ItemNeverHasChildren + + try: + if value_type: + if value_type.has_editor_value(self.model, row, column): + flags |= Qt.ItemFlag.ItemIsEditable + if ( + value_type.has_check_state(self.model, row, column) + and self.model.can_set_value(row, column) + ): + flags |= Qt.ItemFlag.ItemIsUserCheckable + except DataViewGetError: + # expected error, ignore + pass + except Exception: + # unexpected error, log and raise + logger.exception( + "get flags failed: row %r, column %r", + row, + column, + ) + raise + + return flags + + def data(self, index, role=Qt.ItemDataRole.DisplayRole): + row = self._to_row_index(index) + column = self._to_column_index(index) + value_type = self.model.get_value_type(row, column) + try: + if not value_type: + return None + + if role == Qt.ItemDataRole.DisplayRole: + if value_type.has_text(self.model, row, column): + return value_type.get_text(self.model, row, column) + elif role == Qt.ItemDataRole.EditRole: + if value_type.has_editor_value(self.model, row, column): + return value_type.get_editor_value(self.model, row, column) + elif role == Qt.ItemDataRole.DecorationRole: + if value_type.has_image(self.model, row, column): + image = value_type.get_image(self.model, row, column) + if image is not None: + return image.create_image() + elif role == Qt.ItemDataRole.BackgroundRole: + if value_type.has_color(self.model, row, column): + color = value_type.get_color(self.model, row, column) + if color is not None: + return color.to_toolkit() + elif role == Qt.ItemDataRole.ForegroundRole: + if value_type.has_color(self.model, row, column): + color = value_type.get_color(self.model, row, column) + if color is not None and color.is_dark: + return WHITE + else: + return BLACK + elif role == Qt.ItemDataRole.CheckStateRole: + if value_type.has_check_state(self.model, row, column): + value = value_type.get_check_state(self.model, row, column) + return get_check_state_map[value] + elif role == Qt.ItemDataRole.ToolTipRole: + if value_type.has_tooltip(self.model, row, column): + return value_type.get_tooltip(self.model, row, column) + except DataViewGetError: + # expected error, ignore + pass + except Exception: + # unexpected error, log and raise + logger.exception( + "get data failed: row %r, column %r", + row, + column, + ) + raise + + return None + + def setData(self, index, value, role=Qt.ItemDataRole.EditRole): + row = self._to_row_index(index) + column = self._to_column_index(index) + value_type = self.model.get_value_type(row, column) + if not value_type: + return False + + try: + if role == Qt.ItemDataRole.EditRole: + if value_type.has_editor_value(self.model, row, column): + value_type.set_editor_value(self.model, row, column, value) + elif role == Qt.ItemDataRole.DisplayRole: + if value_type.has_text(self.model, row, column): + value_type.set_text(self.model, row, column, value) + elif role == Qt.ItemDataRole.CheckStateRole: + if value_type.has_check_state(self.model, row, column): + state = set_check_state_map[value] + value_type.set_check_state(self.model, row, column, state) + + except DataViewSetError: + return False + except Exception: + # unexpected error, log and persevere + logger.exception( + "setData failed: row %r, column %r, value %r", + row, + column, + value, + ) + return False + else: + return True + + def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole): + if orientation == Qt.Orientation.Horizontal: + row = () + if section == 0: + column = () + else: + column = (section - 1,) + else: + # XXX not currently used, but here for symmetry and completeness + row = (section,) + column = () + + value_type = self.model.get_value_type(row, column) + + try: + if role == Qt.ItemDataRole.DisplayRole: + if value_type.has_text(self.model, row, column): + return value_type.get_text(self.model, row, column) + except DataViewGetError: + # expected error, ignore + pass + except Exception: + # unexpected error, log and raise + logger.exception( + "get header data failed: row %r, column %r", + row, + column, + ) + raise + + return None + + def mimeData(self, indexes): + mimedata = super().mimeData(indexes) + if mimedata is None: + mimedata = QMimeData() + data_wrapper = DataWrapper(toolkit_data=mimedata) + + indices = self._normalize_indices(indexes) + for exporter in self.exporters: + try: + exporter.add_data(data_wrapper, self.model, indices) + except Exception: + # unexpected error, log and raise + logger.exception( + "data export failed: mimetype {}, indices {}", + exporter.format.mimetype, + indices, + ) + raise + + return data_wrapper.toolkit_data + + # Private utility methods + + def _on_destroyed(self): + self._disconnect_model_observers() + self._model = None + + def _connect_model_observers(self): + if getattr(self, "_model", None) is not None: + self._model.observe( + self.on_structure_changed, + 'structure_changed', + dispatch='ui', + ) + self._model.observe( + self.on_values_changed, + 'values_changed', + dispatch='ui', + ) + + def _disconnect_model_observers(self): + if getattr(self, "_model", None) is not None: + self._model.observe( + self.on_structure_changed, + 'structure_changed', + dispatch='ui', + remove=True, + ) + self._model.observe( + self.on_values_changed, + 'values_changed', + dispatch='ui', + remove=True, + ) + + def _to_row_index(self, index): + if not index.isValid(): + row_index = () + else: + parent = index.internalPointer() + if parent == Root: + row_index = () + else: + row_index = self.model.index_manager.to_sequence(parent) + row_index += (index.row(),) + return row_index + + def _to_column_index(self, index): + if not index.isValid(): + return () + else: + column = index.column() + if column == 0: + return () + else: + return (column - 1,) + + def _to_model_index(self, row_index, column_index): + if len(row_index) == 0: + return QModelIndex() + index = self.model.index_manager.from_sequence(row_index[:-1]) + row = row_index[-1] + if len(column_index) == 0: + column = 0 + else: + column = column_index[0] + 1 + + return self.createIndex(row, column, index) + + def _extract_rows(self, indices): + rows = [] + for index in indices: + row = self._to_row_index(index) + if (row, ()) not in rows: + rows.append((row, ())) + return rows + + def _extract_columns(self, indices): + columns = [] + for index in indices: + row = self._to_row_index(index)[:-1] + column = self._to_column_index(index) + if (row, column) not in columns: + columns.append((row, column)) + return columns + + def _extract_indices(self, indices): + return [ + (self._to_row_index(index), self._to_column_index(index)) + for index in indices + ] + + def _normalize_indices(self, indices): + if self.selectionType == 'row': + return self._extract_rows(indices) + elif self.selectionType == 'column': + return self._extract_columns(indices) + else: + return self._extract_indices(indices) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/data_view/data_view_widget.py python-pyface-7.4.0/pyface/ui/qt4/data_view/data_view_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/data_view/data_view_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/data_view/data_view_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,247 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import logging + +from traits.api import Callable, Enum, Instance, observe, provides + +from pyface.qt.QtCore import QAbstractItemModel +from pyface.qt.QtGui import ( + QAbstractItemView, QItemSelection, QItemSelectionModel, QTreeView +) +from pyface.data_view.i_data_view_widget import ( + IDataViewWidget, MDataViewWidget +) +from pyface.ui.qt4.layout_widget import LayoutWidget +from .data_view_item_model import DataViewItemModel + +# XXX This file is scaffolding and may need to be rewritten + +logger = logging.getLogger(__name__) + +qt_selection_types = { + "row": QAbstractItemView.SelectionBehavior.SelectRows, + "column": QAbstractItemView.SelectionBehavior.SelectColumns, + "item": QAbstractItemView.SelectionBehavior.SelectItems, +} +pyface_selection_types = { + value: key for key, value in qt_selection_types.items() +} +qt_selection_modes = { + "none": QAbstractItemView.SelectionMode.NoSelection, + "single": QAbstractItemView.SelectionMode.SingleSelection, + "extended": QAbstractItemView.SelectionMode.ExtendedSelection, +} +pyface_selection_modes = { + value: key for key, value in qt_selection_modes.items() +} + + +class DataViewTreeView(QTreeView): + """ QTreeView subclass that handles drag and drop via DropHandlers. """ + + _widget = None + + def dragEnterEvent(self, event): + drop_handler = self._get_drop_handler(event) + if drop_handler is not None: + event.acceptProposedAction() + else: + super().dragEnterEvent(event) + + def dragMoveEvent(self, event): + drop_handler = self._get_drop_handler(event) + if drop_handler is not None: + event.acceptProposedAction() + else: + super().dragMoveEvent(event) + + def dragLeaveEvent(self, event): + drop_handler = self._get_drop_handler(event) + if drop_handler is not None: + event.acceptProposedAction() + else: + super().dragLeaveEvent(event) + + def dropEvent(self, event): + drop_handler = self._get_drop_handler(event) + if drop_handler is not None: + drop_handler.handle_drop(event, self) + event.acceptProposedAction() + else: + super().dropEvent(event) + + def _get_drop_handler(self, event): + if self._widget is not None: + widget = self._widget + for drop_handler in widget.drop_handlers: + if drop_handler.can_handle_drop(event, self): + return drop_handler + return None + + +@provides(IDataViewWidget) +class DataViewWidget(MDataViewWidget, LayoutWidget): + """ The Qt implementation of the DataViewWidget. """ + + #: Factory for the underlying Qt control, to facilitate replacement + control_factory = Callable(DataViewTreeView) + + # IDataViewWidget Interface traits -------------------------------------- + + #: What can be selected. Qt supports additional selection types. + selection_type = Enum("row", "column", "item") + + #: How selections are modified. Qt supports turning off selections. + selection_mode = Enum("extended", "none", "single") + + # IWidget Interface traits ---------------------------------------------- + + control = Instance(QAbstractItemView) + + # Private traits -------------------------------------------------------- + + #: The QAbstractItemModel instance used by the view. This will + #: usually be a DataViewItemModel subclass. + _item_model = Instance(QAbstractItemModel) + + # ------------------------------------------------------------------------ + # IDataViewWidget Interface + # ------------------------------------------------------------------------ + + def _create_item_model(self): + """ Create the DataViewItemModel which wraps the data model. """ + self._item_model = DataViewItemModel( + self.data_model, + self.selection_type, + self.exporters, + ) + + def _get_control_header_visible(self): + """ Method to get the control's header visibility. """ + return not self.control.isHeaderHidden() + + def _set_control_header_visible(self, header_visible): + """ Method to set the control's header visibility. """ + self.control.setHeaderHidden(not header_visible) + + def _get_control_selection_type(self): + """ Toolkit specific method to get the selection type. """ + qt_selection_type = self.control.selectionBehavior() + return pyface_selection_types[qt_selection_type] + + def _set_control_selection_type(self, selection_type): + """ Toolkit specific method to change the selection type. """ + qt_selection_type = qt_selection_types[selection_type] + self.control.setSelectionBehavior(qt_selection_type) + self._item_model.selectionType = selection_type + + def _get_control_selection_mode(self): + """ Toolkit specific method to get the selection mode. """ + qt_selection_mode = self.control.selectionMode() + return pyface_selection_modes[qt_selection_mode] + + def _set_control_selection_mode(self, selection_mode): + """ Toolkit specific method to change the selection mode. """ + qt_selection_mode = qt_selection_modes[selection_mode] + self.control.setSelectionMode(qt_selection_mode) + + def _get_control_selection(self): + """ Toolkit specific method to get the selection. """ + indices = self.control.selectedIndexes() + if self.selection_type == 'row': + return self._item_model._extract_rows(indices) + elif self.selection_type == 'column': + return self._item_model._extract_columns(indices) + else: + return self._item_model._extract_indices(indices) + + def _set_control_selection(self, selection): + """ Toolkit specific method to change the selection. """ + selection_model = self.control.selectionModel() + select_flags = QItemSelectionModel.SelectionFlag.Select + qt_selection = QItemSelection() + + if self.selection_type == 'row': + select_flags |= QItemSelectionModel.SelectionFlag.Rows + for row, column in selection: + index = self._item_model._to_model_index(row, (0,)) + qt_selection.select(index, index) + elif self.selection_type == 'column': + select_flags |= QItemSelectionModel.SelectionFlag.Columns + for row, column in selection: + index = self._item_model._to_model_index( + row + (0,), column) + qt_selection.select(index, index) + else: + for row, column in selection: + index = self._item_model._to_model_index(row, column) + qt_selection.select(index, index) + selection_model.clearSelection() + selection_model.select(qt_selection, select_flags) + + def _observe_control_selection(self, remove=False): + selection_model = self.control.selectionModel() + if remove: + try: + selection_model.selectionChanged.disconnect( + self._update_selection + ) + except (TypeError, RuntimeError): + # has already been disconnected + logger.info("selectionChanged already disconnected") + else: + selection_model.selectionChanged.connect(self._update_selection) + + # ------------------------------------------------------------------------ + # IWidget Interface + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + """ Create the DataViewWidget's toolkit control. """ + self._create_item_model() + + control = self.control_factory(parent) + control._widget = self + control.setUniformRowHeights(True) + control.setAnimated(True) + control.setDragEnabled(True) + control.setModel(self._item_model) + control.setAcceptDrops(True) + control.setDropIndicatorShown(True) + return control + + def destroy(self): + """ Perform any actions required to destroy the control. + """ + if self.control is not None: + self.control.setModel(None) + + # ensure that we release references + self.control._widget = None + self._item_model = None + + super().destroy() + + # ------------------------------------------------------------------------ + # Private methods + # ------------------------------------------------------------------------ + + # Trait observers + + @observe('data_model', dispatch='ui') + def _update_item_model(self, event): + if self._item_model is not None: + self._item_model.model = event.new + + @observe('exporters.items', dispatch='ui') + def _update_exporters(self, event): + if self._item_model is not None: + self._item_model.exporters = self.exporters diff -Nru python-pyface-6.1.2/pyface/ui/qt4/data_view/data_wrapper.py python-pyface-7.4.0/pyface/ui/qt4/data_view/data_wrapper.py --- python-pyface-6.1.2/pyface/ui/qt4/data_view/data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/data_view/data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,62 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Instance, provides + +from pyface.data_view.i_data_wrapper import IDataWrapper, MDataWrapper +from pyface.qt.QtCore import QMimeData + + +@provides(IDataWrapper) +class DataWrapper(MDataWrapper): + """ Qt implementaton of IDataWrapper. + + This wraps a QMimeData in a straightforward way. + """ + + toolkit_data = Instance(QMimeData, args=(), allow_none=False) + + def mimetypes(self): + """ Return a set of mimetypes holding data. + + Returns + ------- + mimetypes : set of str + The set of mimetypes currently storing data in the toolkit data + object. + """ + return set(self.toolkit_data.formats()) + + def get_mimedata(self, mimetype): + """ Get raw data for the given media type. + + Parameters + ---------- + mimetype : str + The mime media type to be extracted. + + Returns + ------- + mimedata : bytes + The mime media data as bytes. + """ + return self.toolkit_data.data(mimetype).data() + + def set_mimedata(self, mimetype, raw_data): + """ Set raw data for the given media type. + + Parameters + ---------- + mimetype : str + The mime media type to be extracted. + mimedata : bytes + The mime media data encoded as bytes.. + """ + return self.toolkit_data.setData(mimetype, raw_data) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/data_view/tests/test_data_view_item_model.py python-pyface-7.4.0/pyface/ui/qt4/data_view/tests/test_data_view_item_model.py --- python-pyface-6.1.2/pyface/ui/qt4/data_view/tests/test_data_view_item_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/data_view/tests/test_data_view_item_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,74 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from traits.testing.optional_dependencies import numpy as np, requires_numpy + +from pyface.qt.QtCore import QMimeData +# This import results in an error without numpy installed +# see enthought/pyface#742 +if np is not None: + from pyface.data_view.data_models.api import ArrayDataModel +from pyface.data_view.exporters.row_exporter import RowExporter +from pyface.data_view.data_formats import table_format +from pyface.data_view.value_types.api import FloatValue +from pyface.ui.qt4.data_view.data_view_item_model import DataViewItemModel + + +@requires_numpy +class TestDataViewItemModel(TestCase): + + def setUp(self): + self.item_model = self._create_item_model() + + def _create_item_model(self): + self.data = np.arange(120.0).reshape(4, 5, 6) + self.model = ArrayDataModel(data=self.data, value_type=FloatValue()) + return DataViewItemModel( + model=self.model, + selection_type='row', + exporters=[], + ) + + def _make_indexes(self, indices): + return [ + self.item_model._to_model_index(row, column) + for row, column in indices + ] + + def test_mimeData(self): + self.item_model.exporters = [RowExporter(format=table_format)] + indexes = self._make_indexes([ + ((0, row), (column,)) + for column in range(2, 5) + for row in range(2, 4) + ]) + + mime_data = self.item_model.mimeData(indexes) + + self.assertIsInstance(mime_data, QMimeData) + self.assertTrue(mime_data.hasFormat('text/plain')) + + raw_data = mime_data.data('text/plain').data() + data = table_format.deserialize(bytes(raw_data)) + np.testing.assert_array_equal( + data, + [ + ['12', '13', '14', '15', '16', '17'], + ['18', '19', '20', '21', '22', '23'], + ] + ) + + def test_mimeData_empty(self): + mime_data = self.item_model.mimeData([]) + + self.assertIsInstance(mime_data, QMimeData) + # exact contents depend on Qt, so won't test more deeply diff -Nru python-pyface-6.1.2/pyface/ui/qt4/data_view/tests/test_data_wrapper.py python-pyface-7.4.0/pyface/ui/qt4/data_view/tests/test_data_wrapper.py --- python-pyface-6.1.2/pyface/ui/qt4/data_view/tests/test_data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/data_view/tests/test_data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,36 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from pyface.qt.QtCore import QMimeData +from pyface.ui.qt4.data_view.data_wrapper import DataWrapper + + +class TestDataWrapper(TestCase): + + def test_get_mimedata(self): + toolkit_data = QMimeData() + toolkit_data.setData('text/plain', b'hello world') + + data_wrapper = DataWrapper(toolkit_data=toolkit_data) + self.assertEqual(data_wrapper.mimetypes(), {'text/plain'}) + self.assertEqual(data_wrapper.get_mimedata('text/plain'), b'hello world') + + def test_set_mimedata(self): + data_wrapper = DataWrapper() + data_wrapper.set_mimedata('text/plain', b'hello world') + toolkit_data = data_wrapper.toolkit_data + + self.assertEqual(set(toolkit_data.formats()), {'text/plain'}) + self.assertEqual( + toolkit_data.data('text/plain').data(), + b'hello world' + ) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/dialog.py python-pyface-7.4.0/pyface/ui/qt4/dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,23 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, Enum, Int, provides, Str, Unicode -# Local imports. +from traits.api import ( + Any, Bool, Callable, Enum, Int, List, provides, Str, Tuple +) + from pyface.i_dialog import IDialog, MDialog from pyface.constant import OK, CANCEL, YES, NO from .window import Window @@ -25,12 +26,12 @@ # Map PyQt dialog related constants to the pyface equivalents. _RESULT_MAP = { - int(QtGui.QDialog.Accepted): OK, - int(QtGui.QDialog.Rejected): CANCEL, - int(QtGui.QMessageBox.Ok): OK, - int(QtGui.QMessageBox.Cancel): CANCEL, - int(QtGui.QMessageBox.Yes): YES, - int(QtGui.QMessageBox.No): NO + int(QtGui.QDialog.DialogCode.Accepted): OK, + int(QtGui.QDialog.DialogCode.Rejected): CANCEL, + int(QtGui.QMessageBox.StandardButton.Ok): OK, + int(QtGui.QMessageBox.StandardButton.Cancel): CANCEL, + int(QtGui.QMessageBox.StandardButton.Yes): YES, + int(QtGui.QMessageBox.StandardButton.No): NO, } @@ -40,52 +41,62 @@ interface for the API documentation. """ + # 'IDialog' interface -------------------------------------------------# - #### 'IDialog' interface ################################################## - - cancel_label = Unicode + cancel_label = Str() - help_id = Str + help_id = Str() - help_label = Unicode + help_label = Str() - ok_label = Unicode + ok_label = Str() resizeable = Bool(True) return_code = Int(OK) - style = Enum('modal', 'nonmodal') + style = Enum("modal", "nonmodal") + + # 'IWindow' interface -------------------------------------------------# - #### 'IWindow' interface ################################################## + title = Str("Dialog") - title = Unicode("Dialog") + # Private interface ---------------------------------------------------# - ########################################################################### + #: A list of connected Qt signals to be removed before destruction. + #: First item in the tuple is the Qt signal. The second item is the event + #: handler. + _connections_to_remove = List(Tuple(Any, Callable)) + + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_buttons(self, parent): buttons = QtGui.QDialogButtonBox() # 'OK' button. if self.ok_label: - btn = buttons.addButton(self.ok_label, - QtGui.QDialogButtonBox.AcceptRole) + btn = buttons.addButton( + self.ok_label, QtGui.QDialogButtonBox.ButtonRole.AcceptRole + ) else: - btn = buttons.addButton(QtGui.QDialogButtonBox.Ok) + btn = buttons.addButton(QtGui.QDialogButtonBox.StandardButton.Ok) btn.setDefault(True) btn.clicked.connect(self.control.accept) + self._connections_to_remove.append((btn.clicked, self.control.accept)) # 'Cancel' button. if self.cancel_label: - btn = buttons.addButton(self.cancel_label, - QtGui.QDialogButtonBox.RejectRole) + btn = buttons.addButton( + self.cancel_label, QtGui.QDialogButtonBox.ButtonRole.RejectRole + ) else: - btn = buttons.addButton(QtGui.QDialogButtonBox.Cancel) + btn = buttons.addButton(QtGui.QDialogButtonBox.StandardButton.Cancel) btn.clicked.connect(self.control.reject) + self._connections_to_remove.append((btn.clicked, self.control.reject)) # 'Help' button. # FIXME v3: In the original code the only possible hook into the help @@ -94,9 +105,11 @@ # display it but can't actually use it. if len(self.help_id) > 0: if self.help_label: - buttons.addButton(self.help_label, QtGui.QDialogButtonBox.HelpRole) + buttons.addButton( + self.help_label, QtGui.QDialogButtonBox.ButtonRole.HelpRole + ) else: - buttons.addButton(QtGui.QDialogButtonBox.Help) + buttons.addButton(QtGui.QDialogButtonBox.StandardButton.Help) return buttons @@ -104,7 +117,7 @@ layout = QtGui.QVBoxLayout() if not self.resizeable: - layout.setSizeConstraint(QtGui.QLayout.SetFixedSize) + layout.setSizeConstraint(QtGui.QLayout.SizeConstraint.SetFixedSize) layout.addWidget(self._create_dialog_area(parent)) layout.addWidget(self._create_buttons(parent)) @@ -116,28 +129,50 @@ panel.setMinimumSize(QtCore.QSize(100, 200)) palette = panel.palette() - palette.setColor(QtGui.QPalette.Window, QtGui.QColor('red')) + palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("red")) panel.setPalette(palette) panel.setAutoFillBackground(True) return panel def _show_modal(self): - self.control.setWindowModality(QtCore.Qt.ApplicationModal) - retval = self.control.exec_() + dialog = self.control + dialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) + + # Suppress the context-help button hint, which + # results in a non-functional "?" button on Windows. + dialog.setWindowFlags( + dialog.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint + ) + + retval = dialog.exec_() return _RESULT_MAP[retval] - ########################################################################### + # ------------------------------------------------------------------------- + # 'IWidget' interface. + # ------------------------------------------------------------------------- + + def destroy(self): + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) + + super().destroy() + + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): dlg = QtGui.QDialog(parent) # Setting return code and firing close events is handled for 'modal' in # MDialog's open method. For 'nonmodal', we do it here. - if self.style == 'nonmodal': + if self.style == "nonmodal": dlg.finished.connect(self._finished_fired) + self._connections_to_remove.append( + (dlg.finished, self._finished_fired) + ) if self.size != (-1, -1): dlg.resize(*self.size) @@ -149,9 +184,9 @@ return dlg - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _finished_fired(self, result): """ Called when the dialog is closed (and nonmodal). """ diff -Nru python-pyface-6.1.2/pyface/ui/qt4/directory_dialog.py python-pyface-7.4.0/pyface/ui/qt4/directory_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/directory_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/directory_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,26 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Major package imports. from pyface.qt import QtGui -# Enthought library imports. -from traits.api import Bool, provides, Unicode -# Local imports. +from traits.api import Bool, provides, Str + + from pyface.i_directory_dialog import IDirectoryDialog, MDirectoryDialog from .dialog import Dialog -import six @provides(IDirectoryDialog) @@ -29,57 +28,54 @@ IDirectoryDialog interface for the API documentation. """ + # 'IDirectoryDialog' interface ----------------------------------------- - #### 'IDirectoryDialog' interface ######################################### - - default_path = Unicode + default_path = Str() - message = Unicode + message = Str() new_directory = Bool(True) - path = Unicode + path = Str() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In PyQt this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): # Get the path of the chosen directory. files = self.control.selectedFiles() if files: - self.path = six.text_type(files[0]) + self.path = str(files[0]) else: - self.path = '' + self.path = "" # Let the window close as normal. - super(DirectoryDialog, self).close() + super().close() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): dlg = QtGui.QFileDialog(parent, self.title, self.default_path) - dlg.setViewMode(QtGui.QFileDialog.Detail) - dlg.setFileMode(QtGui.QFileDialog.DirectoryOnly) + dlg.setViewMode(QtGui.QFileDialog.ViewMode.Detail) + dlg.setFileMode(QtGui.QFileDialog.FileMode.Directory) if not self.new_directory: - dlg.setOptions(QtGui.QFileDialog.ReadOnly) + dlg.setOptions(QtGui.QFileDialog.Option.ReadOnly) if self.message: - dlg.setLabelText(QtGui.QFileDialog.LookIn, self.message) + dlg.setLabelText(QtGui.QFileDialog.DialogLabel.LookIn, self.message) return dlg - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/fields/combo_field.py python-pyface-7.4.0/pyface/ui/qt4/fields/combo_field.py --- python-pyface-6.1.2/pyface/ui/qt4/fields/combo_field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/fields/combo_field.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2019, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. @@ -12,9 +13,6 @@ """ The Qt-specific implementation of the combo field class """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) from traits.api import provides @@ -35,7 +33,7 @@ def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ control = QComboBox(parent) - control.setInsertPolicy(QComboBox.NoInsert) + control.setInsertPolicy(QComboBox.InsertPolicy.NoInsert) control.setEditable(False) return control @@ -62,7 +60,7 @@ """ Toolkit specific method to get the control's value. """ index = self.control.currentIndex() if index != -1: - return self.control.itemData(index, Qt.DisplayRole) + return self.control.itemData(index, Qt.ItemDataRole.DisplayRole) else: raise IndexError("no value selected") diff -Nru python-pyface-6.1.2/pyface/ui/qt4/fields/field.py python-pyface-7.4.0/pyface/ui/qt4/fields/field.py --- python-pyface-6.1.2/pyface/ui/qt4/fields/field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/fields/field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,70 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The Qt-specific implementation of the text field class """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) -from traits.api import Any, Instance, Unicode, provides +from traits.api import Any, provides -from pyface.qt.QtCore import Qt from pyface.fields.i_field import IField, MField -from pyface.ui.qt4.widget import Widget +from pyface.ui.qt4.layout_widget import LayoutWidget @provides(IField) -class Field(MField, Widget): +class Field(MField, LayoutWidget): """ The Qt-specific implementation of the field class This is an abstract class which is not meant to be instantiated. """ #: The value held by the field. - value = Any - - #: A tooltip for the field. - tooltip = Unicode - - #: An optional context menu for the field. - context_menu = Instance('pyface.action.menu_manager.MenuManager') - - # ------------------------------------------------------------------------ - # Private interface - # ------------------------------------------------------------------------ - - def _get_control_tooltip(self): - """ Toolkit specific method to get the control's tooltip. """ - return self.control.toolTip() - - def _set_control_tooltip(self, tooltip): - """ Toolkit specific method to set the control's tooltip. """ - self.control.setToolTip(tooltip) - - def _observe_control_context_menu(self, remove=False): - """ Toolkit specific method to change the control menu observer. """ - if remove: - self.control.setContextMenuPolicy(Qt.DefaultContextMenu) - self.control.customContextMenuRequested.disconnect( - self._handle_control_context_menu) - else: - self.control.customContextMenuRequested.connect( - self._handle_control_context_menu) - self.control.setContextMenuPolicy(Qt.CustomContextMenu) - - def _handle_control_context_menu(self, pos): - """ Signal handler for displaying context menu. """ - if self.control is not None and self.context_menu is not None: - menu = self.context_menu.create_menu(self.control) - menu.show(pos.x(), pos.y()) + value = Any() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/fields/spin_field.py python-pyface-7.4.0/pyface/ui/qt4/fields/spin_field.py --- python-pyface-6.1.2/pyface/ui/qt4/fields/spin_field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/fields/spin_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. @@ -12,9 +13,6 @@ """ The Qt-specific implementation of the spin field class """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) from traits.api import provides diff -Nru python-pyface-6.1.2/pyface/ui/qt4/fields/text_field.py python-pyface-7.4.0/pyface/ui/qt4/fields/text_field.py --- python-pyface-6.1.2/pyface/ui/qt4/fields/text_field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/fields/text_field.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,23 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The Qt-specific implementation of the text field class """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) -from traits.api import Trait, provides +from traits.api import Map, provides from pyface.fields.i_text_field import ITextField, MTextField from pyface.qt.QtGui import QLineEdit @@ -25,20 +19,17 @@ ECHO_TO_QT_ECHO_MODE = { - 'normal': QLineEdit.Normal, - 'password': QLineEdit.Password, - 'none': QLineEdit.NoEcho, - 'when_editing': QLineEdit.PasswordEchoOnEdit, + "normal": QLineEdit.EchoMode.Normal, + "password": QLineEdit.EchoMode.Password, + "none": QLineEdit.EchoMode.NoEcho, + "when_editing": QLineEdit.EchoMode.PasswordEchoOnEdit, } QT_ECHO_MODE_TO_ECHO = { value: key for key, value in ECHO_TO_QT_ECHO_MODE.items() } # mapped trait for Qt line edit echo modes -Echo = Trait( - 'normal', - ECHO_TO_QT_ECHO_MODE, -) +Echo = Map(ECHO_TO_QT_ECHO_MODE, default_value="normal") @provides(ITextField) @@ -69,7 +60,7 @@ """ Toolkit specific method to set the control's value. """ self.control.setText(value) # fire update - if self.update_text == 'editing_finished': + if self.update_text == "editing_finished": self.control.editingFinished.emit() else: self.control.textEdited.emit(value) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/fields/time_field.py python-pyface-7.4.0/pyface/ui/qt4/fields/time_field.py --- python-pyface-6.1.2/pyface/ui/qt4/fields/time_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/fields/time_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,54 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The Qt-specific implementation of the time field class """ + + +from traits.api import provides + +from pyface.qt.QtGui import QTimeEdit + +from pyface.fields.i_time_field import ITimeField, MTimeField +from pyface.ui.qt4.util.datetime import pytime_to_qtime, qtime_to_pytime +from .field import Field + + +@provides(ITimeField) +class TimeField(MTimeField, Field): + """ The Qt-specific implementation of the time field class """ + + # ------------------------------------------------------------------------ + # IWidget interface + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + + control = QTimeEdit(parent) + return control + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _get_control_value(self): + """ Toolkit specific method to get the control's value. """ + return qtime_to_pytime(self.control.time()) + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + self.control.setTime(pytime_to_qtime(value)) + + def _observe_control_value(self, remove=False): + """ Toolkit specific method to change the control value observer. """ + if remove: + self.control.timeChanged.disconnect(self._update_value) + else: + self.control.timeChanged.connect(self._update_value) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/fields/toggle_field.py python-pyface-7.4.0/pyface/ui/qt4/fields/toggle_field.py --- python-pyface-6.1.2/pyface/ui/qt4/fields/toggle_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/fields/toggle_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,92 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The Qt-specific implementation of the toggle field class """ + + +from traits.api import provides + +from pyface.fields.i_toggle_field import IToggleField, MToggleField +from pyface.qt.QtGui import ( + QCheckBox, QIcon, QPushButton, QRadioButton +) +from .field import Field + + +@provides(IToggleField) +class ToggleField(MToggleField, Field): + """ The Qt-specific implementation of the toggle field class """ + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + # Toolkit control interface --------------------------------------------- + + def _get_control_value(self): + """ Toolkit specific method to get the control's value. """ + return self.control.isChecked() + + def _get_control_text(self): + """ Toolkit specific method to get the control's text. """ + return self.control.text() + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + return self.control.setChecked(value) + + def _set_control_text(self, text): + """ Toolkit specific method to set the control's text. """ + return self.control.setText(text) + + def _set_control_icon(self, icon): + """ Toolkit specific method to set the control's icon. """ + if icon is not None: + self.control.setIcon(icon.create_icon()) + else: + self.control.setIcon(QIcon()) + + def _observe_control_value(self, remove=False): + """ Toolkit specific method to change the control value observer. """ + if remove: + self.control.toggled.disconnect(self._update_value) + else: + self.control.toggled.connect(self._update_value) + + +class CheckBoxField(ToggleField): + """ The Qt-specific implementation of the checkbox class """ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + control = QCheckBox(parent) + return control + + +class RadioButtonField(ToggleField): + """ The Qt-specific implementation of the radio button class + + This is intended to be used in groups, and shouldn't be used by itself. + """ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + control = QRadioButton(parent) + return control + + +class ToggleButtonField(ToggleField): + """ The Qt-specific implementation of the toggle button class """ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + control = QPushButton(parent) + control.setCheckable(True) + return control diff -Nru python-pyface-6.1.2/pyface/ui/qt4/file_dialog.py python-pyface-7.4.0/pyface/ui/qt4/file_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/file_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/file_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,29 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Standard library imports. import os -# Major package imports. + from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Enum, Int, List, provides, Unicode -# Local imports. +from traits.api import Enum, Int, List, provides, Str + + from pyface.i_file_dialog import IFileDialog, MFileDialog from .dialog import Dialog -import six @provides(IFileDialog) @@ -32,32 +31,31 @@ IFileDialog interface for the API documentation. """ + # 'IFileDialog' interface ---------------------------------------------# - #### 'IFileDialog' interface ############################################## + action = Enum("open", "open files", "save as") - action = Enum('open', 'open files', 'save as') + default_directory = Str() - default_directory = Unicode + default_filename = Str() - default_filename = Unicode + default_path = Str() - default_path = Unicode + directory = Str() - directory = Unicode + filename = Str() - filename = Unicode + path = Str() - path = Unicode + paths = List(Str) - paths = List(Unicode) - - wildcard = Unicode + wildcard = Str() wildcard_index = Int(0) - ########################################################################### + # ------------------------------------------------------------------------ # 'MFileDialog' *CLASS* interface. - ########################################################################### + # ------------------------------------------------------------------------ # In Windows, Qt needs only a * while wx needs a *.* WILDCARD_ALL = "All files (*)|*" @@ -66,67 +64,73 @@ def create_wildcard(cls, description, extension): """ Creates a wildcard for a given extension. """ - if isinstance(extension, six.string_types): + if isinstance(extension, str): pattern = extension else: - pattern = ' '.join(extension) + pattern = " ".join(extension) return "%s (%s)|%s|" % (description, pattern, pattern) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In PyQt this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): # Get the path of the chosen directory. files = self.control.selectedFiles() if files: - self.path = six.text_type(files[0]) - self.paths = [six.text_type(file) for file in files] + self.path = str(files[0]) + self.paths = [str(file) for file in files] else: - self.path = '' - self.paths = [''] + self.path = "" + self.paths = [""] # Extract the directory and filename. self.directory, self.filename = os.path.split(self.path) # Get the index of the selected filter. self.wildcard_index = self.control.nameFilters().index( - self.control.selectedNameFilter()) + self.control.selectedNameFilter() + ) # Let the window close as normal. - super(FileDialog, self).close() + super().close() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # If the caller provided a default path instead of a default directory # and filename, split the path into it directory and filename # components. - if len(self.default_path) != 0 and len(self.default_directory) == 0 \ - and len(self.default_filename) == 0: - default_directory, default_filename = os.path.split(self.default_path) + if ( + len(self.default_path) != 0 + and len(self.default_directory) == 0 + and len(self.default_filename) == 0 + ): + default_directory, default_filename = os.path.split( + self.default_path + ) else: default_directory = self.default_directory default_filename = self.default_filename # Convert the filter. filters = [] - for filter_list in self.wildcard.split('|')[::2]: + for filter_list in self.wildcard.split("|")[::2]: # Qt uses spaces instead of semicolons for extension separators - filter_list = filter_list.replace(';', ' ') + filter_list = filter_list.replace(";", " ") filters.append(filter_list) # Set the default directory. @@ -134,32 +138,30 @@ default_directory = QtCore.QDir.currentPath() dlg = QtGui.QFileDialog(parent, self.title, default_directory) - dlg.setViewMode(QtGui.QFileDialog.Detail) + dlg.setViewMode(QtGui.QFileDialog.ViewMode.Detail) dlg.selectFile(default_filename) dlg.setNameFilters(filters) if self.wildcard_index < len(filters): dlg.selectNameFilter(filters[self.wildcard_index]) - if self.action == 'open': - dlg.setAcceptMode(QtGui.QFileDialog.AcceptOpen) - dlg.setFileMode(QtGui.QFileDialog.ExistingFile) - elif self.action == 'open files': - dlg.setAcceptMode(QtGui.QFileDialog.AcceptOpen) - dlg.setFileMode(QtGui.QFileDialog.ExistingFiles) + if self.action == "open": + dlg.setAcceptMode(QtGui.QFileDialog.AcceptMode.AcceptOpen) + dlg.setFileMode(QtGui.QFileDialog.FileMode.ExistingFile) + elif self.action == "open files": + dlg.setAcceptMode(QtGui.QFileDialog.AcceptMode.AcceptOpen) + dlg.setFileMode(QtGui.QFileDialog.FileMode.ExistingFiles) else: - dlg.setAcceptMode(QtGui.QFileDialog.AcceptSave) - dlg.setFileMode(QtGui.QFileDialog.AnyFile) + dlg.setAcceptMode(QtGui.QFileDialog.AcceptMode.AcceptSave) + dlg.setFileMode(QtGui.QFileDialog.FileMode.AnyFile) return dlg - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _wildcard_default(self): """ Return the default wildcard. """ return self.WILDCARD_ALL - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/font_dialog.py python-pyface-7.4.0/pyface/ui/qt4/font_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/font_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/font_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,58 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" A dialog that allows the user to select a font. """ + +from pyface.qt import QtGui + +from traits.api import provides + +from pyface.font import Font +from pyface.ui_traits import PyfaceFont +from pyface.i_font_dialog import IFontDialog +from .dialog import Dialog + + +@provides(IFontDialog) +class FontDialog(Dialog): + """ A dialog that allows the user to choose a font. + """ + + # 'IFontDialog' interface ---------------------------------------------- + + #: The font in the dialog. + font = PyfaceFont() + + # ------------------------------------------------------------------------ + # 'IDialog' interface. + # ------------------------------------------------------------------------ + + def _create_contents(self, parent): + # In PyQt this is a canned dialog so there are no contents. + pass + + # ------------------------------------------------------------------------ + # 'IWindow' interface. + # ------------------------------------------------------------------------ + + def close(self): + if self.control.result() == QtGui.QDialog.DialogCode.Accepted: + qfont = self.control.selectedFont() + self.font = Font.from_toolkit(qfont) + return super(FontDialog, self).close() + + # ------------------------------------------------------------------------ + # 'IWindow' interface. + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + qfont = self.font.to_toolkit() + dialog = QtGui.QFontDialog(qfont, parent) + return dialog diff -Nru python-pyface-6.1.2/pyface/ui/qt4/font.py python-pyface-7.4.0/pyface/ui/qt4/font.py --- python-pyface-6.1.2/pyface/ui/qt4/font.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/font.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,203 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +Font conversion utilities + +This module provides facilities for converting between pyface Font objects +and Qt QFont objects, trying to keep as much similarity as possible between +them. +""" + +from pyface.qt.QtGui import QFont + + +qt_family_to_generic_family = { + QFont.StyleHint.AnyStyle: 'default', + QFont.StyleHint.System: 'default', + QFont.Decorative: 'fantasy', + QFont.Serif: 'serif', + QFont.StyleHint.Cursive: 'cursive', + QFont.SansSerif: 'sans-serif', + QFont.StyleHint.Monospace: 'monospace', + QFont.TypeWriter: 'typewriter', +} +generic_family_to_qt_family = { + 'default': QFont.StyleHint.System, + 'fantasy': QFont.Decorative, + 'decorative': QFont.Decorative, + 'serif': QFont.Serif, + 'roman': QFont.Serif, + 'cursive': QFont.StyleHint.Cursive, + 'script': QFont.StyleHint.Cursive, + 'sans-serif': QFont.SansSerif, + 'swiss': QFont.SansSerif, + 'monospace': QFont.StyleHint.Monospace, + 'modern': QFont.StyleHint.Monospace, + 'typewriter': QFont.TypeWriter, + 'teletype': QFont.TypeWriter, +} + +weight_to_qt_weight = { + 100: QFont.Weight.Thin, + 200: QFont.Weight.ExtraLight, + 300: QFont.Weight.Light, + 400: QFont.Weight.Normal, + 500: QFont.Weight.Medium, + 600: QFont.Weight.DemiBold, + 700: QFont.Weight.Bold, + 800: QFont.Weight.ExtraBold, + 900: QFont.Weight.Black, + 1000: 99, +} +qt_weight_to_weight = { + QFont.Weight.Thin: 'thin', + QFont.Weight.ExtraLight: 'extra-light', + QFont.Weight.Light: 'light', + QFont.Weight.Normal: 'normal', + QFont.Weight.Medium: 'medium', + QFont.Weight.DemiBold: 'demi-bold', + QFont.Weight.Bold: 'bold', + QFont.Weight.ExtraBold: 'extra-bold', + QFont.Weight.Black: 'black', + 99: 'extra-heavy', +} + +style_to_qt_style = { + 'normal': QFont.Style.StyleNormal, + 'oblique': QFont.Style.StyleOblique, + 'italic': QFont.Style.StyleItalic, +} +qt_style_to_style = {value: key for key, value in style_to_qt_style.items()} + + +def font_to_toolkit_font(font): + """ Convert a Pyface font to a Qfont. + + Parameters + ---------- + font : pyface.font.Font + The Pyface font to convert. + + Returns + ------- + qt_font : QFont + The best matching Qt font. + """ + qt_font = QFont() + families = [] + default_family = None + + for family in font.family: + if family not in generic_family_to_qt_family: + families.append(family) + elif default_family is None: + default_family = family + + if families and hasattr(qt_font, 'setFamilies'): + # Qt 5.13 and later + qt_font.setFamilies(families) + elif families: + qt_font.setFamily(families[0]) + # Note: possibily could use substitutions here, + # but not sure if global (which would be bad, so we don't) + + if default_family is not None: + qt_font.setStyleHint(generic_family_to_qt_family[default_family]) + + qt_font.setPointSizeF(font.size) + qt_font.setWeight(weight_to_qt_weight[font.weight_]) + qt_font.setStretch(font.stretch) + qt_font.setStyle(style_to_qt_style[font.style]) + qt_font.setUnderline('underline' in font.decorations) + qt_font.setStrikeOut('strikethrough' in font.decorations) + qt_font.setOverline('overline' in font.decorations) + if 'small-caps' in font.variants: + qt_font.setCapitalization(QFont.Capitalization.SmallCaps) + return qt_font + + +def toolkit_font_to_properties(toolkit_font): + """ Convert a QFont to a dictionary of font properties. + + Parameters + ---------- + toolkit_font : QFont + The Qt QFont to convert. + + Returns + ------- + properties : dict + Font properties suitable for use in creating a Pyface Font. + """ + family = [] + + if toolkit_font.family(): + family.append(toolkit_font.family()) + if hasattr(toolkit_font, 'families'): + # Qt 5.13 and later + family.extend(toolkit_font.families()) + family.append(qt_family_to_generic_family[toolkit_font.styleHint()]) + + size = toolkit_font.pointSizeF() + style = qt_style_to_style[toolkit_font.style()] + weight = map_to_nearest(toolkit_font.weight(), qt_weight_to_weight) + stretch = toolkit_font.stretch() + if stretch == 0: + # stretch 0 means any stretch is allowed, we default to no stretch + stretch = 100.0 + variants = set() + if toolkit_font.capitalization() == QFont.Capitalization.SmallCaps: + variants.add('small-caps') + decorations = set() + if toolkit_font.underline(): + decorations.add('underline') + if toolkit_font.strikeOut(): + decorations.add('strikethrough') + if toolkit_font.overline(): + decorations.add('overline') + + return { + 'family': family, + 'size': size, + 'weight': weight, + 'stretch': stretch, + 'style': style, + 'variants': variants, + 'decorations': decorations, + } + + +def map_to_nearest(target, mapping): + """ Given mapping with keys from 0 and 99, return closest value. + + Parameters + ---------- + target : int + The value to map. + mapping : dict + A dictionary with integer keys ranging from 0 to 99. + + Returns + ------- + value : any + The value corresponding to the nearest key. In the case of a tie, + the first value is returned. + """ + if target in mapping: + return mapping[target] + + distance = 100 + nearest = None + for key in mapping: + if abs(target - key) < distance: + distance = abs(target - key) + nearest = key + return mapping[nearest] diff -Nru python-pyface-6.1.2/pyface/ui/qt4/gui.py python-pyface-7.4.0/pyface/ui/qt4/gui.py --- python-pyface-6.1.2/pyface/ui/qt4/gui.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/gui.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,27 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Standard library imports. import logging -# Major package imports. + from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, HasTraits, provides, Unicode + +from traits.api import Bool, HasTraits, observe, provides, Str from pyface.util.guisupport import start_event_loop_qt4 -# Local imports. + from pyface.i_gui import IGUI, MGUI @@ -35,18 +35,17 @@ for the API documentation. """ - - #### 'GUI' interface ###################################################### + # 'GUI' interface -----------------------------------------------------# busy = Bool(False) started = Bool(False) - state_location = Unicode + state_location = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, splash_screen=None): # Display the (optional) splash screen. @@ -55,9 +54,9 @@ if self._splash_screen is not None: self._splash_screen.open() - ########################################################################### + # ------------------------------------------------------------------------ # 'GUI' class interface. - ########################################################################### + # ------------------------------------------------------------------------ @classmethod def invoke_after(cls, millisecs, callable, *args, **kw): @@ -78,22 +77,22 @@ @staticmethod def process_events(allow_user_events=True): if allow_user_events: - events = QtCore.QEventLoop.AllEvents + events = QtCore.QEventLoop.ProcessEventsFlag.AllEvents else: - events = QtCore.QEventLoop.ExcludeUserInputEvents + events = QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents QtCore.QCoreApplication.processEvents(events) @staticmethod def set_busy(busy=True): if busy: - QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) + QtGui.QApplication.setOverrideCursor(QtCore.Qt.CursorShape.WaitCursor) else: QtGui.QApplication.restoreOverrideCursor() - ########################################################################### + # ------------------------------------------------------------------------ # 'GUI' interface. - ########################################################################### + # ------------------------------------------------------------------------ def start_event_loop(self): if self._splash_screen is not None: @@ -112,20 +111,21 @@ logger.debug("---------- stopping GUI event loop ----------") QtGui.QApplication.quit() - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _state_location_default(self): """ The default state location handler. """ return self._default_state_location() - def _busy_changed(self, new): + @observe("busy") + def _update_busy_state(self, event): """ The busy trait change handler. """ - + new = event.new if new: - QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) + QtGui.QApplication.setOverrideCursor(QtCore.Qt.CursorShape.WaitCursor) else: QtGui.QApplication.restoreOverrideCursor() @@ -143,7 +143,7 @@ _pyface_event = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) def __init__(self, ms, callable, *args, **kw): - super(_FutureCall, self).__init__() + super().__init__() # Save the arguments. self._ms = ms @@ -185,7 +185,7 @@ QtCore.QTimer.singleShot(self._ms, self._dispatch) return True - return super(_FutureCall, self).event(event) + return super().event(event) def _dispatch(self): """ Invoke the callable. diff -Nru python-pyface-6.1.2/pyface/ui/qt4/heading_text.py python-pyface-7.4.0/pyface/ui/qt4/heading_text.py --- python-pyface-6.1.2/pyface/ui/qt4/heading_text.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/heading_text.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,82 +1,54 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - +from pyface.qt import QtGui -# Major package imports. -from pyface.qt import QtCore, QtGui +from traits.api import provides -# Enthought library imports. -from traits.api import Int, provides, Unicode - -# Local imports. from pyface.i_heading_text import IHeadingText, MHeadingText -from .widget import Widget +from .layout_widget import LayoutWidget @provides(IHeadingText) -class HeadingText(MHeadingText, Widget): - """ The toolkit specific implementation of a HeadingText. See the - IHeadingText interface for the API documentation. +class HeadingText(MHeadingText, LayoutWidget): + """ The Qt-specific implementation of a HeadingText. """ - - #### 'IHeadingText' interface ############################################# - - level = Int(1) - - text = Unicode('Default') - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, parent, **traits): - """ Creates the panel. """ - - # Base class constructor. - super(HeadingText, self).__init__(**traits) - - # Create the toolkit-specific control that represents the widget. - self._create_control(parent) - - ########################################################################### - # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'IWidget' interface. + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ + control = QtGui.QLabel(parent) + control.setSizePolicy( + QtGui.QSizePolicy.Policy.Preferred, QtGui.QSizePolicy.Policy.Fixed + ) + return control - self.control = QtGui.QLabel(parent) - self._set_text(self.text) - - self.control.setFrameShape(QtGui.QFrame.StyledPanel) - self.control.setFrameShadow(QtGui.QFrame.Raised) - self.control.setSizePolicy(QtGui.QSizePolicy.Preferred, - QtGui.QSizePolicy.Fixed) + # ------------------------------------------------------------------------ + # Private interface. + # ------------------------------------------------------------------------ - def _set_text(self, text): + def _set_control_text(self, text): """ Set the text on the toolkit specific widget. """ - # Bold the text. Qt supports a limited subset of HTML for rich text. - text = "" + text + "" - + text = f"{text}" self.control.setText(text) - #### Trait event handlers ################################################# - - def _text_changed(self, new): - """ Called when the text is changed. """ - - if self.control is not None: - self._set_text(new) - -#### EOF ###################################################################### + def _get_control_text(self): + """ Get the text on the toolkit specific widget. """ + text = self.control.text() + # remove the bolding from the text + text = text[3:-4] + return text diff -Nru python-pyface-6.1.2/pyface/ui/qt4/image_cache.py python-pyface-7.4.0/pyface/ui/qt4/image_cache.py --- python-pyface-6.1.2/pyface/ui/qt4/image_cache.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/image_cache.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Major package imports. from pyface.qt import QtGui -# Enthought library imports. + from traits.api import HasTraits, provides -# Local imports. + from pyface.i_image_cache import IImageCache, MImageCache @@ -27,18 +27,17 @@ IImageCache interface for the API documentation. """ - - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, width, height): self._width = width self._height = height - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageCache' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, filename): image = QtGui.QPixmapCache.find(filename) @@ -49,7 +48,7 @@ if scaled is not image: # The Qt cache is application wide so we only keep the last # size asked for. - QtGui.QPixmapCache.remove(filename); + QtGui.QPixmapCache.remove(filename) QtGui.QPixmapCache.insert(filename, scaled) else: # Load the image from the file and add it to the cache. @@ -62,18 +61,16 @@ # Qt doesn't distinguish between bitmaps and images. get_bitmap = get_image - ########################################################################### + # ------------------------------------------------------------------------ # Private 'ImageCache' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _qt4_scale(self, image): """ Scales the given image if necessary. """ # Although Qt won't scale the image if it doesn't need to, it will make # a deep copy which we don't need. - if image.width() != self._width or image.height()!= self._height: + if image.width() != self._width or image.height() != self._height: image = image.scaled(self._width, self._height) return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/image_resource.py python-pyface-7.4.0/pyface/ui/qt4/image_resource.py --- python-pyface-6.1.2/pyface/ui/qt4/image_resource.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/image_resource.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Standard library imports. import os -# Major package imports. + from pyface.qt import QtGui -# Enthought library imports. + from traits.api import Any, HasTraits, List, Property, provides -from traits.api import Unicode +from traits.api import Str + -# Local imports. from pyface.i_image_resource import IImageResource, MImageResource @@ -31,23 +31,22 @@ IImageResource interface for the API documentation. """ - - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # The resource manager reference for the image. - _ref = Any + _ref = Any() - #### 'ImageResource' interface ############################################ + # 'ImageResource' interface -------------------------------------------- - absolute_path = Property(Unicode) + absolute_path = Property(Str) - name = Unicode + name = Str() - search_path = List + search_path = List() - ########################################################################### - # 'ImageResource' interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'IImage' interface. + # ------------------------------------------------------------------------ # Qt doesn't specifically require bitmaps anywhere so just use images. create_bitmap = MImageResource.create_image @@ -78,9 +77,9 @@ size = image.size() return (size.width(), size.height()) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_absolute_path(self): # FIXME: This doesn't quite work the new notion of image size. We @@ -94,5 +93,3 @@ absolute_path = self._get_image_not_found().absolute_path return absolute_path - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/images/image_LICENSE.txt python-pyface-7.4.0/pyface/ui/qt4/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/ui/qt4/images/image_LICENSE.txt 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Enthought BSD 3-Clause LICENSE.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/ui/qt4/images: - application.png | Enthought - heading_level_1.png | Enthought diff -Nru python-pyface-6.1.2/pyface/ui/qt4/__init__.py python-pyface-7.4.0/pyface/ui/qt4/__init__.py --- python-pyface-6.1.2/pyface/ui/qt4/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/qt4/init.py python-pyface-7.4.0/pyface/ui/qt4/init.py --- python-pyface-6.1.2/pyface/ui/qt4/init.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/init.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,47 +1,43 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited -# All rights reserved. -# -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license. However, when used with the GPL version of PyQt the additional -# terms described in the PyQt GPL exception also apply. +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited and Enthought developers -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! import sys from traits.trait_notifiers import set_ui_handler, ui_handler -from pyface.qt import QtCore, QtGui, qt_api +from pyface.qt import QtGui from pyface.base_toolkit import Toolkit from .gui import GUI -if qt_api == 'pyqt': - # Check the version numbers are late enough. - if QtCore.QT_VERSION < 0x040200: - raise RuntimeError( - "Need Qt v4.2 or higher, but got v%s" % QtCore.QT_VERSION_STR - ) - - if QtCore.PYQT_VERSION < 0x040100: - raise RuntimeError( - "Need PyQt v4.1 or higher, but got v%s" % QtCore.PYQT_VERSION_STR - ) - # It's possible that it has already been initialised. _app = QtGui.QApplication.instance() if _app is None: + try: + # pyface.qt.QtWebKit tries QtWebEngineWidgets first, but + # if QtWebEngineWidgets is present, it must be imported prior to + # creating a QCoreApplication instance, otherwise importing + # QtWebEngineWidgets later would fail (see enthought/pyface#581). + # Import it here first before creating the instance. + from pyface.qt import QtWebKit # noqa: F401 + except ImportError: + # This error will be raised in the context where + # QtWebKit/QtWebEngine widgets are required. + pass _app = QtGui.QApplication(sys.argv) # create the toolkit object -toolkit_object = Toolkit('pyface', 'qt4', 'pyface.ui.qt4') +toolkit_object = Toolkit("pyface", "qt4", "pyface.ui.qt4") # ensure that Traits has a UI handler appropriate for the toolkit. diff -Nru python-pyface-6.1.2/pyface/ui/qt4/layered_panel.py python-pyface-7.4.0/pyface/ui/qt4/layered_panel.py --- python-pyface-6.1.2/pyface/ui/qt4/layered_panel.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/layered_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,65 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" A Layered panel. """ + +from traits.api import Int, provides + +from pyface.qt.QtGui import QStackedWidget +from pyface.i_layered_panel import ILayeredPanel, MLayeredPanel +from .layout_widget import LayoutWidget + + +@provides(ILayeredPanel) +class LayeredPanel(MLayeredPanel, LayoutWidget): + """ A Layered panel. + + A layered panel contains one or more named layers, with only one layer + visible at any one time (think of a 'tab' control minus the tabs!). Each + layer is a toolkit-specific control. + + """ + + # The minimum sizes of the panel. Ignored in Qt. + min_width = Int(0) + min_height = Int(0) + + # ------------------------------------------------------------------------ + # 'ILayeredPanel' interface. + # ------------------------------------------------------------------------ + + def add_layer(self, name, layer): + """ Adds a layer with the specified name. + + All layers are hidden when they are added. Use 'show_layer' to make a + layer visible. + """ + self.control.addWidget(layer) + self._layers[name] = layer + return layer + + def show_layer(self, name): + """ Shows the layer with the specified name. """ + layer = self._layers[name] + layer_index = self.control.indexOf(layer) + self.control.setCurrentIndex(layer_index) + self.current_layer = layer + self.current_layer_name = name + return layer + + # ------------------------------------------------------------------------ + # "IWidget" interface. + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + + control = QStackedWidget(parent) + return control diff -Nru python-pyface-6.1.2/pyface/ui/qt4/layout_widget.py python-pyface-7.4.0/pyface/ui/qt4/layout_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/layout_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/layout_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,133 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from enum import Enum + +from traits.api import provides + +from pyface.qt import QtGui +from pyface.i_layout_item import DEFAULT_SIZE +from pyface.i_layout_widget import ILayoutWidget, MLayoutWidget +from pyface.ui.qt4.widget import Widget + + +#: Maximum widget size (some versions of PyQt don't export it) +QWIDGETSIZE_MAX = getattr(QtGui, "QWIDGETSIZE_MAX", 1 << 24 - 1) + + +class SizePolicies(Enum): + """ Qt values for size policies + + Note that Qt has additional values that are not mapped to Pyface size + policies. + """ + fixed = QtGui.QSizePolicy.Policy.Fixed + preferred = QtGui.QSizePolicy.Policy.Preferred + expand = QtGui.QSizePolicy.Policy.Expanding + + +@provides(ILayoutWidget) +class LayoutWidget(MLayoutWidget, Widget): + """ A widget which can participate as part of a layout. """ + + def _set_control_minimum_size(self, size): + size = tuple( + x if x != DEFAULT_SIZE else 0 + for x in size + ) + self.control.setMinimumSize(*size) + + def _get_control_minimum_size(self): + size = self.control.minimumSize() + return (size.width(), size.height()) + + def _set_control_maximum_size(self, size): + size = tuple( + x if x != DEFAULT_SIZE else QWIDGETSIZE_MAX + for x in size + ) + self.control.setMaximumSize(*size) + + def _get_control_maximum_size(self): + size = self.control.maximumSize() + return (size.width(), size.height()) + + def _set_control_stretch(self, stretch): + """ Set the stretch factor of the control. + """ + new_size_policy = _clone_size_policy(self.control.sizePolicy()) + new_size_policy.setHorizontalStretch(stretch[0]) + new_size_policy.setVerticalStretch(stretch[1]) + self.control.setSizePolicy(new_size_policy) + + def _get_control_stretch(self): + """ Get the stretch factor of the control. + + This method is only used for testing. + """ + size_policy = self.control.sizePolicy() + return ( + size_policy.horizontalStretch(), + size_policy.verticalStretch(), + ) + + def _set_control_size_policy(self, size_policy): + new_size_policy = _clone_size_policy(self.control.sizePolicy()) + if size_policy[0] != "default": + new_size_policy.setHorizontalPolicy( + SizePolicies[size_policy[0]].value + ) + if size_policy[1] != "default": + new_size_policy.setVerticalPolicy( + SizePolicies[size_policy[1]].value + ) + self.control.setSizePolicy(new_size_policy) + + def _get_control_size_policy(self): + size_policy = self.control.sizePolicy() + if self.size_policy[0] != "default": + horizontal_policy = SizePolicies( + size_policy.horizontalPolicy()).name + else: + horizontal_policy = "default" + if self.size_policy[1] != "default": + vertical_policy = SizePolicies( + size_policy.verticalPolicy()).name + else: + vertical_policy = "default" + return (horizontal_policy, vertical_policy) + + +def _clone_size_policy(size_policy): + """ Clone the state of an existing QSizePolicy object + + This is required because there is no standard Qt copy or clone + method. + """ + new_size_policy = QtGui.QSizePolicy() + new_size_policy.setHorizontalPolicy( + size_policy.horizontalPolicy() + ) + new_size_policy.setVerticalPolicy( + size_policy.verticalPolicy() + ) + new_size_policy.setHorizontalStretch( + size_policy.horizontalStretch() + ) + new_size_policy.setVerticalStretch( + size_policy.verticalStretch() + ) + new_size_policy.setHeightForWidth( + size_policy.hasHeightForWidth() + ) + new_size_policy.setWidthForHeight( + size_policy.hasWidthForHeight() + ) + return new_size_policy diff -Nru python-pyface-6.1.2/pyface/ui/qt4/message_dialog.py python-pyface-7.4.0/pyface/ui/qt4/message_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/message_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/message_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,32 +1,38 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ + +from pyface.qt import QtCore, QtGui -# Major package imports. -from pyface.qt import QtGui +from traits.api import Enum, provides, Str -# Enthought library imports. -from traits.api import Enum, provides, Unicode -# Local imports. from pyface.i_message_dialog import IMessageDialog, MMessageDialog from .dialog import Dialog # Map the ETS severity to the corresponding PyQt standard icon. _SEVERITY_TO_ICON_MAP = { - 'information': QtGui.QMessageBox.Information, - 'warning': QtGui.QMessageBox.Warning, - 'error': QtGui.QMessageBox.Critical + "information": QtGui.QMessageBox.Icon.Information, + "warning": QtGui.QMessageBox.Icon.Warning, + "error": QtGui.QMessageBox.Icon.Critical, +} + +_TEXT_FORMAT_MAP = { + "auto": QtCore.Qt.TextFormat.AutoText, + "plain": QtCore.Qt.TextFormat.PlainText, + "rich": QtCore.Qt.TextFormat.RichText, } @@ -36,35 +42,43 @@ IMessageDialog interface for the API documentation. """ + # 'IMessageDialog' interface ------------------------------------------- - #### 'IMessageDialog' interface ########################################### + message = Str() - message = Unicode + informative = Str() - informative = Unicode + detail = Str() - detail = Unicode + severity = Enum("information", "warning", "error") - severity = Enum('information', 'warning', 'error') + text_format = Enum("auto", "plain", "rich") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In PyQt this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # FIXME: should be possble to set ok_label, but not implemented - message_box = QtGui.QMessageBox(_SEVERITY_TO_ICON_MAP[self.severity], - self.title, self.message, QtGui.QMessageBox.Ok, parent) + message_box = QtGui.QMessageBox( + _SEVERITY_TO_ICON_MAP[self.severity], + self.title, + self.message, + QtGui.QMessageBox.StandardButton.Ok, + parent, + ) message_box.setInformativeText(self.informative) message_box.setDetailedText(self.detail) + message_box.setEscapeButton(QtGui.QMessageBox.StandardButton.Ok) + message_box.setTextFormat(_TEXT_FORMAT_MAP[self.text_format]) if self.size != (-1, -1): message_box.resize(*self.size) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/mimedata.py python-pyface-7.4.0/pyface/ui/qt4/mimedata.py --- python-pyface-6.1.2/pyface/ui/qt4/mimedata.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/mimedata.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,34 @@ - -from six.moves.cPickle import dumps, load, loads, PickleError +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +from pickle import dumps, load, loads, PickleError import warnings import io -import sys from pyface.qt import QtCore -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'PyMimeData' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -if sys.version_info[0] < 3: - def str2bytes(s): - return s -else: - def str2bytes(s): - return bytes(s,'ascii') +def str2bytes(s): + return bytes(s, "ascii") class PyMimeData(QtCore.QMimeData): """ The PyMimeData wraps a Python instance as MIME data. """ + # The MIME type for instances. - MIME_TYPE = u'application/x-ets-qt4-instance' - NOPICKLE_MIME_TYPE = u'application/x-ets-qt4-instance-no-pickle' + MIME_TYPE = "application/x-ets-qt4-instance" + NOPICKLE_MIME_TYPE = "application/x-ets-qt4-instance-no-pickle" def __init__(self, data=None, pickle=True): """ Initialise the instance. @@ -44,10 +48,17 @@ self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata) except (PickleError, TypeError, AttributeError): # if pickle fails, still try to create a draggable - warnings.warn(("Could not pickle dragged object %s, " + - "using %s mimetype instead") % (repr(data), - self.NOPICKLE_MIME_TYPE), RuntimeWarning) - self.setData(self.NOPICKLE_MIME_TYPE, str2bytes(str(id(data)))) + warnings.warn( + ( + "Could not pickle dragged object %s, " + + "using %s mimetype instead" + ) + % (repr(data), self.NOPICKLE_MIME_TYPE), + RuntimeWarning, + ) + self.setData( + self.NOPICKLE_MIME_TYPE, str2bytes(str(id(data))) + ) else: self.setData(self.NOPICKLE_MIME_TYPE, str2bytes(str(id(data)))) @@ -83,10 +94,15 @@ # track whether we should pickle. # XXX lists should suffice for now, but may want other containers if isinstance(md, list): - pickle = not any(item.hasFormat(cls.NOPICKLE_MIME_TYPE) - for item in md if isinstance(item, QtCore.QMimeData)) - md = [item.instance() if isinstance(item, PyMimeData) else item - for item in md] + pickle = not any( + item.hasFormat(cls.NOPICKLE_MIME_TYPE) + for item in md + if isinstance(item, QtCore.QMimeData) + ) + md = [ + item.instance() if isinstance(item, PyMimeData) else item + for item in md + ] # Arbitrary python object, wrap it into PyMimeData nmd = cls(md, pickle) @@ -135,6 +151,6 @@ """ ret = [] for url in self.urls(): - if url.scheme() == 'file': + if url.scheme() == "file": ret.append(url.toLocalFile()) return ret diff -Nru python-pyface-6.1.2/pyface/ui/qt4/pil_image.py python-pyface-7.4.0/pyface/ui/qt4/pil_image.py --- python-pyface-6.1.2/pyface/ui/qt4/pil_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/pil_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,46 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import provides + +from pyface.i_pil_image import IPILImage, MPILImage +from pyface.ui.qt4.util.image_helpers import resize_image + + +@provides(IPILImage) +class PILImage(MPILImage): + """ The toolkit specific implementation of a PILImage. + """ + + # ------------------------------------------------------------------------ + # 'IImage' interface. + # ------------------------------------------------------------------------ + + def create_image(self, size=None): + """ Creates a Qt image for this image. + + Parameters + ---------- + size : (int, int) or None + The desired size as a (width, height) tuple, or None if wanting + default image size. + + Returns + ------- + image : QImage + The toolkit image corresponding to the image and the specified + size. + """ + from PIL.ImageQt import ImageQt + image = ImageQt(self.image) + if size is not None: + return resize_image(image, size) + else: + return image diff -Nru python-pyface-6.1.2/pyface/ui/qt4/progress_dialog.py python-pyface-7.4.0/pyface/ui/qt4/progress_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/progress_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/progress_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,23 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A simple progress bar intended to run in the UI thread """ import time from pyface.qt import QtGui, QtCore -from traits.api import Bool, Instance, Int, Unicode, provides +from traits.api import ( + Any, Bool, Callable, Instance, Int, List, Str, provides, Tuple +) from pyface.i_progress_dialog import IProgressDialog, MProgressDialog from .window import Window @@ -28,22 +27,23 @@ """ A simple progress dialog window which allows itself to be updated """ + # FIXME: buttons are not set up correctly yet #: The progress bar widget progress_bar = Instance(QtGui.QProgressBar) #: The window title - title = Unicode + title = Str() #: The text message to display in the dialog - message = Unicode + message = Str() #: The minimum value of the progress range - min = Int + min = Int() #: The minimum value of the progress range - max = Int + max = Int() #: The margin around the progress bar margin = Int(5) @@ -66,7 +66,7 @@ dialog_size = Instance(QtCore.QRect) #: Label for the 'cancel' button - cancel_button_label = Unicode('Cancel') + cancel_button_label = Str("Cancel") #: Whether or not the dialog was cancelled by the user _user_cancelled = Bool(False) @@ -83,13 +83,20 @@ #: The widget showing the estimated time remaining _remaining_control = Instance(QtGui.QLabel) - #------------------------------------------------------------------------- + # Private interface ---------------------------------------------------# + + #: A list of connected Qt signals to be removed before destruction. + #: First item in the tuple is the Qt signal. The second item is the event + #: handler. + _connections_to_remove = List(Tuple(Any, Callable)) + + # ------------------------------------------------------------------------- # IWindow Interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def open(self): """ Opens the window. """ - super(ProgressDialog, self).open() + super().open() self._start_time = time.time() def close(self): @@ -97,11 +104,22 @@ self.progress_bar.destroy() self.progress_bar = None - super(ProgressDialog, self).close() + super().close() + + # ------------------------------------------------------------------------- + # 'IWidget' interface. + # ------------------------------------------------------------------------- + + def destroy(self): + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) + + super().destroy() - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # IProgressDialog Interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def update(self, value): """ Update the progress bar to the desired value @@ -121,15 +139,15 @@ if self.max > 0: self.progress_bar.setValue(value) - if (self.max != self.min): - percent = (float(value) - self.min)/(self.max - self.min) + if self.max != self.min: + percent = (float(value) - self.min) / (self.max - self.min) else: percent = 1.0 if self.show_time and (percent != 0): current_time = time.time() elapsed = current_time - self._start_time - estimated = elapsed/percent + estimated = elapsed / percent remaining = estimated - elapsed self._set_time_label(elapsed, self._elapsed_control) @@ -148,9 +166,9 @@ return (not self._user_cancelled, False) - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Private Interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def reject(self, event): self._user_cancelled = True @@ -174,26 +192,36 @@ buttons = QtGui.QDialogButtonBox() if self.can_cancel: - buttons.addButton(self.cancel_button_label, QtGui.QDialogButtonBox.RejectRole) + buttons.addButton( + self.cancel_button_label, QtGui.QDialogButtonBox.ButtonRole.RejectRole + ) if self.can_ok: - buttons.addButton(QtGui.QDialogButtonBox.Ok) + buttons.addButton(QtGui.QDialogButtonBox.StandardButton.Ok) # TODO: hookup the buttons to our methods, this may involve subclassing from QDialog if self.can_cancel: buttons.rejected.connect(dialog.reject) + self._connections_to_remove.append( + (buttons.rejected, dialog.reject) + ) if self.can_ok: buttons.accepted.connect(dialog.accept) + self._connections_to_remove.append( + (buttons.accepted, dialog.accept) + ) layout.addWidget(buttons) def _create_label(self, dialog, layout, text): dummy = QtGui.QLabel(text, dialog) - dummy.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) + dummy.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop | QtCore.Qt.AlignmentFlag.AlignLeft) label = QtGui.QLabel("unknown", dialog) - label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft | QtCore.Qt.AlignRight) + label.setAlignment( + QtCore.Qt.AlignmentFlag.AlignTop | QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignRight + ) sub_layout = QtGui.QHBoxLayout() @@ -217,37 +245,43 @@ def _create_message(self, dialog, layout): label = QtGui.QLabel(self.message, dialog) - label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) + label.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop | QtCore.Qt.AlignmentFlag.AlignLeft) layout.addWidget(label) self._message_control = label - return def _create_percent(self, dialog, parent_sizer): if not self.show_percent: return - raise NotImplementedError + raise NotImplementedError() def _create_timer(self, dialog, layout): if not self.show_time: return - self._elapsed_control = self._create_label(dialog, layout, "Elapsed time : ") - self._estimated_control = self._create_label(dialog, layout, "Estimated time : ") - self._remaining_control = self._create_label(dialog, layout, "Remaining time : ") + self._elapsed_control = self._create_label( + dialog, layout, "Elapsed time : " + ) + self._estimated_control = self._create_label( + dialog, layout, "Estimated time : " + ) + self._remaining_control = self._create_label( + dialog, layout, "Remaining time : " + ) def _create_control(self, parent): return QtGui.QDialog(parent) def _create(self): - super(ProgressDialog, self)._create() + super()._create() self._create_contents(self.control) def _create_contents(self, parent): dialog = parent - layout = QtGui.QVBoxLayout(dialog) - layout.setContentsMargins(self.margin, self.margin, - self.margin, self.margin) + layout = QtGui.QVBoxLayout(dialog) + layout.setContentsMargins( + self.margin, self.margin, self.margin, self.margin + ) # The 'guts' of the dialog. self._create_message(dialog, layout) @@ -259,9 +293,9 @@ parent.setLayout(layout) - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Trait change handlers - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def _max_changed(self, new): if self.progress_bar is not None: diff -Nru python-pyface-6.1.2/pyface/ui/qt4/python_editor.py python-pyface-7.4.0/pyface/ui/qt4/python_editor.py --- python-pyface-6.1.2/pyface/ui/qt4/python_editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/python_editor.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,64 +1,70 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply - +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - +# Thanks for using Enthought open source! -# Standard library imports. -import sys +import warnings -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, Event, provides, Unicode -# Local imports. +from traits.api import Bool, Event, provides, Str + + from pyface.i_python_editor import IPythonEditor, MPythonEditor from pyface.key_pressed_event import KeyPressedEvent -from pyface.widget import Widget +from pyface.ui.qt4.layout_widget import LayoutWidget from pyface.ui.qt4.code_editor.code_widget import AdvancedCodeWidget @provides(IPythonEditor) -class PythonEditor(MPythonEditor, Widget): +class PythonEditor(MPythonEditor, LayoutWidget): """ The toolkit specific implementation of a PythonEditor. See the IPythonEditor interface for the API documentation. """ - - #### 'IPythonEditor' interface ############################################ + # 'IPythonEditor' interface -------------------------------------------- dirty = Bool(False) - path = Unicode + path = Str() show_line_numbers = Bool(True) - #### Events #### + # Events ---- - changed = Event + changed = Event() key_pressed = Event(KeyPressedEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ + + def __init__(self, parent=None, **traits): + + create = traits.pop("create", True) + + super().__init__(parent=parent, **traits) - def __init__(self, parent, **traits): - super(PythonEditor, self).__init__(parent=parent, **traits) - self._create() + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def load(self, path=None): """ Loads the contents of the editor. @@ -68,11 +74,11 @@ # We will have no path for a new script. if len(path) > 0: - f = open(self.path, 'r') + f = open(self.path, "r") text = f.read() f.close() else: - text = '' + text = "" self.control.code.setPlainText(text) self.dirty = False @@ -83,7 +89,7 @@ if path is None: path = self.path - f = open(path, 'w') + f = open(path, "w") f.write(self.control.code.toPlainText()) f.close() @@ -93,15 +99,16 @@ """ Selects the specified line. """ self.control.code.set_line_column(lineno, 0) - self.control.code.moveCursor(QtGui.QTextCursor.EndOfLine, - QtGui.QTextCursor.KeepAnchor) + self.control.code.moveCursor( + QtGui.QTextCursor.MoveOperation.EndOfLine, QtGui.QTextCursor.MoveMode.KeepAnchor + ) - ########################################################################### + # ------------------------------------------------------------------------ # 'Widget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _add_event_listeners(self): - super(PythonEditor, self)._add_event_listeners() + super()._add_event_listeners() self.control.code.installEventFilter(self._event_filter) # Connect signals for text changes. @@ -112,20 +119,23 @@ if self.control is not None: # Disconnect signals for text changes. self.control.code.modificationChanged.disconnect( - self._on_dirty_changed) + self._on_dirty_changed + ) self.control.code.textChanged.disconnect(self._on_text_changed) + # Disconnect signals from control and other dependent widgets + self.control._remove_event_listeners() if self._event_filter is not None: self.control.code.removeEventFilter(self._event_filter) - super(PythonEditor, self)._remove_event_listeners() + super()._remove_event_listeners() def __event_filter_default(self): return PythonEditorEventFilter(self, self.control) - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _path_changed(self): self._changed_path() @@ -133,12 +143,13 @@ def _show_line_numbers_changed(self): if self.control is not None: self.control.code.line_number_widget.setVisible( - self.show_line_numbers) + self.show_line_numbers + ) self.control.code.update_line_number_width() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the toolkit-specific control for the widget. @@ -169,20 +180,26 @@ """ def __init__(self, editor, parent): - super(PythonEditorEventFilter, self).__init__(parent) + super().__init__(parent) self.__editor = editor def eventFilter(self, obj, event): """ Reimplemented to trap key presses. """ - if self.__editor.control and obj == self.__editor.control and \ - event.type() == QtCore.QEvent.FocusOut: + if ( + self.__editor.control + and obj == self.__editor.control + and event.type() == QtCore.QEvent.Type.FocusOut + ): # Hack for Traits UI compatibility. self.__editor.control.lostFocus.emit() - elif self.__editor.control and obj == self.__editor.control.code and \ - event.type() == QtCore.QEvent.KeyPress: - # Pyface doesn't seem to be Unicode aware. Only keep the key code + elif ( + self.__editor.control + and obj == self.__editor.control.code + and event.type() == QtCore.QEvent.Type.KeyPress + ): + # Pyface doesn't seem to be Str aware. Only keep the key code # if it corresponds to a single Latin1 character. kstr = event.text() try: @@ -192,13 +209,18 @@ mods = event.modifiers() self.key_pressed = KeyPressedEvent( - alt_down = ((mods & QtCore.Qt.AltModifier) == - QtCore.Qt.AltModifier), - control_down = ((mods & QtCore.Qt.ControlModifier) == - QtCore.Qt.ControlModifier), - shift_down = ((mods & QtCore.Qt.ShiftModifier) == - QtCore.Qt.ShiftModifier), - key_code = kcode, - event = event) + alt_down=( + (mods & QtCore.Qt.KeyboardModifier.AltModifier) == QtCore.Qt.KeyboardModifier.AltModifier + ), + control_down=( + (mods & QtCore.Qt.KeyboardModifier.ControlModifier) + == QtCore.Qt.KeyboardModifier.ControlModifier + ), + shift_down=( + (mods & QtCore.Qt.KeyboardModifier.ShiftModifier) == QtCore.Qt.KeyboardModifier.ShiftModifier + ), + key_code=kcode, + event=event, + ) - return super(PythonEditorEventFilter, self).eventFilter(obj, event) + return super().eventFilter(obj, event) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/python_shell.py python-pyface-7.4.0/pyface/ui/qt4/python_shell.py --- python-pyface-6.1.2/pyface/ui/qt4/python_shell.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/python_shell.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,68 +1,81 @@ -# Copyright (c) 2011, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Evan Patterson -# Standard library imports. -import six.moves.builtins +import builtins from code import compile_command, InteractiveInterpreter -from six.moves import cStringIO as StringIO +from io import StringIO import sys from time import time +import warnings + -# System package imports. from pyface.qt import QtCore, QtGui from pygments.lexers import PythonLexer -# Enthought library imports. + from traits.api import Event, provides from traits.util.clean_strings import python_name -# Local imports. + from .code_editor.pygments_highlighter import PygmentsHighlighter -from .console.api import BracketMatcher, CallTipWidget, CompletionLexer, \ - HistoryConsoleWidget +from .console.api import ( + BracketMatcher, + CallTipWidget, + CompletionLexer, + HistoryConsoleWidget, +) from pyface.i_python_shell import IPythonShell, MPythonShell from pyface.key_pressed_event import KeyPressedEvent -from .widget import Widget -import six +from .layout_widget import LayoutWidget @provides(IPythonShell) -class PythonShell(MPythonShell, Widget): +class PythonShell(MPythonShell, LayoutWidget): """ The toolkit specific implementation of a PythonShell. See the IPythonShell interface for the API documentation. """ + # 'IPythonShell' interface --------------------------------------------- - #### 'IPythonShell' interface ############################################# - - command_executed = Event + command_executed = Event() key_pressed = Event(KeyPressedEvent) - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'object' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # FIXME v3: Either make this API consistent with other Widget sub-classes # or make it a sub-class of HasTraits. - def __init__(self, parent, **traits): - super(PythonShell, self).__init__(parent=parent, **traits) + def __init__(self, parent=None, **traits): - # Create the toolkit-specific control that represents the widget. - self._create() + create = traits.pop("create", True) - #-------------------------------------------------------------------------- + super().__init__(parent=parent, **traits) + + if create: + # Create the toolkit-specific control that represents the widget. + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + # -------------------------------------------------------------------------- # 'IPythonShell' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def interpreter(self): return self.control.interpreter @@ -99,15 +112,15 @@ history_index = len(history) self.control._set_history(history, history_index) - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'IWidget' interface. - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def _create_control(self, parent): return PyfacePythonWidget(self, parent) def _add_event_listeners(self): - super(PythonShell, self)._add_event_listeners() + super()._add_event_listeners() # Connect signals for events. self.control.executed.connect(self._on_command_executed) @@ -116,31 +129,36 @@ def _remove_event_listeners(self): if self.control is not None: # Disconnect signals for events. - self.control.executed.connect(self._on_command_executed) + self.control.executed.disconnect(self._on_command_executed) self._event_filter.signal.disconnect(self._on_obj_drop) - super(PythonShell, self)._remove_event_listeners() + self.control._remove_event_listeners() + + super()._remove_event_listeners() def __event_filter_default(self): return _DropEventEmitter(self.control) - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'Private' interface. - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def _on_obj_drop(self, obj): """ Handle dropped objects and add to interpreter local namespace. """ # If we can't create a valid Python identifier for the name of an # object we use this instead. - name = 'dragged' + name = "dragged" - if hasattr(obj, 'name') \ - and isinstance(obj.name, six.string_types) and len(obj.name) > 0: + if ( + hasattr(obj, "name") + and isinstance(obj.name, str) + and len(obj.name) > 0 + ): py_name = python_name(obj.name) # Make sure that the name is actually a valid Python identifier. try: - if eval(py_name, {py_name : True}): + if eval(py_name, {py_name: True}): name = py_name except Exception: pass @@ -157,15 +175,15 @@ # Emitted when a command has been executed in the interpeter. executed = QtCore.Signal() - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'object' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def __init__(self, parent=None): - super(PythonWidget, self).__init__(parent) + super().__init__(parent) # PythonWidget attributes. - self.locals = dict(__name__='__console__', __doc__=None) + self.locals = dict(__name__="__console__", __doc__=None) self.interpreter = InteractiveInterpreter(self.locals) # PythonWidget protected attributes. @@ -182,7 +200,7 @@ # Configure the ConsoleWidget. self.tab_width = 4 - self._set_continuation_prompt('... ') + self._set_continuation_prompt("... ") # Configure the CallTipWidget. self._call_tip_widget.setFont(self.font) @@ -195,9 +213,18 @@ # Display the banner and initial prompt. self.reset() - #-------------------------------------------------------------------------- + def _remove_event_listeners(self): + self.font_changed.disconnect(self._call_tip_widget.setFont) + document = self._control.document() + document.contentsChange.disconnect(self._document_contents_change) + + self._bracket_matcher._remove_event_listeners() + + super()._remove_event_listeners() + + # -------------------------------------------------------------------------- # file-like object interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def flush(self): """ Flush the buffer by writing its contents to the screen. @@ -208,7 +235,7 @@ self._buffer = StringIO() self._append_plain_text(text) - self._control.moveCursor(QtGui.QTextCursor.End) + self._control.moveCursor(QtGui.QTextCursor.MoveOperation.End) def readline(self, prompt=None): """ Read and return one line of input from the user. @@ -232,9 +259,9 @@ for line in lines: self.write(line, refresh=refresh) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'ConsoleWidget' abstract interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _is_complete(self, source, interactive): """ Returns whether 'source' can be completely processed and a new @@ -250,7 +277,7 @@ # We'll let the interpeter handle the error. return True else: - return lines[-1].strip() == '' + return lines[-1].strip() == "" else: return True @@ -268,7 +295,7 @@ # Run the source code in the interpeter self._hidden = hidden try: - more = self.interpreter.runsource(source) + self.interpreter.runsource(source) finally: self._hidden = False @@ -306,20 +333,22 @@ text = self._get_input_buffer_cursor_line() if text is None: return False - complete = bool(text[:self._get_input_buffer_cursor_column()].strip()) + complete = bool(text[: self._get_input_buffer_cursor_column()].strip()) if complete: self._complete() return not complete - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'ConsoleWidget' protected interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _event_filter_console_keypress(self, event): """ Reimplemented for smart backspace. """ - if event.key() == QtCore.Qt.Key_Backspace and \ - not event.modifiers() & QtCore.Qt.AltModifier: + if ( + event.key() == QtCore.Qt.Key.Key_Backspace + and not event.modifiers() & QtCore.Qt.KeyboardModifier.AltModifier + ): # Smart backspace: remove four characters in one backspace if: # 1) everything left of the cursor is whitespace # 2) the four characters immediately left of the cursor are spaces @@ -327,34 +356,35 @@ cursor = self._control.textCursor() if col > 3 and not cursor.hasSelection(): text = self._get_input_buffer_cursor_line()[:col] - if text.endswith(' ') and not text.strip(): - cursor.movePosition(QtGui.QTextCursor.Left, - QtGui.QTextCursor.KeepAnchor, 4) + if text.endswith(" ") and not text.strip(): + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Left, QtGui.QTextCursor.MoveMode.KeepAnchor, 4 + ) cursor.removeSelectedText() return True - return super(PythonWidget, self)._event_filter_console_keypress(event) + return super()._event_filter_console_keypress(event) def _insert_continuation_prompt(self, cursor): """ Reimplemented for auto-indentation. """ - super(PythonWidget, self)._insert_continuation_prompt(cursor) + super()._insert_continuation_prompt(cursor) source = self.input_buffer space = 0 for c in source.splitlines()[-1]: - if c == '\t': + if c == "\t": space += 4 - elif c == ' ': + elif c == " ": space += 1 else: break - if source.rstrip().endswith(':'): + if source.rstrip().endswith(":"): space += 4 - cursor.insertText(' ' * space) + cursor.insertText(" " * space) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'PythonWidget' public interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def execute_file(self, path, hidden=False): """ Attempts to execute file with 'path'. If 'hidden', no output is @@ -374,17 +404,17 @@ self._append_plain_text(self._get_banner()) self._show_interpreter_prompt() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'PythonWidget' protected interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _call_tip(self): """ Shows a call tip, if appropriate, at the current cursor location. """ # Decide if it makes sense to show a call tip cursor = self._get_cursor() - cursor.movePosition(QtGui.QTextCursor.Left) - if cursor.document().characterAt(cursor.position()) != '(': + cursor.movePosition(QtGui.QTextCursor.MoveOperation.Left) + if cursor.document().characterAt(cursor.position()) != "(": return False context = self._get_context(cursor) if not context: @@ -392,7 +422,7 @@ # Look up the context and show a tip for it symbol, leftover = self._get_symbol_from_context(context) - doc = getattr(symbol, '__doc__', None) + doc = getattr(symbol, "__doc__", None) if doc is not None and not leftover: self._call_tip_widget.show_call_info(doc=doc) return True @@ -408,21 +438,24 @@ leftover = leftover[0] if symbol is None: names = list(self.interpreter.locals.keys()) - names += list(six.moves.builtins.__dict__.keys()) + names += list(builtins.__dict__.keys()) else: names = dir(symbol) - completions = [ n for n in names if n.startswith(leftover) ] + completions = [n for n in names if n.startswith(leftover)] if completions: cursor = self._get_cursor() - cursor.movePosition(QtGui.QTextCursor.Left, - n=len(context[-1])) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.Left, n=len(context[-1]) + ) self._complete_with_items(cursor, completions) def _get_banner(self): """ Gets a banner to display at the beginning of a session. """ - banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \ + banner = ( + 'Python %s on %s\nType "help", "copyright", "credits" or ' '"license" for more information.' + ) return banner % (sys.version, sys.platform) def _get_context(self, cursor=None): @@ -431,8 +464,9 @@ """ if cursor is None: cursor = self._get_cursor() - cursor.movePosition(QtGui.QTextCursor.StartOfBlock, - QtGui.QTextCursor.KeepAnchor) + cursor.movePosition( + QtGui.QTextCursor.MoveOperation.StartOfBlock, QtGui.QTextCursor.MoveMode.KeepAnchor + ) text = cursor.selection().toPlainText() return self._completion_lexer.get_context(text) @@ -447,7 +481,7 @@ base_symbol_string = context[0] symbol = self.interpreter.locals.get(base_symbol_string, None) if symbol is None: - symbol = six.moves.builtins.__dict__.get(base_symbol_string, None) + symbol = builtins.__dict__.get(base_symbol_string, None) if symbol is None: return None, context @@ -465,9 +499,9 @@ """ Shows a prompt for the interpreter. """ self.flush() - self._show_prompt('>>> ') + self._show_prompt(">>> ") - #------ Signal handlers ---------------------------------------------------- + # Signal handlers ---------------------------------------------------- def _document_contents_change(self, position, removed, added): """ Called whenever the document's content changes. Display a call tip @@ -476,13 +510,14 @@ # Calculate where the cursor should be *after* the change: position += added - document = self._control.document() if position == self._get_cursor().position(): self._call_tip() -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # 'PythonWidgetHighlighter' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class PythonWidgetHighlighter(PygmentsHighlighter): """ A PygmentsHighlighter that can be turned on and off and that ignores @@ -490,8 +525,7 @@ """ def __init__(self, python_widget): - super(PythonWidgetHighlighter, self).__init__( - python_widget._control.document()) + super().__init__(python_widget._control.document()) self._current_offset = 0 self._python_widget = python_widget self.highlighting_on = False @@ -521,33 +555,35 @@ else: self._current_offset = 0 - super(PythonWidgetHighlighter, self).highlightBlock(string) + super().highlightBlock(string) def rehighlightBlock(self, block): """ Reimplemented to temporarily enable highlighting if disabled. """ old = self.highlighting_on self.highlighting_on = True - super(PythonWidgetHighlighter, self).rehighlightBlock(block) + super().rehighlightBlock(block) self.highlighting_on = old def setFormat(self, start, count, format): """ Reimplemented to highlight selectively. """ start += self._current_offset - super(PythonWidgetHighlighter, self).setFormat(start, count, format) + super().setFormat(start, count, format) + -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'PyfacePythonWidget' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class PyfacePythonWidget(PythonWidget): """ A PythonWidget customized to support the IPythonShell interface. """ - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # 'object' interface - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def __init__(self, pyface_widget, *args, **kw): """ Reimplemented to store a reference to the Pyface widget which @@ -555,16 +591,16 @@ """ self._pyface_widget = pyface_widget - super(PyfacePythonWidget, self).__init__(*args, **kw) + super().__init__(*args, **kw) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'QWidget' interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def keyPressEvent(self, event): """ Reimplemented to generate Pyface key press events. """ - # Pyface doesn't seem to be Unicode aware. Only keep the key code if it + # Pyface doesn't seem to be Str aware. Only keep the key code if it # corresponds to a single Latin1 character. kstr = event.text() try: @@ -574,20 +610,23 @@ mods = event.modifiers() self._pyface_widget.key_pressed = KeyPressedEvent( - alt_down = ((mods & QtCore.Qt.AltModifier) == - QtCore.Qt.AltModifier), - control_down = ((mods & QtCore.Qt.ControlModifier) == - QtCore.Qt.ControlModifier), - shift_down = ((mods & QtCore.Qt.ShiftModifier) == - QtCore.Qt.ShiftModifier), - key_code = kcode, - event = event) + alt_down=((mods & QtCore.Qt.KeyboardModifier.AltModifier) == QtCore.Qt.KeyboardModifier.AltModifier), + control_down=( + (mods & QtCore.Qt.KeyboardModifier.ControlModifier) == QtCore.Qt.KeyboardModifier.ControlModifier + ), + shift_down=( + (mods & QtCore.Qt.KeyboardModifier.ShiftModifier) == QtCore.Qt.KeyboardModifier.ShiftModifier + ), + key_code=kcode, + event=event, + ) - super(PyfacePythonWidget, self).keyPressEvent(event) + super().keyPressEvent(event) class _DropEventEmitter(QtCore.QObject): """ Handle object drops on widget. """ + signal = QtCore.Signal(object) def __init__(self, widget): @@ -600,16 +639,16 @@ def eventFilter(self, source, event): """ Handle drop events on widget. """ typ = event.type() - if typ == QtCore.QEvent.DragEnter: - if hasattr(event.mimeData(), 'instance'): + if typ == QtCore.QEvent.Type.DragEnter: + if hasattr(event.mimeData(), "instance"): # It is pymimedata and has instance data obj = event.mimeData().instance() if obj is not None: event.accept() return True - elif typ == QtCore.QEvent.Drop: - if hasattr(event.mimeData(), 'instance'): + elif typ == QtCore.QEvent.Type.Drop: + if hasattr(event.mimeData(), "instance"): # It is pymimedata and has instance data obj = event.mimeData().instance() if obj is not None: diff -Nru python-pyface-6.1.2/pyface/ui/qt4/resource_manager.py python-pyface-7.4.0/pyface/ui/qt4/resource_manager.py --- python-pyface-6.1.2/pyface/ui/qt4/resource_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/resource_manager.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,39 +1,39 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Major package imports. from pyface.qt import QtCore, QtGui, QtSvg -# Enthought library imports. + from pyface.resource.api import ResourceFactory class PyfaceResourceFactory(ResourceFactory): """ The implementation of a shared resource manager. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceFactory' interface. - ########################################################################### + # ------------------------------------------------------------------------ def image_from_file(self, filename): """ Creates an image from the data in the specified filename. """ # Although QPixmap can load SVG directly, it does not respect the # default size, so we use a QSvgRenderer to get the default size. - if filename.endswith(('.svg', '.SVG')): + if filename.endswith((".svg", ".SVG")): renderer = QtSvg.QSvgRenderer(filename) pixmap = QtGui.QPixmap(renderer.defaultSize()) - pixmap.fill(QtCore.Qt.transparent) + pixmap.fill(QtCore.Qt.GlobalColor.transparent) painter = QtGui.QPainter(pixmap) renderer.render(painter) @@ -49,5 +49,3 @@ image.loadFromData(data) return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/single_choice_dialog.py python-pyface-7.4.0/pyface/ui/qt4/single_choice_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/single_choice_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/single_choice_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,25 +1,23 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2016, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + from pyface.qt import QtCore, QtGui from traits.api import Any, List, Str, provides from pyface.constant import CANCEL -from pyface.i_single_choice_dialog import ISingleChoiceDialog, MSingleChoiceDialog +from pyface.i_single_choice_dialog import ( + ISingleChoiceDialog, + MSingleChoiceDialog, +) from .dialog import Dialog, _RESULT_MAP @@ -31,29 +29,29 @@ is ignored, and the list of displayed strings must be unique. """ - #### 'ISingleChoiceDialog' interface ###################################### + # 'ISingleChoiceDialog' interface -------------------------------------# #: List of objects to choose from. choices = List(Any) #: The object chosen, if any. - choice = Any + choice = Any() #: An optional attribute to use for the name of each object in the dialog. - name_attribute = Str + name_attribute = Str() #: The message to display to the user. - message = Str + message = Str() def set_dialog_choice(self, choice): if self.control is not None: - if self.name_attribute != '': + if self.name_attribute != "": choice = getattr(choice, self.name_attribute) self.control.setTextValue(str(choice)) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Creates the window contents. """ @@ -61,7 +59,7 @@ pass def _show_modal(self): - self.control.setWindowModality(QtCore.Qt.ApplicationModal) + self.control.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) retval = self.control.exec_() if self.control is None: # dialog window closed, treat as Cancel, nullify choice @@ -70,9 +68,9 @@ retval = _RESULT_MAP[retval] return retval - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): """ Closes the window. """ @@ -86,19 +84,21 @@ self.choice = self.choices[idx] else: self.choice = None + else: + self.choice = None # Let the window close as normal. - super(SingleChoiceDialog, self).close() + super().close() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the window. """ dialog = QtGui.QInputDialog(parent) - dialog.setOption(QtGui.QInputDialog.UseListViewForComboBoxItems, True) + dialog.setOption(QtGui.QInputDialog.InputDialogOption.UseListViewForComboBoxItems, True) dialog.setWindowTitle(self.title) dialog.setLabelText(self.message) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/splash_screen.py python-pyface-7.4.0/pyface/ui/qt4/splash_screen.py --- python-pyface-6.1.2/pyface/ui/qt4/splash_screen.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/splash_screen.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Standard library imports. from logging import DEBUG -# Major package imports. -from pyface.qt import QtCore, QtGui - -# Enthought library imports. -from traits.api import Any, Bool, Font, Instance, Int, provides -from traits.api import Tuple, Unicode +from traits.api import Any, Bool, Int, Tuple, Str, provides -# Local imports. +from pyface.qt import QtCore, QtGui from pyface.i_splash_screen import ISplashScreen, MSplashScreen -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image +from .image_resource import ImageResource from .window import Window @@ -33,26 +29,25 @@ ISplashScreen interface for the API documentation. """ + # 'ISplashScreen' interface -------------------------------------------- - #### 'ISplashScreen' interface ############################################ - - image = Instance(ImageResource, ImageResource('splash')) + image = Image(ImageResource("splash")) log_level = Int(DEBUG) show_log_messages = Bool(True) - text = Unicode + text = Str() - text_color = Any + text_color = Any() - text_font = Any + text_font = Any() - text_location = Tuple(5, 5) + text_location = Tuple(5, 5) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): splash_screen = QtGui.QSplashScreen(self.image.create_image()) @@ -60,9 +55,9 @@ return splash_screen - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _text_changed(self): """ Called when the splash screen text has been changed. """ @@ -77,12 +72,10 @@ control.setFont(self.text_font) if self.text_color is None: - text_color = QtCore.Qt.black + text_color = QtCore.Qt.GlobalColor.black else: # Until we get the type of this trait finalised (ie. when TraitsUI # supports PyQt) convert it explcitly to a colour. text_color = QtGui.QColor(self.text_color) - control.showMessage(self.text, QtCore.Qt.AlignLeft, text_color) - -#### EOF ###################################################################### + control.showMessage(self.text, QtCore.Qt.AlignmentFlag.AlignLeft, text_color) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/split_widget.py python-pyface-7.4.0/pyface/ui/qt4/split_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/split_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/split_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,47 +1,34 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ """ Mix-in class for split widgets. """ - -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Callable, Enum, Float, HasTraits, provides +from traits.api import provides -# Local imports. from pyface.i_split_widget import ISplitWidget, MSplitWidget @provides(ISplitWidget) -class SplitWidget(MSplitWidget, HasTraits): +class SplitWidget(MSplitWidget): """ The toolkit specific implementation of a SplitWidget. See the ISPlitWidget interface for the API documentation. """ - - #### 'ISplitWidget' interface ############################################# - - direction = Enum('vertical', 'vertical', 'horizontal') - - ratio = Float(0.5) - - lhs = Callable - - rhs = Callable - - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'ISplitWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_splitter(self, parent): """ Create the toolkit-specific control that represents the widget. """ @@ -49,8 +36,8 @@ splitter = QtGui.QSplitter(parent) # Yes, this is correct. - if self.direction == 'horizontal': - splitter.setOrientation(QtCore.Qt.Vertical) + if self.direction == "horizontal": + splitter.setOrientation(QtCore.Qt.Orientation.Vertical) # Only because the wx implementation does the same. splitter.setChildrenCollapsible(False) @@ -62,12 +49,14 @@ splitter.addWidget(self._create_rhs(splitter)) # Set the initial splitter position. - if self.direction == 'horizontal': + if self.direction == "horizontal": pos = splitter.sizeHint().height() else: pos = splitter.sizeHint().width() - splitter.setSizes([int(pos * self.ratio), int(pos * (1.0 - self.ratio))]) + splitter.setSizes( + [int(pos * self.ratio), int(pos * (1.0 - self.ratio))] + ) return splitter @@ -98,5 +87,3 @@ rhs = QtGui.QWidget(parent) return rhs - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/system_metrics.py python-pyface-7.4.0/pyface/ui/qt4/system_metrics.py --- python-pyface-6.1.2/pyface/ui/qt4/system_metrics.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/system_metrics.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,23 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ +from pyface.qt import QtGui, is_qt4 -# Major package imports. -from pyface.qt import QtGui -# Enthought library imports. from traits.api import HasTraits, Int, Property, provides, Tuple -# Local imports. + from pyface.i_system_metrics import ISystemMetrics, MSystemMetrics @@ -27,8 +27,7 @@ ISystemMetrics interface for the API documentation. """ - - #### 'ISystemMetrics' interface ########################################### + # 'ISystemMetrics' interface ------------------------------------------- screen_width = Property(Int) @@ -36,20 +35,37 @@ dialog_background_color = Property(Tuple) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_screen_width(self): - return QtGui.QApplication.instance().desktop().screenGeometry().width() + # QDesktopWidget.screenGeometry() is deprecated and Qt docs + # suggest using screens() instead, but screens in not available in qt4 + # see issue: enthought/pyface#721 + if not is_qt4: + return QtGui.QApplication.instance().screens()[0].availableGeometry().width() + else: + return QtGui.QApplication.instance().desktop().availableGeometry().width() def _get_screen_height(self): - return QtGui.QApplication.instance().desktop().screenGeometry().height() + # QDesktopWidget.screenGeometry(int screen) is deprecated and Qt docs + # suggest using screens() instead, but screens in not available in qt4 + # see issue: enthought/pyface#721 + if not is_qt4: + return ( + QtGui.QApplication.instance().screens()[0].availableGeometry().height() + ) + else: + return ( + QtGui.QApplication.instance().desktop().availableGeometry().height() + ) def _get_dialog_background_color(self): - color = QtGui.QApplication.instance().palette().color(QtGui.QPalette.Window) + color = ( + QtGui.QApplication.instance() + .palette() + .color(QtGui.QPalette.ColorRole.Window) + ) return (color.redF(), color.greenF(), color.blueF()) - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/advanced_editor_area_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/advanced_editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/advanced_editor_area_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/advanced_editor_area_pane.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,13 +1,24 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import sys -# System library imports. -from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import DelegatesTo, Instance, on_trait_change, provides +from pyface.qt import QtCore, QtGui, is_pyside, is_qt6 + + +from traits.api import ( + Any, Callable, DelegatesTo, Instance, List, observe, provides, Tuple +) + -# Local imports. from pyface.tasks.i_advanced_editor_area_pane import IAdvancedEditorAreaPane from pyface.tasks.i_editor_area_pane import MEditorAreaPane from .editor_area_pane import EditorAreaDropFilter @@ -15,9 +26,10 @@ from .task_pane import TaskPane from .util import set_focus -############################################################################### +# ---------------------------------------------------------------------------- # 'AdvancedEditorAreaPane' class. -############################################################################### +# ---------------------------------------------------------------------------- + @provides(IAdvancedEditorAreaPane) class AdvancedEditorAreaPane(TaskPane, MEditorAreaPane): @@ -26,61 +38,90 @@ See the IAdvancedEditorAreaPane interface for API documentation. """ - - #### Private interface #################################################### + # Private interface ---------------------------------------------------- _main_window_layout = Instance(MainWindowLayout) - ########################################################################### + #: A list of connected Qt signals to be removed before destruction. + #: First item in the tuple is the Qt signal. The second item is the event + #: handler. + _connections_to_remove = List(Tuple(Any, Callable)) + + # ------------------------------------------------------------------------ # 'TaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the pane. """ - self.control = control = EditorAreaWidget(self, parent) + self.control = EditorAreaWidget(self, parent) self._filter = EditorAreaDropFilter(self) self.control.installEventFilter(self._filter) # Add shortcuts for scrolling through tabs. - if sys.platform == 'darwin': - next_seq = 'Ctrl+}' - prev_seq = 'Ctrl+{' + if sys.platform == "darwin": + next_seq = "Ctrl+}" + prev_seq = "Ctrl+{" else: - next_seq = 'Ctrl+PgDown' - prev_seq = 'Ctrl+PgUp' + next_seq = "Ctrl+PgDown" + prev_seq = "Ctrl+PgUp" shortcut = QtGui.QShortcut(QtGui.QKeySequence(next_seq), self.control) shortcut.activated.connect(self._next_tab) + self._connections_to_remove.append( + (shortcut.activated, self._next_tab) + ) shortcut = QtGui.QShortcut(QtGui.QKeySequence(prev_seq), self.control) shortcut.activated.connect(self._previous_tab) + self._connections_to_remove.append( + (shortcut.activated, self._previous_tab) + ) # Add shortcuts for switching to a specific tab. - mod = 'Ctrl+' if sys.platform == 'darwin' else 'Alt+' + mod = "Ctrl+" if sys.platform == "darwin" else "Alt+" mapper = QtCore.QSignalMapper(self.control) - mapper.mapped.connect(self._activate_tab) + if is_pyside and is_qt6: + mapper.mappedInt.connect(self._activate_tab) + self._connections_to_remove.append( + (mapper.mappedInt, self._activate_tab) + ) + else: + mapper.mapped.connect(self._activate_tab) + self._connections_to_remove.append( + (mapper.mapped, self._activate_tab) + ) + for i in range(1, 10): sequence = QtGui.QKeySequence(mod + str(i)) shortcut = QtGui.QShortcut(sequence, self.control) shortcut.activated.connect(mapper.map) + self._connections_to_remove.append( + (shortcut.activated, mapper.map) + ) mapper.setMapping(shortcut, i - 1) def destroy(self): """ Destroy the toolkit-specific control that represents the pane. """ self.control.removeEventFilter(self._filter) + self.control._remove_event_listeners() self._filter = None for editor in self.editors: editor_widget = editor.control.parent() self.control.destroy_editor_widget(editor_widget) editor.editor_area = None + self.active_editor = None - super(AdvancedEditorAreaPane, self).destroy() + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) - ########################################################################### + super().destroy() + + # ------------------------------------------------------------------------ # 'IEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activates the specified editor in the pane. @@ -109,26 +150,28 @@ if not self.editors: self.active_editor = None - ########################################################################### + # ------------------------------------------------------------------------ # 'IAdvancedEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_layout(self): """ Returns a LayoutItem that reflects the current state of the editors. """ return self._main_window_layout.get_layout_for_area( - QtCore.Qt.LeftDockWidgetArea) + QtCore.Qt.DockWidgetArea.LeftDockWidgetArea + ) def set_layout(self, layout): """ Applies a LayoutItem to the editors in the pane. """ if layout is not None: self._main_window_layout.set_layout_for_area( - layout, QtCore.Qt.LeftDockWidgetArea) + layout, QtCore.Qt.DockWidgetArea.LeftDockWidgetArea + ) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _activate_tab(self, index): """ Activates the tab with the specified index, if there is one. @@ -160,28 +203,31 @@ """ label = editor.name if editor.dirty: - label = '*' + label + label = "*" + label return label - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def __main_window_layout_default(self): return EditorAreaMainWindowLayout(editor_area=self) - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - @on_trait_change('editors:[dirty, name]') - def _update_label(self, editor, name, new): + @observe("editors:items:[dirty, name]") + def _update_label(self, event): + editor = event.object editor.control.parent().update_title() - @on_trait_change('editors:tooltip') - def _update_tooltip(self, editor, name, new): + @observe("editors:items:tooltip") + def _update_tooltip(self, event): + editor = event.object editor.control.parent().update_tooltip() -############################################################################### +# ---------------------------------------------------------------------------- # Auxillary classes. -############################################################################### +# ---------------------------------------------------------------------------- + class EditorAreaMainWindowLayout(MainWindowLayout): """ A MainWindowLayout for implementing AdvancedEditorAreaPane. @@ -189,17 +235,17 @@ Used for getting and setting layouts for the pane. """ - #### 'MainWindowLayout' interface ######################################### + # 'MainWindowLayout' interface ----------------------------------------- - control = DelegatesTo('editor_area') + control = DelegatesTo("editor_area") - #### 'TaskWindowLayout' interface ######################################### + # 'TaskWindowLayout' interface ----------------------------------------- editor_area = Instance(AdvancedEditorAreaPane) - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' abstract interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_dock_widget(self, pane): """ Returns the QDockWidget associated with a PaneItem. @@ -223,12 +269,12 @@ """ An auxillary widget for implementing AdvancedEditorAreaPane. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'EditorAreaWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, editor_area, parent=None): - super(EditorAreaWidget, self).__init__(parent) + super().__init__(parent) self.editor_area = editor_area self.reset_drag() @@ -241,7 +287,7 @@ break # Monitor focus changes so we can set the active editor. - QtGui.QApplication.instance().focusChanged.connect(self._focus_changed) + QtGui.QApplication.instance().focusChanged.connect(self._update_active_editor) # Configure the QMainWindow. # FIXME: Currently animation is not supported. @@ -249,21 +295,29 @@ self.setAnimated(False) self.setDockNestingEnabled(True) self.setDocumentMode(True) - self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setTabPosition(QtCore.Qt.AllDockWidgetAreas, - QtGui.QTabWidget.North) + self.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) + self.setTabPosition( + QtCore.Qt.DockWidgetArea.AllDockWidgetAreas, QtGui.QTabWidget.TabPosition.North + ) + + def _remove_event_listeners(self): + """ Disconnects focusChanged signal of the application """ + app = QtGui.QApplication.instance() + app.focusChanged.disconnect(self._update_active_editor) def add_editor_widget(self, editor_widget): """ Adds a dock widget to the editor area. """ editor_widget.installEventFilter(self) - self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, editor_widget) + self.addDockWidget(QtCore.Qt.DockWidgetArea.LeftDockWidgetArea, editor_widget) # Try to place the editor in a sensible spot. top_left = None for widget in self.get_dock_widgets(): - if top_left is None or (widget.pos().manhattanLength() < - top_left.pos().manhattanLength()): + if top_left is None or ( + widget.pos().manhattanLength() + < top_left.pos().manhattanLength() + ): top_left = widget if top_left: self.tabifyDockWidget(top_left, editor_widget) @@ -277,14 +331,18 @@ """ editor_widget.hide() editor_widget.removeEventFilter(self) + editor_widget._remove_event_listeners() editor_widget.editor.destroy() self.removeDockWidget(editor_widget) def get_dock_widgets(self): """ Gets all visible dock widgets. """ - return [ child for child in self.children() - if isinstance(child, QtGui.QDockWidget) and child.isVisible() ] + return [ + child + for child in self.children() + if isinstance(child, QtGui.QDockWidget) and child.isVisible() + ] def get_dock_widgets_for_bar(self, tab_bar): """ Get the dock widgets, in order, attached to given tab bar. @@ -306,10 +364,19 @@ """ children = [] for child in self.children(): - if (child.isWidgetType() and child.isVisible() and - ((isinstance(child, QtGui.QTabBar) and not visible_only) or - (isinstance(child, QtGui.QDockWidget) and - (visible_only or not self.tabifiedDockWidgets(child))))): + if ( + child.isWidgetType() + and child.isVisible() + and ( + (isinstance(child, QtGui.QTabBar) and not visible_only) + or ( + isinstance(child, QtGui.QDockWidget) + and ( + visible_only or not self.tabifiedDockWidgets(child) + ) + ) + ) + ): children.append(child) children.sort(key=lambda _child: (_child.pos().y(), _child.pos().x())) @@ -328,8 +395,11 @@ tabified = self.tabifiedDockWidgets(editor_widget) if tabified: widgets = self.get_dock_widgets_ordered() - tabified = [widget for widget in widgets \ - if widget in tabified or widget == editor_widget] + tabified = [ + widget + for widget in widgets + if widget in tabified or widget == editor_widget + ] visible = self.get_dock_widgets_ordered(visible_only=True) # Destroy and remove the editor. Get the active widget first, since it @@ -341,9 +411,11 @@ editor_area = self.editor_area choices = tabified if len(tabified) >= 2 else visible if len(choices) >= 2 and editor_widget == next_widget: - i = choices.index(editor_widget) - next_widget = choices[i+1] if i+1 < len(choices) else choices[i-1] - editor_area.activate_editor(next_widget.editor) + i = choices.index(editor_widget) + next_widget = ( + choices[i + 1] if i + 1 < len(choices) else choices[i - 1] + ) + editor_area.activate_editor(next_widget.editor) # Update tab bar hide state. if len(tabified) == 2: @@ -379,28 +451,30 @@ elif len(self.tabifiedDockWidgets(widget)) == 1: widget.set_title_bar(False) - ########################################################################### + # ------------------------------------------------------------------------ # Event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def childEvent(self, event): """ Reimplemented to gain access to the tab bars as they are created. """ - super(EditorAreaWidget, self).childEvent(event) + super().childEvent(event) if event.polished(): child = event.child() if isinstance(child, QtGui.QTabBar): # Use UniqueConnections since Qt recycles the tab bars. child.installEventFilter(self) - child.currentChanged.connect(self._tab_index_changed, - QtCore.Qt.UniqueConnection) + child.currentChanged.connect( + self._update_editor_in_focus, QtCore.Qt.ConnectionType.UniqueConnection + ) child.setTabsClosable(True) child.setUsesScrollButtons(True) - child.tabCloseRequested.connect(self._tab_close_requested, - QtCore.Qt.UniqueConnection) + child.tabCloseRequested.connect( + self._tab_close_requested, QtCore.Qt.ConnectionType.UniqueConnection + ) # FIXME: We would like to have the tabs movable, but this # confuses the QMainWindowLayout. For now, we disable this. - #child.setMovable(True) + # child.setMovable(True) def eventFilter(self, obj, event): """ Reimplemented to dispatch to sub-handlers. @@ -419,18 +493,22 @@ def _filter_dock_widget(self, widget, event): """ Support hover widget state tracking. """ - if self._drag_widget and event.type() == QtCore.QEvent.Resize: + if self._drag_widget and event.type() == QtCore.QEvent.Type.Resize: if widget.geometry() == self._rubber_band.geometry(): self.set_hover_widget(widget) - elif self._drag_widget == widget and event.type() == QtCore.QEvent.Move: + elif ( + self._drag_widget == widget and event.type() == QtCore.QEvent.Type.Move + ): if len(self._tear_widgets) == 1 and not self._tear_handled: widget = self._tear_widgets[0] widget.set_title_bar(True) self._tear_handled = True - elif self._drag_widget == widget and \ - event.type() == QtCore.QEvent.MouseButtonRelease: + elif ( + self._drag_widget == widget + and event.type() == QtCore.QEvent.Type.MouseButtonRelease + ): self.reset_drag() return False @@ -438,8 +516,10 @@ def _filter_rubber_band(self, rubber_band, event): """ Support hover widget state tracking. """ - if self._drag_widget and event.type() in (QtCore.QEvent.Resize, - QtCore.QEvent.Move): + if self._drag_widget and event.type() in ( + QtCore.QEvent.Type.Resize, + QtCore.QEvent.Type.Move, + ): self.set_hover_widget(None) return False @@ -447,7 +527,7 @@ def _filter_tab_bar(self, tab_bar, event): """ Support 'tearing off' a tab. """ - if event.type() == QtCore.QEvent.MouseMove: + if event.type() == QtCore.QEvent.Type.MouseMove: if tab_bar.rect().contains(event.pos()): self.reset_drag() else: @@ -458,24 +538,33 @@ pos = QtCore.QPoint(0, 0) press_event = QtGui.QMouseEvent( - QtCore.QEvent.MouseButtonPress, pos, - widget.mapToGlobal(pos), QtCore.Qt.LeftButton, - QtCore.Qt.LeftButton, event.modifiers()) + QtCore.QEvent.Type.MouseButtonPress, + pos, + widget.mapToGlobal(pos), + QtCore.Qt.MouseButton.LeftButton, + QtCore.Qt.MouseButton.LeftButton, + event.modifiers(), + ) QtCore.QCoreApplication.sendEvent(widget, press_event) return True event = QtGui.QMouseEvent( - QtCore.QEvent.MouseMove, event.pos(), event.globalPos(), - event.button(), event.buttons(), event.modifiers()) + QtCore.QEvent.Type.MouseMove, + event.pos(), + event.globalPos(), + event.button(), + event.buttons(), + event.modifiers(), + ) QtCore.QCoreApplication.sendEvent(self._drag_widget, event) return True - elif event.type() == QtCore.QEvent.ToolTip: + elif event.type() == QtCore.QEvent.Type.ToolTip: # QDockAreaLayout forces the tooltips to be QDockWidget.windowTitle, # so we provide the tooltips manually. widgets = self.get_dock_widgets_for_bar(tab_bar) index = tab_bar.tabAt(event.pos()) - tooltip = widgets[index].editor.tooltip if index >= 0 else '' + tooltip = widgets[index].editor.tooltip if index >= 0 else "" if tooltip: QtGui.QToolTip.showText(event.globalPos(), tooltip, tab_bar) return True @@ -489,24 +578,24 @@ if active_editor: set_focus(active_editor.control) - ########################################################################### + # ------------------------------------------------------------------------ # Signal handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _focus_changed(self, old, new): + def _update_active_editor(self, old, new): """ Handle an application-level focus change. """ if new is not None: for editor in self.editor_area.editors: control = editor.control if control is not None and control.isAncestorOf(new): - self.editor_area.active_editor = focused = editor + self.editor_area.active_editor = editor break else: if not self.editor_area.editors: self.editor_area.active_editor = None - def _tab_index_changed(self, index): + def _update_editor_in_focus(self, index): """ Handle a tab selection. """ widgets = self.get_dock_widgets_for_bar(self.sender()) @@ -526,25 +615,33 @@ """ def __init__(self, editor, parent=None): - super(EditorWidget, self).__init__(parent) + super().__init__(parent) self.editor = editor self.editor.create(self) - self.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea) - self.setFeatures(QtGui.QDockWidget.DockWidgetClosable | - QtGui.QDockWidget.DockWidgetMovable) + self.setAllowedAreas(QtCore.Qt.DockWidgetArea.LeftDockWidgetArea) + self.setFeatures( + QtGui.QDockWidget.DockWidgetFeature.DockWidgetClosable + | QtGui.QDockWidget.DockWidgetFeature.DockWidgetMovable + ) self.setWidget(editor.control) self.update_title() # Update the minimum size. contents_minsize = editor.control.minimumSize() style = self.style() - contents_minsize.setHeight(contents_minsize.height() - + style.pixelMetric(style.PM_DockWidgetHandleExtent)) + contents_minsize.setHeight( + contents_minsize.height() + + style.pixelMetric(style.PM_DockWidgetHandleExtent) + ) self.setMinimumSize(contents_minsize) self.dockLocationChanged.connect(self.update_title_bar) self.visibilityChanged.connect(self.update_title_bar) + def _remove_event_listeners(self): + self.dockLocationChanged.disconnect(self.update_title_bar) + self.visibilityChanged.disconnect(self.update_title_bar) + def update_title(self): title = self.editor.editor_area._get_label(self.editor) self.setWindowTitle(title) @@ -566,8 +663,11 @@ def set_title_bar(self, title_bar): current = self.titleBarWidget() editor_area = self.editor.editor_area - if title_bar and editor_area and (not editor_area.hide_tab_bar or - len(editor_area.editors) > 1): + if ( + title_bar + and editor_area + and (not editor_area.hide_tab_bar or len(editor_area.editors) > 1) + ): if not isinstance(current, EditorTitleBarWidget): self.setTitleBarWidget(EditorTitleBarWidget(self)) elif current is None or isinstance(current, EditorTitleBarWidget): @@ -579,7 +679,7 @@ """ def __init__(self, editor_widget): - super(EditorTitleBarWidget, self).__init__(editor_widget) + super().__init__(editor_widget) self.addTab(editor_widget.windowTitle()) self.setTabToolTip(0, editor_widget.editor.tooltip) self.setDocumentMode(True) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/dock_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/dock_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/dock_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/dock_pane.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,22 +1,33 @@ -# Standard library imports +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from contextlib import contextmanager -# Enthought library imports. + from pyface.tasks.i_dock_pane import IDockPane, MDockPane -from traits.api import Bool, on_trait_change, Property, provides, Tuple +from traits.api import Bool, observe, Property, provides, Tuple + -# System library imports. from pyface.qt import QtCore, QtGui -# Local imports. + from .task_pane import TaskPane from .util import set_focus # Constants. -AREA_MAP = { 'left' : QtCore.Qt.LeftDockWidgetArea, - 'right' : QtCore.Qt.RightDockWidgetArea, - 'top' : QtCore.Qt.TopDockWidgetArea, - 'bottom' : QtCore.Qt.BottomDockWidgetArea } +AREA_MAP = { + "left": QtCore.Qt.DockWidgetArea.LeftDockWidgetArea, + "right": QtCore.Qt.DockWidgetArea.RightDockWidgetArea, + "top": QtCore.Qt.DockWidgetArea.TopDockWidgetArea, + "bottom": QtCore.Qt.DockWidgetArea.BottomDockWidgetArea, +} INVERSE_AREA_MAP = dict((int(v), k) for k, v in AREA_MAP.items()) @@ -27,17 +38,17 @@ See the IDockPane interface for API documentation. """ - #### 'IDockPane' interface ################################################ + # 'IDockPane' interface ------------------------------------------------ size = Property(Tuple) - #### Protected traits ##################################################### + # Protected traits ----------------------------------------------------- _receiving = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the dock widget that contains the pane contents. @@ -47,13 +58,17 @@ # Set the widget's object name. This important for QMainWindow state # saving. Use the task ID and the pane ID to avoid collisions when a # pane is present in multiple tasks attached to the same window. - control.setObjectName(self.task.id + ':' + self.id) + control.setObjectName(self.task.id + ":" + self.id) + + # Ensure that undocked ("floating") windows are visible on macOS + # when focus is switched, for consistency with Linux and Windows. + control.setAttribute(QtCore.Qt.WidgetAttribute.WA_MacAlwaysShowToolWindow) # Configure the dock widget according to the DockPane settings. - self._set_dock_features() - self._set_dock_title() - self._set_floating() - self._set_visible() + self._set_dock_features(event=None) + self._set_dock_title(event=None) + self._set_floating(event=None) + self._set_visible(event=None) # Connect signal handlers for updating DockPane traits. control.dockLocationChanged.connect(self._receive_dock_area) @@ -68,32 +83,45 @@ # of its widgets contents_minsize = contents.minimumSize() style = control.style() - contents_minsize.setHeight(contents_minsize.height() - + style.pixelMetric(style.PM_DockWidgetHandleExtent)) + contents_minsize.setHeight( + contents_minsize.height() + + style.pixelMetric(style.PM_DockWidgetHandleExtent) + ) control.setMinimumSize(contents_minsize) # Hide the control by default. Otherwise, the widget will visible in its # parent immediately! control.hide() + def destroy(self): + """ Destroy the toolkit-specific control that represents the pane. + """ + if self.control is not None: + control = self.control + control.dockLocationChanged.disconnect(self._receive_dock_area) + control.topLevelChanged.disconnect(self._receive_floating) + control.visibilityChanged.disconnect(self._receive_visible) + + super().destroy() + def set_focus(self): """ Gives focus to the control that represents the pane. """ if self.control is not None: set_focus(self.control.widget()) - ########################################################################### + # ------------------------------------------------------------------------ # 'IDockPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ return QtGui.QWidget(parent) - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ @contextmanager def _signal_context(self): @@ -105,58 +133,60 @@ yield self._receiving = original - #### Trait property getters/setters ####################################### + # Trait property getters/setters --------------------------------------- def _get_size(self): if self.control is not None: return (self.control.width(), self.control.height()) return (-1, -1) - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - @on_trait_change('dock_area') - def _set_dock_area(self): + @observe("dock_area") + def _set_dock_area(self, event): if self.control is not None and not self._receiving: # Only attempt to adjust the area if the task is active. main_window = self.task.window.control if main_window and self.task == self.task.window.active_task: # Qt will automatically remove the dock widget from its previous # area, if it had one. - main_window.addDockWidget(AREA_MAP[self.dock_area], - self.control) + main_window.addDockWidget( + AREA_MAP[self.dock_area], self.control + ) - @on_trait_change('closable,floatable,movable') - def _set_dock_features(self): + @observe("closable,floatable,movable") + def _set_dock_features(self, event): if self.control is not None: - features = QtGui.QDockWidget.NoDockWidgetFeatures + features = QtGui.QDockWidget.DockWidgetFeature.NoDockWidgetFeatures if self.closable: - features |= QtGui.QDockWidget.DockWidgetClosable + features |= QtGui.QDockWidget.DockWidgetFeature.DockWidgetClosable if self.floatable: - features |= QtGui.QDockWidget.DockWidgetFloatable + features |= QtGui.QDockWidget.DockWidgetFeature.DockWidgetFloatable if self.movable: - features |= QtGui.QDockWidget.DockWidgetMovable + features |= QtGui.QDockWidget.DockWidgetFeature.DockWidgetMovable self.control.setFeatures(features) - @on_trait_change('name') - def _set_dock_title(self): + @observe("name") + def _set_dock_title(self, event): if self.control is not None: self.control.setWindowTitle(self.name) - @on_trait_change('floating') - def _set_floating(self): + @observe("floating") + def _set_floating(self, event): if self.control is not None and not self._receiving: self.control.setFloating(self.floating) - @on_trait_change('visible') - def _set_visible(self): + @observe("visible") + def _set_visible(self, event): if self.control is not None and not self._receiving: self.control.setVisible(self.visible) - #### Signal handlers ###################################################### + # Signal handlers -----------------------------------------------------# def _receive_dock_area(self, area): with self._signal_context(): - self.dock_area = INVERSE_AREA_MAP[int(area)] + if int(area) in INVERSE_AREA_MAP: + self.dock_area = INVERSE_AREA_MAP[int(area)] def _receive_floating(self, floating): with self._signal_context(): diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/editor_area_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/editor_area_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/editor_area_pane.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,21 +1,30 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import sys -# Enthought library imports. -from pyface.tasks.i_editor_area_pane import IEditorAreaPane, \ - MEditorAreaPane -from traits.api import on_trait_change, provides -# System library imports. -from pyface.qt import QtCore, QtGui +from pyface.tasks.i_editor_area_pane import IEditorAreaPane, MEditorAreaPane +from traits.api import Any, Callable, List, observe, provides, Tuple + + +from pyface.qt import QtCore, QtGui, is_qt6, is_pyside + -# Local imports. from .task_pane import TaskPane from .util import set_focus -############################################################################### +# ---------------------------------------------------------------------------- # 'EditorAreaPane' class. -############################################################################### +# ---------------------------------------------------------------------------- + @provides(IEditorAreaPane) class EditorAreaPane(TaskPane, MEditorAreaPane): @@ -23,11 +32,16 @@ See the IEditorAreaPane interface for API documentation. """ + # Private interface ---------------------------------------------------# + #: A list of connected Qt signals to be removed before destruction. + #: First item in the tuple is the Qt signal. The second item is the event + #: handler. + _connections_to_remove = List(Tuple(Any, Callable)) - ########################################################################### + # ------------------------------------------------------------------------ # 'TaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -41,28 +55,52 @@ # Connect to the widget's signals. control.currentChanged.connect(self._update_active_editor) + self._connections_to_remove.append( + (control.currentChanged, self._update_active_editor) + ) control.tabCloseRequested.connect(self._close_requested) + self._connections_to_remove.append( + (control.tabCloseRequested, self._close_requested) + ) # Add shortcuts for scrolling through tabs. - if sys.platform == 'darwin': - next_seq = 'Ctrl+}' - prev_seq = 'Ctrl+{' + if sys.platform == "darwin": + next_seq = "Ctrl+}" + prev_seq = "Ctrl+{" else: - next_seq = 'Ctrl+PgDown' - prev_seq = 'Ctrl+PgUp' + next_seq = "Ctrl+PgDown" + prev_seq = "Ctrl+PgUp" shortcut = QtGui.QShortcut(QtGui.QKeySequence(next_seq), self.control) shortcut.activated.connect(self._next_tab) + self._connections_to_remove.append( + (shortcut.activated, self._next_tab) + ) shortcut = QtGui.QShortcut(QtGui.QKeySequence(prev_seq), self.control) shortcut.activated.connect(self._previous_tab) + self._connections_to_remove.append( + (shortcut.activated, self._previous_tab) + ) # Add shortcuts for switching to a specific tab. - mod = 'Ctrl+' if sys.platform == 'darwin' else 'Alt+' + mod = "Ctrl+" if sys.platform == "darwin" else "Alt+" mapper = QtCore.QSignalMapper(self.control) - mapper.mapped.connect(self.control.setCurrentIndex) + if is_pyside and is_qt6: + mapper.mappedInt.connect(self.control.setCurrentIndex) + self._connections_to_remove.append( + (mapper.mappedInt, self.control.setCurrentIndex) + ) + else: + mapper.mapped.connect(self.control.setCurrentIndex) + self._connections_to_remove.append( + (mapper.mapped, self.control.setCurrentIndex) + ) for i in range(1, 10): sequence = QtGui.QKeySequence(mod + str(i)) shortcut = QtGui.QShortcut(sequence, self.control) shortcut.activated.connect(mapper.map) + self._connections_to_remove.append( + (shortcut.activated, mapper.map) + ) mapper.setMapping(shortcut, i - 1) def destroy(self): @@ -74,11 +112,15 @@ for editor in self.editors: self.remove_editor(editor) - super(EditorAreaPane, self).destroy() + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) + + super().destroy() - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activates the specified editor in the pane. @@ -93,7 +135,7 @@ index = self.control.addTab(editor.control, self._get_label(editor)) self.control.setTabToolTip(index, editor.tooltip) self.editors.append(editor) - self._update_tab_bar() + self._update_tab_bar(event=None) # The 'currentChanged' signal, used below, is not emitted when the first # editor is added. @@ -107,20 +149,20 @@ self.control.removeTab(self.control.indexOf(editor.control)) editor.destroy() editor.editor_area = None - self._update_tab_bar() + self._update_tab_bar(event=None) if not self.editors: self.active_editor = None - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_label(self, editor): """ Return a tab label for an editor. """ label = editor.name if editor.dirty: - label = '*' + label + label = "*" + label return label def _get_editor_with_control(self, control): @@ -141,19 +183,21 @@ """ self.control.setCurrentIndex(self.control.currentIndex() - 1) - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - @on_trait_change('editors:[dirty, name]') - def _update_label(self, editor, name, new): + @observe("editors:items:[dirty, name]") + def _update_label(self, event): + editor = event.object index = self.control.indexOf(editor.control) self.control.setTabText(index, self._get_label(editor)) - @on_trait_change('editors:tooltip') - def _update_tooltip(self, editor, name, new): + @observe("editors:items:tooltip") + def _update_tooltip(self, event): + editor = event.object index = self.control.indexOf(editor.control) self.control.setTabToolTip(index, editor.tooltip) - #### Signal handlers ###################################################### + # Signal handlers -----------------------------------------------------# def _close_requested(self, index): control = self.control.widget(index) @@ -168,28 +212,30 @@ control = self.control.widget(index) self.active_editor = self._get_editor_with_control(control) - @on_trait_change('hide_tab_bar') - def _update_tab_bar(self): + @observe("hide_tab_bar") + def _update_tab_bar(self, event): if self.control is not None: visible = self.control.count() > 1 if self.hide_tab_bar else True self.control.tabBar().setVisible(visible) -############################################################################### + +# ---------------------------------------------------------------------------- # Auxillary classes. -############################################################################### +# ---------------------------------------------------------------------------- + class EditorAreaWidget(QtGui.QTabWidget): """ An auxillary widget for implementing AdvancedEditorAreaPane. """ def __init__(self, editor_area, parent=None): - super(EditorAreaWidget, self).__init__(parent) + super().__init__(parent) self.editor_area = editor_area # Configure the QTabWidget. self.setAcceptDrops(True) self.setDocumentMode(True) - self.setFocusPolicy(QtCore.Qt.StrongFocus) + self.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.setFocusProxy(None) self.setMovable(True) self.setTabsClosable(True) @@ -202,18 +248,19 @@ if active_editor: set_focus(active_editor.control) + class EditorAreaDropFilter(QtCore.QObject): """ Implements drag and drop support. """ def __init__(self, editor_area): - super(EditorAreaDropFilter, self).__init__() + super().__init__() self.editor_area = editor_area def eventFilter(self, object, event): """ Handle drag and drop events with MIME type 'text/uri-list'. """ - if event.type() in (QtCore.QEvent.DragEnter, QtCore.QEvent.Drop): + if event.type() in (QtCore.QEvent.Type.DragEnter, QtCore.QEvent.Type.Drop): # Build list of accepted files. extensions = tuple(self.editor_area.file_drop_extensions) file_paths = [] @@ -223,15 +270,15 @@ file_paths.append(file_path) # Accept the event if we have at least one accepted file. - if event.type() == QtCore.QEvent.DragEnter: + if event.type() == QtCore.QEvent.Type.DragEnter: if file_paths: event.acceptProposedAction() # Dispatch the events. - elif event.type() == QtCore.QEvent.Drop: + elif event.type() == QtCore.QEvent.Type.Drop: for file_path in file_paths: self.editor_area.file_dropped = file_path return True - return super(EditorAreaDropFilter, self).eventFilter(object, event) + return super().eventFilter(object, event) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/editor.py python-pyface-7.4.0/pyface/ui/qt4/tasks/editor.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.tasks.i_editor import IEditor, MEditor from traits.api import Bool, Property, provides -# System library imports. + from pyface.qt import QtGui @@ -13,14 +22,13 @@ See the IEditor interface for API documentation. """ - - #### 'IEditor' interface ################################################## + # 'IEditor' interface -------------------------------------------------# has_focus = Property(Bool) - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -36,9 +44,9 @@ self.control.deleteLater() self.control = None - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_has_focus(self): if self.control is not None: diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/main_window_layout.py python-pyface-7.4.0/pyface/ui/qt4/tasks/main_window_layout.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/main_window_layout.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/main_window_layout.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,21 +1,38 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from itertools import combinations import logging -# System library imports. -from pyface.qt import QtCore, QtGui -# Enthought library imports. +from pyface.qt import QtCore, QtGui, is_qt4 + + from traits.api import Any, HasTraits -# Local imports. -from pyface.tasks.task_layout import LayoutContainer, PaneItem, Tabbed, \ - Splitter, HSplitter, VSplitter + +from pyface.tasks.task_layout import ( + LayoutContainer, + PaneItem, + Tabbed, + Splitter, + HSplitter, + VSplitter, +) from .dock_pane import AREA_MAP # Contants. -ORIENTATION_MAP = { 'horizontal' : QtCore.Qt.Horizontal, - 'vertical': QtCore.Qt.Vertical } +ORIENTATION_MAP = { + "horizontal": QtCore.Qt.Orientation.Horizontal, + "vertical": QtCore.Qt.Orientation.Vertical, +} # Logging. logger = logging.getLogger(__name__) @@ -25,14 +42,14 @@ """ A class for applying declarative layouts to a QMainWindow. """ - #### 'MainWindowLayout' interface ######################################### + # 'MainWindowLayout' interface ----------------------------------------- # The QMainWindow control to lay out. - control = Any + control = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_layout(self, layout, include_sizes=True): """ Get the layout by adding sublayouts to the specified DockLayout. @@ -50,13 +67,20 @@ for child in self.control.children(): # Iterate through *visibile* dock widgets. (Inactive tabbed dock # widgets are "visible" but have invalid positions.) - if isinstance(child, QtGui.QDockWidget) and child.isVisible() and \ - self.control.dockWidgetArea(child) == q_dock_area and \ - child.x() >= 0 and child.y() >= 0: + if ( + isinstance(child, QtGui.QDockWidget) + and child.isVisible() + and self.control.dockWidgetArea(child) == q_dock_area + and child.x() >= 0 + and child.y() >= 0 + ): # Get the list of dock widgets in this tab group in order. geometry = child.geometry() - tabs = [ tab for tab in self.control.tabifiedDockWidgets(child) - if tab.isVisible() ] + tabs = [ + tab + for tab in self.control.tabifiedDockWidgets(child) + if tab.isVisible() + ] if tabs: tab_bar = self._get_tab_bar(child) tab_index = tab_bar.currentIndex() @@ -65,8 +89,10 @@ # Create the leaf-level item for the child. if tabs: - panes = [ self._prepare_pane(dock_widget, include_sizes) - for dock_widget in tabs ] + panes = [ + self._prepare_pane(dock_widget, include_sizes) + for dock_widget in tabs + ] item = Tabbed(*panes, active_tab=panes[tab_index].id) else: item = self._prepare_pane(child, include_sizes) @@ -81,12 +107,12 @@ if item1 not in remove and item2 not in remove: rect1, rect2 = rects[item1], rects[item2] orient = self._get_division_orientation(rect1, rect2, True) - if orient == QtCore.Qt.Horizontal: + if orient == QtCore.Qt.Orientation.Horizontal: if rect1.y() < rect2.y(): item = VSplitter(item1, item2) else: item = VSplitter(item2, item1) - elif orient == QtCore.Qt.Vertical: + elif orient == QtCore.Qt.Orientation.Vertical: if rect1.x() < rect2.x(): item = HSplitter(item1, item2) else: @@ -102,7 +128,9 @@ items.difference_update(remove) else: # Raise an exception instead of falling into an infinite loop. - raise RuntimeError('Unable to extract layout from QMainWindow.') + raise RuntimeError( + "Unable to extract layout from QMainWindow." + ) if items: return items.pop() @@ -122,14 +150,17 @@ for name, q_dock_area in AREA_MAP.items(): sublayout = getattr(layout, name) if sublayout: - self.set_layout_for_area(sublayout, q_dock_area, - _toplevel_call=False) + self.set_layout_for_area( + sublayout, q_dock_area, _toplevel_call=False + ) - # Remove the fixed sizes once Qt activates the layout. - QtCore.QTimer.singleShot(0, self._reset_fixed_sizes) + if is_qt4: + # Remove the fixed sizes once Qt activates the layout. + QtCore.QTimer.singleShot(0, self._reset_fixed_sizes) - def set_layout_for_area(self, layout, q_dock_area, - _toplevel_added=False, _toplevel_call=True): + def set_layout_for_area( + self, layout, q_dock_area, _toplevel_added=False, _toplevel_call=True + ): """ Applies a LayoutItem to the specified dock area. """ # If we try to do the layout bottom-up, Qt will become confused. In @@ -187,33 +218,38 @@ # Now we can recurse. for i, item in enumerate(layout.items): - self.set_layout_for_area(item, q_dock_area, - _toplevel_added=True, _toplevel_call=False) + self.set_layout_for_area( + item, + q_dock_area, + _toplevel_added=True, + _toplevel_call=False, + ) else: raise MainWindowLayoutError("Unknown layout item %r" % layout) - # Remove the fixed sizes once Qt activates the layout. - if _toplevel_call: - QtCore.QTimer.singleShot(0, self._reset_fixed_sizes) + if is_qt4: + # Remove the fixed sizes once Qt activates the layout. + if _toplevel_call: + QtCore.QTimer.singleShot(0, self._reset_fixed_sizes) - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' abstract interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_dock_widget(self, pane): """ Returns the QDockWidget associated with a PaneItem. """ - raise NotImplementedError + raise NotImplementedError() def _get_pane(self, dock_widget): """ Returns a PaneItem for a QDockWidget. """ - raise NotImplementedError + raise NotImplementedError() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_division_orientation(self, one, two, splitter=False): """ Returns whether there is a division between two visible QWidgets. @@ -224,16 +260,23 @@ united = one.united(two) if splitter: sep = self.control.style().pixelMetric( - QtGui.QStyle.PM_DockWidgetSeparatorExtent, None, self.control) + QtGui.QStyle.PixelMetric.PM_DockWidgetSeparatorExtent, None, self.control + ) united.adjust(0, 0, -sep, -sep) - if one.x() == two.x() and one.width() == two.width() and \ - united.height() == one.height() + two.height(): - return QtCore.Qt.Horizontal - - elif one.y() == two.y() and one.height() == two.height() and \ - united.width() == one.width() + two.width(): - return QtCore.Qt.Vertical + if ( + one.x() == two.x() + and one.width() == two.width() + and united.height() == one.height() + two.height() + ): + return QtCore.Qt.Orientation.Horizontal + + elif ( + one.y() == two.y() + and one.height() == two.height() + and united.width() == one.width() + two.width() + ): + return QtCore.Qt.Orientation.Vertical return 0 @@ -264,13 +307,18 @@ if isinstance(layout, PaneItem): dock_widget = self._get_dock_widget(layout) if dock_widget is None: - logger.warning('Cannot retrieve dock widget for pane %r' - % layout.id) + logger.warning( + "Cannot retrieve dock widget for pane %r" % layout.id + ) else: - if layout.width > 0: - dock_widget.widget().setFixedWidth(layout.width) - if layout.height > 0: - dock_widget.widget().setFixedHeight(layout.height) + if is_qt4: + if layout.width > 0: + dock_widget.widget().setFixedWidth(layout.width) + if layout.height > 0: + dock_widget.widget().setFixedHeight(layout.height) + else: + sizeHint = lambda: QtCore.QSize(layout.width, layout.height) + dock_widget.widget().sizeHint = sizeHint return dock_widget elif isinstance(layout, LayoutContainer): @@ -284,7 +332,7 @@ """ if self.control is None: return - QWIDGETSIZE_MAX = (1 << 24) - 1 # Not exposed by Qt bindings. + QWIDGETSIZE_MAX = (1 << 24) - 1 # Not exposed by Qt bindings. for child in self.control.children(): if isinstance(child, QtGui.QDockWidget): child.widget().setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX) @@ -295,9 +343,9 @@ child.setMinimumSize(0, 0) - class MainWindowLayoutError(ValueError): """ Exception raised when a malformed LayoutItem is passed to the MainWindowLayout. """ + pass diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/split_editor_area_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/split_editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/split_editor_area_pane.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/split_editor_area_pane.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,12 +1,32 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import sys -# Enthought library imports. -from pyface.tasks.i_editor_area_pane import IEditorAreaPane, \ - MEditorAreaPane -from traits.api import Bool, cached_property, Callable, Dict, Instance, List, \ - on_trait_change, Property, provides, Str -from pyface.qt import is_qt4, QtCore, QtGui + +from pyface.tasks.i_editor_area_pane import IEditorAreaPane, MEditorAreaPane +from traits.api import ( + Any, + Bool, + cached_property, + Callable, + Dict, + Instance, + List, + observe, + Property, + provides, + Str, + Tuple, +) +from pyface.qt import is_qt4, QtCore, QtGui, is_qt6, is_pyside from pyface.action.api import Action, Group, MenuManager from pyface.tasks.task_layout import PaneItem, Tabbed, Splitter from pyface.mimedata import PyMimeData @@ -14,12 +34,13 @@ from pyface.constant import OK from pyface.drop_handler import IDropHandler, BaseDropHandler, FileDropHandler -# Local imports. + from .task_pane import TaskPane -############################################################################### +# ---------------------------------------------------------------------------- # 'SplitEditorAreaPane' class. -############################################################################### +# ---------------------------------------------------------------------------- + @provides(IEditorAreaPane) class SplitEditorAreaPane(TaskPane, MEditorAreaPane): @@ -28,8 +49,7 @@ See the IEditorAreaPane interface for API documentation. """ - - #### SplitEditorAreaPane interface ##################################### + # SplitEditorAreaPane interface ------------------------------------- # Currently active tabwidget active_tabwidget = Instance(QtGui.QTabWidget) @@ -49,12 +69,17 @@ # The constructor of the empty widget which comes up when one creates a split create_empty_widget = Callable - #### Private interface ################################################### + # Private interface --------------------------------------------------- + + #: A list of connected Qt signals to be removed before destruction. + #: First item in the tuple is the Qt signal. The second item is the event + #: handler. + _connections_to_remove = List(Tuple(Any, Callable)) _private_drop_handlers = List(IDropHandler) _all_drop_handlers = Property( List(IDropHandler), - depends_on=['drop_handlers', '_private_drop_handlers'] + observe=["drop_handlers", "_private_drop_handlers"], ) def __private_drop_handlers_default(self): @@ -64,20 +89,24 @@ 2. For dropping of supported files from file-browser pane or outside the application """ - return [TabDropHandler(), - FileDropHandler(extensions=self.file_drop_extensions, - open_file=lambda path:self.trait_set(file_dropped=path))] + return [ + TabDropHandler(), + FileDropHandler( + extensions=self.file_drop_extensions, + open_file=lambda path: self.trait_set(file_dropped=path), + ), + ] @cached_property def _get__all_drop_handlers(self): return self.drop_handlers + self._private_drop_handlers def _create_empty_widget_default(self): - return lambda : self.active_tabwidget.create_empty_widget() + return lambda: self.active_tabwidget.create_empty_widget() - ########################################################################### + # ------------------------------------------------------------------------ # 'TaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -98,21 +127,31 @@ """ # disconnect application level focus change signals first, else it gives # weird runtime errors trying to access non-existent objects - QtGui.QApplication.instance().focusChanged.disconnect(self._focus_changed) + QtGui.QApplication.instance().focusChanged.disconnect( + self._focus_changed + ) for editor in self.editors[:]: self.remove_editor(editor) - super(SplitEditorAreaPane, self).destroy() + while self._connections_to_remove: + signal, handler = self._connections_to_remove.pop() + signal.disconnect(handler) + + # Remove reference to active tabwidget so that it can be deleted + # together with the main control + self.active_tabwidget = None - ########################################################################### + super().destroy() + + # ------------------------------------------------------------------------ # 'IEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activates the specified editor in the pane. """ - active_tabwidget = editor.control.parent().parent() + active_tabwidget = self._get_editor_tabwidget(editor) active_tabwidget.setCurrentWidget(editor.control) self.active_tabwidget = active_tabwidget editor_widget = editor.control.parent() @@ -132,8 +171,9 @@ """ editor.editor_area = self editor.create(self.active_tabwidget) - index = self.active_tabwidget.addTab(editor.control, - self._get_label(editor)) + index = self.active_tabwidget.addTab( + editor.control, self._get_label(editor) + ) # There seem to be a bug in pyside or qt, where the index is set to 1 # when you create the first tab. This is a hack to fix it. if self.active_tabwidget.count() == 1: @@ -144,18 +184,17 @@ def remove_editor(self, editor): """ Removes an editor from the associated tabwidget """ - tabwidget = editor.control.parent().parent() - tabwidget.removeTab(tabwidget.indexOf(editor.control)) + tabwidget, index = self._get_editor_tabwidget_index(editor) + tabwidget.removeTab(index) self.editors.remove(editor) editor.destroy() editor.editor_area = None if not self.editors: self.active_editor = None - - ########################################################################## + # ------------------------------------------------------------------------ # 'IAdvancedEditorAreaPane' interface. - ########################################################################## + # ------------------------------------------------------------------------ def get_layout(self): """ Returns a LayoutItem that reflects the current state of the @@ -168,68 +207,86 @@ """ self.control.set_layout(layout) - ########################################################################## + # ------------------------------------------------------------------------ # 'SplitEditorAreaPane' interface. - ########################################################################## + # ------------------------------------------------------------------------ def get_context_menu(self, pos): - """ Returns a context menu containing split/collapse actions + """ Return a context menu containing split/collapse actions. - pos : position (in global coordinates) where the context menu was - requested + Parameters + ---------- + pos : QtCore.QPoint + Mouse position in global coordinates for which the context menu was + requested. + + Returns + ------- + menu : pyface.action.menu_manager.MenuManager or None + Context menu, or None if the given position doesn't correspond + to any of the tab widgets. """ menu = MenuManager() - splitter = None - splitter = None for tabwidget in self.tabwidgets(): - # obtain tabwidget's bounding rectangle in global coordinates - global_rect = QtCore.QRect(tabwidget.mapToGlobal(QtCore.QPoint(0, 0)), - tabwidget.size()) - if global_rect.contains(pos): + widget_pos = tabwidget.mapFromGlobal(pos) + if tabwidget.rect().contains(widget_pos): splitter = tabwidget.parent() - - # no split/collapse context menu for positions outside any tabwidget - # region - if not splitter: - return + break + else: + # no split/collapse context menu for positions outside any + # tabwidget region + return None # add split actions (only show for non-empty tabwidgets) if not splitter.is_empty(): - actions = [Action(id='split_hor', name='Create new pane to the right', - on_perform=lambda : splitter.split(orientation= - QtCore.Qt.Horizontal)), - Action(id='split_ver', name='Create new pane to the bottom', - on_perform=lambda : splitter.split(orientation= - QtCore.Qt.Vertical))] - - splitgroup = Group(*actions, id='split') - menu.append(splitgroup) + split_group = Group( + Action( + id="split_hor", + name="Create new pane to the right", + on_perform=lambda: splitter.split( + orientation=QtCore.Qt.Orientation.Horizontal + ), + ), + Action( + id="split_ver", + name="Create new pane below", + on_perform=lambda: splitter.split( + orientation=QtCore.Qt.Orientation.Vertical + ), + ), + id="split", + ) + menu.append(split_group) # add collapse action (only show for collapsible splitters) if splitter.is_collapsible(): if splitter is splitter.parent().leftchild: - if splitter.parent().orientation() == QtCore.Qt.Horizontal: - text = 'Merge with right pane' + if splitter.parent().orientation() == QtCore.Qt.Orientation.Horizontal: + text = "Merge with right pane" else: - text = 'Merge with bottom pane' + text = "Merge with bottom pane" else: - if splitter.parent().orientation() == QtCore.Qt.Horizontal: - text = 'Merge with left pane' + if splitter.parent().orientation() == QtCore.Qt.Orientation.Horizontal: + text = "Merge with left pane" else: - text = 'Merge with top pane' - actions = [Action(id='merge', name=text, - on_perform=lambda : splitter.collapse())] + text = "Merge with top pane" - collapsegroup = Group(*actions, id='collapse') - menu.append(collapsegroup) + collapse_group = Group( + Action( + id="merge", + name=text, + on_perform=lambda: splitter.collapse(), + ), + id="collapse", + ) + menu.append(collapse_group) - # return QMenu object return menu - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_label(self, editor): """ Return a tab label for an editor. @@ -237,9 +294,9 @@ try: label = editor.name if editor.dirty: - label = '*' + label + label = "*" + label except AttributeError: - label = '' + label = "" return label def _get_editor(self, editor_widget): @@ -254,25 +311,43 @@ """ Set keyboard shortcuts for tabbed navigation """ # Add shortcuts for scrolling through tabs. - if sys.platform == 'darwin': - next_seq = 'Ctrl+}' - prev_seq = 'Ctrl+{' + if sys.platform == "darwin": + next_seq = "Ctrl+}" + prev_seq = "Ctrl+{" else: - next_seq = 'Ctrl+PgDown' - prev_seq = 'Ctrl+PgUp' + next_seq = "Ctrl+PgDown" + prev_seq = "Ctrl+PgUp" shortcut = QtGui.QShortcut(QtGui.QKeySequence(next_seq), self.control) shortcut.activated.connect(self._next_tab) + self._connections_to_remove.append( + (shortcut.activated, self._next_tab) + ) shortcut = QtGui.QShortcut(QtGui.QKeySequence(prev_seq), self.control) shortcut.activated.connect(self._previous_tab) + self._connections_to_remove.append( + (shortcut.activated, self._previous_tab) + ) # Add shortcuts for switching to a specific tab. - mod = 'Ctrl+' if sys.platform == 'darwin' else 'Alt+' + mod = "Ctrl+" if sys.platform == "darwin" else "Alt+" mapper = QtCore.QSignalMapper(self.control) - mapper.mapped.connect(self._activate_tab) + if is_pyside and is_qt6: + mapper.mappedInt.connect(self._activate_tab) + self._connections_to_remove.append( + (mapper.mappedInt, self._activate_tab) + ) + else: + mapper.mapped.connect(self._activate_tab) + self._connections_to_remove.append( + (mapper.mapped, self._activate_tab) + ) for i in range(1, 10): sequence = QtGui.QKeySequence(mod + str(i)) shortcut = QtGui.QShortcut(sequence, self.control) shortcut.activated.connect(mapper.map) + self._connections_to_remove.append( + (shortcut.activated, mapper.map) + ) mapper.setMapping(shortcut, i - 1) def _activate_tab(self, index): @@ -288,14 +363,18 @@ """ Activate the tab after the currently active tab. """ index = self.active_tabwidget.currentIndex() - new_index = index + 1 if index < self.active_tabwidget.count() - 1 else 0 + new_index = ( + index + 1 if index < self.active_tabwidget.count() - 1 else 0 + ) self._activate_tab(new_index) def _previous_tab(self): """ Activate the tab before the currently active tab. """ index = self.active_tabwidget.currentIndex() - new_index = index - 1 if index > 0 else self.active_tabwidget.count() - 1 + new_index = ( + index - 1 if index > 0 else self.active_tabwidget.count() - 1 + ) self._activate_tab(new_index) def tabwidgets(self): @@ -304,19 +383,31 @@ """ return self.control.tabwidgets() - #### Trait change handlers ################################################ + def _get_editor_tabwidget(self, editor): + """ Given an editor, return its tabwidget. """ + return editor.control.parent().parent() + + def _get_editor_tabwidget_index(self, editor): + """ Given an editor, return its tabwidget and index. """ + tabwidget = self._get_editor_tabwidget(editor) + index = tabwidget.indexOf(editor.control) + return tabwidget, index + + # Trait change handlers ------------------------------------------------ + + @observe("editors:items:[dirty, name]") + def _update_label(self, event): + editor = event.object + tabwidget, index = self._get_editor_tabwidget_index(editor) + tabwidget.setTabText(index, self._get_label(editor)) + + @observe("editors:items:tooltip") + def _update_tooltip(self, event): + editor = event.object + tabwidget, index = self._get_editor_tabwidget_index(editor) + tabwidget.setTabToolTip(index, editor.tooltip) - @on_trait_change('editors:[dirty, name]') - def _update_label(self, editor, name, new): - index = self.active_tabwidget.indexOf(editor.control) - self.active_tabwidget.setTabText(index, self._get_label(editor)) - - @on_trait_change('editors:tooltip') - def _update_tooltip(self, editor, name, new): - index = self.active_tabwidget.indexOf(editor.control) - self.active_tabwidget.setTabToolTip(index, self._get_label(editor)) - - #### Signal handlers ###################################################### + # Signal handlers -----------------------------------------------------# def _find_ancestor_draggable_tab_widget(self, control): """ Find the draggable tab widget to which a widget belongs. """ @@ -336,16 +427,18 @@ self.active_tabwidget = new elif isinstance(new, QtGui.QTabBar): if self.control.isAncestorOf(new): - self.active_tabwidget = \ - self._find_ancestor_draggable_tab_widget(new) + self.active_tabwidget = self._find_ancestor_draggable_tab_widget( + new + ) else: # Check if any of the editor widgets have focus. # If so, make it active. for editor in self.editors: control = editor.control if control is not None and control.isAncestorOf(new): - active_tabwidget = \ - self._find_ancestor_draggable_tab_widget(control) + active_tabwidget = self._find_ancestor_draggable_tab_widget( + control + ) active_tabwidget.setCurrentWidget(control) self.active_tabwidget = active_tabwidget break @@ -362,9 +455,10 @@ self.active_editor = active_editor -############################################################################### +# ---------------------------------------------------------------------------- # Auxiliary classes. -############################################################################### +# ---------------------------------------------------------------------------- + class EditorAreaWidget(QtGui.QSplitter): """ Container widget to hold a QTabWidget which are separated by other @@ -382,12 +476,13 @@ tabwidget : tabwidget object contained by this splitter """ - super(EditorAreaWidget, self).__init__(parent=parent) + super().__init__(parent=parent) self.editor_area = editor_area if not tabwidget: - tabwidget = DraggableTabWidget(editor_area=self.editor_area, - parent=self) + tabwidget = DraggableTabWidget( + editor_area=self.editor_area, parent=self + ) # add the tabwidget to the splitter self.addWidget(tabwidget) @@ -403,20 +498,24 @@ """ Returns a LayoutItem that reflects the layout of the current splitter. """ - ORIENTATION_MAP = {QtCore.Qt.Horizontal: 'horizontal', - QtCore.Qt.Vertical: 'vertical'} + ORIENTATION_MAP = { + QtCore.Qt.Orientation.Horizontal: "horizontal", + QtCore.Qt.Orientation.Vertical: "vertical", + } # obtain layout based on children layouts if not self.is_leaf(): - layout = Splitter(self.leftchild.get_layout(), - self.rightchild.get_layout(), - orientation=ORIENTATION_MAP[self.orientation()]) + layout = Splitter( + self.leftchild.get_layout(), + self.rightchild.get_layout(), + orientation=ORIENTATION_MAP[self.orientation()], + ) # obtain the Tabbed layout else: if self.is_empty(): - layout = Tabbed(PaneItem(id=-1, - width=self.width(), - height=self.height()), - active_tab=0) + layout = Tabbed( + PaneItem(id=-1, width=self.width(), height=self.height()), + active_tab=0, + ) else: items = [] for i in range(self.tabwidget().count()): @@ -426,17 +525,23 @@ item_id = self.editor_area.editors.index(editor) item_width = self.width() item_height = self.height() - items.append(PaneItem(id=item_id, - width=item_width, - height=item_height)) - layout = Tabbed(*items, active_tab=self.tabwidget().currentIndex()) + items.append( + PaneItem( + id=item_id, width=item_width, height=item_height + ) + ) + layout = Tabbed( + *items, active_tab=self.tabwidget().currentIndex() + ) return layout def set_layout(self, layout): """ Applies the given LayoutItem to current splitter. """ - ORIENTATION_MAP = {'horizontal': QtCore.Qt.Horizontal, - 'vertical': QtCore.Qt.Vertical} + ORIENTATION_MAP = { + "horizontal": QtCore.Qt.Orientation.Horizontal, + "vertical": QtCore.Qt.Orientation.Vertical, + } # if not a leaf splitter if isinstance(layout, Splitter): self.split(orientation=ORIENTATION_MAP[layout.orientation]) @@ -444,7 +549,7 @@ self.rightchild.set_layout(layout=layout.items[1]) # setting sizes of children along splitter direction - if layout.orientation=='horizontal': + if layout.orientation == "horizontal": sizes = [self.leftchild.width(), self.rightchild.width()] self.resize(sum(sizes), self.leftchild.height()) else: @@ -460,10 +565,11 @@ self.tabwidget().clear() for item in layout.items: - if not item.id==-1: + if not item.id == -1: editor = self.editor_area.editors[item.id] - self.tabwidget().addTab(editor.control, - self.editor_area._get_label(editor)) + self.tabwidget().addTab( + editor.control, self.editor_area._get_label(editor) + ) self.resize(item.width, item.height) self.tabwidget().setCurrentIndex(layout.active_tab) @@ -545,7 +651,7 @@ else: return False - def split(self, orientation=QtCore.Qt.Horizontal): + def split(self, orientation=QtCore.Qt.Orientation.Horizontal): """ Split the current splitter into two children splitters. The current splitter's tabwidget is moved to the left child while a new empty tabwidget is added to the right child. @@ -557,17 +663,19 @@ orig_size = self.sizes()[0] # create new children - self.leftchild = EditorAreaWidget(self.editor_area, parent=self, - tabwidget=self.tabwidget()) - self.rightchild = EditorAreaWidget(self.editor_area, parent=self, - tabwidget=None) + self.leftchild = EditorAreaWidget( + self.editor_area, parent=self, tabwidget=self.tabwidget() + ) + self.rightchild = EditorAreaWidget( + self.editor_area, parent=self, tabwidget=None + ) # add newly generated children self.addWidget(self.leftchild) self.addWidget(self.rightchild) # set equal sizes of splits - self.setSizes([orig_size/2,orig_size/2]) + self.setSizes([orig_size // 2, orig_size // 2]) # make the rightchild's tabwidget active & show its empty widget self.editor_area.active_tabwidget = self.rightchild.tabwidget() @@ -609,19 +717,25 @@ left = parent.leftchild.tabwidget() right = parent.rightchild.tabwidget() - target = DraggableTabWidget(editor_area=self.editor_area, parent=parent) + target = DraggableTabWidget( + editor_area=self.editor_area, parent=parent + ) # add tabs of left and right tabwidgets to target for source in (left, right): # Note: addTab removes widgets from source tabwidget, so # grabbing all the source widgets beforehand # (not grabbing empty_widget) - widgets = [source.widget(i) for i in range(source.count()) if not - source.widget(i) is source.empty_widget] + widgets = [ + source.widget(i) + for i in range(source.count()) + if not source.widget(i) is source.empty_widget + ] for editor_widget in widgets: editor = self.editor_area._get_editor(editor_widget) - target.addTab(editor_widget, - self.editor_area._get_label(editor)) + target.addTab( + editor_widget, self.editor_area._get_label(editor) + ) # add target to parent parent.addWidget(target) @@ -647,21 +761,21 @@ editor_area : global SplitEditorAreaPane instance parent : parent of the tabwidget """ - super(DraggableTabWidget, self).__init__(parent) + super().__init__(parent) self.editor_area = editor_area # configure QTabWidget self.setTabBar(DraggableTabBar(editor_area=editor_area, parent=self)) self.setDocumentMode(True) - self.setFocusPolicy(QtCore.Qt.StrongFocus) + self.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.setFocusProxy(None) - self.setMovable(False) # handling move events myself + self.setMovable(False) # handling move events myself self.setTabsClosable(True) self.setAutoFillBackground(True) # set drop and context menu policies self.setAcceptDrops(True) - self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.DefaultContextMenu) # connecting signals self.tabCloseRequested.connect(self._close_requested) @@ -680,7 +794,7 @@ # callback to editor_area's public `create_empty_widget` Callable trait empty_widget = self.editor_area.create_empty_widget() - self.addTab(empty_widget, '') + self.addTab(empty_widget, "") self.empty_widget = empty_widget self.setFocus() @@ -688,7 +802,7 @@ if self.parent().is_root(): self.setTabsClosable(False) - self.setTabText(0, ' ') + self.setTabText(0, " ") def hide_empty_widget(self): """ Hides the empty widget (containing buttons to open new file, and @@ -705,16 +819,16 @@ empty. """ frame = QtGui.QFrame(parent=self) - frame.setFrameShape(QtGui.QFrame.StyledPanel) + frame.setFrameShape(QtGui.QFrame.Shape.StyledPanel) layout = QtGui.QVBoxLayout(frame) # Add new file button and open file button only if the `callbacks` trait # of the editor_area has a callable for key `new` and key `open` - new_file_action = self.editor_area.callbacks.get('new', None) - open_file_action = self.editor_area.callbacks.get('open_dialog', None) + new_file_action = self.editor_area.callbacks.get("new", None) + open_file_action = self.editor_area.callbacks.get("open_dialog", None) open_show_dialog = False if open_file_action is None: - open_file_action = self.editor_area.callbacks.get('open', None) + open_file_action = self.editor_area.callbacks.get("open", None) open_show_dialog = True if not (new_file_action and open_file_action): return frame @@ -722,37 +836,45 @@ layout.addStretch() # generate new file button - newfile_btn = QtGui.QPushButton('Create a new file', parent=frame) + newfile_btn = QtGui.QPushButton("Create a new file", parent=frame) newfile_btn.clicked.connect(new_file_action) - layout.addWidget(newfile_btn, alignment=QtCore.Qt.AlignHCenter) + layout.addWidget(newfile_btn, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter) # generate label label = QtGui.QLabel(parent=frame) - label.setText(""" + label.setText( + """ or - """) - layout.addWidget(label, alignment=QtCore.Qt.AlignHCenter) + """ + ) + layout.addWidget(label, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter) # generate open button - open_btn = QtGui.QPushButton('Select files from your computer', parent=frame) + open_btn = QtGui.QPushButton( + "Select files from your computer", parent=frame + ) + def _open(): if open_show_dialog: - open_dlg = FileDialog(action='open') + open_dlg = FileDialog(action="open") open_dlg.open() self.editor_area.active_tabwidget = self if open_dlg.return_code == OK: open_file_action(open_dlg.path) else: open_file_action() + open_btn.clicked.connect(_open) - layout.addWidget(open_btn, alignment=QtCore.Qt.AlignHCenter) + layout.addWidget(open_btn, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter) # generate label label = QtGui.QLabel(parent=frame) - label.setText(""" + label.setText( + """ Tip: You can also drag and drop files/tabs here. - """) - layout.addWidget(label, alignment=QtCore.Qt.AlignHCenter) + """ + ) + layout.addWidget(label, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter) layout.addStretch() frame.setLayout(layout) @@ -771,7 +893,7 @@ names.append(editor.name) return names - ###### Signal handlers #################################################### + # Signal handlers ---------------------------------------------------- def _close_requested(self, index): """ Re-implemented to close the editor when it's tab is closed @@ -790,7 +912,9 @@ """ self.setCurrentIndex(index) editor_widget = self.widget(index) - self.editor_area.active_editor = self.editor_area._get_editor(editor_widget) + self.editor_area.active_editor = self.editor_area._get_editor( + editor_widget + ) def tabInserted(self, index): """ Re-implemented to hide empty_widget when adding a new widget @@ -809,20 +933,26 @@ if not self.count() and not self.empty_widget: self.show_empty_widget() - ##### Event handlers ###################################################### + ## Event handlers -----------------------------------------------------# def contextMenuEvent(self, event): """ To show collapse context menu even on empty tabwidgets + + Parameters + ---------- + event : QtGui.QContextMenuEvent """ local_pos = event.pos() - if (self.empty_widget is not None or - self.tabBar().rect().contains(local_pos)): + if self.empty_widget is not None or self.tabBar().rect().contains( + local_pos + ): # Only display if we are in the tab bar region or the whole area if # we are displaying the default empty widget. global_pos = self.mapToGlobal(local_pos) menu = self.editor_area.get_context_menu(pos=global_pos) - qmenu = menu.create_menu(self) - qmenu.exec_(global_pos) + if menu is not None: + qmenu = menu.create_menu(self) + qmenu.exec_(global_pos) def dragEnterEvent(self, event): """ Re-implemented to highlight the tabwidget on drag enter @@ -830,11 +960,11 @@ for handler in self.editor_area._all_drop_handlers: if handler.can_handle_drop(event, self): self.editor_area.active_tabwidget = self - self.setBackgroundRole(QtGui.QPalette.Highlight) + self.setBackgroundRole(QtGui.QPalette.ColorRole.Highlight) event.acceptProposedAction() return - super(DraggableTabWidget, self).dragEnterEvent(event) + super().dragEnterEvent(event) def dropEvent(self, event): """ Re-implemented to handle drop events @@ -842,33 +972,38 @@ for handler in self.editor_area._all_drop_handlers: if handler.can_handle_drop(event, self): handler.handle_drop(event, self) - self.setBackgroundRole(QtGui.QPalette.Window) + self.setBackgroundRole(QtGui.QPalette.ColorRole.Window) event.acceptProposedAction() break def dragLeaveEvent(self, event): """ Clear widget highlight on leaving """ - self.setBackgroundRole(QtGui.QPalette.Window) - return super(DraggableTabWidget, self).dragLeaveEvent(event) + self.setBackgroundRole(QtGui.QPalette.ColorRole.Window) + return super().dragLeaveEvent(event) class DraggableTabBar(QtGui.QTabBar): """ Implements a QTabBar with event filters for tab drag """ + def __init__(self, editor_area, parent): - super(DraggableTabBar, self).__init__(parent) + super().__init__(parent) self.editor_area = editor_area - self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.DefaultContextMenu) self.drag_obj = None def mousePressEvent(self, event): - if event.button()==QtCore.Qt.LeftButton: + if event.button() == QtCore.Qt.MouseButton.LeftButton: index = self.tabAt(event.pos()) tabwidget = self.parent() - if tabwidget.widget(index) and (not tabwidget.widget(index) == tabwidget.empty_widget): - self.drag_obj = TabDragObject(start_pos=event.pos(), tabBar=self) - return super(DraggableTabBar, self).mousePressEvent(event) + if tabwidget.widget(index) and ( + not tabwidget.widget(index) == tabwidget.empty_widget + ): + self.drag_obj = TabDragObject( + start_pos=event.pos(), tabBar=self + ) + return super().mousePressEvent(event) def mouseMoveEvent(self, event): """ Re-implemented to create a drag event when the mouse is moved for a @@ -877,11 +1012,12 @@ # go into the drag logic only if a drag_obj is active if self.drag_obj: # is the left mouse button still pressed? - if not event.buttons()==QtCore.Qt.LeftButton: + if not event.buttons() == QtCore.Qt.MouseButton.LeftButton: pass # has the mouse been dragged for sufficient distance? - elif ((event.pos() - self.drag_obj.start_pos).manhattanLength() - < QtGui.QApplication.startDragDistance()): + elif ( + event.pos() - self.drag_obj.start_pos + ).manhattanLength() < QtGui.QApplication.startDragDistance(): pass # initiate drag else: @@ -891,16 +1027,16 @@ drag.setHotSpot(self.drag_obj.get_hotspot()) drag.setMimeData(mimedata) drag.exec_() - self.drag_obj = None # deactivate the drag_obj again + self.drag_obj = None # deactivate the drag_obj again return - return super(DraggableTabBar, self).mouseMoveEvent(event) + return super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): """ Re-implemented to deactivate the drag when mouse button is released """ self.drag_obj = None - return super(DraggableTabBar, self).mouseReleaseEvent(event) + return super().mouseReleaseEvent(event) class TabDragObject(object): @@ -931,12 +1067,12 @@ result_pixmap = QtGui.QPixmap(size) painter = QtGui.QStylePainter(result_pixmap, tabBar) - painter.fillRect(result_pixmap.rect(), QtCore.Qt.lightGray) - painter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver) + painter.fillRect(result_pixmap.rect(), QtCore.Qt.GlobalColor.lightGray) + painter.setCompositionMode(QtGui.QPainter.CompositionMode.CompositionMode_SourceOver) optTabBase = QtGui.QStyleOptionTabBarBase() optTabBase.initFrom(tabBar) - painter.drawPrimitive(QtGui.QStyle.PE_FrameTabBarBase, optTabBase) + painter.drawPrimitive(QtGui.QStyle.PrimitiveElement.PE_FrameTabBarBase, optTabBase) # region of active tab if is_qt4: # grab wasn't introduced until Qt5 @@ -951,7 +1087,9 @@ pixmap2 = QtGui.QPixmap.grabWidget(self.widget) else: pixmap2 = self.widget.grab() - painter.drawPixmap(0, tab_rect.height(), size.width(), size.height(), pixmap2) + painter.drawPixmap( + 0, tab_rect.height(), size.width(), size.height(), pixmap2 + ) # finish painting painter.end() @@ -959,11 +1097,16 @@ return result_pixmap def get_hotspot(self): - return self.start_pos - self.from_tabbar.tabRect(self.from_index).topLeft() + return ( + self.start_pos + - self.from_tabbar.tabRect(self.from_index).topLeft() + ) + -############################################################################### +# ---------------------------------------------------------------------------- # Default drop handlers. -############################################################################### +# ---------------------------------------------------------------------------- + class TabDropHandler(BaseDropHandler): """ Class to handle tab drop events @@ -973,8 +1116,9 @@ allow_cross_window_drop = Bool(False) def can_handle_drop(self, event, target): - if isinstance(event.mimeData(), PyMimeData) and \ - isinstance(event.mimeData().instance(), TabDragObject): + if isinstance(event.mimeData(), PyMimeData) and isinstance( + event.mimeData().instance(), TabDragObject + ): if not self.allow_cross_window_drop: drag_obj = event.mimeData().instance() return drag_obj.from_editor_area == target.editor_area @@ -994,7 +1138,7 @@ label = target.editor_area._get_label(editor) # if drop occurs at a tab bar, insert the tab at that position - if not target.tabBar().tabAt(event.pos())==-1: + if not target.tabBar().tabAt(event.pos()) == -1: index = target.tabBar().tabAt(event.pos()) target.insertTab(index, drag_obj.widget, label) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/task_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/task_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/task_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,11 +1,20 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.tasks.i_task_pane import ITaskPane, MTaskPane from traits.api import provides -# System library imports. + from pyface.qt import QtGui -# Local imports. + from .util import set_focus @@ -16,10 +25,9 @@ See the ITaskPane interface for API documentation. """ - - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/task_window_backend.py python-pyface-7.4.0/pyface/ui/qt4/tasks/task_window_backend.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/task_window_backend.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/task_window_backend.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,10 +1,19 @@ -# System library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.qt import QtCore, QtGui -# Enthought library imports. + from traits.api import Instance, List -# Local imports. + from pyface.tasks.i_task_window_backend import MTaskWindowBackend from pyface.tasks.task_layout import PaneItem, TaskLayout from .dock_pane import AREA_MAP, INVERSE_AREA_MAP @@ -12,10 +21,10 @@ # Constants. CORNER_MAP = { - 'top_left': QtCore.Qt.TopLeftCorner, - 'top_right': QtCore.Qt.TopRightCorner, - 'bottom_left': QtCore.Qt.BottomLeftCorner, - 'bottom_right': QtCore.Qt.BottomRightCorner + "top_left": QtCore.Qt.Corner.TopLeftCorner, + "top_right": QtCore.Qt.Corner.TopRightCorner, + "bottom_left": QtCore.Qt.Corner.BottomLeftCorner, + "bottom_right": QtCore.Qt.Corner.BottomRightCorner, } @@ -25,13 +34,13 @@ See the ITaskWindowBackend interface for API documentation. """ - #### Private interface #################################################### + # Private interface ---------------------------------------------------- _main_window_layout = Instance(MainWindowLayout) - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskWindowBackend' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the TaskWindow's contents. @@ -72,7 +81,7 @@ # Show the dock panes. self._layout_state(state) - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_layout(self): """ Returns a TaskLayout for the current state of the window. @@ -85,7 +94,7 @@ # Extract the window's corner configuration. for name, corner in CORNER_MAP.items(): area = INVERSE_AREA_MAP[int(self.control.corner(corner))] - setattr(layout, name + '_corner', area) + setattr(layout, name + "_corner", area) return layout @@ -96,9 +105,9 @@ self.window._active_state.layout = layout self._layout_state(self.window._active_state) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _layout_state(self, state): """ Layout the dock panes in the specified TaskState using its @@ -106,7 +115,7 @@ """ # Assign the window's corners to the appropriate dock areas. for name, corner in CORNER_MAP.items(): - area = getattr(state.layout, name + '_corner') + area = getattr(state.layout, name + "_corner") self.control.setCorner(corner, AREA_MAP[area]) # Add all panes in the TaskLayout. @@ -123,12 +132,12 @@ if dock_pane.visible: dock_pane.control.show() - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def __main_window_layout_default(self): return TaskWindowLayout(control=self.control) - #### Signal handlers ###################################################### + # Signal handlers -----------------------------------------------------# def _focus_changed_signal(self, old, new): if self.window.active_task: @@ -144,24 +153,24 @@ """ A MainWindowLayout for a TaskWindow. """ - #### 'TaskWindowLayout' interface ######################################### + # 'TaskWindowLayout' interface ----------------------------------------- - consumed = List - state = Instance('pyface.tasks.task_window.TaskState') + consumed = List() + state = Instance("pyface.tasks.task_window.TaskState") - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def set_layout(self, layout): """ Applies a DockLayout to the window. """ self.consumed = [] - super(TaskWindowLayout, self).set_layout(layout) + super().set_layout(layout) - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' abstract interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_dock_widget(self, pane): """ Returns the QDockWidget associated with a PaneItem. diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/tests/test_dock_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/tests/test_dock_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/tests/test_dock_pane.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/tests/test_dock_pane.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,102 @@ +# Copyright (c) 2014-2022 by Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! + +import sys +import unittest + +from pyface.qt import QtCore +from pyface.tasks.api import ( + DockPane, EditorAreaPane, PaneItem, Task, TaskFactory, TaskLayout, + TasksApplication) + + +class MyDockPane(DockPane): + id = "my_dock_pane" + name = u"My dock pane" + + +class MyTask(Task): + id = "my_task" + name = u"My task" + + def _default_layout_default(self): + return TaskLayout(left=PaneItem("my_dock_pane", width=200)) + + def create_central_pane(self): + return EditorAreaPane() + + def create_dock_panes(self): + return [MyDockPane()] + + +class MyApplication(TasksApplication): + id = "my_application" + name = u"My application" + + def _task_factories_default(self): + return [ + TaskFactory( + id="my_task_factory", + name=u"My task factory", + factory=MyTask, + ), + ] + + +class TestDockPane(unittest.TestCase): + + @unittest.skipUnless(sys.platform == "darwin", "only applicable to macOS") + def test_dock_windows_visible_on_macos(self): + # Regression test for enthought/pyface#427: check that dock panes + # are displayed on macOS even when the application doesn't have + # focus. + + tool_attributes = [] + + def check_panes_and_exit(app_event): + app = app_event.application + for window in app.windows: + for dock_pane in window.dock_panes: + attr = dock_pane.control.testAttribute( + QtCore.Qt.WidgetAttribute.WA_MacAlwaysShowToolWindow) + tool_attributes.append(attr) + + app.exit() + + app = MyApplication() + app.on_trait_change(check_panes_and_exit, "application_initialized") + app.run() + + self.assertTrue(tool_attributes) + for attr in tool_attributes: + self.assertTrue(attr) + + def test_dock_windows_undock(self): + # Regression test for enthought/pyface#1028: check that undocking + # dockpanes doesn't crash + + tool_attributes = [] + + def check_panes_and_exit(app_event): + app = app_event.application + app.windows[0].dock_panes[0].control.setFloating(True) + for window in app.windows: + for dock_pane in window.dock_panes: + attr = dock_pane.dock_area + tool_attributes.append(attr) + + app.exit() + + app = MyApplication() + app.on_trait_change(check_panes_and_exit, "application_initialized") + app.run() + + self.assertTrue(tool_attributes) + for attr in tool_attributes: + self.assertEqual(attr, 'left') diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/tests/test_main_window_layout.py python-pyface-7.4.0/pyface/ui/qt4/tasks/tests/test_main_window_layout.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/tests/test_main_window_layout.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/tests/test_main_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,128 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest +from unittest import mock + +from pyface.tasks.api import TaskLayout, PaneItem +from pyface.toolkit import toolkit_object +from pyface.window import Window + +try: + from pyface.qt import QtGui + from pyface.ui.qt4.tasks.main_window_layout import MainWindowLayout +except ImportError: + if toolkit_object.toolkit == "qt4": + raise + + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") + + +def create_dummy_dock_widget(parent): + """ Create a dummy QDockWidget with a dummy child widget for test. + + Parameters + ---------- + parent : QObject + + Returns + ------- + dock_widget : QDockWidget + """ + dock_widget = QtGui.QDockWidget(parent) + content_widget = QtGui.QWidget(parent) + dock_widget.setWidget(content_widget) + return dock_widget + + +@unittest.skipIf( + toolkit_object.toolkit != "qt4", + "This test targets Qt specific MainWindowLayout. " + "Current toolkit is not Qt." +) +class TestMainWindowLayout(unittest.TestCase, GuiTestAssistant): + """ Test Qt specific MainWindowLayout. + + Note that MainWindowLayout does not have a toolkit-agnostic interface + in the ``pyface.tasks`` package. Therefore this test is Qt-only. + """ + + def setUp(self): + GuiTestAssistant.setUp(self) + self.window = Window(size=(500, 500)) + self.window._create() + + def tearDown(self): + if self.window.control is not None: + with self.delete_widget(self.window.control): + self.window.destroy() + del self.window + GuiTestAssistant.tearDown(self) + + def setup_window_with_central_widget(self): + # Add a central widget to the main window. + # The main window takes ownership of the child widget. + central_widget = QtGui.QWidget(parent=self.window.control) + self.window.control.setCentralWidget(central_widget) + + def test_set_pane_item_width_in_main_window_layout(self): + # Test the dock pane width is as expected. + + self.setup_window_with_central_widget() + + # Set the dock widget expected width to be smaller than the window + # for a meaningful test. + expected_width = self.window.size[0] // 2 + window_layout = MainWindowLayout(control=self.window.control) + dock_layout = TaskLayout( + left=PaneItem(width=expected_width) + ) + dock_widget = create_dummy_dock_widget(parent=self.window.control) + patch_get_dock_widget = mock.patch.object( + MainWindowLayout, "_get_dock_widget", + return_value=dock_widget, + ) + + # when + with self.event_loop(): + with patch_get_dock_widget: + window_layout.set_layout(dock_layout) + + # then + size = dock_widget.widget().size() + self.assertEqual(size.width(), expected_width) + + def test_set_pane_item_height_in_main_window_layout(self): + # Test the dock pane height is as expected. + + self.setup_window_with_central_widget() + + # Set the dock widget expected height to be smaller than the window + # for a meaningful test. + expected_height = self.window.size[1] // 2 + window_layout = MainWindowLayout(control=self.window.control) + dock_layout = TaskLayout( + bottom=PaneItem(height=expected_height) + ) + dock_widget = create_dummy_dock_widget(parent=self.window.control) + patch_get_dock_widget = mock.patch.object( + MainWindowLayout, "_get_dock_widget", + return_value=dock_widget, + ) + + # when + with self.event_loop(): + with patch_get_dock_widget: + window_layout.set_layout(dock_layout) + + # then + size = dock_widget.widget().size() + self.assertEqual(size.height(), expected_height) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/tests/test_split_editor_area_pane.py python-pyface-7.4.0/pyface/ui/qt4/tasks/tests/test_split_editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/tests/test_split_editor_area_pane.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/tests/test_split_editor_area_pane.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,16 +1,33 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Tests for the SplitEditorAreaPane class. """ import os import tempfile import unittest -from traits.api import HasTraits, Instance +from traits.api import Instance from pyface.qt import QtGui, QtCore -from pyface.tasks.split_editor_area_pane import EditorAreaWidget, \ - SplitEditorAreaPane -from pyface.tasks.api import Editor, PaneItem, Splitter, Tabbed, Task, \ - TaskWindow +from pyface.tasks.split_editor_area_pane import ( + EditorAreaWidget, + SplitEditorAreaPane, +) +from pyface.tasks.api import ( + Editor, + PaneItem, + Splitter, + Tabbed, + Task, + TaskWindow, +) from pyface.util.guisupport import get_app_qt4 from pyface.ui.qt4.util.testing import event_loop @@ -18,14 +35,14 @@ class ViewWithTabsEditor(Editor): """ Test editor, displaying a labels in tabs. """ - name = 'Test Editor' + name = "Test Editor" def create(self, parent): """ Create and set the toolkit-specific contents of the editor. """ control = QtGui.QTabWidget() - control.addTab(QtGui.QLabel('tab 1'), 'group 1') - control.addTab(QtGui.QLabel('tab 2'), 'group 2') + control.addTab(QtGui.QLabel("tab 1"), "group 1") + control.addTab(QtGui.QLabel("tab 2"), "group 2") self.control = control def destroy(self): @@ -37,8 +54,8 @@ class SplitEditorAreaPaneTestTask(Task): """ A test task containing a SplitEditorAreaPane. """ - id = 'test_task' - name = 'Test Task' + id = "test_task" + name = "Test Task" editor_area = Instance(SplitEditorAreaPane, ()) @@ -54,13 +71,14 @@ parent : parent of the returned root """ - root = EditorAreaWidget(editor_area=SplitEditorAreaPane(), - parent=parent) - btn0 = QtGui.QPushButton('0') - btn1 = QtGui.QPushButton('1') + root = EditorAreaWidget( + editor_area=SplitEditorAreaPane(), parent=parent + ) + btn0 = QtGui.QPushButton("0") + btn1 = QtGui.QPushButton("1") tabwidget = root.tabwidget() - tabwidget.addTab(btn0, '0') - tabwidget.addTab(btn1, '1') + tabwidget.addTab(btn0, "0") + tabwidget.addTab(btn1, "1") tabwidget.setCurrentIndex(1) return root @@ -75,7 +93,7 @@ btn1 = tabwidget.widget(1) # perform - root.split(orientation=QtCore.Qt.Horizontal) + root.split(orientation=QtCore.Qt.Orientation.Horizontal) # test # do we get correct leftchild and rightchild? @@ -83,30 +101,33 @@ self.assertIsNotNone(root.rightchild) self.assertIsInstance(root.leftchild, EditorAreaWidget) self.assertIsInstance(root.rightchild, EditorAreaWidget) - self.assertEquals(root.leftchild.count(), 1) - self.assertEquals(root.rightchild.count(), 1) + self.assertEqual(root.leftchild.count(), 1) + self.assertEqual(root.rightchild.count(), 1) # are the tabwidgets laid out correctly? - self.assertEquals(root.leftchild.tabwidget(), tabwidget) + self.assertEqual(root.leftchild.tabwidget(), tabwidget) self.assertIsNotNone(root.rightchild.tabwidget().empty_widget) # are the contents of the left tabwidget correct? - self.assertEquals(root.leftchild.tabwidget().count(), 2) - self.assertEquals(root.leftchild.tabwidget().widget(0), btn0) - self.assertEquals(root.leftchild.tabwidget().widget(1), btn1) - self.assertEquals(root.leftchild.tabwidget().currentWidget(), btn1) + self.assertEqual(root.leftchild.tabwidget().count(), 2) + self.assertEqual(root.leftchild.tabwidget().widget(0), btn0) + self.assertEqual(root.leftchild.tabwidget().widget(1), btn1) + self.assertEqual(root.leftchild.tabwidget().currentWidget(), btn1) # does the right tabwidget contain nothing but the empty widget? - self.assertEquals(root.rightchild.tabwidget().count(), 1) - self.assertEquals(root.rightchild.tabwidget().widget(0), - root.rightchild.tabwidget().empty_widget) + self.assertEqual(root.rightchild.tabwidget().count(), 1) + self.assertEqual( + root.rightchild.tabwidget().widget(0), + root.rightchild.tabwidget().empty_widget, + ) # do we have an equally sized split? - self.assertEquals(root.leftchild.width(), root.rightchild.width()) + self.assertEqual(root.leftchild.width(), root.rightchild.width()) # is the rightchild active? - self.assertEquals(root.editor_area.active_tabwidget, - root.rightchild.tabwidget()) + self.assertEqual( + root.editor_area.active_tabwidget, root.rightchild.tabwidget() + ) def _setUp_collapse(self, parent=None): """ Creates a root, its leftchild and rightchild, so that collapse can @@ -118,20 +139,20 @@ """ # setup leftchild left = EditorAreaWidget(editor_area=SplitEditorAreaPane(), parent=None) - btn0 = QtGui.QPushButton('btn0') - btn1 = QtGui.QPushButton('btn1') + btn0 = QtGui.QPushButton("btn0") + btn1 = QtGui.QPushButton("btn1") tabwidget = left.tabwidget() - tabwidget.addTab(btn0, '0') - tabwidget.addTab(btn1, '1') + tabwidget.addTab(btn0, "0") + tabwidget.addTab(btn1, "1") tabwidget.setCurrentIndex(1) # setup rightchild right = EditorAreaWidget(editor_area=left.editor_area, parent=None) - btn2 = QtGui.QPushButton('btn2') - btn3 = QtGui.QPushButton('btn3') + btn2 = QtGui.QPushButton("btn2") + btn3 = QtGui.QPushButton("btn3") tabwidget = right.tabwidget() - tabwidget.addTab(btn2, '2') - tabwidget.addTab(btn3, '3') + tabwidget.addTab(btn2, "2") + tabwidget.addTab(btn3, "3") tabwidget.setCurrentIndex(0) # setup root @@ -151,23 +172,20 @@ the tabs of the collapsing tabwidgets. """ # setup root - root, left, right = self._setUp_collapse() - btn0 = left.tabwidget().widget(0) - btn1 = left.tabwidget().widget(1) + root, _, right = self._setUp_collapse() btn2 = right.tabwidget().widget(0) - btn3 = right.tabwidget().widget(1) # perform collapse on rightchild root.rightchild.collapse() # test # has the root now become the leaf? - self.assertEquals(root.count(), 1) + self.assertEqual(root.count(), 1) self.assertIsInstance(root.widget(0), QtGui.QTabWidget) # how does the combined list look? - self.assertEquals(root.tabwidget().count(), 4) - self.assertEquals(root.tabwidget().currentWidget(), btn2) + self.assertEqual(root.tabwidget().count(), 4) + self.assertEqual(root.tabwidget().currentWidget(), btn2) def test_collapse_empty(self): """ Test for collapse function when the collapse origin is an empty @@ -188,19 +206,20 @@ # test # is the layout of root now same as left? - self.assertEquals(root.count(), 2) - self.assertEquals(root.leftchild, left_left) - self.assertEquals(root.rightchild, left_right) + self.assertEqual(root.count(), 2) + self.assertEqual(root.leftchild, left_left) + self.assertEqual(root.rightchild, left_right) # are the contents of left_left and left_right preserved - self.assertEquals(root.leftchild.tabwidget().count(), 2) - self.assertEquals(root.rightchild.tabwidget().count(), 2) - self.assertEquals(root.leftchild.tabwidget().currentIndex(), 1) - self.assertEquals(root.rightchild.tabwidget().currentIndex(), 0) + self.assertEqual(root.leftchild.tabwidget().count(), 2) + self.assertEqual(root.rightchild.tabwidget().count(), 2) + self.assertEqual(root.leftchild.tabwidget().currentIndex(), 1) + self.assertEqual(root.rightchild.tabwidget().currentIndex(), 0) # what is the current active_tabwidget? - self.assertEquals(root.editor_area.active_tabwidget, - root.leftchild.tabwidget()) + self.assertEqual( + root.editor_area.active_tabwidget, root.leftchild.tabwidget() + ) def test_persistence(self): """ Tests whether get_layout/set_layout work correctly by setting a @@ -210,21 +229,24 @@ # on the rightchild of horizontal split, where the top tabwidget of # the vertical split is empty. layout = Splitter( - Tabbed(PaneItem(id=0, width=600, height=600), - active_tab=0), - Splitter(Tabbed(PaneItem(id=-1, width=600, height=300), - active_tab=0), - Tabbed(PaneItem(id=1, width=600, height=300), - PaneItem(id=2, width=600, height=300), - active_tab=0), - orientation='vertical'), - orientation='horizontal') + Tabbed(PaneItem(id=0, width=600, height=600), active_tab=0), + Splitter( + Tabbed(PaneItem(id=-1, width=600, height=300), active_tab=0), + Tabbed( + PaneItem(id=1, width=600, height=300), + PaneItem(id=2, width=600, height=300), + active_tab=0, + ), + orientation="vertical", + ), + orientation="horizontal", + ) # a total of 3 files are needed to give this layout - one on the # leftchild of horizontal split, and the other two on the bottom # tabwidget of the rightchild's vertical split - file0 = open(os.path.join(tempfile.gettempdir(), 'file0'), 'w+b') - file1 = open(os.path.join(tempfile.gettempdir(), 'file1'), 'w+b') - file2 = open(os.path.join(tempfile.gettempdir(), 'file2'), 'w+b') + file0 = open(os.path.join(tempfile.gettempdir(), "file0"), "w+b") + file1 = open(os.path.join(tempfile.gettempdir(), "file1"), "w+b") + file2 = open(os.path.join(tempfile.gettempdir(), "file2"), "w+b") # adding the editors editor_area = SplitEditorAreaPane() @@ -233,16 +255,19 @@ editor_area.add_editor(Editor(obj=file1, tooltip="test_tooltip1")) editor_area.add_editor(Editor(obj=file2, tooltip="test_tooltip2")) - ######## test tooltips ############# + # test tooltips - self.assertEquals(editor_area.active_tabwidget.tabToolTip(0), - "test_tooltip0") - self.assertEquals(editor_area.active_tabwidget.tabToolTip(1), - "test_tooltip1") - self.assertEquals(editor_area.active_tabwidget.tabToolTip(2), - "test_tooltip2") + self.assertEqual( + editor_area.active_tabwidget.tabToolTip(0), "test_tooltip0" + ) + self.assertEqual( + editor_area.active_tabwidget.tabToolTip(1), "test_tooltip1" + ) + self.assertEqual( + editor_area.active_tabwidget.tabToolTip(2), "test_tooltip2" + ) - ######## test set_layout ############# + # test set_layout # set the layout editor_area.set_layout(layout) @@ -250,14 +275,14 @@ # file0 goes to left pane? left = editor_area.control.leftchild editor = editor_area._get_editor(left.tabwidget().widget(0)) - self.assertEquals(editor.obj, file0) + self.assertEqual(editor.obj, file0) # right pane is a splitter made of two panes? right = editor_area.control.rightchild self.assertFalse(right.is_leaf()) # right pane is vertical splitter? - self.assertEquals(right.orientation(), QtCore.Qt.Vertical) + self.assertEqual(right.orientation(), QtCore.Qt.Orientation.Vertical) # top pane of this vertical split is empty? right_top = right.leftchild @@ -269,44 +294,49 @@ # file1 goes first on bottom pane? editor = editor_area._get_editor(right_bottom.tabwidget().widget(0)) - self.assertEquals(editor.obj, file1) + self.assertEqual(editor.obj, file1) # file2 goes second on bottom pane? editor = editor_area._get_editor(right_bottom.tabwidget().widget(1)) - self.assertEquals(editor.obj, file2) + self.assertEqual(editor.obj, file2) # file1 tab is active? - self.assertEquals(right_bottom.tabwidget().currentIndex(), 0) + self.assertEqual(right_bottom.tabwidget().currentIndex(), 0) - ######### test get_layout ############# + # test get_layout # obtain layout layout_new = editor_area.get_layout() # is the top level a horizontal splitter? self.assertIsInstance(layout_new, Splitter) - self.assertEquals(layout_new.orientation, 'horizontal') + self.assertEqual(layout_new.orientation, "horizontal") # tests on left child left = layout_new.items[0] self.assertIsInstance(left, Tabbed) - self.assertEquals(left.items[0].id, 0) + self.assertEqual(left.items[0].id, 0) # tests on right child right = layout_new.items[1] self.assertIsInstance(right, Splitter) - self.assertEquals(right.orientation, 'vertical') + self.assertEqual(right.orientation, "vertical") # tests on top pane of right child right_top = right.items[0] self.assertIsInstance(right_top, Tabbed) - self.assertEquals(right_top.items[0].id, -1) + self.assertEqual(right_top.items[0].id, -1) # tests on bottom pane of right child right_bottom = right.items[1] self.assertIsInstance(right_bottom, Tabbed) - self.assertEquals(right_bottom.items[0].id, 1) - self.assertEquals(right_bottom.items[1].id, 2) + self.assertEqual(right_bottom.items[0].id, 1) + self.assertEqual(right_bottom.items[1].id, 2) + + # Close all of the opened temporary files + file0.close() + file1.close() + file2.close() def test_context_menu_merge_text_left_right_split(self): # Regression test for enthought/pyface#422 @@ -321,7 +351,7 @@ editor_area_widget = editor_area.control with event_loop(): - editor_area_widget.split(orientation=QtCore.Qt.Horizontal) + editor_area_widget.split(orientation=QtCore.Qt.Orientation.Horizontal) # Get the tabs. left_tab, right_tab = editor_area_widget.tabwidgets() @@ -358,7 +388,7 @@ editor_area_widget = editor_area.control with event_loop(): - editor_area_widget.split(orientation=QtCore.Qt.Vertical) + editor_area_widget.split(orientation=QtCore.Qt.Orientation.Vertical) # Get the tabs. top_tab, bottom_tab = editor_area_widget.tabwidgets() @@ -382,6 +412,39 @@ with event_loop(): window.close() + def test_no_context_menu_if_outside_tabwidgets(self): + # Check that the case of a position not in any of the tab widgets + # is handled correctly. + window = TaskWindow() + + task = SplitEditorAreaPaneTestTask() + window.add_task(task) + + with event_loop(): + window.open() + + editor_area = task.editor_area + editor_area_widget = editor_area.control + tab_widget, = editor_area_widget.tabwidgets() + + # Position is relative to the receiving widget, so (-1, -1) should be + # reliably outside. + pos = QtCore.QPoint(-1, -1) + context_menu_event = QtGui.QContextMenuEvent( + QtGui.QContextMenuEvent.Reason.Mouse, pos + ) + + global_pos = editor_area_widget.mapToGlobal(pos) + self.assertIsNone(editor_area.get_context_menu(global_pos)) + + # Exercise the context menu code to make sure it doesn't raise. (It + # should do nothing.) + with event_loop(): + tab_widget.contextMenuEvent(context_menu_event) + + with event_loop(): + window.close() + def test_active_tabwidget_after_editor_containing_tabs_gets_focus(self): # Regression test: if an editor contains tabs, a change in focus # sets the editor area pane `active_tabwidget` to one of those tabs, @@ -410,8 +473,9 @@ editor_area.activate_editor(editor) # Check that the active tabwidget is the right one. - self.assertIs(editor_area.active_tabwidget, - editor_area.control.tabwidget()) + self.assertIs( + editor_area.active_tabwidget, editor_area.control.tabwidget() + ) with event_loop(): window.close() @@ -438,7 +502,7 @@ with event_loop(): editor_area.add_editor(left_editor) with event_loop(): - editor_area.control.split(orientation=QtCore.Qt.Horizontal) + editor_area.control.split(orientation=QtCore.Qt.Orientation.Horizontal) with event_loop(): editor_area.add_editor(right_editor) @@ -454,5 +518,80 @@ with event_loop(): window.close() -if __name__ == '__main__': - unittest.main() + def test_editor_label_change_inactive(self): + # regression test for pyface#523 + window = TaskWindow(size=(800, 600)) + + task = SplitEditorAreaPaneTestTask() + editor_area = task.editor_area + window.add_task(task) + + # Show the window. + with event_loop(): + window.open() + + with event_loop(): + app = get_app_qt4() + app.setActiveWindow(window.control) + + # Add and activate an editor which contains tabs. + left_editor = ViewWithTabsEditor() + right_editor = ViewWithTabsEditor() + + with event_loop(): + editor_area.add_editor(left_editor) + with event_loop(): + editor_area.control.split(orientation=QtCore.Qt.Orientation.Horizontal) + with event_loop(): + editor_area.add_editor(right_editor) + + editor_area.activate_editor(right_editor) + + # change the name of the inactive editor + left_editor.name = "New Name" + + # the text of the editor's tab should have changed + left_tabwidget = editor_area.tabwidgets()[0] + index = left_tabwidget.indexOf(left_editor.control) + tab_text = left_tabwidget.tabText(index) + + self.assertEqual(tab_text, "New Name") + + def test_editor_tooltip_change_inactive(self): + # regression test related to pyface#523 + window = TaskWindow(size=(800, 600)) + + task = SplitEditorAreaPaneTestTask() + editor_area = task.editor_area + window.add_task(task) + + # Show the window. + with event_loop(): + window.open() + + with event_loop(): + app = get_app_qt4() + app.setActiveWindow(window.control) + + # Add and activate an editor which contains tabs. + left_editor = ViewWithTabsEditor() + right_editor = ViewWithTabsEditor() + + with event_loop(): + editor_area.add_editor(left_editor) + with event_loop(): + editor_area.control.split(orientation=QtCore.Qt.Orientation.Horizontal) + with event_loop(): + editor_area.add_editor(right_editor) + + editor_area.activate_editor(right_editor) + + # change the name of the inactive editor + left_editor.tooltip = "New Tooltip" + + # the text of the editor's tab should have changed + left_tabwidget = editor_area.tabwidgets()[0] + index = left_tabwidget.indexOf(left_editor.control) + tab_tooltip = left_tabwidget.tabToolTip(index) + + self.assertEqual(tab_tooltip, "New Tooltip") diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tasks/util.py python-pyface-7.4.0/pyface/ui/qt4/tasks/util.py --- python-pyface-6.1.2/pyface/ui/qt4/tasks/util.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tasks/util.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,9 +1,19 @@ -# System library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.qt import QtCore -############################################################################### +# ---------------------------------------------------------------------------- # Functions. -############################################################################### +# ---------------------------------------------------------------------------- + def set_focus(control): """ Assign keyboard focus to the given control. @@ -15,15 +25,15 @@ 1. If the control itself accepts focus, use it. This is important since the control may have custom focus dispatching logic. - + 2. Otherwise, if there is a child widget of the control that previously had focus, use it. - + 3. Finally, have Qt determine the next item using its internal logic. Qt will only restrict itself to this widget's children if it is a Qt::Window or Qt::SubWindow, hence the hack below. """ - if control.focusPolicy() != QtCore.Qt.NoFocus: + if control.focusPolicy() != QtCore.Qt.FocusPolicy.NoFocus: control.setFocus() else: widget = control.focusWidget() @@ -31,7 +41,7 @@ widget.setFocus() else: flags = control.windowFlags() - control.setWindowFlags(flags | QtCore.Qt.SubWindow) + control.setWindowFlags(flags | QtCore.Qt.WindowType.SubWindow) try: control.focusNextChild() finally: diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tests/bad_import.py python-pyface-7.4.0/pyface/ui/qt4/tests/bad_import.py --- python-pyface-6.1.2/pyface/ui/qt4/tests/bad_import.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tests/bad_import.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # This is used to test what happens when there is an unrelated import error # when importing a toolkit object -raise ImportError('No module named nonexistent') +raise ImportError("No module named nonexistent") diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tests/test_gui.py python-pyface-7.4.0/pyface/ui/qt4/tests/test_gui.py --- python-pyface-6.1.2/pyface/ui/qt4/tests/test_gui.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tests/test_gui.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,16 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Qt-specific tests for the Qt GUI implementation. """ -from __future__ import absolute_import + import unittest @@ -18,14 +27,15 @@ Simple application that attempts to set a trait at start time, and immediately exits in response to that trait. """ + # The GUI instance underlying this app. gui = Instance(IGUI) # Event fired after the event loop starts. - application_running = Event + application_running = Event() def __init__(self): - super(HasStrictTraits, self).__init__() + super().__init__() self.gui = GUI() def start(self): @@ -33,7 +43,7 @@ Start the application. """ # This shouldn't be executed until after the event loop is running. - self.gui.set_trait_later(self, 'application_running', True) + self.gui.set_trait_later(self, "application_running", True) self.gui.start_event_loop() def stop(self): @@ -47,14 +57,12 @@ application_running = [] - def exit_app(): + def exit_app(event): # Record whether the event loop is running or not, then exit. - application_running.append( - is_event_loop_running_qt4() - ) + application_running.append(is_event_loop_running_qt4()) application.stop() - application.on_trait_change(exit_app, 'application_running') + application.observe(exit_app, "application_running") # Make sure that the application stops after 10 seconds, no matter # what. @@ -71,6 +79,5 @@ # Attempt to leave the QApplication in a reasonably clean # state in case of failure. qt_app.sendPostedEvents() - qt_app.flush() self.assertTrue(application_running[0]) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tests/test_message_dialog.py python-pyface-7.4.0/pyface/ui/qt4/tests/test_message_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/tests/test_message_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tests/test_message_dialog.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,86 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +Qt-specific tests for the MessageDialog +""" + +import contextlib +import unittest + +from pyface.api import MessageDialog +from pyface.qt import QtCore, QtGui +from pyface.ui.qt4.util.gui_test_assistant import GuiTestAssistant + + +class TestMessageDialog(GuiTestAssistant, unittest.TestCase): + def test_escape_button_no_details(self): + dialog = MessageDialog( + parent=None, + title="Dialog title", + message="Printer on fire", + informative="Your printer is on fire", + severity="error", + size=(600, 400), + ) + + with self.create_dialog(dialog): + escape_button = dialog.control.escapeButton() + ok_button = dialog.control.button(QtGui.QMessageBox.StandardButton.Ok) + # It's possible for both the above to be None, so double check. + self.assertIsNotNone(escape_button) + self.assertIs(escape_button, ok_button) + + def test_escape_button_with_details(self): + dialog = MessageDialog( + parent=None, + title="Dialog title", + message="Printer on fire", + informative="Your printer is on fire", + details="Temperature exceeds 1000 degrees", + severity="error", + size=(600, 400), + ) + + with self.create_dialog(dialog): + escape_button = dialog.control.escapeButton() + ok_button = dialog.control.button(QtGui.QMessageBox.StandardButton.Ok) + # It's possible for both the above to be None, so double check. + self.assertIsNotNone(escape_button) + self.assertIs(escape_button, ok_button) + + def test_text_format(self): + dialog = MessageDialog( + parent=None, + title="Dialog title", + message="Printer on fire", + informative="Your printer is on fire", + details="Temperature exceeds 1000 degrees", + severity="error", + text_format="plain", + size=(600, 400), + ) + + with self.create_dialog(dialog): + text_format = dialog.control.textFormat() + self.assertEqual(text_format, QtCore.Qt.TextFormat.PlainText) + + @contextlib.contextmanager + def create_dialog(self, dialog): + """ + Create a dialog, then destroy at the end of a with block. + """ + with self.event_loop(): + dialog._create() + try: + yield + finally: + with self.event_loop(): + dialog.destroy() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tests/test_mimedata.py python-pyface-7.4.0/pyface/ui/qt4/tests/test_mimedata.py --- python-pyface-6.1.2/pyface/ui/qt4/tests/test_mimedata.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tests/test_mimedata.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,15 @@ -# -# (C) Copyright 2013 Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This file is open source software distributed according to the terms in -# LICENSE.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # +# Thanks for using Enthought open source! import unittest -from six.moves.cPickle import dumps +from pickle import dumps from pyface.qt import QtCore from ..mimedata import PyMimeData, str2bytes @@ -26,8 +28,9 @@ self.assertEqual(md._local_instance, 0) self.assertTrue(md.hasFormat(PyMimeData.MIME_TYPE)) self.assertFalse(md.hasFormat(PyMimeData.NOPICKLE_MIME_TYPE)) - self.assertEqual(md.data(PyMimeData.MIME_TYPE).data(), - dumps(int)+dumps(0)) + self.assertEqual( + md.data(PyMimeData.MIME_TYPE).data(), dumps(int) + dumps(0) + ) def test_nopickle(self): md = PyMimeData(data=0, pickle=False) @@ -36,7 +39,7 @@ self.assertFalse(md.hasFormat(PyMimeData.MIME_TYPE)) self.assertEqual( md.data(PyMimeData.NOPICKLE_MIME_TYPE).data(), - str2bytes(str(id(0))) + str2bytes(str(id(0))), ) def test_cant_pickle(self): @@ -47,7 +50,7 @@ self.assertFalse(md.hasFormat(PyMimeData.MIME_TYPE)) self.assertEqual( md.data(PyMimeData.NOPICKLE_MIME_TYPE).data(), - str2bytes(str(id(unpicklable))) + str2bytes(str(id(unpicklable))), ) def test_coerce_pymimedata(self): @@ -72,7 +75,9 @@ self.assertEqual(md._local_instance, 0) self.assertTrue(md.hasFormat(PyMimeData.MIME_TYPE)) self.assertFalse(md.hasFormat(PyMimeData.NOPICKLE_MIME_TYPE)) - self.assertEqual(md.data(PyMimeData.MIME_TYPE).data(), dumps(int)+dumps(0)) + self.assertEqual( + md.data(PyMimeData.MIME_TYPE).data(), dumps(int) + dumps(0) + ) def test_coerce_unpicklable(self): unpicklable = lambda: None @@ -86,7 +91,9 @@ self.assertEqual(md._local_instance, [0]) self.assertTrue(md.hasFormat(PyMimeData.MIME_TYPE)) self.assertFalse(md.hasFormat(PyMimeData.NOPICKLE_MIME_TYPE)) - self.assertEqual(md.data(PyMimeData.MIME_TYPE).data(), dumps(list)+dumps([0])) + self.assertEqual( + md.data(PyMimeData.MIME_TYPE).data(), dumps(list) + dumps([0]) + ) def test_coerce_list_pymimedata(self): md = PyMimeData(data=0) @@ -94,8 +101,9 @@ self.assertEqual(md2._local_instance, [0]) self.assertTrue(md2.hasFormat(PyMimeData.MIME_TYPE)) self.assertFalse(md2.hasFormat(PyMimeData.NOPICKLE_MIME_TYPE)) - self.assertEqual(md2.data(PyMimeData.MIME_TYPE).data(), - dumps(list)+dumps([0])) + self.assertEqual( + md2.data(PyMimeData.MIME_TYPE).data(), dumps(list) + dumps([0]) + ) def test_coerce_list_pymimedata_nopickle(self): md = PyMimeData(data=0, pickle=False) @@ -118,7 +126,9 @@ self.assertTrue(isinstance(md2, PMDSubclass)) self.assertTrue(md2.hasFormat(PyMimeData.MIME_TYPE)) self.assertFalse(md2.hasFormat(PyMimeData.NOPICKLE_MIME_TYPE)) - self.assertEqual(md2.data(PyMimeData.MIME_TYPE).data(), dumps(int)+dumps(0)) + self.assertEqual( + md2.data(PyMimeData.MIME_TYPE).data(), dumps(int) + dumps(0) + ) def test_instance(self): md = PyMimeData(data=0) @@ -151,7 +161,3 @@ # remove local instance to simulate cross-process md._local_instance = None self.assertEqual(md.instanceType(), None) - - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tests/test_progress_dialog.py python-pyface-7.4.0/pyface/ui/qt4/tests/test_progress_dialog.py --- python-pyface-6.1.2/pyface/ui/qt4/tests/test_progress_dialog.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tests/test_progress_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import unittest @@ -7,7 +16,6 @@ class TestProgressDialog(unittest.TestCase, GuiTestAssistant): - def setUp(self): GuiTestAssistant.setUp(self) self.dialog = ProgressDialog() @@ -33,7 +41,7 @@ def test_show_time(self): # test that creation works with show_time - self.dialog.show_time = True + self.dialog.show_time = True self.dialog._create() self.gui.process_events() self.assertIsNotNone(self.dialog._elapsed_control) @@ -44,7 +52,7 @@ def test_show_percent(self): # test that creation works with show_percent - self.dialog.show_percent = True + self.dialog.show_percent = True self.dialog._create() self.gui.process_events() self.assertEqual(self.dialog.progress_bar.format(), "%p%") @@ -77,13 +85,14 @@ self.dialog.max = 10 self.dialog.open() for i in range(11): - self.dialog.change_message('Updating {}'.format(i)) + self.dialog.change_message("Updating {}".format(i)) result = self.dialog.update(i) self.gui.process_events() self.assertEqual(result, (True, False)) - self.assertEqual(self.dialog.message, 'Updating {}'.format(i)) - self.assertEqual(self.dialog._message_control.text(), - 'Updating {}'.format(i)) + self.assertEqual(self.dialog.message, "Updating {}".format(i)) + self.assertEqual( + self.dialog._message_control.text(), "Updating {}".format(i) + ) self.assertIsNone(self.dialog.control) self.gui.process_events() @@ -92,13 +101,14 @@ self.dialog.max = 10 self.dialog.open() for i in range(11): - self.dialog.message = 'Updating {}'.format(i) + self.dialog.message = "Updating {}".format(i) result = self.dialog.update(i) self.gui.process_events() self.assertEqual(result, (True, False)) - self.assertEqual(self.dialog.message, 'Updating {}'.format(i)) - self.assertEqual(self.dialog._message_control.text(), - 'Updating {}'.format(i)) + self.assertEqual(self.dialog.message, "Updating {}".format(i)) + self.assertEqual( + self.dialog._message_control.text(), "Updating {}".format(i) + ) self.assertIsNone(self.dialog.control) self.gui.process_events() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/tests/test_qt_imports.py python-pyface-7.4.0/pyface/ui/qt4/tests/test_qt_imports.py --- python-pyface-6.1.2/pyface/ui/qt4/tests/test_qt_imports.py 2019-06-18 10:52:41.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/tests/test_qt_imports.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,43 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import sys import unittest +import warnings class TestPyfaceQtImports(unittest.TestCase): - def test_imports(self): # check that all Qt API imports work - import pyface.qt.QtCore - import pyface.qt.QtGui - import pyface.qt.QtNetwork - import pyface.qt.QtOpenGL - import pyface.qt.QtScript - import pyface.qt.QtSvg - import pyface.qt.QtTest - import pyface.qt.QtWebKit \ No newline at end of file + import pyface.qt.QtCore # noqa: F401 + import pyface.qt.QtGui # noqa: F401 + import pyface.qt.QtNetwork # noqa: F401 + import pyface.qt.QtOpenGL # noqa: F401 + import pyface.qt.QtSvg # noqa: F401 + import pyface.qt.QtTest # noqa: F401 + import pyface.qt.QtMultimedia # noqa: F401 + import pyface.qt.QtMultimediaWidgets # noqa: F401 + + @unittest.skipIf(sys.version_info > (3, 6), "WebKit is not available") + def test_import_web_kit(self): + import pyface.qt.QtWebKit # noqa: F401 + + def test_import_QtScript(self): + # QtScript is not supported on PyQt5/PySide2 and + # this import will raise a deprecation warning. + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always", category=DeprecationWarning) + + import pyface.qt.QtScript # noqa: F401 + + self.assertTrue(len(w) == 1) + for warn in w: + self.assertEqual(warn.category, DeprecationWarning) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/timer/do_later.py python-pyface-7.4.0/pyface/ui/qt4/timer/do_later.py --- python-pyface-6.1.2/pyface/ui/qt4/timer/do_later.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/timer/do_later.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,16 @@ -# Copyright (c) 2018, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! +# Thanks for using Enthought open source! """ DoLaterTimer class Provided for backward compatibility. """ -from pyface.timer.do_later import DoLaterTimer \ No newline at end of file +from pyface.timer.do_later import DoLaterTimer # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/ui/qt4/timer/__init__.py python-pyface-7.4.0/pyface/ui/qt4/timer/__init__.py --- python-pyface-6.1.2/pyface/ui/qt4/timer/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/timer/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/qt4/timer/timer.py python-pyface-7.4.0/pyface/ui/qt4/timer/timer.py --- python-pyface-6.1.2/pyface/ui/qt4/timer/timer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/timer/timer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,13 @@ -# Copyright (c) 2007, Riverbank Computing Limited -# Copyright (c) 2018, Enthought Inc +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license. However, when used with the GPL version of PyQt the additional -# terms described in the PyQt GPL exception also apply +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from traits.api import Instance @@ -19,8 +22,8 @@ _timer = Instance(QTimer, (), allow_none=False) def __init__(self, **traits): - traits.setdefault('_timer', QTimer()) - super(PyfaceTimer, self).__init__(**traits) + traits.setdefault("_timer", QTimer()) + super().__init__(**traits) self._timer.timeout.connect(self.perform) def _start(self): diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/datetime.py python-pyface-7.4.0/pyface/ui/qt4/util/datetime.py --- python-pyface-6.1.2/pyface/ui/qt4/util/datetime.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/datetime.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,59 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Utility functions for handling Qt dates and times. """ + +from pyface.qt import is_pyqt +from pyface.qt.QtCore import QTime + + +def qtime_to_pytime(qtime): + """ Convert a QTime to a Python datetime.time + + This is needed to paper over the differences between PyQt and PySide. + + Parameters + ---------- + qtime : QTime + The Qt QTime to convert + + Returns + ------- + pytime : datetime.time + The corresponding datetime.time. + """ + if is_pyqt: + return qtime.toPyTime() + else: + return qtime.toPython() + + +def pytime_to_qtime(pytime): + """ Convert a Python datetime.time to a QTime + + This is a convenience function to construct a qtime from a Python time. + This loses any :attr:`fold` information in the Python time. + + Parameters + ---------- + pytime : datetime.time + The datetime.time to convert + + Returns + ------- + qtime : QTime + The corresponding Qt QTime. + """ + return QTime( + pytime.hour, + pytime.minute, + pytime.second, + pytime.microsecond // 1000 + ) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/event_loop_helper.py python-pyface-7.4.0/pyface/ui/qt4/util/event_loop_helper.py --- python-pyface-6.1.2/pyface/ui/qt4/util/event_loop_helper.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/event_loop_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,18 @@ -# (C) Copyright 2014-15 Enthought, Inc., Austin, TX -# All right reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! import contextlib import threading -from pyface.gui import GUI +from pyface.i_gui import IGUI from pyface.qt import QtCore, QtGui from traits.api import HasStrictTraits, Instance @@ -38,7 +39,7 @@ qt_app = Instance(QtGui.QApplication) - gui = Instance(GUI) + gui = Instance(IGUI) def event_loop_with_timeout(self, repeat=2, timeout=10.0): """Helper function to send all posted events to the event queue and @@ -52,7 +53,12 @@ timeout: float, optional, keyword only Number of seconds to run the event loop in the case that the trait change does not occur. Default value is 10.0. + + Notes + ----- + `timeout` is rounded to the nearest millisecond. """ + def repeat_loop(condition, repeat): # We sendPostedEvents to ensure that enaml events are processed self.qt_app.sendPostedEvents() @@ -67,7 +73,8 @@ condition = threading.Event() self.gui.invoke_later(repeat_loop, repeat=repeat, condition=condition) self.event_loop_until_condition( - condition=condition.is_set, timeout=timeout) + condition=condition.is_set, timeout=timeout + ) def event_loop(self, repeat=1): """Emulates an event loop `repeat` times with @@ -98,7 +105,12 @@ timeout : float Number of seconds to run the event loop in the case that the trait change does not occur. + + Notes + ----- + `timeout` is rounded to the nearest millisecond. """ + def handler(): if condition(): self.qt_app.quit() @@ -110,7 +122,7 @@ condition_timer.timeout.connect(handler) timeout_timer = QtCore.QTimer() timeout_timer.setSingleShot(True) - timeout_timer.setInterval(timeout * 1000) + timeout_timer.setInterval(round(timeout * 1000)) timeout_timer.timeout.connect(self.qt_app.quit) timeout_timer.start() condition_timer.start() @@ -118,7 +130,8 @@ self.qt_app.exec_() if not condition(): raise ConditionTimeoutError( - 'Timed out waiting for condition') + "Timed out waiting for condition" + ) finally: timeout_timer.stop() condition_timer.stop() @@ -136,10 +149,14 @@ timeout : float Number of seconds to run the event loop in the case that the widget is not deleted. + + Notes + ----- + `timeout` is rounded to the nearest millisecond. """ timer = QtCore.QTimer() timer.setSingleShot(True) - timer.setInterval(timeout * 1000) + timer.setInterval(round(timeout * 1000)) timer.timeout.connect(self.qt_app.quit) widget.destroyed.connect(self.qt_app.quit) yield @@ -148,4 +165,5 @@ if not timer.isActive(): # We exited the event loop on timeout raise ConditionTimeoutError( - 'Could not destroy widget before timeout: {!r}'.format(widget)) + "Could not destroy widget before timeout: {!r}".format(widget) + ) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/gui_test_assistant.py python-pyface-7.4.0/pyface/ui/qt4/util/gui_test_assistant.py --- python-pyface-6.1.2/pyface/ui/qt4/util/gui_test_assistant.py 2019-07-20 10:50:48.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/gui_test_assistant.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,35 +1,34 @@ -# Copyright (c) 2013-2017 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! import contextlib import gc import threading -import six -if six.PY2: - import mock -else: - import unittest.mock as mock + +import unittest.mock as mock from pyface.qt.QtGui import QApplication from pyface.ui.qt4.gui import GUI -from traits.testing.unittest_tools import UnittestTools -from traits.testing.unittest_tools import _TraitsChangeCollector as \ - TraitsChangeCollector +from traits.testing.api import UnittestTools +from traits.testing.unittest_tools import ( + _TraitsChangeCollector as TraitsChangeCollector, +) -from .testing import find_qt_widget, print_qt_widget_tree +from .testing import find_qt_widget from .event_loop_helper import EventLoopHelper, ConditionTimeoutError class GuiTestAssistant(UnittestTools): - #### 'TestCase' protocol ################################################## + # 'TestCase' protocol -------------------------------------------------# def setUp(self): qt_app = QApplication.instance() @@ -38,24 +37,25 @@ self.qt_app = qt_app self.gui = GUI() self.event_loop_helper = EventLoopHelper( - qt_app=self.qt_app, - gui=self.gui + qt_app=self.qt_app, gui=self.gui ) try: - import traitsui.api + import traitsui.api # noqa: F401 except ImportError: self.traitsui_raise_patch = None else: self.traitsui_raise_patch = mock.patch( - 'traitsui.qt4.ui_base._StickyDialog.raise_') + "traitsui.qt4.ui_base._StickyDialog.raise_" + ) self.traitsui_raise_patch.start() def new_activate(self): self.control.activateWindow() self.pyface_raise_patch = mock.patch( - 'pyface.ui.qt4.window.Window.activate', - new_callable=lambda: new_activate) + "pyface.ui.qt4.window.Window.activate", + new_callable=lambda: new_activate, + ) self.pyface_raise_patch.start() def tearDown(self): @@ -75,7 +75,6 @@ with self.event_loop_with_timeout(repeat=5): self.gui.invoke_later(self.qt_app.closeAllWindows) - self.qt_app.flush() self.pyface_raise_patch.stop() if self.traitsui_raise_patch is not None: self.traitsui_raise_patch.stop() @@ -86,7 +85,7 @@ del self.gui del self.qt_app - #### 'GuiTestAssistant' protocol ########################################## + # 'GuiTestAssistant' protocol -----------------------------------------# @contextlib.contextmanager def event_loop(self, repeat=1): @@ -121,8 +120,9 @@ with self.event_loop_helper.delete_widget(widget, timeout=timeout): yield except ConditionTimeoutError: - self.fail('Could not destroy widget before timeout: {!r}'.format( - widget)) + self.fail( + "Could not destroy widget before timeout: {!r}".format(widget) + ) @contextlib.contextmanager def event_loop_until_condition(self, condition, timeout=10.0): @@ -132,6 +132,20 @@ This should not be used to wait for widget deletion. Use delete_widget() instead. + Notes + ----- + + This runs the real Qt event loop, polling the condition every 50 ms and + returning as soon as the condition becomes true. If the condition does + not become true within the given timeout, a ConditionTimeoutError is + raised. + + Because the state of the condition is only polled every 50 ms, it + may fail to detect transient states that appear and disappear within + a 50 ms window. Code should ensure that any state that is being + tested by the condition cannot revert to a False value once it becomes + True. + Parameters ---------- condition : callable @@ -144,13 +158,54 @@ try: yield self.event_loop_helper.event_loop_until_condition( - condition, timeout=timeout) + condition, timeout=timeout + ) except ConditionTimeoutError: - self.fail('Timed out waiting for condition') + self.fail("Timed out waiting for condition") + + def assertEventuallyTrueInGui(self, condition, timeout=10.0): + """ + Assert that the given condition becomes true if we run the GUI + event loop for long enough. + + Notes + ----- + + This assertion runs the real Qt event loop, polling the condition + every 50 ms and returning as soon as the condition becomes true. If + the condition does not become true within the given timeout, the + assertion fails. + + Because the state of the condition is only polled every 50 ms, it + may fail to detect transient states that appear and disappear within + a 50 ms window. Tests should ensure that any state that is being + tested by the condition cannot revert to a False value once it becomes + True. + + Parameters + ---------- + condition : callable() -> bool + Callable accepting no arguments and returning a bool. + timeout : float + Maximum length of time to wait for the condition to become + true, in seconds. + + Raises + ------ + self.failureException + If the condition does not become true within the given timeout. + """ + try: + self.event_loop_helper.event_loop_until_condition( + condition, timeout=timeout + ) + except ConditionTimeoutError: + self.fail("Timed out waiting for condition to become true.") @contextlib.contextmanager - def assertTraitChangesInEventLoop(self, obj, trait, condition, count=1, - timeout=10.0): + def assertTraitChangesInEventLoop( + self, obj, trait, condition, count=1, timeout=10.0 + ): """Runs the real Qt event loop, collecting trait change events until the provided condition evaluates to True. @@ -162,7 +217,7 @@ The extended trait name of trait changes to listen to. condition : callable A callable to determine if the stop criteria have been met. This - should accept no arguments. + takes obj as the only argument. count : int The expected number of times the event should be fired. The default is to expect one event. @@ -171,21 +226,25 @@ change does not occur. """ condition_ = lambda: condition(obj) - collector = TraitsChangeCollector(obj=obj, trait=trait) + collector = TraitsChangeCollector(obj=obj, trait_name=trait) collector.start_collecting() try: try: yield collector self.event_loop_helper.event_loop_until_condition( - condition_, timeout=timeout) + condition_, timeout=timeout + ) except ConditionTimeoutError: actual_event_count = collector.event_count - msg = ("Expected {} event on {} to be fired at least {} " - "times, but the event was only fired {} times " - "before timeout ({} seconds).") + msg = ( + "Expected {} event on {} to be fired at least {} " + "times, but the event was only fired {} times " + "before timeout ({} seconds)." + ) msg = msg.format( - trait, obj, count, actual_event_count, timeout) + trait, obj, count, actual_event_count, timeout + ) self.fail(msg) finally: collector.stop_collecting() @@ -205,7 +264,7 @@ Number of seconds to run the event loop in the case that the trait change does not occur. Default value is 10.0. """ - timeout = kw.pop('timeout', 10.0) + timeout = kw.pop("timeout", 10.0) condition = threading.Event() traits = set(traits) @@ -221,21 +280,23 @@ condition.set() def make_handler(trait): - def handler(): + def handler(event): set_event(trait) + return handler handlers = {trait: make_handler(trait) for trait in traits} for trait, handler in handlers.items(): - traits_object.on_trait_change(handler, trait) + traits_object.observe(handler, trait) try: with self.event_loop_until_condition( - condition=condition.is_set, timeout=timeout): + condition=condition.is_set, timeout=timeout + ): yield finally: for trait, handler in handlers.items(): - traits_object.on_trait_change(handler, trait, remove=True) + traits_object.observe(handler, trait, remove=True) @contextlib.contextmanager def event_loop_with_timeout(self, repeat=2, timeout=10.0): @@ -256,7 +317,8 @@ """ yield self.event_loop_helper.event_loop_with_timeout( - repeat=repeat, timeout=timeout) + repeat=repeat, timeout=timeout + ) def find_qt_widget(self, start, type_, test=None): """Recursively walks the Qt widget tree from Qt widget `start` until it diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/image_helpers.py python-pyface-7.4.0/pyface/ui/qt4/util/image_helpers.py --- python-pyface-6.1.2/pyface/ui/qt4/util/image_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/image_helpers.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,175 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Helper functions for working with images + +This module provides helper functions for converting between numpy arrays +and Qt QImages, as well as between the various image types in a standardized +way. +""" + +from enum import Enum + +from pyface.qt import qt_api +from pyface.qt.QtCore import Qt +from pyface.qt.QtGui import QImage, QPixmap, QIcon + + +class ScaleMode(Enum): + fast = Qt.TransformationMode.FastTransformation + smooth = Qt.TransformationMode.SmoothTransformation + + +class AspectRatio(Enum): + ignore = Qt.AspectRatioMode.IgnoreAspectRatio + keep_constrain = Qt.AspectRatioMode.KeepAspectRatio + keep_expand = Qt.AspectRatioMode.KeepAspectRatioByExpanding + + +def image_to_bitmap(image): + """ Convert a QImage to a QPixmap. + Parameters + ---------- + image : QImage + The QImage to convert. + + Return + ------ + bitmap : QPixmap + The corresponding QPixmap. + """ + return QPixmap.fromImage(image) + + +def bitmap_to_image(bitmap): + """ Convert a QPixmap to a QImage. + Parameters + ---------- + bitmap : QPixmap + The QPixmap to convert. + + Return + ------ + image : QImage + The corresponding QImage. + """ + return bitmap.toImage() + + +def bitmap_to_icon(bitmap): + """ Convert a QPixmap to a QIcon. + Parameters + ---------- + bitmap : QPixmap + The QPixmap to convert. + + Return + ------ + icon : QIcon + The corresponding QIcon. + """ + return QIcon(bitmap) + + +def resize_image(image, size, aspect_ratio=AspectRatio.ignore, + mode=ScaleMode.fast): + """ Resize a toolkit image to the given size. """ + return image.scaled(*size, aspect_ratio.value, mode.value) + + +def resize_bitmap(bitmap, size, aspect_ratio=AspectRatio.ignore, + mode=ScaleMode.fast): + """ Resize a toolkit bitmap to the given size. """ + return bitmap.scaled(*size, aspect_ratio.value, mode.value) + + +def image_to_array(image): + """ Convert a QImage to a numpy array. + + This copies the data returned from Qt. + + Parameters + ---------- + image : QImage + The QImage that we want to extract the values from. The format must + be either RGB32 or ARGB32. + + Return + ------ + array : ndarray + An N x M x 4 array of unsigned 8-bit ints as RGBA values. + """ + import numpy as np + + width, height = image.width(), image.height() + channels = image.pixelFormat().channelCount() + data = image.bits() + if qt_api in {'pyqt', 'pyqt5'}: + data = data.asarray(width * height * channels) + array = np.array(data, dtype='uint8') + array.shape = (height, width, channels) + if image.format() in {QImage.Format.Format_RGB32, QImage.Format.Format_ARGB32}: + # comes in as BGRA, but want RGBA + array = array[:, :, [2, 1, 0, 3]] + else: + raise ValueError( + "Unsupported QImage format {}".format(image.format()) + ) + return array + + +def array_to_image(array): + """ Convert a numpy array to a QImage. + + This copies the data before passing it to Qt. + + Parameters + ---------- + array : ndarray + An N x M x {3, 4} array of unsigned 8-bit ints. The image + format is assumed to be RGB or RGBA, based on the shape. + + Return + ------ + image : QImage + The QImage created from the data. The pixel format is + QImage.Format.Format_RGB32. + """ + import numpy as np + + if array.ndim != 3: + raise ValueError("Array must be either RGB or RGBA values.") + + height, width, channels = array.shape + data = np.empty((height, width, 4), dtype='uint8') + if channels == 3: + data[:, :, [2, 1, 0]] = array + data[:, :, 3] = 0xff + elif channels == 4: + data[:, :, [2, 1, 0, 3]] = array + else: + raise ValueError("Array must be either RGB or RGBA values.") + + bytes_per_line = 4 * width + + if channels == 3: + image = QImage(data.data, width, height, bytes_per_line, + QImage.Format.Format_RGB32) + + elif channels == 4: + image = QImage(data.data, width, height, bytes_per_line, + QImage.Format.Format_ARGB32) + image._numpy_data = data + return image + + +# backwards compatible names - will be removed in Pyface 8 +array_to_QImage = array_to_image +QImage_to_array = image_to_array diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/modal_dialog_tester.py python-pyface-7.4.0/pyface/ui/qt4/util/modal_dialog_tester.py --- python-pyface-6.1.2/pyface/ui/qt4/util/modal_dialog_tester.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/modal_dialog_tester.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,28 @@ -# (C) Copyright 2014-2017 Enthought, Inc., Austin, TX -# All right reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ A class to facilitate testing components that use TraitsUI or Qt Dialogs. """ import contextlib -import platform import sys import traceback from pyface.api import GUI, OK, CANCEL, YES, NO -from pyface.qt import QtCore, QtGui, qt_api +from pyface.qt import QtCore, QtGui from traits.api import Undefined from .event_loop_helper import EventLoopHelper from .testing import find_qt_widget -BUTTON_TEXT = { - OK: u'OK', - CANCEL: u'Cancel', - YES: u'Yes', - NO: u'No', -} +BUTTON_TEXT = {OK: "OK", CANCEL: "Cancel", YES: "Yes", NO: "No"} class ModalDialogTester(object): @@ -58,6 +53,7 @@ manager is used from the GuiTestAssistant when necessary. """ + def __init__(self, function): #: The command to call that will cause a dialog to open. self.function = function @@ -133,7 +129,8 @@ self._gui.invoke_later(self.open, *args, **kwargs) # wait in the event loop until timeout or a return value assigned. self._helper.event_loop_until_condition( - condition=self.value_assigned, timeout=15) + condition=self.value_assigned, timeout=15 + ) finally: condition_timer.stop() condition_timer.timeout.disconnect(handler) @@ -181,7 +178,7 @@ if self._dialog_widget is None: return False else: - value = (self.get_dialog_widget() != self._dialog_widget) + value = self.get_dialog_widget() != self._dialog_widget if value: # process any pending events so that we have a clean # event loop before we exit. @@ -200,7 +197,8 @@ self._gui.invoke_later(self.open, *args, **kwargs) # wait in the event loop until timeout or a return value assigned. self._helper.event_loop_until_condition( - condition=condition, timeout=15) + condition=condition, timeout=15 + ) finally: condition_timer.stop() condition_timer.timeout.disconnect(handler) @@ -257,14 +255,14 @@ """ if len(self._event_loop_error) > 0: - msg = 'The following error(s) were detected:\n\n{0}' + msg = "The following error(s) were detected:\n\n{0}" tracebacks = [] for type_, message in self._event_loop_error: if isinstance(type_, AssertionError): - msg = 'The following failure(s) were detected:\n\n{0}' + msg = "The following failure(s) were detected:\n\n{0}" tracebacks.append(message) - raise type_(msg.format('\n\n'.join(tracebacks))) + raise type_(msg.format("\n\n".join(tracebacks))) def click_widget(self, text, type_=QtGui.QPushButton): """ Execute click on the widget of `type_` with `text`. @@ -278,13 +276,9 @@ # XXX asking for widget.text() causes occasional segfaults on Linux # and pyqt (both 4 and 5). Not sure why this is happening. # See issue #282 - return widget.text().replace('&', '') == text + return widget.text().replace("&", "") == text - widget = find_qt_widget( - control, - type_, - test=test - ) + widget = find_qt_widget(control, type_, test=test) if widget is None: # this will only occur if there is some problem with the test raise RuntimeError("Could not find matching child widget.") @@ -312,7 +306,7 @@ dialog = self.get_dialog_widget() if dialog is None: return False - if hasattr(dialog, '_ui'): + if hasattr(dialog, "_ui"): # This is a traitsui dialog, we need one more check. ui = dialog._ui return ui.info.initialized diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/testing.py python-pyface-7.4.0/pyface/ui/qt4/util/testing.py --- python-pyface-6.1.2/pyface/ui/qt4/util/testing.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/testing.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,16 @@ -# Copyright (c) 2013 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Tools for testing. """ -from __future__ import print_function + from contextlib import contextmanager import os import sys @@ -40,7 +41,7 @@ timer = QTimer() timer.setSingleShot(True) - timer.setInterval(timeout*1000) + timer.setInterval(round(timeout * 1000)) timer.timeout.connect(app.quit) widget.destroyed.connect(app.quit) @@ -51,7 +52,7 @@ if not timer.isActive(): # We exited the event loop on timeout. - msg = 'Could not destroy widget before timeout: {!r}' + msg = "Could not destroy widget before timeout: {!r}" raise AssertionError(msg.format(widget)) @@ -60,7 +61,7 @@ """ If 'stream' is None, provide a temporary handle to /dev/null. """ if stream is None: - out = open(os.devnull, 'w') + out = open(os.devnull, "w") try: yield out finally: @@ -105,7 +106,7 @@ level = level + 4 if level == 0: print() - print(' '*level, widget) + print(" " * level, widget) for child in widget.children(): print_qt_widget_tree(child, level=level) if level == 0: diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_datetime.py python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_datetime.py --- python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_datetime.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_datetime.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,41 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Utility functions for handling Qt dates and times. """ + +import datetime +import unittest + +from pyface.qt.QtCore import QTime + +from ..datetime import pytime_to_qtime, qtime_to_pytime + + +class TestTimeConversion(unittest.TestCase): + + def test_pytime_to_qtime(self): + pytime = datetime.time(9, 8, 7, 123456) + + qtime = pytime_to_qtime(pytime) + + self.assertEqual(qtime.hour(), 9) + self.assertEqual(qtime.minute(), 8) + self.assertEqual(qtime.second(), 7) + self.assertEqual(qtime.msec(), 123) + + def test_qtime_to_pytime(self): + qtime = QTime(9, 8, 7, 123) + + pytime = qtime_to_pytime(qtime) + + self.assertEqual(pytime.hour, 9) + self.assertEqual(pytime.minute, 8) + self.assertEqual(pytime.second, 7) + self.assertEqual(pytime.microsecond, 123000) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_event_loop_helper.py python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_event_loop_helper.py --- python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_event_loop_helper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_event_loop_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,28 @@ +# Copyright (c) 2005-2022, Enthought Inc. +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +import unittest + +from traits.api import HasTraits, provides + +from pyface.i_gui import IGUI +from pyface.ui.qt4.util.event_loop_helper import EventLoopHelper + + +@provides(IGUI) +class DummyGUI(HasTraits): + pass + + +class TestEventLoopHelper(unittest.TestCase): + + def test_gui_trait_expects_IGUI_interface(self): + # Trivial test where we simply set the trait + # and the test passes because no errors are raised. + event_loop_helper = EventLoopHelper() + event_loop_helper.gui = DummyGUI() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_gui_test_assistant.py python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_gui_test_assistant.py --- python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_gui_test_assistant.py 2019-07-20 10:50:48.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_gui_test_assistant.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,24 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import unittest -from pyface.ui.qt4.util.gui_test_assistant import \ - GuiTestAssistant +from pyface.timer.api import CallbackTimer +from pyface.ui.qt4.util.gui_test_assistant import GuiTestAssistant from traits.api import Event, HasStrictTraits class ExampleObject(HasStrictTraits): """ Test class; target for test_event_loop_until_traits_change. """ - spam = Event - eggs = Event + + spam = Event() + eggs = Event() class TestGuiTestAssistant(GuiTestAssistant, unittest.TestCase): @@ -17,7 +27,7 @@ obj = ExampleObject() # Successful case. - with self.event_loop_until_traits_change(obj, 'spam'): + with self.event_loop_until_traits_change(obj, "spam"): obj.spam = True def test_event_loop_until_traits_change_with_single_trait_failure(self): @@ -26,14 +36,13 @@ # Failing case. with self.assertRaises(AssertionError): - with self.event_loop_until_traits_change(obj, 'spam', - timeout=0.1): + with self.event_loop_until_traits_change(obj, "spam", timeout=0.1): obj.eggs = True def test_event_loop_until_traits_change_with_multiple_traits_success(self): # event_loop_until_traits_change calls self.fail on timeout. obj = ExampleObject() - with self.event_loop_until_traits_change(obj, 'spam', 'eggs'): + with self.event_loop_until_traits_change(obj, "spam", "eggs"): obj.spam = True obj.eggs = True @@ -41,14 +50,16 @@ # event_loop_until_traits_change calls self.fail on timeout. obj = ExampleObject() with self.assertRaises(AssertionError): - with self.event_loop_until_traits_change(obj, 'spam', 'eggs', - timeout=0.1): + with self.event_loop_until_traits_change( + obj, "spam", "eggs", timeout=0.1 + ): obj.eggs = True # event_loop_until_traits_change calls self.fail on timeout. with self.assertRaises(AssertionError): - with self.event_loop_until_traits_change(obj, 'spam', 'eggs', - timeout=0.1): + with self.event_loop_until_traits_change( + obj, "spam", "eggs", timeout=0.1 + ): obj.spam = True def test_event_loop_until_traits_change_with_no_traits_success(self): @@ -58,3 +69,28 @@ # Successful case. with self.event_loop_until_traits_change(obj): pass + + def test_assert_eventually_true_in_gui_success(self): + my_list = [] + + timer = CallbackTimer( + interval=0.05, callback=my_list.append, args=("bob",), repeat=1 + ) + + timer.start() + try: + self.assertEventuallyTrueInGui(lambda: len(my_list) > 0) + self.assertEqual(my_list, ["bob"]) + finally: + timer.stop() + + def test_assert_eventually_true_in_gui_already_true(self): + my_list = ["bob"] + self.assertEventuallyTrueInGui(lambda: len(my_list) > 0) + + def test_assert_eventually_true_in_gui_failure(self): + my_list = [] + with self.assertRaises(AssertionError): + self.assertEventuallyTrueInGui( + lambda: len(my_list) > 0, timeout=0.1 + ) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_image_helpers.py python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_image_helpers.py --- python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_image_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_image_helpers.py 2022-02-01 15:52:57.000000000 +0000 @@ -0,0 +1,220 @@ +# Copyright (c) 2005-2022, Enthought Inc. +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! + +import unittest +import sys + +from traits.testing.optional_dependencies import numpy as np, requires_numpy + +from pyface.qt import qt_api +from pyface.qt.QtGui import QColor, QIcon, QImage, QPixmap + +from ..image_helpers import ( + bitmap_to_icon, bitmap_to_image, image_to_array, image_to_bitmap, + array_to_image, AspectRatio, ScaleMode, resize_image, resize_bitmap, +) + + +class TestImageHelpers(unittest.TestCase): + + def test_image_to_bitmap(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + qpixmap = image_to_bitmap(qimage) + + self.assertIsInstance(qpixmap, QPixmap) + + def test_bitmap_to_image(self): + qpixmap = QPixmap(32, 64) + qpixmap.fill(QColor(0x44, 0x88, 0xcc)) + + qimage = bitmap_to_image(qpixmap) + + self.assertIsInstance(qimage, QImage) + + def test_bitmap_to_icon(self): + qpixmap = QPixmap(32, 64) + qpixmap.fill(QColor(0x44, 0x88, 0xcc)) + + qimage = bitmap_to_icon(qpixmap) + + self.assertIsInstance(qimage, QIcon) + + def test_resize_image(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + qimage = resize_image(qimage, (128, 128)) + + self.assertIsInstance(qimage, QImage) + self.assertEqual(qimage.width(), 128) + self.assertEqual(qimage.height(), 128) + + def test_resize_image_smooth(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + qimage = resize_image(qimage, (128, 128), mode=ScaleMode.smooth) + + self.assertIsInstance(qimage, QImage) + self.assertEqual(qimage.width(), 128) + self.assertEqual(qimage.height(), 128) + + def test_resize_image_constrain(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + qimage = resize_image(qimage, (128, 128), AspectRatio.keep_constrain) + + self.assertIsInstance(qimage, QImage) + self.assertEqual(qimage.width(), 64) + self.assertEqual(qimage.height(), 128) + + def test_resize_image_expand(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + qimage = resize_image(qimage, (128, 128), AspectRatio.keep_expand) + + self.assertIsInstance(qimage, QImage) + self.assertEqual(qimage.width(), 128) + self.assertEqual(qimage.height(), 256) + + def test_resize_bitmap(self): + qpixmap = QPixmap(32, 64) + qpixmap.fill(QColor(0x44, 0x88, 0xcc)) + + qpixmap = resize_bitmap(qpixmap, (128, 128)) + + self.assertIsInstance(qpixmap, QPixmap) + self.assertEqual(qpixmap.width(), 128) + self.assertEqual(qpixmap.height(), 128) + + def test_resize_bitmap_smooth(self): + qpixmap = QPixmap(32, 64) + qpixmap.fill(QColor(0x44, 0x88, 0xcc)) + + qpixmap = resize_bitmap(qpixmap, (128, 128), mode=ScaleMode.smooth) + + self.assertIsInstance(qpixmap, QPixmap) + self.assertEqual(qpixmap.width(), 128) + self.assertEqual(qpixmap.height(), 128) + + def test_resize_bitmap_constrain(self): + qpixmap = QPixmap(32, 64) + qpixmap.fill(QColor(0x44, 0x88, 0xcc)) + + qpixmap = resize_bitmap(qpixmap, (128, 128), AspectRatio.keep_constrain) + + self.assertIsInstance(qpixmap, QPixmap) + self.assertEqual(qpixmap.width(), 64) + self.assertEqual(qpixmap.height(), 128) + + def test_resize_bitmap_expand(self): + qpixmap = QPixmap(32, 64) + qpixmap.fill(QColor(0x44, 0x88, 0xcc)) + + qpixmap = resize_bitmap(qpixmap, (128, 128), AspectRatio.keep_expand) + + self.assertIsInstance(qpixmap, QPixmap) + self.assertEqual(qpixmap.width(), 128) + self.assertEqual(qpixmap.height(), 256) + + +@requires_numpy +class TestArrayImageHelpers(unittest.TestCase): + + def test_image_to_array_rgb(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + array = image_to_array(qimage) + + self.assertEqual(array.shape, (64, 32, 4)) + self.assertEqual(array.dtype, np.dtype('uint8')) + self.assertTrue(np.all(array[:, :, 3] == 0xff)) + self.assertTrue(np.all(array[:, :, 0] == 0x44)) + self.assertTrue(np.all(array[:, :, 1] == 0x88)) + self.assertTrue(np.all(array[:, :, 2] == 0xcc)) + + def test_image_to_array_rgba(self): + qimage = QImage(32, 64, QImage.Format.Format_ARGB32) + qimage.fill(QColor(0x44, 0x88, 0xcc, 0xee)) + + array = image_to_array(qimage) + + self.assertEqual(array.shape, (64, 32, 4)) + self.assertEqual(array.dtype, np.dtype('uint8')) + self.assertTrue(np.all(array[:, :, 0] == 0x44)) + self.assertTrue(np.all(array[:, :, 1] == 0x88)) + self.assertTrue(np.all(array[:, :, 2] == 0xcc)) + self.assertTrue(np.all(array[:, :, 3] == 0xee)) + + def test_image_to_array_bad(self): + qimage = QImage(32, 64, QImage.Format.Format_RGB30) + qimage.fill(QColor(0x44, 0x88, 0xcc)) + + with self.assertRaises(ValueError): + image_to_array(qimage) + + @unittest.skipIf( + qt_api == 'pyside2' and sys.platform == 'linux', + "Pyside2 QImage.pixel returns signed integers on linux" + ) + def test_array_to_image_rgb(self): + array = np.empty((64, 32, 3), dtype='uint8') + array[:, :, 0] = 0x44 + array[:, :, 1] = 0x88 + array[:, :, 2] = 0xcc + + qimage = array_to_image(array) + + self.assertEqual(qimage.width(), 32) + self.assertEqual(qimage.height(), 64) + self.assertEqual(qimage.format(), QImage.Format.Format_RGB32) + self.assertTrue(all( + qimage.pixel(i, j) == 0xff4488cc + for i in range(32) for j in range(64) + )) + + @unittest.skipIf( + qt_api == 'pyside2' and sys.platform == 'linux', + "Pyside2 QImage.pixel returns signed integers on linux" + ) + def test_array_to_image_rgba(self): + array = np.empty((64, 32, 4), dtype='uint8') + array[:, :, 0] = 0x44 + array[:, :, 1] = 0x88 + array[:, :, 2] = 0xcc + array[:, :, 3] = 0xee + + qimage = array_to_image(array) + + self.assertEqual(qimage.width(), 32) + self.assertEqual(qimage.height(), 64) + self.assertEqual(qimage.format(), QImage.Format.Format_ARGB32) + self.assertTrue(all( + qimage.pixel(i, j) == 0xee4488cc + for i in range(32) for j in range(64) + )) + + def test_array_to_image_bad_channels(self): + array = np.empty((64, 32, 2), dtype='uint8') + array[:, :, 0] = 0x44 + array[:, :, 1] = 0x88 + + with self.assertRaises(ValueError): + array_to_image(array) + + def test_array_to_image_bad_ndim(self): + array = np.full((64, 32), 0x44, dtype='uint8') + + with self.assertRaises(ValueError): + array_to_image(array) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_modal_dialog_tester.py python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_modal_dialog_tester.py --- python-pyface-6.1.2/pyface/ui/qt4/util/tests/test_modal_dialog_tester.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/util/tests/test_modal_dialog_tester.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,21 @@ -# Copyright (c) 2013-2015 by Enthought, Inc., Austin, TX +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ Tests for the tabular editor tester. """ -from __future__ import absolute_import + import unittest -from six.moves import cStringIO as StringIO -import platform +from io import StringIO -from pyface.qt import QtGui +from pyface.qt import QtGui, is_qt6 from pyface.api import Dialog, MessageDialog, OK, CANCEL -from pyface.toolkit import toolkit_object from traits.api import HasStrictTraits from pyface.ui.qt4.util.testing import silence_output @@ -25,39 +24,32 @@ from pyface.util.testing import skip_if_no_traitsui -is_qt = toolkit_object.toolkit == 'qt4' -if is_qt: - from pyface.qt import qt_api -is_pyqt5 = (is_qt and qt_api == 'pyqt5') - - class MyClass(HasStrictTraits): - def default_traits_view(self): from traitsui.api import CancelButton, OKButton, View view = View( buttons=[OKButton, CancelButton], resizable=False, - title='My class dialog') + title="My class dialog", + ) return view def run(self): - ui = self.edit_traits(kind='livemodal') + ui = self.edit_traits(kind="livemodal") if ui.result: - return 'accepted' + return "accepted" else: - return 'rejected' + return "rejected" def do_not_show_dialog(self): return True -@unittest.skipIf(is_pyqt5, "ModalDialogTester not working on pyqt5. Issue #302") -class TestModalDialogTester(unittest.TestCase, GuiTestAssistant): +class TestModalDialogTester(GuiTestAssistant, unittest.TestCase): """ Tests for the modal dialog tester. """ - #### Tests ################################################################ + # Tests ---------------------------------------------------------------- def test_on_message_dialog(self): dialog = MessageDialog() @@ -83,13 +75,13 @@ # accept tester.open_and_run(when_opened=lambda x: x.close(accept=True)) self.assertTrue(tester.value_assigned()) - self.assertEqual(tester.result, 'accepted') + self.assertEqual(tester.result, "accepted") self.assertTrue(tester.dialog_was_opened) # reject tester.open_and_run(when_opened=lambda x: x.close()) self.assertTrue(tester.value_assigned()) - self.assertEqual(tester.result, 'rejected') + self.assertEqual(tester.result, "rejected") self.assertTrue(tester.dialog_was_opened) @skip_if_no_traitsui @@ -105,6 +97,7 @@ # but no dialog is opened self.assertFalse(tester.dialog_was_opened) + @unittest.skipIf(is_qt6, "TEMPORARY: getting tests to run on pyside6") def test_capture_errors_on_failure(self): dialog = MessageDialog() tester = ModalDialogTester(dialog.open) @@ -122,8 +115,9 @@ alt_stderr = StringIO with silence_output(err=alt_stderr): tester.open_and_run(when_opened=failure) - self.assertIn('raise self.failureException(msg)', alt_stderr) + self.assertIn("raise self.failureException(msg)", alt_stderr) + @unittest.skipIf(is_qt6, "TEMPORARY: getting tests to run on pyside6") def test_capture_errors_on_error(self): dialog = MessageDialog() tester = ModalDialogTester(dialog.open) @@ -141,7 +135,7 @@ alt_stderr = StringIO with silence_output(err=alt_stderr): tester.open_and_run(when_opened=raise_error) - self.assertIn('ZeroDivisionError', alt_stderr) + self.assertIn("ZeroDivisionError", alt_stderr) @unittest.skip("has_widget code not working as designed. Issue #282.") def test_has_widget(self): @@ -152,10 +146,10 @@ try: with tester.capture_error(): self.assertTrue( - tester.has_widget('OK', QtGui.QAbstractButton) + tester.has_widget("OK", QtGui.QAbstractButton) ) self.assertFalse( - tester.has_widget(text='I am a virtual button') + tester.has_widget(text="I am a virtual button") ) finally: tester.close() @@ -172,14 +166,10 @@ with tester.capture_error(): widget = tester.find_qt_widget( type_=QtGui.QAbstractButton, - test=lambda x: x.text() == 'OK' + test=lambda x: x.text() == "OK", ) self.assertIsInstance(widget, QtGui.QPushButton) finally: tester.close() tester.open_and_run(when_opened=check_and_close) - - -if __name__ == '__main__': - unittest.main() diff -Nru python-pyface-6.1.2/pyface/ui/qt4/widget.py python-pyface-7.4.0/pyface/ui/qt4/widget.py --- python-pyface-6.1.2/pyface/ui/qt4/widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,22 +1,20 @@ -# Copyright (c) 2007, Riverbank Computing Limited -# All rights reserved. -# Copyright (c) 2017, Enthought, Inc +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # -# This software is provided without warranty under the terms of the BSD license. -# However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Description: +# Thanks for using Enthought open source! -# Major package imports. -from pyface.qt import QtCore, QtGui +from pyface.qt import QtCore +from pyface.qt.QtCore import Qt -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, Instance, provides +from traits.api import Any, Bool, HasTraits, Instance, Str, provides -# Local imports. from pyface.i_widget import IWidget, MWidget @@ -29,10 +27,10 @@ # 'IWidget' interface ---------------------------------------------------- #: The toolkit specific control that represents the widget. - control = Any + control = Any() #: The control's optional parent control. - parent = Any + parent = Any() #: Whether or not the control is visible visible = Bool(True) @@ -40,6 +38,12 @@ #: Whether or not the control is enabled enabled = Bool(True) + #: A tooltip for the widget. + tooltip = Str() + + #: An optional context menu for the widget. + context_menu = Instance("pyface.action.menu_manager.MenuManager") + # Private interface ---------------------------------------------------- #: The event filter for the widget. @@ -73,14 +77,33 @@ if self.control is not None: self.control.setEnabled(enabled) + def focus(self): + """ Set the keyboard focus to this widget. + """ + if self.control is not None: + self.control.setFocus() + + def has_focus(self): + """ Does the widget currently have keyboard focus? + + Returns + ------- + focus_state : bool + Whether or not the widget has keyboard focus. + """ + return ( + self.control is not None + and self.control.hasFocus() + ) + def destroy(self): - self._remove_event_listeners() if self.control is not None: self.control.hide() self.control.deleteLater() - self.control = None + super().destroy() def _add_event_listeners(self): + super()._add_event_listeners() self.control.installEventFilter(self._event_filter) def _remove_event_listeners(self): @@ -88,6 +111,38 @@ if self.control is not None: self.control.removeEventFilter(self._event_filter) self._event_filter = None + super()._remove_event_listeners() + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _get_control_tooltip(self): + """ Toolkit specific method to get the control's tooltip. """ + return self.control.toolTip() + + def _set_control_tooltip(self, tooltip): + """ Toolkit specific method to set the control's tooltip. """ + self.control.setToolTip(tooltip) + + def _observe_control_context_menu(self, remove=False): + """ Toolkit specific method to change the control menu observer. """ + if remove: + self.control.setContextMenuPolicy(Qt.ContextMenuPolicy.DefaultContextMenu) + self.control.customContextMenuRequested.disconnect( + self._handle_control_context_menu + ) + else: + self.control.customContextMenuRequested.connect( + self._handle_control_context_menu + ) + self.control.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) + + def _handle_control_context_menu(self, pos): + """ Signal handler for displaying context menu. """ + if self.control is not None and self.context_menu is not None: + menu = self.context_menu.create_menu(self.control) + menu.show(pos.x(), pos.y()) # Trait change handlers -------------------------------------------------- @@ -106,6 +161,10 @@ class WidgetEventFilter(QtCore.QObject): """ An internal class that watches for certain events on behalf of the Widget instance. + + This filter watches for show and hide events to make sure that visible + state of the widget is the opposite of Qt's isHidden() state. This is + needed in case other code hides the toolkit widget """ def __init__(self, widget): @@ -122,7 +181,7 @@ event_type = event.type() - if event_type in {QtCore.QEvent.Show, QtCore.QEvent.Hide}: - widget.visible = widget.control.isVisible() + if event_type in {QtCore.QEvent.Type.Show, QtCore.QEvent.Type.Hide}: + widget.visible = not widget.control.isHidden() return False diff -Nru python-pyface-6.1.2/pyface/ui/qt4/window.py python-pyface-7.4.0/pyface/ui/qt4/window.py --- python-pyface-6.1.2/pyface/ui/qt4/window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/window.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,24 +1,30 @@ -# Copyright (c) 2007, Riverbank Computing Limited -# Copyright (c) 2017-18, Enthought, Inc. +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license. However, when used with the GPL version of PyQt the additional -# terms described in the PyQt GPL exception also apply +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Riverbank Computing Limited -# Author: Enhtought, Inc. -# Description: +# Thanks for using Enthought open source! + +import weakref -# Major package imports. from pyface.qt import QtCore, QtGui -# Enthought library imports. + from traits.api import ( - Enum, Event, Property, Tuple, Unicode, VetoableEvent, provides + Enum, + Event, + Property, + Tuple, + Str, + VetoableEvent, + provides, ) -# Local imports. + from pyface.i_window import IWindow, MWindow from pyface.key_pressed_event import KeyPressedEvent from .gui import GUI @@ -37,29 +43,29 @@ size = Property(Tuple) - size_state = Enum('normal', 'maximized') + size_state = Enum("normal", "maximized") - title = Unicode + title = Str() # Window Events ---------------------------------------------------------- #: The window has been opened. - opened = Event + opened = Event() #: The window is about to open. - opening = VetoableEvent + opening = VetoableEvent() #: The window has been activated. - activated = Event + activated = Event() #: The window has been closed. - closed = Event + closed = Event() #: The window is about to be closed. - closing = VetoableEvent + closing = VetoableEvent() #: The window has been deactivated. - deactivated = Event + deactivated = Event() # Private interface ------------------------------------------------------ @@ -91,7 +97,7 @@ control.resize(*self.size) if self.position != (-1, -1): control.move(*self.position) - if self.size_state != 'normal': + if self.size_state != "normal": self._size_state_changed(self.size_state) control.setWindowTitle(self.title) control.setEnabled(self.enabled) @@ -106,7 +112,6 @@ # ------------------------------------------------------------------------- def destroy(self): - self._remove_event_listeners() if self.control is not None: # Avoid problems with recursive calls. @@ -120,7 +125,7 @@ # hides it), but the close may trigger an application shutdown, # which can take a long time and may also attempt to recursively # destroy the window again. - super(Window, self).destroy() + super().destroy() control.close() # ------------------------------------------------------------------------- @@ -141,7 +146,7 @@ old = self._position self._position = position - self.trait_property_changed('position', old, position) + self.trait_property_changed("position", old, position) def _get_size(self): """ Property getter for size. """ @@ -157,20 +162,20 @@ old = self._size self._size = size - self.trait_property_changed('size', old, size) + self.trait_property_changed("size", old, size) def _size_state_changed(self, state): control = self.control if control is None: return # Nothing to do here - if state == 'maximized': + if state == "maximized": control.setWindowState( - control.windowState() | QtCore.Qt.WindowMaximized + control.windowState() | QtCore.Qt.WindowState.WindowMaximized ) - elif state == 'normal': + elif state == "normal": control.setWindowState( - control.windowState() & ~QtCore.Qt.WindowMaximized + control.windowState() & ~QtCore.Qt.WindowState.WindowMaximized ) def _title_changed(self, title): @@ -191,20 +196,22 @@ def __init__(self, window): """ Initialise the event filter. """ QtCore.QObject.__init__(self) - self._window = window + # use a weakref to fix finalization issues with circular references + # we don't want to be the last thing holding a reference to the window + self._window = weakref.ref(window) def eventFilter(self, obj, e): """ Adds any event listeners required by the window. """ - window = self._window + window = self._window() # Sanity check. - if obj is not window.control: + if window is None or obj is not window.control: return False typ = e.type() - if typ == QtCore.QEvent.Close: + if typ == QtCore.QEvent.Type.Close: # Do not destroy the window during its event handler. GUI.invoke_later(window.close) @@ -213,30 +220,30 @@ return True - if typ == QtCore.QEvent.WindowActivate: + if typ == QtCore.QEvent.Type.WindowActivate: window.activated = window - elif typ == QtCore.QEvent.WindowDeactivate: + elif typ == QtCore.QEvent.Type.WindowDeactivate: window.deactivated = window - elif typ in {QtCore.QEvent.Show, QtCore.QEvent.Hide}: + elif typ in {QtCore.QEvent.Type.Show, QtCore.QEvent.Type.Hide}: window.visible = window.control.isVisible() - elif typ == QtCore.QEvent.Resize: + elif typ == QtCore.QEvent.Type.Resize: # Get the new size and set the shadow trait without performing # notification. size = e.size() window._size = (size.width(), size.height()) - elif typ == QtCore.QEvent.Move: + elif typ == QtCore.QEvent.Type.Move: # Get the real position and set the trait without performing # notification. Don't use event.pos(), as this excludes the window # frame geometry. pos = window.control.pos() window._position = (pos.x(), pos.y()) - elif typ == QtCore.QEvent.KeyPress: - # Pyface doesn't seem to be Unicode aware. Only keep the key code + elif typ == QtCore.QEvent.Type.KeyPress: + # Pyface doesn't seem to be Str aware. Only keep the key code # if it corresponds to a single Latin1 character. kstr = e.text() try: @@ -246,22 +253,26 @@ mods = e.modifiers() window.key_pressed = KeyPressedEvent( - alt_down=((mods & - QtCore.Qt.AltModifier) == QtCore.Qt.AltModifier), - control_down=((mods & QtCore.Qt.ControlModifier - ) == QtCore.Qt.ControlModifier), - shift_down=((mods & QtCore.Qt.ShiftModifier - ) == QtCore.Qt.ShiftModifier), + alt_down=( + (mods & QtCore.Qt.KeyboardModifier.AltModifier) == QtCore.Qt.KeyboardModifier.AltModifier + ), + control_down=( + (mods & QtCore.Qt.KeyboardModifier.ControlModifier) + == QtCore.Qt.KeyboardModifier.ControlModifier + ), + shift_down=( + (mods & QtCore.Qt.KeyboardModifier.ShiftModifier) == QtCore.Qt.KeyboardModifier.ShiftModifier + ), key_code=kcode, - event=e + event=e, ) - elif typ == QtCore.QEvent.WindowStateChange: + elif typ == QtCore.QEvent.Type.WindowStateChange: # set the size_state of the window. state = obj.windowState() - if state & QtCore.Qt.WindowMaximized: - window.size_state = 'maximized' + if state & QtCore.Qt.WindowState.WindowMaximized: + window.size_state = "maximized" else: - window.size_state = 'normal' + window.size_state = "normal" return False diff -Nru python-pyface-6.1.2/pyface/ui/qt4/wizard/__init__.py python-pyface-7.4.0/pyface/ui/qt4/wizard/__init__.py --- python-pyface-6.1.2/pyface/ui/qt4/wizard/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/wizard/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/qt4/wizard/wizard_page.py python-pyface-7.4.0/pyface/ui/qt4/wizard/wizard_page.py --- python-pyface-6.1.2/pyface/ui/qt4/wizard/wizard_page.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/wizard/wizard_page.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,22 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2008, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2008 Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ + """ A page in a wizard. """ -# Major package imports. -from pyface.qt import QtCore, QtGui +from pyface.qt import QtGui + -# Enthought library imports. -from traits.api import Bool, HasTraits, provides, Str, Tuple, Unicode +from traits.api import Bool, HasTraits, provides, Str, Tuple from pyface.wizard.i_wizard_page import IWizardPage, MWizardPage @@ -28,26 +32,25 @@ """ + # 'IWizardPage' interface ---------------------------------------------# - #### 'IWizardPage' interface ############################################## + id = Str() - id = Str - - next_id = Str + next_id = Str() last_page = Bool(False) complete = Bool(False) - heading = Unicode + heading = Str() - subheading = Unicode + subheading = Str() - size = Tuple + size = Tuple() - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_page(self, parent): """ Creates the wizard page. """ @@ -85,9 +88,9 @@ return content - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_page_content(self, parent): """ Creates the actual page content. """ @@ -96,7 +99,7 @@ control = QtGui.QWidget(parent) palette = control.palette() - palette.setColor(QtGui.QPalette.Window, QtGui.QColor('yellow')) + palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("yellow")) control.setPalette(palette) control.setAutoFillBackground(True) @@ -114,7 +117,7 @@ self.pyface_wizard = None - page.on_trait_change(self._on_complete_changed, 'complete') + page.observe(self._on_complete_changed, "complete") self._page = page def initializePage(self): @@ -134,9 +137,7 @@ return self._page.complete - def _on_complete_changed(self): + def _on_complete_changed(self, event): """ The trait handler for when the page's completion state changes. """ self.completeChanged.emit() - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/wizard/wizard.py python-pyface-7.4.0/pyface/ui/qt4/wizard/wizard.py --- python-pyface-6.1.2/pyface/ui/qt4/wizard/wizard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/wizard/wizard.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,23 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2008, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2008 Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ + """ The base class for all pyface wizards. """ -# Major package imports. -from __future__ import print_function from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Bool, Instance, List, Property, provides, Unicode + +from traits.api import Bool, Instance, List, Property, provides, Str from pyface.api import Dialog from pyface.wizard.i_wizard import IWizard, MWizard from pyface.wizard.i_wizard_controller import IWizardController @@ -32,8 +35,7 @@ """ - - #### 'IWizard' interface ################################################## + # 'IWizard' interface -------------------------------------------------# pages = Property(List(IWizardPage)) @@ -41,13 +43,13 @@ show_cancel = Bool(True) - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------# - title = Unicode('Wizard') + title = Str("Wizard") - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizard' interface. - ########################################################################### + # ------------------------------------------------------------------------ # Override MWizard implementation to do nothing. We still call these methods # because it expected by IWizard, and users may wish to hook in custom code @@ -59,26 +61,28 @@ def previous(self): pass - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): pass - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): control = _Wizard(parent, self) - control.setOptions(QtGui.QWizard.NoDefaultButton | - QtGui.QWizard.NoBackButtonOnStartPage) - control.setModal(self.style == 'modal') + control.setOptions( + QtGui.QWizard.WizardOption.NoDefaultButton + | QtGui.QWizard.WizardOption.NoBackButtonOnStartPage + ) + control.setModal(self.style == "modal") control.setWindowTitle(self.title) # Necessary for 'nonmodal'. See Dialog for more info. - if self.style == 'nonmodal': + if self.style == "nonmodal": control.finished.connect(self._finished_fired) if self.size != (-1, -1): @@ -87,10 +91,10 @@ control.resize(size) if not self.show_cancel: - control.setOption(QtGui.QWizard.NoCancelButton) + control.setOption(QtGui.QWizard.WizardOption.NoCancelButton) if self.help_id: - control.setOption(QtGui.QWizard.HaveHelpButton) + control.setOption(QtGui.QWizard.WizardOption.HaveHelpButton) control.helpRequested.connect(self._help_requested) # Add the initial pages. @@ -103,9 +107,9 @@ return control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _help_requested(self): """ Called when the 'Help' button is pressed. """ @@ -113,7 +117,7 @@ # FIXME: Hook into a help system. print("Show help for", self.help_id) - #### Trait handlers ####################################################### + # Trait handlers ------------------------------------------------------- def _get_pages(self): """ The pages getter. """ @@ -216,5 +220,3 @@ id = -1 return id - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/workbench/editor.py python-pyface-7.4.0/pyface/ui/qt4/workbench/editor.py --- python-pyface-6.1.2/pyface/ui/qt4/workbench/editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/workbench/editor.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,17 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Local imports. from traits.api import Event, Bool from pyface.workbench.i_editor import MEditor @@ -27,9 +27,9 @@ _loading = Event(Bool) _loading_on_open = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_control(self, parent): """ Create the toolkit-specific control that represents the part. """ @@ -40,7 +40,7 @@ control = QtGui.QWidget(parent) pal = control.palette() - pal.setColour(QtGui.QPalette.Window, QtCore.Qt.yellow) + pal.setColour(QtGui.QPalette.ColorRole.Window, QtCore.Qt.GlobalColor.yellow) control.setPalette(pal) control.setAutoFillBackground(True) @@ -66,8 +66,6 @@ _control.close() _control.deleteLater() - return - def set_focus(self): """ Set the focus to the appropriate control in the part. """ @@ -75,5 +73,3 @@ self.control.setFocus() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/workbench/__init__.py python-pyface-7.4.0/pyface/ui/qt4/workbench/__init__.py --- python-pyface-6.1.2/pyface/ui/qt4/workbench/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/workbench/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,2 +1,9 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/qt4/workbench/split_tab_widget.py python-pyface-7.4.0/pyface/ui/qt4/workbench/split_tab_widget.py --- python-pyface-6.1.2/pyface/ui/qt4/workbench/split_tab_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/workbench/split_tab_widget.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,17 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2008, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2008 Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + -# Standard library imports. import sys -import six -# Major library imports. + from pyface.qt import QtCore, QtGui, qt_api from pyface.image_resource import ImageResource @@ -33,7 +40,7 @@ # The different hotspots of a QTabWidget. An non-negative value is a tab # index and the hotspot is to the left of it. - tabTextChanged = QtCore.Signal(QtGui.QWidget, six.text_type) + tabTextChanged = QtCore.Signal(QtGui.QWidget, str) _HS_NONE = -1 _HS_AFTER_LAST_TAB = -2 _HS_NORTH = -3 @@ -93,8 +100,8 @@ for t in range(ch.count()): # A tab state is a tuple of the widget's object name and # the title. - name = six.text_type(ch.widget(t).objectName()) - title = six.text_type(ch.tabText(t)) + name = str(ch.widget(t).objectName()) + title = str(ch.tabText(t)) tab_states.append((name, title)) @@ -178,7 +185,7 @@ ch = _TabWidget(self) self.addWidget(ch) - idx = ch.insertTab(self._current_tab_idx+1, w, text) + idx = ch.insertTab(self._current_tab_idx + 1, w, text) # If the tab has been added to the current tab widget then make it the # current tab. @@ -273,11 +280,13 @@ nfw = fw.nextInFocusChain() while nfw is not fw: - if nfw.focusPolicy() & QtCore.Qt.TabFocus and \ - nfw.focusProxy() is None and \ - nfw.isVisibleTo(w) and \ - nfw.isEnabled() and \ - w.isAncestorOf(nfw): + if ( + nfw.focusPolicy() & QtCore.Qt.FocusPolicy.TabFocus + and nfw.focusProxy() is None + and nfw.isVisibleTo(w) + and nfw.isEnabled() + and w.isAncestorOf(nfw) + ): break nfw = nfw.nextInFocusChain() @@ -293,8 +302,9 @@ # It is possible for the C++ layer of this object to be deleted between # the time when the focus change signal is emitted and time when the # slots are dispatched by the Qt event loop. This may be a bug in PyQt4. - if qt_api == 'pyqt': + if qt_api == "pyqt": import sip + if sip.isdeleted(self): return @@ -394,8 +404,9 @@ self._rband.deleteLater() position = QtCore.QPoint(*hs_geom[0:2]) window = tw.window() - self._rband = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, - window) + self._rband = QtGui.QRubberBand( + QtGui.QRubberBand.Shape.Rectangle, window + ) self._rband.move(window.mapFromGlobal(position)) self._rband.resize(*hs_geom[2:4]) self._rband.show() @@ -423,8 +434,8 @@ if dhs == self._HS_OUTSIDE: # Disable tab tear-out for now. It works, but this is something that # should be turned on manually. We need an interface for this. - #ticon, ttext, ttextcolor, tbuttn, twidg = self._remove_tab(stab_w, stab) - #self.new_window_request.emit(pos, twidg) + # ticon, ttext, ttextcolor, tbuttn, twidg = self._remove_tab(stab_w, stab) + # self.new_window_request.emit(pos, twidg) return # See if the tab is being moved to an existing tab widget. @@ -434,12 +445,17 @@ if stab == dhs: return - if dhs == self._HS_AFTER_LAST_TAB and stab == stab_w.count()-1: + if ( + dhs == self._HS_AFTER_LAST_TAB + and stab == stab_w.count() - 1 + ): return QtGui.QApplication.instance().blockSignals(True) - ticon, ttext, ttextcolor, tbuttn, twidg = self._remove_tab(stab_w, stab) + ticon, ttext, ttextcolor, tbuttn, twidg = self._remove_tab( + stab_w, stab + ) if dhs == self._HS_AFTER_LAST_TAB: idx = dtab_w.addTab(twidg, ticon, ttext) @@ -458,7 +474,7 @@ idx = dtab_w.insertTab(dhs, twidg, ticon, ttext) dtab_w.tabBar().setTabTextColor(idx, ttextcolor) - if (tbuttn): + if tbuttn: dtab_w.show_button(idx) dsplit_w._set_current_tab(dtab_w, idx) @@ -471,7 +487,9 @@ # Remove the tab from its current tab widget and create a new one # for it. - ticon, ttext, ttextcolor, tbuttn, twidg = self._remove_tab(stab_w, stab) + ticon, ttext, ttextcolor, tbuttn, twidg = self._remove_tab( + stab_w, stab + ) new_tw = _TabWidget(dsplit_w) idx = new_tw.addTab(twidg, ticon, ttext) new_tw.tabBar().setTabTextColor(0, ttextcolor) @@ -483,7 +501,9 @@ dspl_idx = dspl.indexOf(dtab_w) if dhs in (self._HS_NORTH, self._HS_SOUTH): - dspl, dspl_idx = dsplit_w._horizontal_split(dspl, dspl_idx, dhs) + dspl, dspl_idx = dsplit_w._horizontal_split( + dspl, dspl_idx, dhs + ) else: dspl, dspl_idx = dsplit_w._vertical_split(dspl, dspl_idx, dhs) @@ -505,18 +525,18 @@ should be put. """ - if spl.orientation() == QtCore.Qt.Vertical: + if spl.orientation() == QtCore.Qt.Orientation.Vertical: if hs == self._HS_SOUTH: idx += 1 elif spl is self and spl.count() == 1: # The splitter is the root and only has one child so we can just # change its orientation. - spl.setOrientation(QtCore.Qt.Vertical) + spl.setOrientation(QtCore.Qt.Orientation.Vertical) if hs == self._HS_SOUTH: idx = -1 else: - new_spl = QtGui.QSplitter(QtCore.Qt.Vertical) + new_spl = QtGui.QSplitter(QtCore.Qt.Orientation.Vertical) new_spl.addWidget(spl.widget(idx)) spl.insertWidget(idx, new_spl) @@ -534,18 +554,18 @@ should be put. """ - if spl.orientation() == QtCore.Qt.Horizontal: + if spl.orientation() == QtCore.Qt.Orientation.Horizontal: if hs == self._HS_EAST: idx += 1 elif spl is self and spl.count() == 1: # The splitter is the root and only has one child so we can just # change its orientation. - spl.setOrientation(QtCore.Qt.Horizontal) + spl.setOrientation(QtCore.Qt.Orientation.Horizontal) if hs == self._HS_EAST: idx = -1 else: - new_spl = QtGui.QSplitter(QtCore.Qt.Horizontal) + new_spl = QtGui.QSplitter(QtCore.Qt.Orientation.Horizontal) new_spl.addWidget(spl.widget(idx)) spl.insertWidget(idx, new_spl) @@ -566,7 +586,7 @@ icon = tab_w.tabIcon(tab) text = tab_w.tabText(tab) text_color = tab_w.tabBar().tabTextColor(tab) - button = tab_w.tabBar().tabButton(tab, QtGui.QTabBar.LeftSide) + button = tab_w.tabBar().tabButton(tab, QtGui.QTabBar.ButtonPosition.LeftSide) w = tab_w.widget(tab) tab_w.removeTab(tab) @@ -593,11 +613,14 @@ for split_widget in top_widget.findChildren(SplitTabWidget, None): visible_region = split_widget.visibleRegion() widget_pos = split_widget.mapFromGlobal(global_pos) - if cloned_rect and split_widget.geometry().contains(widget_pos): + if cloned_rect and split_widget.geometry().contains( + widget_pos + ): visible_rect = visible_region.boundingRect() widget_rect = QtCore.QRect( split_widget.mapFromGlobal(cloned_rect.topLeft()), - split_widget.mapFromGlobal(cloned_rect.bottomRight())) + split_widget.mapFromGlobal(cloned_rect.bottomRight()), + ) if not visible_rect.intersected(widget_rect).isEmpty(): break elif visible_region.contains(widget_pos): @@ -649,21 +672,31 @@ return (tw, self._HS_NORTH, (gx, gy, w, h / 4)) if y >= (3 * h) / 4: - return (tw, self._HS_SOUTH, (gx, gy + (3*h) / 4, w, h / 4)) + return ( + tw, + self._HS_SOUTH, + (gx, gy + (3 * h) / 4, w, h / 4), + ) if x < w / 4: return (tw, self._HS_WEST, (gx, gy, w / 4, h)) if x >= (3 * w) / 4: - return (tw, self._HS_EAST, (gx + (3*w) / 4, gy, w / 4, h)) + return ( + tw, + self._HS_EAST, + (gx + (3 * w) / 4, gy, w / 4, h), + ) return miss # See if the hotspot is in the tab area. tpos = tw.mapFrom(split_widget, pos) tab_bar = tw.tabBar() - top_bottom = tw.tabPosition() in (QtGui.QTabWidget.North, - QtGui.QTabWidget.South) + top_bottom = tw.tabPosition() in ( + QtGui.QTabWidget.TabPosition.North, + QtGui.QTabWidget.TabPosition.South, + ) for i in range(tw.count()): rect = tab_bar.tabRect(i) @@ -708,13 +741,17 @@ w = rect.width() h = rect.height() if top_bottom: - tab_widths = sum(tab_bar.tabRect(i).width() - for i in range(tab_bar.count())) + tab_widths = sum( + tab_bar.tabRect(i).width() + for i in range(tab_bar.count()) + ) w -= tab_widths gx += tab_widths else: - tab_heights = sum(tab_bar.tabRect(i).height() - for i in range(tab_bar.count())) + tab_heights = sum( + tab_bar.tabRect(i).height() + for i in range(tab_bar.count()) + ) h -= tab_heights gy -= tab_heights return (tw, self._HS_AFTER_LAST_TAB, (gx, gy, w, h)) @@ -732,6 +769,7 @@ } """ + class _TabWidget(QtGui.QTabWidget): """ The _TabWidget class is a QTabWidget with a dragable tab bar. """ @@ -746,9 +784,9 @@ QtGui.QTabWidget.__init__(self, *args) # XXX this requires Qt > 4.5 - if sys.platform == 'darwin': + if sys.platform == "darwin": self.setDocumentMode(True) - #self.setStyleSheet(inactive_style) + # self.setStyleSheet(inactive_style) self._root = root @@ -760,22 +798,24 @@ self.tabCloseRequested.connect(self._close_tab) if not (_TabWidget._spinner_data): - _TabWidget._spinner_data = ImageResource('spinner.gif') + _TabWidget._spinner_data = ImageResource("spinner.gif") def show_button(self, index): lbl = QtGui.QLabel(self) - movie = QtGui.QMovie(_TabWidget._spinner_data.absolute_path, parent=lbl) - movie.setCacheMode(QtGui.QMovie.CacheAll) + movie = QtGui.QMovie( + _TabWidget._spinner_data.absolute_path, parent=lbl + ) + movie.setCacheMode(QtGui.QMovie.CacheMode.CacheAll) movie.setScaledSize(QtCore.QSize(16, 16)) lbl.setMovie(movie) movie.start() - self.tabBar().setTabButton(index, QtGui.QTabBar.LeftSide, lbl) + self.tabBar().setTabButton(index, QtGui.QTabBar.ButtonPosition.LeftSide, lbl) def hide_button(self, index): - curr = self.tabBar().tabButton(index, QtGui.QTabBar.LeftSide) + curr = self.tabBar().tabButton(index, QtGui.QTabBar.ButtonPosition.LeftSide) if curr: curr.close() - self.tabBar().setTabButton(index, QtGui.QTabBar.LeftSide, None) + self.tabBar().setTabButton(index, QtGui.QTabBar.ButtonPosition.LeftSide, None) def active_icon(self): """ Return the QIcon to be used to indicate an active tab page. """ @@ -805,8 +845,8 @@ # Draw the circle. p.setBrush(rg) - p.setPen(QtCore.Qt.NoPen) - p.setRenderHint(QtGui.QPainter.Antialiasing) + p.setPen(QtCore.Qt.PenStyle.NoPen) + p.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing) p.drawEllipse(0, 0, width, height) p.end() @@ -840,7 +880,10 @@ self._still_needed() - if self._root._current_tab_w is self and self._root._current_tab_idx == idx: + if ( + self._root._current_tab_w is self + and self._root._current_tab_idx == idx + ): self._root._current_tab_w = None def _close_tab(self, index): @@ -848,12 +891,14 @@ self._root._close_tab_request(self.widget(index)) + class _IndependentLineEdit(QtGui.QLineEdit): def keyPressEvent(self, e): QtGui.QLineEdit.keyPressEvent(self, e) - if (e.key() == QtCore.Qt.Key_Escape): + if e.key() == QtCore.Qt.Key.Key_Escape: self.hide() + class _DragableTabBar(QtGui.QTabBar): """ The _DragableTabBar class is a QTabBar that can be dragged around. """ @@ -863,7 +908,7 @@ QtGui.QTabBar.__init__(self, parent) # XXX this requires Qt > 4.5 - if sys.platform == 'darwin': + if sys.platform == "darwin": self.setDocumentMode(True) self._root = root @@ -884,9 +929,9 @@ def keyPressEvent(self, e): """ Reimplemented to handle traversal across different tab widgets. """ - if e.key() == QtCore.Qt.Key_Left: + if e.key() == QtCore.Qt.Key.Key_Left: self._root._move_left(self.parent(), self.currentIndex()) - elif e.key() == QtCore.Qt.Key_Right: + elif e.key() == QtCore.Qt.Key.Key_Right: self._root._move_right(self.parent(), self.currentIndex()) else: e.ignore() @@ -914,7 +959,7 @@ self._root._set_current_tab(self.parent(), self.currentIndex()) self._root._set_focus() - if e.button() != QtCore.Qt.LeftButton: + if e.button() != QtCore.Qt.MouseButton.LeftButton: return if self._drag_state is not None: @@ -945,15 +990,15 @@ # If the mouse has moved far enough that dragging has started then # tell the user. if self._drag_state.dragging: - QtGui.QApplication.setOverrideCursor(QtCore.Qt.OpenHandCursor) + QtGui.QApplication.setOverrideCursor(QtCore.Qt.CursorShape.OpenHandCursor) def mouseReleaseEvent(self, e): """ Reimplemented to handle mouse release events. """ QtGui.QTabBar.mouseReleaseEvent(self, e) - if e.button() != QtCore.Qt.LeftButton: - if e.button() == QtCore.Qt.MidButton: + if e.button() != QtCore.Qt.MouseButton.LeftButton: + if e.button() == QtCore.Qt.MidddleButton: self.tabCloseRequested.emit(self.tabAt(e.pos())) return @@ -975,15 +1020,15 @@ def _setCurrentTabText(self): idx = self.currentIndex() text = self._title_edit.text() - self.setTabText(idx, u'\u25b6'+text) + self.setTabText(idx, "\u25b6" + text) self._root.tabTextChanged.emit(self.parent().widget(idx), text) def _resize_title_edit_to_current_tab(self): idx = self.currentIndex() tab = QtGui.QStyleOptionTabV3() self.initStyleOption(tab, idx) - rect = self.style().subElementRect(QtGui.QStyle.SE_TabBarTabText, tab) - self._title_edit.setGeometry(rect.adjusted(0,8,0,-8)) + rect = self.style().subElementRect(QtGui.QStyle.SubElement.SE_TabBarTabText, tab) + self._title_edit.setGeometry(rect.adjusted(0, 8, 0, -8)) class _DragState(object): @@ -1003,7 +1048,9 @@ def start_dragging(self, pos): """ Start dragging a tab. """ - if (pos - self._start_pos).manhattanLength() <= QtGui.QApplication.startDragDistance(): + if ( + pos - self._start_pos + ).manhattanLength() <= QtGui.QApplication.startDragDistance(): return self.dragging = True @@ -1013,13 +1060,15 @@ tab = self._tab ctb = self._clone = QtGui.QTabBar() - if sys.platform == 'darwin' and QtCore.QT_VERSION >= 0x40500: + if sys.platform == "darwin" and QtCore.QT_VERSION >= 0x40500: ctb.setDocumentMode(True) - ctb.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents) - ctb.setWindowFlags(QtCore.Qt.FramelessWindowHint | - QtCore.Qt.Tool | - QtCore.Qt.X11BypassWindowManagerHint) + ctb.setAttribute(QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents) + ctb.setWindowFlags( + QtCore.Qt.WindowType.FramelessWindowHint + | QtCore.Qt.WindowType.Tool + | QtCore.Qt.WindowType.X11BypassWindowManagerHint + ) ctb.setWindowOpacity(0.5) ctb.setElideMode(otb.elideMode()) ctb.setShape(otb.shape()) @@ -1044,8 +1093,9 @@ """ Handle the movement of the cloned tab during dragging. """ self._clone.move(self._tab_bar.mapToGlobal(pos) + self._clone_offset) - self._root._select(self._tab_bar.mapTo(self._root, - pos + self._centre_offset)) + self._root._select( + self._tab_bar.mapTo(self._root, pos + self._centre_offset) + ) def drop(self, pos): """ Handle the drop of the cloned tab. """ diff -Nru python-pyface-6.1.2/pyface/ui/qt4/workbench/tests/test_workbench_window_layout.py python-pyface-7.4.0/pyface/ui/qt4/workbench/tests/test_workbench_window_layout.py --- python-pyface-6.1.2/pyface/ui/qt4/workbench/tests/test_workbench_window_layout.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/workbench/tests/test_workbench_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,45 @@ -from __future__ import absolute_import -import mock +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +import sys import unittest +from unittest import mock from pyface.ui.qt4.workbench.split_tab_widget import SplitTabWidget -from pyface.ui.qt4.workbench.workbench_window_layout import \ - WorkbenchWindowLayout +from pyface.ui.qt4.workbench.workbench_window_layout import ( + WorkbenchWindowLayout, +) class TestWorkbenchWindowLayout(unittest.TestCase): + + @unittest.skipIf(sys.version_info == (3, 8), "Can't mock SplitTabWidget") def test_change_of_active_qt_editor(self): # Test error condition for enthought/mayavi#321 + # This doesn't work on Python 3.8 because of some incompatibility + # between unittest.mock.Mock and Qt, I think mock_split_tab_widget = mock.Mock(spec=SplitTabWidget) layout = WorkbenchWindowLayout(_qt4_editor_area=mock_split_tab_widget) + class DummyEvent: + def __init__(self, new): + self.new = new + # This should not throw - layout._qt4_active_editor_changed(None, None) + layout._qt4_active_editor_changed(DummyEvent(new=None)) self.assertEqual(mock_split_tab_widget.setTabTextColor.called, False) mock_active_editor = mock.Mock() - layout._qt4_active_editor_changed(None, mock_active_editor) + layout._qt4_active_editor_changed(mock_active_editor) self.assertEqual(mock_split_tab_widget.setTabTextColor.called, True) diff -Nru python-pyface-6.1.2/pyface/ui/qt4/workbench/view.py python-pyface-7.4.0/pyface/ui/qt4/workbench/view.py --- python-pyface-6.1.2/pyface/ui/qt4/workbench/view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/workbench/view.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,17 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2007 Riverbank Computing Limited # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Enthought library imports. from pyface.workbench.i_view import MView @@ -22,9 +22,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_control(self, parent): """ Create the toolkit-specific control that represents the part. """ @@ -34,7 +34,7 @@ control = QtGui.QWidget(parent) palette = control.palette() - palette.setColor(QtGui.QPalette.Window, QtGui.QColor('red')) + palette.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor("red")) control.setPalette(palette) control.setAutoFillBackground(True) @@ -48,8 +48,6 @@ self.control.deleteLater() self.control = None - return - def set_focus(self): """ Set the focus to the appropriate control in the part. """ @@ -57,5 +55,3 @@ self.control.setFocus() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/qt4/workbench/workbench_window_layout.py python-pyface-7.4.0/pyface/ui/qt4/workbench/workbench_window_layout.py --- python-pyface-6.1.2/pyface/ui/qt4/workbench/workbench_window_layout.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/qt4/workbench/workbench_window_layout.py 2022-02-01 15:52:57.000000000 +0000 @@ -1,31 +1,31 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2008, Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +# (C) Copyright 2008 Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD license. # However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply -# -# Author: Riverbank Computing Limited -# Description: -#------------------------------------------------------------------------------ - -# Standard library imports. import logging -# Major package imports. + from pyface.qt import QtCore, QtGui -# Enthought library imports. -from traits.api import Instance, on_trait_change -# Local imports. +from traits.api import Instance, observe + + from pyface.message_dialog import error -from pyface.workbench.i_workbench_window_layout import \ - MWorkbenchWindowLayout +from pyface.workbench.i_workbench_window_layout import MWorkbenchWindowLayout from .split_tab_widget import SplitTabWidget -import six # Logging. @@ -34,18 +34,18 @@ # For mapping positions relative to the editor area. _EDIT_AREA_MAP = { - 'left': QtCore.Qt.LeftDockWidgetArea, - 'right': QtCore.Qt.RightDockWidgetArea, - 'top': QtCore.Qt.TopDockWidgetArea, - 'bottom': QtCore.Qt.BottomDockWidgetArea + "left": QtCore.Qt.DockWidgetArea.LeftDockWidgetArea, + "right": QtCore.Qt.DockWidgetArea.RightDockWidgetArea, + "top": QtCore.Qt.DockWidgetArea.TopDockWidgetArea, + "bottom": QtCore.Qt.DockWidgetArea.BottomDockWidgetArea, } # For mapping positions relative to another view. _VIEW_AREA_MAP = { - 'left': (QtCore.Qt.Horizontal, True), - 'right': (QtCore.Qt.Horizontal, False), - 'top': (QtCore.Qt.Vertical, True), - 'bottom': (QtCore.Qt.Vertical, False) + "left": (QtCore.Qt.Orientation.Horizontal, True), + "right": (QtCore.Qt.Orientation.Horizontal, False), + "top": (QtCore.Qt.Orientation.Vertical, True), + "bottom": (QtCore.Qt.Orientation.Vertical, False), } @@ -56,16 +56,16 @@ """ - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # The widget that provides the editor area. We keep (and use) this # separate reference because we can't always assume that it has been set to # be the main window's central widget. _qt4_editor_area = Instance(SplitTabWidget) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): if editor.control is not None: @@ -87,12 +87,14 @@ return None try: - self._qt4_editor_area.addTab(self._qt4_get_editor_control(editor), title) + self._qt4_editor_area.addTab( + self._qt4_get_editor_control(editor), title + ) if editor._loading_on_open: - self._qt4_editor_tab_spinner(editor, '', True) + self._qt4_editor_tab_spinner(editor, "", True) except Exception: - logger.exception('error creating editor control [%s]', editor.id) + logger.exception("error creating editor control [%s]", editor.id) return editor @@ -104,7 +106,7 @@ self._qt4_add_view(view, position, relative_to, size) view.visible = True except Exception: - logger.exception('error creating view control [%s]', view.id) + logger.exception("error creating view control [%s]", view.id) # Even though we caught the exception, it sometimes happens that # the view's control has been created as a child of the application @@ -113,8 +115,11 @@ view.destroy_control() # Additionally, display an error message to the user. - error(self.window.control, 'Unable to add view [%s]' % view.id, - 'Workbench Plugin Error') + error( + self.window.control, + "Unable to add view [%s]" % view.id, + "Workbench Plugin Error", + ) return view @@ -131,7 +136,9 @@ def close(self): # Don't fire signals for editors that have destroyed their controls. - self._qt4_editor_area.editor_has_focus.disconnect(self._qt4_editor_focus) + self._qt4_editor_area.editor_has_focus.disconnect( + self._qt4_editor_focus + ) self._qt4_editor_area.clear() @@ -158,7 +165,7 @@ return editor_area def contains_view(self, view): - return hasattr(view, '_qt4_dock') + return hasattr(view, "_qt4_dock") def hide_editor_area(self): self._qt4_editor_area.hide() @@ -188,7 +195,7 @@ view._qt4_dock.show() view.visible = True - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_view_memento(self): # Get the IDs of the views in the main window. This information is @@ -229,14 +236,14 @@ self._qt4_create_view_dock_widget(v).setVisible(False) if v in dock_views: - delattr(v, '_qt4_gone') + delattr(v, "_qt4_gone") break # Remove any remain unused dock widgets. for v in dock_views: try: - delattr(v, '_qt4_gone') + delattr(v, "_qt4_gone") except AttributeError: pass else: @@ -273,7 +280,8 @@ # Create the restored editor. editor = self.window.editor_manager.set_editor_memento( - editor_memento) + editor_memento + ) if editor is None: return None @@ -286,54 +294,63 @@ self._qt4_editor_area.restoreState(editor_layout, resolve_id) def get_toolkit_memento(self): - return (0, {'geometry' : self.window.control.saveGeometry()}) + return (0, {"geometry": self.window.control.saveGeometry()}) def set_toolkit_memento(self, memento): - if hasattr(memento, 'toolkit_data'): + if hasattr(memento, "toolkit_data"): data = memento.toolkit_data if isinstance(data, tuple) and len(data) == 2: version, datadict = data if version == 0: - geometry = datadict.pop('geometry', None) + geometry = datadict.pop("geometry", None) if geometry is not None: self.window.control.restoreGeometry(geometry) def is_editor_area_visible(self): return self._qt4_editor_area.isVisible() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _qt4_editor_focus(self, new): """ Handle an editor getting the focus. """ for editor in self.window.editors: control = editor.control - editor.has_focus = control is new or \ - (control is not None and new in control.children()) + editor.has_focus = control is new or ( + control is not None and new in control.children() + ) def _qt4_editor_title_changed(self, control, title): """ Handle the title being changed """ for editor in self.window.editors: - if editor.control == control: editor.name = six.text_type(title) + if editor.control == control: + editor.name = str(title) + + def _qt4_editor_tab_spinner(self, event): + editor = event.object - def _qt4_editor_tab_spinner(self, editor, name, new): # Do we need to do this verification? tw, tidx = self._qt4_editor_area._tab_widget(editor.control) - if new: tw.show_button(tidx) - else: tw.hide_button(tidx) + if event.new: + tw.show_button(tidx) + else: + tw.hide_button(tidx) - if not new and not editor == self.window.active_editor: - self._qt4_editor_area.setTabTextColor(editor.control, QtCore.Qt.red) + if not event.new and not editor == self.window.active_editor: + self._qt4_editor_area.setTabTextColor( + editor.control, QtCore.Qt.GlobalColor.red + ) - @on_trait_change('window:active_editor') - def _qt4_active_editor_changed(self, old, new): + @observe("window:active_editor") + def _qt4_active_editor_changed(self, event): """ Handle change of active editor """ # Reset tab title to foreground color - if new is not None: - self._qt4_editor_area.setTabTextColor(new.control) + editor = event.new + if editor is not None: + self._qt4_editor_area.setTabTextColor(editor.control) def _qt4_view_focus_changed(self, old, new): """ Handle the change of focus for a view. """ @@ -351,7 +368,11 @@ if old is not None: # Handle focus changes from views. for view in self.window.views: - if view is not focus_part and view.control is not None and view.control.isAncestorOf(old): + if ( + view is not focus_part + and view.control is not None + and view.control.isAncestorOf(old) + ): view.has_focus = False break @@ -424,14 +445,15 @@ editor.control = editor.create_control(self.window.control) editor.control.setObjectName(editor.id) - editor.on_trait_change(self._qt4_editor_tab_spinner, '_loading') + editor.observe(self._qt4_editor_tab_spinner, "_loading") self.editor_opened = editor - def on_name_changed(editor, trait_name, old, new): + def on_name_changed(event): + editor = event.object self._qt4_editor_area.setWidgetTitle(editor.control, editor.name) - editor.on_trait_change(on_name_changed, 'name') + editor.observe(on_name_changed, "name") self._qt4_monitor(editor.control) @@ -456,8 +478,8 @@ if rel_dw is None: # If we are trying to add a view with a non-existent item, then # just default to the left of the editor area. - if position == 'with': - position = 'left' + if position == "with": + position = "left" # Position the view relative to the editor area. try: @@ -466,7 +488,7 @@ raise ValueError("unknown view position: %s" % position) mw.addDockWidget(dwa, dw) - elif position == 'with': + elif position == "with": # FIXME v3: The Qt documentation says that the second should be # placed above the first, but it always seems to be underneath (ie. # hidden) which is not what the user is expecting. @@ -499,16 +521,17 @@ dw.setWidget(_ViewContainer(size, self.window.control)) dw.setObjectName(view.id) dw.toggleViewAction().toggled.connect( - self._qt4_handle_dock_visibility) + self._qt4_handle_dock_visibility + ) dw.visibilityChanged.connect(self._qt4_handle_dock_visibility) # Save the dock window. view._qt4_dock = dw - def on_name_changed(): + def on_name_changed(event): view._qt4_dock.setWindowTitle(view.name) - view.on_trait_change(on_name_changed, 'name') + view.observe(on_name_changed, "name") # Make sure the view control exists. if view.control is None: @@ -519,7 +542,7 @@ view.control = view.create_control(dw.widget()) except: # Tidy up if the view couldn't be created. - delattr(view, '_qt4_dock') + delattr(view, "_qt4_dock") self.window.control.removeDockWidget(dw) dw.deleteLater() del dw @@ -538,7 +561,7 @@ if view.control is not None: view.control.setParent(None) - delattr(view, '_qt4_dock') + delattr(view, "_qt4_dock") # Delete the dock (and the view container). self.window.control.removeDockWidget(dw) @@ -555,8 +578,7 @@ continue sender = dw.sender() - if (sender is dw.toggleViewAction() or - sender in dw.children()): + if sender is dw.toggleViewAction() or sender in dw.children(): # Toggling the action or pressing the close button on # the view v.visible = checked @@ -641,5 +663,3 @@ self._width = self._height = -1 QtGui.QMainWindow.showEvent(self, e) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/about_dialog.py python-pyface-7.4.0/pyface/ui/wx/about_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/about_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/about_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,40 +1,32 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. import sys -# Major package imports. import wx import wx.html import wx.lib.wxpTag -# Enthought library imports. -from traits.api import Instance, List, provides, Unicode +from traits.api import List, provides, Str -# Local imports. from pyface.i_about_dialog import IAboutDialog, MAboutDialog -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from .dialog import Dialog +from .image_resource import ImageResource -_DIALOG_TEXT = ''' +_DIALOG_TEXT = """
@@ -54,6 +46,9 @@ wxPython %s

+ %s +

+

Copyright © 2003-2010 Enthought, Inc.

@@ -66,7 +61,7 @@
-''' +""" @provides(IAboutDialog) @@ -75,16 +70,17 @@ IAboutDialog interface for the API documentation. """ + # 'IAboutDialog' interface --------------------------------------------- - #### 'IAboutDialog' interface ############################################# + additions = List(Str) - additions = List(Unicode) + copyrights = List(Str) - image = Instance(ImageResource, ImageResource('about')) + image = Image(ImageResource("about")) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): if parent.GetParent() is not None: @@ -98,10 +94,6 @@ # Load the image to be displayed in the about box. image = self.image.create_image() - path = self.image.absolute_path - - # The additional strings. - additions = '
'.join(self.additions) # The width of a wx HTML window is fixed (and is given in the # constructor). We set it to the width of the image plus a fudge @@ -109,23 +101,13 @@ width = image.GetWidth() + 80 html = wx.html.HtmlWindow(parent, -1, size=(width, -1)) - # Get the version numbers. - py_version = sys.version[0:sys.version.find("(")] - wx_version = wx.VERSION_STRING - - # Get the text of the OK button. - if self.ok_label is None: - ok = "OK" - else: - ok = self.ok_label - # Set the page contents. - html.SetPage( - _DIALOG_TEXT % (path, additions, py_version, wx_version, ok) - ) + html.SetPage(self._create_html()) # Make the 'OK' button the default button. - ok_button = html.FindWindowById(wx.ID_OK) + ok_button = parent.FindWindowById( + wx.ID_OK + ) # html.Window.FindWindowById(wx.ID_OK) ok_button.SetDefault() # Set the height of the HTML window to match the height of the content. @@ -136,7 +118,36 @@ # We add a fudge factor to the height here, although I'm not sure why # it should be necessary, the HTML window should report its required # size!?! - width, height = html.GetSize() + width, height = html.GetSize().Get() parent.SetClientSize((width, height + 10)) -### EOF ####################################################################### + def _create_html(self): + # Load the image to be displayed in the about box. + path = self.image.absolute_path + + # The additional strings. + additions = "
".join(self.additions) + + # Get the version numbers. + py_version = sys.version[0:sys.version.find("(")] + wx_version = wx.VERSION_STRING + + # The additional copyright strings. + copyrights = "
".join( + ["Copyright © %s" % line for line in self.copyrights] + ) + + # Get the text of the OK button. + if self.ok_label is None: + ok = "OK" + else: + ok = self.ok_label + + return _DIALOG_TEXT % ( + path, + additions, + py_version, + wx_version, + copyrights, + ok, + ) diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/action_item.py python-pyface-7.4.0/pyface/ui/wx/action/action_item.py --- python-pyface-6.1.2/pyface/ui/wx/action/action_item.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/action_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,54 +1,47 @@ -# Copyright (c) 2005-19, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. +# Thanks for using Enthought open source! """ The wx specific implementations the action manager internal classes. """ -# Standard libary imports. -import six -if six.PY2: - from inspect import getargspec -else: - # avoid deprecation warning - from inspect import getfullargspec as getargspec -# Major package imports. +from inspect import getfullargspec + + import wx -# Enthought library imports. + from traits.api import Any, Bool, HasTraits -# Local imports. + from pyface.action.action_event import ActionEvent _STYLE_TO_KIND_MAP = { - 'push' : wx.ITEM_NORMAL, - 'radio' : wx.ITEM_RADIO, - 'toggle' : wx.ITEM_CHECK, - 'widget' : None, + "push": wx.ITEM_NORMAL, + "radio": wx.ITEM_RADIO, + "toggle": wx.ITEM_CHECK, + "widget": None, } class _MenuItem(HasTraits): """ A menu item representation of an action item. """ - #### '_MenuItem' interface ################################################ + # '_MenuItem' interface ------------------------------------------------ # Is the item checked? checked = Bool(False) # A controller object we delegate taking actions through (if any). - controller = Any + controller = Any() # Is the item enabled? enabled = Bool(True) @@ -58,11 +51,11 @@ # The radio group we are part of (None if the menu item is not part of such # a group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, menu, item, controller): """ Creates a new menu item for an action item. """ @@ -72,18 +65,19 @@ # Create an appropriate menu item depending on the style of the action. # # N.B. Don't try to use -1 as the Id for the menu item... wx does not - # ---- like it! - action = item.action - label = action.name - kind = _STYLE_TO_KIND_MAP[action.style] + # like it! + action = item.action + label = action.name + kind = _STYLE_TO_KIND_MAP[action.style] longtip = action.description or action.tooltip if action.style == "widget": raise NotImplementedError( - "WxPython does not support widgets in menus") + "WxPython does not support widgets in menus" + ) if len(action.accelerator) > 0: - label = label + '\t' + action.accelerator + label = label + "\t" + action.accelerator # This just helps with debugging when people forget to specify a name # for their action (without this wx just barfs which is not very @@ -91,8 +85,7 @@ if len(label) == 0: label = item.action.__class__.__name__ - - if getattr(action, 'menu_role', False): + if getattr(action, "menu_role", False): if action.menu_role == "About": self.control_id = wx.ID_ABOUT elif action.menu_role == "Preferences": @@ -100,86 +93,78 @@ elif action.menu_role == "Quit": self.control_id = wx.ID_EXIT else: - self.control_id = wx.NewId() + self.control_id = wx.ID_ANY self.control = wx.MenuItem(menu, self.control_id, label, longtip, kind) # If the action has an image then display it. if action.image is not None: try: self.control.SetBitmap(action.image.create_bitmap()) - except: + except Exception: # Some platforms don't allow radio buttons to have # bitmaps, so just ignore the exception if it happens pass - menu.AppendItem(self.control) + menu.Append(self.control) menu.menu_items.append(self) # Set the initial enabled/disabled state of the action. self.control.Enable(action.enabled and action.visible) # Set the initial checked state. - if action.style in ['radio', 'toggle']: + if action.style in ["radio", "toggle"]: self.control.Check(action.checked) # Wire it up...create an ugly flag since some platforms dont skip the # event when we thought they would self._skip_menu_event = False - wx.EVT_MENU(parent, self.control_id, self._on_menu) + parent.Bind(wx.EVT_MENU, self._on_menu, self.control) # Listen for trait changes on the action (so that we can update its # enabled/disabled/checked state etc). - action.on_trait_change(self._on_action_enabled_changed, 'enabled') - action.on_trait_change(self._on_action_visible_changed, 'visible') - action.on_trait_change(self._on_action_checked_changed, 'checked') - action.on_trait_change(self._on_action_name_changed, 'name') + action.observe(self._on_action_enabled_changed, "enabled") + action.observe(self._on_action_visible_changed, "visible") + action.observe(self._on_action_checked_changed, "checked") + action.observe(self._on_action_name_changed, "name") + action.observe(self._on_action_image_changed, "image") if controller is not None: self.controller = controller controller.add_to_menu(self) - return - def dispose(self): action = self.item.action - action.on_trait_change(self._on_action_enabled_changed, 'enabled', - remove=True) - action.on_trait_change(self._on_action_visible_changed, 'visible', - remove=True) - action.on_trait_change(self._on_action_checked_changed, 'checked', - remove=True) - action.on_trait_change(self._on_action_name_changed, 'name', - remove=True) + action.observe(self._on_action_enabled_changed, "enabled", remove=True) + action.observe(self._on_action_visible_changed, "visible", remove=True) + action.observe(self._on_action_checked_changed, "checked", remove=True) + action.observe(self._on_action_name_changed, "name", remove=True) + action.observe(self._on_action_image_changed, "image", remove=True) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- def _enabled_changed(self): """ Called when our 'enabled' trait is changed. """ self.control.Enable(self.enabled and self.visible) - return - def _visible_changed(self): """ Called when our 'visible' trait is changed. """ self.control.Enable(self.visible and self.enabled) - return - def _checked_changed(self): """ Called when our 'checked' trait is changed. """ - if self.item.action.style == 'radio': + if self.item.action.style == "radio": # fixme: Not sure why this is even here, we had to guard it to # make it work? Must take a look at svn blame! # FIXME v3: Note that menu_checked() doesn't seem to exist, so we # comment it out and do the following instead. - #if self.group is not None: + # if self.group is not None: # self.group.menu_checked(self) # If we're turning this one on, then we need to turn all the others @@ -192,31 +177,25 @@ self.control.Check(self.checked) - return - - def _on_action_enabled_changed(self, action, trait_name, old, new): + def _on_action_enabled_changed(self, event): """ Called when the enabled trait is changed on an action. """ - + action = event.object self.control.Enable(action.enabled and action.visible) - return - - def _on_action_visible_changed(self, action, trait_name, old, new): + def _on_action_visible_changed(self, event): """ Called when the visible trait is changed on an action. """ - + action = event.object self.control.Enable(action.visible and action.enabled) - return - - def _on_action_checked_changed(self, action, trait_name, old, new): + def _on_action_checked_changed(self, event): """ Called when the checked trait is changed on an action. """ - - if self.item.action.style == 'radio': + action = event.object + if self.item.action.style == "radio": # fixme: Not sure why this is even here, we had to guard it to # make it work? Must take a look at svn blame! # FIXME v3: Note that menu_checked() doesn't seem to exist, so we # comment it out and do the following instead. - #if self.group is not None: + # if self.group is not None: # self.group.menu_checked(self) # If we're turning this one on, then we need to turn all the others @@ -232,19 +211,23 @@ self.control.Check(action.checked) self._skip_menu_event = False - return - - def _on_action_name_changed(self, action, trait_name, old, new): + def _on_action_name_changed(self, event): """ Called when the name trait is changed on an action. """ - + action = event.object label = action.name if len(action.accelerator) > 0: - label = label + '\t' + action.accelerator + label = label + "\t" + action.accelerator self.control.SetText(label) + def _on_action_image_changed(self, event): + """ Called when the name trait is changed on an action. """ + action = event.object + if self.control is not None: + self.control.SetIcon(action.image.create_icon()) + return - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_menu(self, event): """ Called when the menu item is clicked. """ @@ -256,7 +239,7 @@ action = self.item.action action_event = ActionEvent() - is_checkable = action.style in ['radio', 'toggle'] + is_checkable = action.style in ["radio", "toggle"] # Perform the action! if self.controller is not None: @@ -272,7 +255,7 @@ # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. This is also # useful as Traits UI controllers *never* require the event. - argspec = getargspec(self.controller.perform) + argspec = getfullargspec(self.controller.perform) # If the only arguments are 'self' and 'action' then don't pass # the event! @@ -289,7 +272,7 @@ # Most of the time, action's do no care about the event (it # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. - argspec = getargspec(action.perform) + argspec = getfullargspec(action.perform) # If the only argument is 'self' then don't pass the event! if len(argspec.args) == 1: @@ -298,19 +281,17 @@ else: action.perform(action_event) - return - class _Tool(HasTraits): """ A tool bar tool representation of an action item. """ - #### '_Tool' interface #################################################### + # '_Tool' interface ---------------------------------------------------- # Is the item checked? checked = Bool(False) # A controller object we delegate taking actions through (if any). - controller = Any + controller = Any() # Is the item enabled? enabled = Bool(True) @@ -320,29 +301,30 @@ # The radio group we are part of (None if the tool is not part of such a # group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, tool_bar, image_cache, item, controller, - show_labels): + def __init__( + self, parent, tool_bar, image_cache, item, controller, show_labels + ): """ Creates a new tool bar tool for an action item. """ self.item = item self.tool_bar = tool_bar # Create an appropriate tool depending on the style of the action. - action = self.item.action - label = action.name + action = self.item.action + label = action.name # Tool bar tools never have '...' at the end! - if label.endswith('...'): + if label.endswith("..."): label = label[:-3] # And they never contain shortcuts. - label = label.replace('&', '') + label = label.replace("&", "") # If the action has an image then convert it to a bitmap (as required # by the toolbar). @@ -351,37 +333,45 @@ self.tool_bar.GetToolBitmapSize() ) path = action.image.absolute_path - bmp = image_cache.get_bitmap(path) + bmp = image_cache.get_bitmap(path) else: from pyface.api import ImageResource - image = ImageResource('foo') - bmp = image.create_bitmap() - kind = _STYLE_TO_KIND_MAP[action.style] + image = ImageResource("image_not_found") + bmp = image.create_bitmap() + + kind = _STYLE_TO_KIND_MAP[action.style] tooltip = action.tooltip longtip = action.description if not show_labels: - label = '' + label = "" else: self.tool_bar.SetSize((-1, 50)) - if action.style == 'widget': + if action.style == "widget": widget = action.create_control(self.tool_bar) self.control = tool_bar.AddControl(widget, label) self.control_id = self.control.GetId() else: - self.control_id = wx.NewId() - self.control = tool_bar.AddLabelTool( - self.control_id, label, bmp, wx.NullBitmap, kind, tooltip, longtip, None + self.control = tool_bar.AddTool( + wx.ID_ANY, + label, + bmp, + wx.NullBitmap, + kind, + tooltip, + longtip, + None, ) + self.control_id = self.control.GetId() # Set the initial checked state. tool_bar.ToggleTool(self.control_id, action.checked) - if hasattr(tool_bar, 'ShowTool'): + if hasattr(tool_bar, "ShowTool"): # Set the initial enabled/disabled state of the action. tool_bar.EnableTool(self.control_id, action.enabled) @@ -390,52 +380,61 @@ else: # Set the initial enabled/disabled state of the action. tool_bar.EnableTool( - self.control_id, action.enabled and action.visible) + self.control_id, action.enabled and action.visible + ) # Wire it up. - wx.EVT_TOOL(parent, self.control_id, self._on_tool) + parent.Bind(wx.EVT_TOOL, self._on_tool, self.control) # Listen for trait changes on the action (so that we can update its # enabled/disabled/checked state etc). - action.on_trait_change(self._on_action_enabled_changed, 'enabled') - action.on_trait_change(self._on_action_visible_changed, 'visible') - action.on_trait_change(self._on_action_checked_changed, 'checked') + action.observe(self._on_action_enabled_changed, "enabled") + action.observe(self._on_action_visible_changed, "visible") + action.observe(self._on_action_checked_changed, "checked") if controller is not None: self.controller = controller controller.add_to_toolbar(self) - ########################################################################### + def dispose(self): + action = self.item.action + action.observe(self._on_action_enabled_changed, "enabled", remove=True) + action.observe(self._on_action_visible_changed, "visible", remove=True) + action.observe(self._on_action_checked_changed, "checked", remove=True) + + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- def _enabled_changed(self): """ Called when our 'enabled' trait is changed. """ - if hasattr(self.tool_bar, 'ShowTool'): + if hasattr(self.tool_bar, "ShowTool"): self.tool_bar.EnableTool(self.control_id, self.enabled) else: self.tool_bar.EnableTool( - self.control_id, self.enabled and self.visible) + self.control_id, self.enabled and self.visible + ) def _visible_changed(self): """ Called when our 'visible' trait is changed. """ - if hasattr(self.tool_bar, 'ShowTool'): + if hasattr(self.tool_bar, "ShowTool"): self.tool_bar.ShowTool(self.control_id, self.visible) else: self.tool_bar.EnableTool( - self.control_id, self.enabled and self.visible) + self.control_id, self.enabled and self.visible + ) def _checked_changed(self): """ Called when our 'checked' trait is changed. """ - if self.item.action.style == 'radio': + if self.item.action.style == "radio": # FIXME v3: Note that toolbar_checked() doesn't seem to exist, so # we comment it out and do the following instead. - #self.group.toolbar_checked(self) + # self.group.toolbar_checked(self) # If we're turning this one on, then we need to turn all the others # off. But if we're turning this one off, don't worry about the @@ -447,44 +446,44 @@ self.tool_bar.ToggleTool(self.control_id, self.checked) - return - - def _on_action_enabled_changed(self, action, trait_name, old, new): + def _on_action_enabled_changed(self, event): """ Called when the enabled trait is changed on an action. """ - - if hasattr(self.tool_bar, 'ShowTool'): + action = event.object + if hasattr(self.tool_bar, "ShowTool"): self.tool_bar.EnableTool(self.control_id, action.enabled) else: self.tool_bar.EnableTool( - self.control_id, action.enabled and action.visible) + self.control_id, action.enabled and action.visible + ) - def _on_action_visible_changed(self, action, trait_name, old, new): + def _on_action_visible_changed(self, event): """ Called when the visible trait is changed on an action. """ - - if hasattr(self.tool_bar, 'ShowTool'): + action = event.object + if hasattr(self.tool_bar, "ShowTool"): self.tool_bar.ShowTool(self.control_id, action.visible) else: self.tool_bar.EnableTool( - self.control_id, self.enabled and action.visible) + self.control_id, self.enabled and action.visible + ) - def _on_action_checked_changed(self, action, trait_name, old, new): + def _on_action_checked_changed(self, event): """ Called when the checked trait is changed on an action. """ - - if action.style == 'radio': + action = event.object + if action.style == "radio": # If we're turning this one on, then we need to turn all the others # off. But if we're turning this one off, don't worry about the # others. - if new: + if event.new: for item in self.item.parent.items: if item is not self.item: item.action.checked = False # This will *not* emit a tool event. - self.tool_bar.ToggleTool(self.control_id, new) + self.tool_bar.ToggleTool(self.control_id, event.new) return - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_tool(self, event): """ Called when the tool bar tool is clicked. """ @@ -492,8 +491,6 @@ action = self.item.action action_event = ActionEvent() - is_checkable = (action.style == 'radio' or action.style == 'check') - # Perform the action! if self.controller is not None: # fixme: There is a difference here between having a controller @@ -507,7 +504,7 @@ # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. This is also # useful as Traits UI controllers *never* require the event. - argspec = getargspec(self.controller.perform) + argspec = getfullargspec(self.controller.perform) # If the only arguments are 'self' and 'action' then don't pass # the event! @@ -523,7 +520,7 @@ # Most of the time, action's do no care about the event (it # contains information about the time the event occurred etc), so # we only pass it if the perform method requires it. - argspec = getargspec(action.perform) + argspec = getfullargspec(action.perform) # If the only argument is 'self' then don't pass the event! if len(argspec.args) == 1: @@ -532,21 +529,19 @@ else: action.perform(action_event) - return - class _PaletteTool(HasTraits): """ A tool palette representation of an action item. """ - #### '_PaletteTool' interface ############################################# + # '_PaletteTool' interface --------------------------------------------- # The radio group we are part of (None if the tool is not part of such a # group). - group = Any + group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, tool_palette, image_cache, item, show_labels): """ Creates a new tool palette tool for an action item. """ @@ -559,70 +554,75 @@ if action.style == "widget": raise NotImplementedError( - "WxPython does not support widgets in palettes") + "WxPython does not support widgets in palettes" + ) # Tool palette tools never have '...' at the end. - if label.endswith('...'): + if label.endswith("..."): label = label[:-3] # And they never contain shortcuts. - label = label.replace('&', '') + label = label.replace("&", "") - image = action.image.create_image() path = action.image.absolute_path bmp = image_cache.get_bitmap(path) - kind = action.style + kind = action.style tooltip = action.tooltip longtip = action.description if not show_labels: - label = '' + label = "" # Add the tool to the tool palette. - self.tool_id = tool_palette.add_tool(label, bmp, kind, tooltip,longtip) + self.tool_id = tool_palette.add_tool( + label, bmp, kind, tooltip, longtip + ) tool_palette.toggle_tool(self.tool_id, action.checked) tool_palette.enable_tool(self.tool_id, action.enabled) tool_palette.on_tool_event(self.tool_id, self._on_tool) # Listen to the trait changes on the action (so that we can update its # enabled/disabled/checked state etc). - action.on_trait_change(self._on_action_enabled_changed, 'enabled') - action.on_trait_change(self._on_action_checked_changed, 'checked') + action.observe(self._on_action_enabled_changed, "enabled") + action.observe(self._on_action_checked_changed, "checked") return - ########################################################################### + def dispose(self): + action = self.item.action + action.observe(self._on_action_enabled_changed, "enabled", remove=True) + action.observe(self._on_action_checked_changed, "checked", remove=True) + + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_action_enabled_changed(self, action, trait_name, old, new): + def _on_action_enabled_changed(self, event): """ Called when the enabled trait is changed on an action. """ - + action = event.object self.tool_palette.enable_tool(self.tool_id, action.enabled) - return - - def _on_action_checked_changed(self, action, trait_name, old, new): + def _on_action_checked_changed(self, event): """ Called when the checked trait is changed on an action. """ - - if action.style == 'radio': + action = event.object + if action.style == "radio": # If we're turning this one on, then we need to turn all the others # off. But if we're turning this one off, don't worry about the # others. - if new: + if event.new: for item in self.item.parent.items: if item is not self.item: item.action.checked = False # This will *not* emit a tool event. - self.tool_palette.toggle_tool(self.tool_id, new) + self.tool_palette.toggle_tool(self.tool_id, event.new) return - #### Tool palette event handlers ########################################## + # Tool palette event handlers -----------------------------------------# def _on_tool(self, event): """ Called when the tool palette button is clicked. """ @@ -630,12 +630,8 @@ action = self.item.action action_event = ActionEvent() - is_checkable = (action.style == 'radio' or action.style == 'check') - # Perform the action! action.checked = self.tool_palette.get_tool_state(self.tool_id) == 1 action.perform(action_event) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/__init__.py python-pyface-7.4.0/pyface/ui/wx/action/__init__.py --- python-pyface-6.1.2/pyface/ui/wx/action/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,9 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/menu_bar_manager.py python-pyface-7.4.0/pyface/ui/wx/action/menu_bar_manager.py --- python-pyface-6.1.2/pyface/ui/wx/action/menu_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/menu_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,35 +1,30 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The wx specific implementation of a menu bar manager. """ -# Major package imports. + import wx -# Local imports. + from pyface.action.action_manager import ActionManager class MenuBarManager(ActionManager): """ A menu bar manager realizes itself in errr, a menu bar control. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'MenuBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu_bar(self, parent, controller=None): """ Creates a menu bar representation of the manager. """ @@ -50,5 +45,3 @@ menu_bar.Append(menu, item.name) return menu_bar - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/menu_manager.py python-pyface-7.4.0/pyface/ui/wx/action/menu_manager.py --- python-pyface-6.1.2/pyface/ui/wx/action/menu_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,24 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The wx specific implementation of a menu manager. """ -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Unicode, Bool -# Local imports. +from traits.api import Str, Bool + + from pyface.action.action_manager import ActionManager from pyface.action.action_manager_item import ActionManagerItem from pyface.action.group import Group @@ -35,18 +30,18 @@ This could be a sub-menu or a context (popup) menu. """ - #### 'MenuManager' interface ############################################## + # 'MenuManager' interface ---------------------------------------------# # The menu manager's name (if the manager is a sub-menu, this is what its # label will be). - name = Unicode + name = Str() # Does the menu require a separator before the menu item name? separator = Bool(True) - ########################################################################### + # ------------------------------------------------------------------------ # 'MenuManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu(self, parent, controller=None): """ Creates a menu representation of the manager. """ @@ -60,23 +55,21 @@ return _Menu(self, parent, controller) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManagerItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_to_menu(self, parent, menu, controller): """ Adds the item to a menu. """ - id = wx.NewId() sub = self.create_menu(parent, controller) + id = sub.GetId() # fixme: Nasty hack to allow enabling/disabling of menus. sub._id = id sub._menu = menu - menu.AppendMenu(id, self.name, sub) - - return + menu.Append(id, self.name, sub) def add_to_toolbar(self, parent, tool_bar, image_cache, controller): """ Adds the item to a tool bar. """ @@ -87,9 +80,9 @@ class _Menu(wx.Menu): """ The toolkit-specific menu control. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, manager, parent, controller): """ Creates a new tree. """ @@ -113,14 +106,20 @@ self.refresh() # Listen to the manager being updated. - self._manager.on_trait_change(self.refresh, 'changed') - self._manager.on_trait_change(self._on_enabled_changed, 'enabled') + self._manager.observe(self.refresh, "changed") + self._manager.observe(self._on_enabled_changed, "enabled") return - ########################################################################### + def dispose(self): + self._manager.observe(self.refresh, "changed", remove=True) + self._manager.observe(self._on_enabled_changed, "enabled", remove=True) + # Removes event listeners from downstream menu items + self.clear() + + # ------------------------------------------------------------------------ # '_Menu' interface. - ########################################################################### + # ------------------------------------------------------------------------ def clear(self): """ Clears the items from the menu. """ @@ -135,20 +134,18 @@ self.menu_items = [] - return - def is_empty(self): """ Is the menu empty? """ return self.GetMenuItemCount() == 0 - def refresh(self): + def refresh(self, event=None): """ Ensures that the menu reflects the state of the manager. """ self.clear() manager = self._manager - parent = self._parent + parent = self._parent previous_non_empty_group = None for group in manager.groups: @@ -156,23 +153,21 @@ parent, group, previous_non_empty_group ) - return - def show(self, x=None, y=None): """ Show the menu at the specified location. """ if x is None or y is None: self._parent.PopupMenu(self) else: - self._parent.PopupMenuXY(self, x, y) + self._parent.PopupMenu(self, x, y) return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_enabled_changed(self, obj, trait_name, old, new): + def _on_enabled_changed(self, event): """ Dynamic trait change handler. """ # fixme: Nasty hack to allow enabling/disabling of menus. @@ -180,10 +175,8 @@ # We cannot currently (AFAIK) disable menus on the menu bar. Hence # we don't give them an '_id'... - if hasattr(self, '_id'): - self._menu.Enable(self._id, new) - - return + if hasattr(self, "_id"): + self._menu.Enable(self._id, event.new) def _add_group(self, parent, group, previous_non_empty_group=None): """ Adds a group to a menu. """ @@ -200,9 +193,11 @@ if len(item.items) > 0: self._add_group(parent, item, previous_non_empty_group) - if previous_non_empty_group is not None \ - and previous_non_empty_group.separator \ - and item.separator: + if ( + previous_non_empty_group is not None + and previous_non_empty_group.separator + and item.separator + ): self.AppendSeparator() previous_non_empty_group = item @@ -217,5 +212,3 @@ previous_non_empty_group = group return previous_non_empty_group - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/status_bar_manager.py python-pyface-7.4.0/pyface/ui/wx/action/status_bar_manager.py --- python-pyface-6.1.2/pyface/ui/wx/action/status_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/status_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,22 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A status bar manager realizes itself in a status bar control. """ -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Any, HasTraits, List, Property, Str, Unicode + +from traits.api import Any, HasTraits, List, Property, Str class StatusBarManager(HasTraits): @@ -31,14 +26,14 @@ message = Property # The messages to be displayed in the status bar fields. - messages = List(Unicode) + messages = List(Str) # The toolkit-specific control that represents the status bar. - status_bar = Any + status_bar = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'StatusBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_status_bar(self, parent): """ Creates a status bar. """ @@ -63,9 +58,9 @@ self.status_bar._pyface_control = None self.status_bar = None - ########################################################################### + # ------------------------------------------------------------------------ # Property handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _get_message(self): """ Property getter. """ @@ -74,7 +69,7 @@ message = self.messages[0] else: - message = '' + message = "" return message @@ -86,16 +81,16 @@ self.messages[0] = value else: - old = '' + old = "" self.messages.append(value) - self.trait_property_changed('message', old, value) + self.trait_property_changed("message", old, value) return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _messages_changed(self): """ Sets the text displayed on the status bar. """ @@ -104,8 +99,6 @@ for i in range(len(self.messages)): self.status_bar.SetStatusText(self.messages[i], i) - return - def _messages_items_changed(self): """ Sets the text displayed on the status bar. """ @@ -114,5 +107,3 @@ self.status_bar.SetStatusText(self.messages[i], i) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/tool_bar_manager.py python-pyface-7.4.0/pyface/ui/wx/action/tool_bar_manager.py --- python-pyface-6.1.2/pyface/ui/wx/action/tool_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/tool_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,25 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The wx specific implementation of the tool bar manager. """ -# Major package imports. + import wx -# Enthought library imports. + from traits.api import Bool, Enum, Instance, Str, Tuple -# Local imports. -from pyface.wx.aui import aui + +from pyface.wx.aui import aui as AUI from pyface.image_cache import ImageCache from pyface.action.action_manager import ActionManager @@ -32,7 +27,7 @@ class ToolBarManager(ActionManager): """ A tool bar manager realizes itself in errr, a tool bar control. """ - #### 'ToolBarManager' interface ########################################### + # 'ToolBarManager' interface ------------------------------------------- # Is the tool bar enabled? enabled = Bool(True) @@ -44,10 +39,10 @@ image_size = Tuple((16, 16)) # The toolbar name (used to distinguish multiple toolbars). - name = Str('ToolBar') + name = Str("ToolBar") # The orientation of the toolbar. - orientation = Enum('horizontal', 'vertical') + orientation = Enum("horizontal", "vertical") # Should we display the name of each tool bar tool under its image? show_tool_names = Bool(True) @@ -55,20 +50,20 @@ # Should we display the horizontal divider? show_divider = Bool(False) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Cache of tool images (scaled to the appropriate size). _image_cache = Instance(ImageCache) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *args, **traits): """ Creates a new tool bar manager. """ # Base class contructor. - super(ToolBarManager, self).__init__(*args, **traits) + super().__init__(*args, **traits) # An image cache to make sure that we only load each image used in the # tool bar exactly once. @@ -76,12 +71,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ToolBarManager' interface. - ########################################################################### - - #### Trait change handlers ################################################ - #### Methods ############################################################## + # ------------------------------------------------------------------------ def create_tool_bar(self, parent, controller=None, aui=False): """ Creates a tool bar. """ @@ -95,24 +87,28 @@ # Determine the wx style for the tool bar based on any optional # settings. - style = wx.NO_BORDER | wx.TB_FLAT | wx.CLIP_CHILDREN - - if self.show_tool_names: - style |= wx.TB_TEXT - - if self.orientation == 'horizontal': - style |= wx.TB_HORIZONTAL - - else: - style |= wx.TB_VERTICAL - - if not self.show_divider: - style |= wx.TB_NODIVIDER - - # Create the control. + style = wx.NO_BORDER | wx.CLIP_CHILDREN if aui: - tool_bar = _AuiToolBar(self, parent, -1, style=style) + aui_style = AUI.AUI_TB_PLAIN_BACKGROUND + if self.show_tool_names: + aui_style |= AUI.AUI_TB_TEXT + if self.orientation != "horizontal": + aui_style |= AUI.AUI_TB_VERTICAL + if not self.show_divider: + style |= wx.TB_NODIVIDER + tool_bar = _AuiToolBar( + self, parent, -1, style=style, agwStyle=aui_style + ) else: + style |= wx.TB_FLAT + if self.show_tool_names: + style |= wx.TB_TEXT + if self.orientation == "horizontal": + style |= wx.TB_HORIZONTAL + else: + style |= wx.TB_VERTICAL + if not self.show_divider: + style |= wx.TB_NODIVIDER tool_bar = _ToolBar(self, parent, -1, style=style) # fixme: Setting the tool bitmap size seems to be the only way to @@ -132,9 +128,9 @@ return tool_bar - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _wx_add_tools(self, parent, tool_bar, controller): """ Adds tools for all items in the list of groups. """ @@ -155,11 +151,9 @@ tool_bar, self._image_cache, controller, - self.show_tool_names + self.show_tool_names, ) - return - def _wx_set_initial_tool_state(self, tool_bar): """ Workaround for the wxPython tool bar bug. @@ -173,12 +167,14 @@ for item in group.items: # If the group is a radio group, set the initial checked state # of every tool in it. - if item.action.style == 'radio': + if item.action.style == "radio": if item.control_id is not None: # Only set checked state if control has been created. # Using extra_actions of tasks, it appears that this # may be called multiple times. - tool_bar.ToggleTool(item.control_id, item.action.checked) + tool_bar.ToggleTool( + item.control_id, item.action.checked + ) checked = checked or item.action.checked # Every item in a radio group MUST be 'radio' style, so we @@ -193,15 +189,13 @@ if not checked and len(group.items) > 0: group.items[0].action.checked = True - return - class _ToolBar(wx.ToolBar): """ The toolkit-specific tool bar implementation. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, tool_bar_manager, parent, id, style): """ Constructor. """ @@ -212,57 +206,53 @@ # visibility. self.tool_bar_manager = tool_bar_manager - self.tool_bar_manager.on_trait_change( - self._on_tool_bar_manager_enabled_changed, 'enabled' + self.tool_bar_manager.observe( + self._on_tool_bar_manager_enabled_changed, "enabled" ) - self.tool_bar_manager.on_trait_change( - self._on_tool_bar_manager_visible_changed, 'visible' + self.tool_bar_manager.observe( + self._on_tool_bar_manager_visible_changed, "visible" ) return - ########################################################################### + # ------------------------------------------------------------------------ # Trait change handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_tool_bar_manager_enabled_changed(self, obj, trait_name, old, new): + def _on_tool_bar_manager_enabled_changed(self, event): """ Dynamic trait change handler. """ - obj.window._wx_enable_tool_bar(self, new) - - return + event.object.window._wx_enable_tool_bar(self, event.new) - def _on_tool_bar_manager_visible_changed(self, obj, trait_name, old, new): + def _on_tool_bar_manager_visible_changed(self, event): """ Dynamic trait change handler. """ - obj.window._wx_show_tool_bar(self, new) - - return + event.object.window._wx_show_tool_bar(self, event.new) -class _AuiToolBar(aui.AuiToolBar): +class _AuiToolBar(AUI.AuiToolBar): """ The toolkit-specific tool bar implementation for AUI windows. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, tool_bar_manager, parent, id, style): + def __init__(self, tool_bar_manager, parent, id, style, agwStyle): """ Constructor. """ - aui.AuiToolBar.__init__(self, parent, -1, style=style) + super().__init__(parent, -1, style=style, agwStyle=agwStyle) # Listen for changes to the tool bar manager's enablement and # visibility. self.tool_bar_manager = tool_bar_manager - self.tool_bar_manager.on_trait_change( - self._on_tool_bar_manager_enabled_changed, 'enabled' + self.tool_bar_manager.observe( + self._on_tool_bar_manager_enabled_changed, "enabled" ) - self.tool_bar_manager.on_trait_change( - self._on_tool_bar_manager_visible_changed, 'visible' + self.tool_bar_manager.observe( + self._on_tool_bar_manager_visible_changed, "visible" ) # we need to defer hiding tools until first time Realize is called so @@ -275,14 +265,12 @@ # removed from the toolbar the item would be garbage collected. self.tool_map = {} - return - def Realize(self): if len(self.tool_map) == 0: for pos in range(self.GetToolsCount()): tool = self.GetToolByPos(pos) self.tool_map[tool.GetId()] = (pos, tool) - aui.AuiToolBar.Realize(self) + AUI.AuiToolBar.Realize(self) if len(self.initially_hidden_tool_ids) > 0: for tool_id in self.initially_hidden_tool_ids: self.RemoveTool(tool_id) @@ -304,30 +292,56 @@ self.EnableTool(tool_id, True) self.Realize() # Update the toolbar in the AUI manager to force toolbar resize - wx.CallAfter(self.tool_bar_manager.controller.task.window._aui_manager.Update) + try: + wx.CallAfter( + self.tool_bar_manager.controller.task.window._aui_manager.Update + ) + except: + pass elif not state and tool is not None: self.RemoveTool(tool_id) # Update the toolbar in the AUI manager to force toolbar resize - wx.CallAfter(self.tool_bar_manager.controller.task.window._aui_manager.Update) + try: + wx.CallAfter( + self.tool_bar_manager.controller.task.window._aui_manager.Update + ) + except: + pass def InsertToolInOrder(self, tool_id): orig_pos, tool = self.tool_map[tool_id] + pos = -1 for pos in range(self.GetToolsCount()): - existing_tool = self.GetToolByPos(pos) - existing_id = existing_tool.GetId() existing_orig_pos, _ = self.tool_map[tool_id] if existing_orig_pos > orig_pos: break - self.InsertToolItem(pos+1, tool) + self.InsertToolItem(pos + 1, tool) + ## Additional convenience functions for the normal AGW AUI toolbar - ##### Additional convenience functions for the normal AGW AUI toolbar - - def AddLabelTool(self, id, label, bitmap, bmpDisabled, kind, shortHelp, - longHelp, clientData): + def AddLabelTool( + self, + id, + label, + bitmap, + bmpDisabled, + kind, + shortHelp, + longHelp, + clientData, + ): "The full AddTool() function." - return self.AddTool(id, label, bitmap, bmpDisabled, kind, shortHelp, - longHelp, clientData, None) + return self.AddTool( + id, + label, + bitmap, + bmpDisabled, + kind, + shortHelp, + longHelp, + clientData, + None, + ) def InsertToolItem(self, pos, tool): self._items[pos:pos] = [tool] @@ -350,7 +364,6 @@ return False - def RemoveTool(self, tool_id): """ Removes the specified tool from the toolbar but doesn't delete it. @@ -370,39 +383,45 @@ return False - FindById = aui.AuiToolBar.FindTool + FindById = AUI.AuiToolBar.FindTool - GetToolState = aui.AuiToolBar.GetToolToggled + GetToolState = AUI.AuiToolBar.GetToolToggled - GetToolsCount = aui.AuiToolBar.GetToolCount + GetToolsCount = AUI.AuiToolBar.GetToolCount def GetToolByPos(self, pos): return self._items[pos] def OnSize(self, event): # Quickly short-circuit if the toolbar isn't realized - if not hasattr(self, '_absolute_min_size'): + if not hasattr(self, "_absolute_min_size"): return - aui.AuiToolBar.OnSize(self, event) - + AUI.AuiToolBar.OnSize(self, event) - ########################################################################### + # ------------------------------------------------------------------------ # Trait change handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_tool_bar_manager_enabled_changed(self, obj, trait_name, old, new): + def _on_tool_bar_manager_enabled_changed(self, event): """ Dynamic trait change handler. """ - obj.controller.task.window._wx_enable_tool_bar(self, new) + try: + event.object.controller.task.window._wx_enable_tool_bar( + self, event.new + ) + except: - return + pass - def _on_tool_bar_manager_visible_changed(self, obj, trait_name, old, new): + def _on_tool_bar_manager_visible_changed(self, event): """ Dynamic trait change handler. """ - obj.controller.task.window._wx_show_tool_bar(self, new) + try: + event.object.controller.task.window._wx_show_tool_bar( + self, event.new + ) + except: + pass return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/tool_palette_manager.py python-pyface-7.4.0/pyface/ui/wx/action/tool_palette_manager.py --- python-pyface-6.1.2/pyface/ui/wx/action/tool_palette_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/tool_palette_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,21 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A tool bar manager realizes itself in a tool palette control. """ -# Major package imports. -import wx -# Enthought library imports. -from traits.api import Any, Bool, Enum, Instance, Tuple +from traits.api import Bool, Instance, Tuple + -# Local imports. from pyface.image_cache import ImageCache from pyface.action.action_manager import ActionManager from .tool_palette import ToolPalette @@ -32,7 +24,7 @@ class ToolPaletteManager(ActionManager): """ A tool bar manager realizes itself in a tool palette bar control. """ - #### 'ToolPaletteManager' interface ####################################### + # 'ToolPaletteManager' interface --------------------------------------- # The size of tool images (width, height). image_size = Tuple((16, 16)) @@ -40,20 +32,20 @@ # Should we display the name of each tool bar tool under its image? show_tool_names = Bool(True) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Cache of tool images (scaled to the appropriate size). _image_cache = Instance(ImageCache) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, *args, **traits): """ Creates a new tool bar manager. """ # Base class contructor. - super(ToolPaletteManager, self).__init__(*args, **traits) + super().__init__(*args, **traits) # An image cache to make sure that we only load each image used in the # tool bar exactly once. @@ -61,9 +53,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ToolPaletteManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_tool_palette(self, parent, controller=None): """ Creates a tool bar. """ @@ -78,36 +70,32 @@ return tool_palette - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _add_tools(self, tool_palette, groups): """ Adds tools for all items in a list of groups. """ - previous_non_empty_group = None + # previous_non_empty_group = None for group in self.groups: if len(group.items) > 0: # Is a separator required? -## FIXME : Does the palette need the notion of a separator? -## if previous_non_empty_group is not None and group.separator: -## tool_bar.AddSeparator() -## -## previous_non_empty_group = group + ## FIXME : Does the palette need the notion of a separator? + ## if previous_non_empty_group is not None and group.separator: + ## tool_bar.AddSeparator() + ## + ## previous_non_empty_group = group # Create a tool bar tool for each item in the group. for item in group.items: control_id = item.add_to_palette( - tool_palette, - self._image_cache, - self.show_tool_names + tool_palette, self._image_cache, self.show_tool_names ) item.control_id = control_id tool_palette.realize() - return - def _set_initial_tool_state(self, tool_palette, groups): """ Workaround for the wxPython tool bar bug. @@ -121,8 +109,10 @@ for item in group.items: # If the group is a radio group, set the initial checked state # of every tool in it. - if item.action.style == 'radio': - tool_palette.toggle_tool(item.control_id, item.action.checked) + if item.action.style == "radio": + tool_palette.toggle_tool( + item.control_id, item.action.checked + ) checked = checked or item.action.checked # Every item in a radio group MUST be 'radio' style, so we @@ -138,5 +128,3 @@ group.items[0].action.checked = True return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/action/tool_palette.py python-pyface-7.4.0/pyface/ui/wx/action/tool_palette.py --- python-pyface-6.1.2/pyface/ui/wx/action/tool_palette.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/action/tool_palette.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,13 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ View of an ActionManager drawn as a rectangle of buttons. """ @@ -21,6 +16,7 @@ from pyface.widget import Widget from traits.api import Bool, Dict, Int, List, Tuple + # HTML templates. # FIXME : Not quite the right color. HTML = """ @@ -38,39 +34,39 @@ class ToolPalette(Widget): - tools = List + tools = List() - id_tool_map = Dict + id_tool_map = Dict() - tool_id_to_button_map = Dict + tool_id_to_button_map = Dict() button_size = Tuple((25, 25), Int, Int) is_realized = Bool(False) - tool_listeners = Dict + tool_listeners = Dict() # Maps a button id to its tool id. - button_tool_map = Dict + button_tool_map = Dict() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, **traits): """ Creates a new tool palette. """ # Base class constructor. - super(ToolPalette, self).__init__(**traits) + super().__init__(**traits) # Create the toolkit-specific control that represents the widget. self.control = self._create_control(parent) return - ########################################################################### + # ------------------------------------------------------------------------ # ToolPalette interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_tool(self, label, bmp, kind, tooltip, longtip): """ Add a tool with the specified properties to the palette. @@ -78,7 +74,7 @@ Return an id that can be used to reference this tool in the future. """ - wxid = wx.NewId() + wxid = wx.NewIdRef() params = (wxid, label, bmp, kind, tooltip, longtip) self.tools.append(params) self.id_tool_map[wxid] = params @@ -97,11 +93,9 @@ """ button = self.tool_id_to_button_map.get(id, None) - if button is not None and hasattr(button, 'SetToggle'): + if button is not None and hasattr(button, "SetToggle"): button.SetToggle(checked) - return - def enable_tool(self, id, enabled): """ Enable or disable the tool identified by 'id'. """ @@ -109,29 +103,23 @@ if button is not None: button.SetEnabled(enabled) - return - def on_tool_event(self, id, callback): """ Register a callback for events on the tool identified by 'id'. """ callbacks = self.tool_listeners.setdefault(id, []) callbacks.append(callback) - return - def realize(self): """ Realize the control so that it can be displayed. """ self.is_realized = True self._reflow() - return - def get_tool_state(self, id): """ Get the toggle state of the tool identified by 'id'. """ button = self.tool_id_to_button_map.get(id, None) - if hasattr(button, 'GetToggle'): + if hasattr(button, "GetToggle"): if button.GetToggle(): state = 1 else: @@ -141,10 +129,9 @@ return state - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): @@ -152,18 +139,16 @@ return html_window - def _reflow(self): """ Reflow the layout. """ - # Create a bit of html for each tool. parts = [] for param in self.tools: parts.append(PART % (str(param[0]), self.button_size)) # Create the entire html page. - html = HTML % ''.join(parts) + html = HTML % "".join(parts) # Set the HTML on the widget. This will create all of the buttons. self.control.SetPage(html) @@ -171,8 +156,6 @@ for param in self.tools: self._initialize_tool(param) - return - def _initialize_tool(self, param): """ Initialize the tool palette button. """ @@ -187,22 +170,21 @@ from wx.lib.buttons import GenBitmapToggleButton, GenBitmapButton - if kind == 'radio': - button = GenBitmapToggleButton(panel, -1, None, size=self.button_size) + if kind == "radio": + button = GenBitmapToggleButton( + panel, -1, None, size=self.button_size + ) else: button = GenBitmapButton(panel, -1, None, size=self.button_size) self.button_tool_map[button.GetId()] = wxid self.tool_id_to_button_map[wxid] = button - wx.EVT_BUTTON(panel, button.GetId(), self._on_button) + panel.Bind(wx.EVT_BUTTON, self._on_button, button) button.SetBitmapLabel(bmp) - button.SetToolTipString(label) + button.SetToolTip(label) sizer.Add(button, 0, wx.EXPAND) - - return - def _on_button(self, event): button_id = event.GetId() @@ -212,5 +194,3 @@ listener(event) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/application_window.py python-pyface-7.4.0/pyface/ui/wx/application_window.py --- python-pyface-6.1.2/pyface/ui/wx/application_window.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,29 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. -import sys - -# Major package imports. import wx -from pyface.wx.aui import aui, PyfaceAuiManager -# Enthought library imports. +from traits.api import Instance, List, Str, observe, provides + from pyface.action.api import MenuBarManager, StatusBarManager from pyface.action.api import ToolBarManager -from traits.api import Instance, List, on_trait_change, provides, Unicode -from pyface.i_application_window import IApplicationWindow -from pyface.i_application_window import MApplicationWindow -from pyface.image_resource import ImageResource - -# Local imports. +from pyface.i_application_window import ( + IApplicationWindow, MApplicationWindow, +) +from pyface.ui_traits import Image +from pyface.wx.aui import aui, PyfaceAuiManager +from .image_resource import ImageResource from .window import Window @@ -42,10 +33,9 @@ IApplicationWindow interface for the API documentation. """ + # 'IApplicationWindow' interface --------------------------------------- - #### 'IApplicationWindow' interface ####################################### - - icon = Instance(ImageResource) + icon = Image() menu_bar_manager = Instance(MenuBarManager) @@ -57,7 +47,7 @@ # this list instead. tool_bar_managers = List(ToolBarManager) - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------# # fixme: We can't set the default value of the actual 'size' trait here as # in the toolkit-specific event handlers for window size and position @@ -65,19 +55,17 @@ # that by doing that traits never knows that the trait has been set and # hence always returns the default value! Using a trait initializer method # seems to work however (e.g. 'def _size_default'). Hmmmm.... -## size = (800, 600) + ## size = (800, 600) - title = Unicode("Pyface") + title = Str("Pyface") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): panel = wx.Panel(parent, -1, name="ApplicationWindow") - panel.SetSize((500, 400)) - panel.SetBackgroundColour('blue') - + panel.SetBackgroundColour("blue") return panel def _create_menu_bar(self, parent): @@ -90,45 +78,38 @@ status_bar = self.status_bar_manager.create_status_bar(parent) self.control.SetStatusBar(status_bar) - return - def _create_tool_bar(self, parent): tool_bar_managers = self._get_tool_bar_managers() if len(tool_bar_managers) > 0: for tool_bar_manager in reversed(tool_bar_managers): tool_bar = tool_bar_manager.create_tool_bar(parent, aui=True) - self._add_toolbar_to_aui_manager( - tool_bar - ) + self._add_toolbar_to_aui_manager(tool_bar) self._aui_manager.Update() def _set_window_icon(self): if self.icon is None: - icon = ImageResource('application.ico') + icon = ImageResource("application.ico") else: icon = self.icon if self.control is not None: self.control.SetIcon(icon.create_icon()) - return - - ########################################################################### + # ------------------------------------------------------------------------ # 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _size_default(self): """ Trait initialiser. """ - return (800, 600) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create(self): - super(ApplicationWindow, self)._create() + super()._create() self._aui_manager = PyfaceAuiManager() self._aui_manager.SetManagedWindow(self.control) @@ -138,6 +119,7 @@ self.control._aui_manager = self._aui_manager contents = self._create_contents(self.control) + self._aui_manager.AddPane(contents, aui.AuiPaneInfo().CenterPane()) self._create_trim_widgets(self.control) @@ -145,17 +127,19 @@ # to it (this allows batch updates). self._aui_manager.Update() - return - def _create_control(self, parent): - style = wx.DEFAULT_FRAME_STYLE \ - | wx.FRAME_NO_WINDOW_MENU \ - | wx.CLIP_CHILDREN + style = ( + wx.DEFAULT_FRAME_STYLE | wx.FRAME_NO_WINDOW_MENU | wx.CLIP_CHILDREN + ) control = wx.Frame( - parent, -1, self.title, style=style, size=self.size, - pos=self.position + parent, + -1, + self.title, + style=style, + size=self.size, + pos=self.position, ) # Mac/Win needs this, otherwise background color is black @@ -167,11 +151,11 @@ def destroy(self): if self.control: self._aui_manager.UnInit() - super(ApplicationWindow, self).destroy() + super().destroy() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _add_toolbar_to_aui_manager(self, tool_bar): """ Add a toolbar to the AUI manager. """ @@ -179,8 +163,6 @@ info = self._get_tool_bar_pane_info(tool_bar) self._aui_manager.AddPane(tool_bar, info) - return - def _get_tool_bar_pane_info(self, tool_bar): info = aui.AuiPaneInfo() info.Caption(tool_bar.tool_bar_manager.name) @@ -209,8 +191,6 @@ # AUI toolbars cannot be enabled/disabled. - return - def _wx_show_tool_bar(self, tool_bar, visible): """ Hide/Show a tool bar. """ @@ -232,7 +212,7 @@ return - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ def _menu_bar_manager_changed(self): if self.control is not None: @@ -245,12 +225,10 @@ old.remove_status_bar(self.control) self._create_status_bar(self.control) - @on_trait_change('tool_bar_manager, tool_bar_managers') - def _update_tool_bar_managers(self): + @observe("tool_bar_manager, tool_bar_managers.items") + def _update_tool_bar_managers(self, event): if self.control is not None: self._create_tool_bar(self.control) def _icon_changed(self): self._set_window_icon() - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/beep.py python-pyface-7.4.0/pyface/ui/wx/beep.py --- python-pyface-6.1.2/pyface/ui/wx/beep.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/beep.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # Copyright 2012 Philip Chimento """Sound the system bell, Wx implementation.""" diff -Nru python-pyface-6.1.2/pyface/ui/wx/clipboard.py python-pyface-7.4.0/pyface/ui/wx/clipboard.py --- python-pyface-6.1.2/pyface/ui/wx/clipboard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/clipboard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,161 +1,210 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2009, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! -#------------------------------------------------------------------------------ -# Standard library imports -from six.moves import cStringIO as StringIO -from six.moves.cPickle import dumps, load, loads -# System library imports +from contextlib import contextmanager +from io import BytesIO +import logging +from pickle import dumps, load, loads + import wx -# ETS imports from traits.api import provides from pyface.i_clipboard import IClipboard, BaseClipboard -import six + + +logger = logging.getLogger(__name__) # Data formats -PythonObjectFormat = wx.CustomDataFormat('PythonObject') -TextFormat = wx.DataFormat(wx.DF_TEXT) -FileFormat = wx.DataFormat(wx.DF_FILENAME) +PythonObjectFormat = wx.DataFormat("PythonObject") +TextFormat = wx.DataFormat(wx.DF_TEXT) +FileFormat = wx.DataFormat(wx.DF_FILENAME) # Shortcuts -cb = wx.TheClipboard +cb = wx.TheClipboard + + +@contextmanager +def _ensure_clipboard(): + """ Ensure use of X11 clipboard rather than primary selection on X11. + + X11 allows pasting from either the clipboard or the primary selection. + This context manager ensures that the clipboard is always used for Pyface, + no matter what the wider application state is currently using. + + On non-X11 platforms this does nothing. + """ + using_primary = cb.IsUsingPrimarySelection() + if using_primary: + cb.UsePrimarySelection(False) + try: + yield + finally: + cb.UsePrimarySelection(True) + else: + yield + + +@contextmanager +def _close_clipboard(flush=False): + """ Ensures clipboard is closed and (optionally) flushed. + + Parameters + ---------- + flush : bool + Whether or not to flush the clipboard. Should be true when setting + data to the clipboard. + """ + try: + yield + finally: + cb.Close() + if flush: + cb.Flush() @provides(IClipboard) class Clipboard(BaseClipboard): + """ WxPython implementation of the IClipboard interface. + Python object data is transmitted as bytes consisting of the pickled class + object followed by the corresponding pickled instance object. This means + that copy/paste of Python objects may not work unless compatible Python + libraries are available at the pasting location. + """ - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_has_data(self): result = False - if cb.Open(): - result = (cb.IsSupported(TextFormat) or - cb.IsSupported(FileFormat) or - cb.IsSupported(PythonObjectFormat)) - cb.Close() + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(): + result = ( + cb.IsSupported(TextFormat) + or cb.IsSupported(FileFormat) + or cb.IsSupported(PythonObjectFormat) + ) return result - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'object_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_object_data(self): result = None - if cb.Open(): - try: - if cb.IsSupported(PythonObjectFormat): - cdo = wx.CustomDataObject(PythonObjectFormat) - if cb.GetData(cdo): - file = StringIO(cdo.GetData()) - klass = load(file) - result = load(file) - finally: - cb.Close() + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(): + if cb.IsSupported(PythonObjectFormat): + cdo = wx.CustomDataObject(PythonObjectFormat) + if cb.GetData(cdo): + file = BytesIO(cdo.GetData()) + _ = load(file) + result = load(file) return result def _set_object_data(self, data): - if cb.Open(): - try: - cdo = wx.CustomDataObject(PythonObjectFormat) - cdo.SetData(dumps(data.__class__) + dumps(data)) - # fixme: There seem to be cases where the '-1' value creates - # pickles that can't be unpickled (e.g. some TraitDictObject's) - #cdo.SetData(dumps(data, -1)) - cb.SetData(cdo) - finally: - cb.Close() - cb.Flush() + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(flush=True): + cdo = wx.CustomDataObject(PythonObjectFormat) + cdo.SetData(dumps(data.__class__) + dumps(data)) + # fixme: There seem to be cases where the '-1' value creates + # pickles that can't be unpickled (e.g. some TraitDictObject's) + # cdo.SetData(dumps(data, -1)) + cb.SetData(cdo) def _get_has_object_data(self): return self._has_this_data(PythonObjectFormat) def _get_object_type(self): - result = '' - if cb.Open(): - try: - if cb.IsSupported(PythonObjectFormat): - cdo = wx.CustomDataObject(PythonObjectFormat) - if cb.GetData(cdo): - try: - # We may not be able to load the required class: - result = loads(cdo.GetData()) - except: - pass - finally: - cb.Close() + result = "" + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(): + if cb.IsSupported(PythonObjectFormat): + cdo = wx.CustomDataObject(PythonObjectFormat) + if cb.GetData(cdo): + try: + # We may not be able to load the required class: + result = loads(cdo.GetData()) + except Exception: + logger.exception("Cannot load data from clipboard.") return result - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'text_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_text_data(self): - result = '' - if cb.Open(): - if cb.IsSupported(TextFormat): - tdo = wx.TextDataObject() - if cb.GetData(tdo): - result = tdo.GetText() - cb.Close() - return result + result = "" + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(): + if cb.IsSupported(TextFormat): + tdo = wx.TextDataObject() + if cb.GetData(tdo): + result = tdo.GetText() + return result def _set_text_data(self, data): - if cb.Open(): - cb.SetData(wx.TextDataObject(str(data))) - cb.Close() - cb.Flush() + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(flush=True): + cb.SetData(wx.TextDataObject(str(data))) def _get_has_text_data(self): return self._has_this_data(TextFormat) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # 'file_data' property methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _get_file_data(self): - result = [] - if cb.Open(): - if cb.IsSupported(FileFormat): - tfo = wx.FileDataObject() - if cb.GetData(tfo): - result = tfo.GetFilenames() - cb.Close() - return result + with _ensure_clipboard(): + result = [] + if cb.Open(): + with _close_clipboard(): + if cb.IsSupported(FileFormat): + tfo = wx.FileDataObject() + if cb.GetData(tfo): + result = tfo.GetFilenames() + return result def _set_file_data(self, data): - if cb.Open(): - tfo = wx.FileDataObject() - if isinstance(data, six.string_types): - tfo.AddFile(data) - else: - for filename in data: - tfo.AddFile(filename) - cb.SetData(tfo) - cb.Close() - cb.Flush() + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(flush=True): + tfo = wx.FileDataObject() + if isinstance(data, str): + tfo.AddFile(data) + else: + for filename in data: + tfo.AddFile(filename) + cb.SetData(tfo) def _get_has_file_data(self): return self._has_this_data(FileFormat) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Private helper methods: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _has_this_data(self, format): result = False - if cb.Open(): - result = cb.IsSupported(format) - cb.Close() + with _ensure_clipboard(): + if cb.Open(): + with _close_clipboard(): + result = cb.IsSupported(format) + return result diff -Nru python-pyface-6.1.2/pyface/ui/wx/color_dialog.py python-pyface-7.4.0/pyface/ui/wx/color_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/color_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/color_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,69 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The interface for a dialog that allows the user to select a color. """ + +import wx + +from traits.api import Bool, provides + +from pyface.color import Color +from pyface.ui_traits import PyfaceColor +from pyface.i_color_dialog import IColorDialog +from .dialog import Dialog + +# The WxPython version in a convenient to compare form. +wx_version = tuple(int(x) for x in wx.__version__.split('.')[:3]) + + +@provides(IColorDialog) +class ColorDialog(Dialog): + """ A dialog for selecting colors. + """ + + # 'IColorDialog' interface ---------------------------------------------- + + #: The color in the dialog. + color = PyfaceColor() + + #: Whether or not to allow the user to chose an alpha value. Only works + #: for wxPython 4.1 and higher. + show_alpha = Bool(False) + + # ------------------------------------------------------------------------ + # 'IDialog' interface. + # ------------------------------------------------------------------------ + + def _create_contents(self, parent): + # In wx this is a canned dialog. + pass + + # ------------------------------------------------------------------------ + # 'IWindow' interface. + # ------------------------------------------------------------------------ + + def close(self): + colour_data = self.control.GetColourData() + wx_colour = colour_data.GetColour() + self.color = Color.from_toolkit(wx_colour) + super(ColorDialog, self).close() + + # ------------------------------------------------------------------------ + # 'IWidget' interface. + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + wx_colour = self.color.to_toolkit() + data = wx.ColourData() + data.SetColour(wx_colour) + if wx_version >= (4, 1): + data.SetChooseAlpha(self.show_alpha) + dialog = wx.ColourDialog(parent, data) + return dialog diff -Nru python-pyface-6.1.2/pyface/ui/wx/color.py python-pyface-7.4.0/pyface/ui/wx/color.py --- python-pyface-6.1.2/pyface/ui/wx/color.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/color.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,58 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Color conversion routines for the wx toolkit. + +This module provides a couple of utility methods to support the +pyface.color.Color class to_toolkit and from_toolkit methods. +""" + + +import wx + +from pyface.color import channels_to_ints, ints_to_channels + + +def toolkit_color_to_rgba(wx_colour): + """ Convert a wx.Colour to an RGBA tuple. + + Parameters + ---------- + wx_color : wx.Colour + A wx.Colour object. + + Returns + ------- + rgba : tuple + A tuple of 4 floating point values between 0.0 and 1.0 inclusive. + """ + values = ( + wx_colour.Red(), + wx_colour.Green(), + wx_colour.Blue(), + wx_colour.Alpha(), + ) + return ints_to_channels(values) + + +def rgba_to_toolkit_color(rgba): + """ Convert an RGBA tuple to a wx.Colour. + + Parameters + ---------- + rgba : tuple + A tuple of 4 floating point values between 0.0 and 1.0 inclusive. + + Returns + ------- + wx_color : wx.Colour + A wx.Colour object. + """ + values = channels_to_ints(rgba) + return wx.Colour(*values) diff -Nru python-pyface-6.1.2/pyface/ui/wx/confirmation_dialog.py python-pyface-7.4.0/pyface/ui/wx/confirmation_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/confirmation_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/confirmation_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,30 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Bool, Enum, Instance, provides, Unicode +from traits.api import Bool, Enum, provides, Str -# Local imports. -from pyface.i_confirmation_dialog import IConfirmationDialog, MConfirmationDialog +from pyface.i_confirmation_dialog import ( + IConfirmationDialog, + MConfirmationDialog, +) from pyface.constant import CANCEL, YES, NO -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from .dialog import Dialog +from .image_resource import ImageResource @provides(IConfirmationDialog) @@ -36,28 +33,27 @@ IConfirmationDialog interface for the API documentation. """ - - #### 'IConfirmationDialog' interface ###################################### + # 'IConfirmationDialog' interface -------------------------------------# cancel = Bool(False) default = Enum(NO, YES, CANCEL) - image = Instance(ImageResource) + image = Image() - message = Unicode + message = Str() - informative = Unicode + informative = Str() - detail = Unicode + detail = Str() - no_label = Unicode + no_label = Str() - yes_label = Unicode + yes_label = Str() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_buttons(self, parent): sizer = wx.StdDialogButtonSizer() @@ -71,7 +67,7 @@ self._yes = yes = wx.Button(parent, wx.ID_YES, label) if self.default == YES: yes.SetDefault() - wx.EVT_BUTTON(parent, wx.ID_YES, self._on_yes) + parent.Bind(wx.EVT_BUTTON, self._on_yes, yes) sizer.AddButton(yes) # 'NO' button. @@ -83,7 +79,7 @@ self._no = no = wx.Button(parent, wx.ID_NO, label) if self.default == NO: no.SetDefault() - wx.EVT_BUTTON(parent, wx.ID_NO, self._on_no) + parent.Bind(wx.EVT_BUTTON, self._on_no, no) sizer.AddButton(no) if self.cancel: @@ -97,7 +93,7 @@ if self.default == CANCEL: cancel.SetDefault() - wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel) + parent.Bind(wx.EVT_BUTTON, self._wx_on_cancel, cancel) sizer.AddButton(cancel) sizer.Realize() @@ -111,7 +107,7 @@ # The image. if self.image is None: - image_rc = ImageResource('warning') + image_rc = ImageResource("warning") else: image_rc = self.image @@ -120,7 +116,7 @@ # The message. if self.informative: - message = self.message + '\n\n' + self.informative + message = self.message + "\n\n" + self.informative else: message = self.message message = wx.StaticText(panel, -1, message) @@ -131,11 +127,11 @@ return panel - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_yes(self, event): """ Called when the 'Yes' button is pressed. """ @@ -146,5 +142,3 @@ """ Called when the 'No' button is pressed. """ self.control.EndModal(wx.ID_NO) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/data_view/data_view_model.py python-pyface-7.4.0/pyface/ui/wx/data_view/data_view_model.py --- python-pyface-6.1.2/pyface/ui/wx/data_view/data_view_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/data_view/data_view_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,202 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import logging + +from pyface.data_view.data_view_errors import ( + DataViewGetError, DataViewSetError +) +from pyface.data_view.index_manager import Root +from wx.dataview import DataViewItem, DataViewModel as wxDataViewModel + + +logger = logging.getLogger(__name__) + + +# XXX This file is scaffolding and may need to be rewritten or expanded + +class DataViewModel(wxDataViewModel): + """ A wxDataViewModel that understands AbstractDataModels. """ + + def __init__(self, model): + super().__init__() + self.model = model + + @property + def model(self): + return self._model + + @model.setter + def model(self, model): + if hasattr(self, '_model'): + # disconnect trait listeners + self._model.observe( + self.on_structure_changed, + 'structure_changed', + dispatch='ui', + remove=True, + ) + self._model.observe( + self.on_values_changed, + 'values_changed', + dispatch='ui', + remove=True, + ) + self._model = model + else: + # model is being initialized + self._model = model + + # hook up trait listeners + self._model.observe( + self.on_structure_changed, + 'structure_changed', + dispatch='ui', + ) + self._model.observe( + self.on_values_changed, + 'values_changed', + dispatch='ui', + ) + + def on_structure_changed(self, event): + self.Cleared() + + def on_values_changed(self, event): + top, left, bottom, right = event.new + if top == () and bottom == (): + # this is a column header change, reset everything + self.Cleared() + elif left == () and right == (): + # this is a row header change + # XXX this is currently not supported and not needed + pass + else: + for i, (top_row, bottom_row) in enumerate(zip(top, bottom)): + if top_row != bottom_row: + break + top = top[:i+1] + bottom = bottom[:i+1] + + if top == bottom and left == right: + # single value change + self.ValueChanged(self._to_item(top), left[0]) + elif top == bottom: + # single item change + self.ItemChanged(self._to_item(top)) + else: + # multiple item change + items = [ + self._to_item(top[:i] + [row]) + for row in range(top[i], bottom[i]+1) + ] + self.ItemsChanged(items) + + def GetParent(self, item): + index = self._to_index(item) + if index == Root: + return DataViewItem() + parent, row = self.model.index_manager.get_parent_and_row(index) + parent_id = self.model.index_manager.id(parent) + if parent_id == 0: + return DataViewItem() + return DataViewItem(parent_id) + + def GetChildren(self, item, children): + index = self._to_index(item) + row_index = self.model.index_manager.to_sequence(index) + n_children = self.model.get_row_count(row_index) + for i in range(n_children): + child_index = self.model.index_manager.create_index(index, i) + child_id = self.model.index_manager.id(child_index) + children.append(DataViewItem(child_id)) + return n_children + + def IsContainer(self, item): + row_index = self._to_row_index(item) + return self.model.can_have_children(row_index) + + def HasValue(self, item, column): + return True + + def HasChildren(self, item): + row_index = self._to_row_index(item) + return self.model.has_child_rows(row_index) + + def GetValue(self, item, column): + row_index = self._to_row_index(item) + if column == 0: + column_index = () + else: + column_index = (column - 1,) + value_type = self.model.get_value_type(row_index, column_index) + try: + if value_type.has_text(self.model, row_index, column_index): + return value_type.get_text(self.model, row_index, column_index) + except DataViewGetError: + return '' + except Exception: + # unexpected error, log and raise + logger.exception( + "get data failed: row %r, column %r", + row_index, + column_index, + ) + raise + return '' + + def SetValue(self, value, item, column): + row_index = self._to_row_index(item) + if column == 0: + column_index = () + else: + column_index = (column - 1,) + try: + value_type = self.model.get_value_type(row_index, column_index) + value_type.set_text(self.model, row_index, column_index, value) + except DataViewSetError: + return False + except Exception: + logger.exception( + "SetValue failed: row %r, column %r, value %r", + row_index, + column_index, + value, + ) + return False + else: + return True + + def GetColumnCount(self): + return self.model.get_column_count() + 1 + + def GetColumnType(self, column): + # XXX This may need refinement when we deal with different editor types + return "string" + + def _to_row_index(self, item): + id = item.GetID() + if id is None: + id = 0 + index = self.model.index_manager.from_id(int(id)) + return self.model.index_manager.to_sequence(index) + + def _to_item(self, row_index): + if len(row_index) == 0: + return DataViewItem() + index = self.model.index_manager.from_sequence(row_index) + id = self.model.index_manager.id(index) + return DataViewItem(id) + + def _to_index(self, item): + id = item.GetID() + if id is None: + id = 0 + return self.model.index_manager.from_id(int(id)) diff -Nru python-pyface-6.1.2/pyface/ui/wx/data_view/data_view_widget.py python-pyface-7.4.0/pyface/ui/wx/data_view/data_view_widget.py --- python-pyface-6.1.2/pyface/ui/wx/data_view/data_view_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/data_view/data_view_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,212 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import logging +import warnings + +import wx +from wx.dataview import ( + DataViewCtrl, DataViewEvent, DataViewItemArray, + DataViewModel as wxDataViewModel, + DATAVIEW_CELL_EDITABLE, DATAVIEW_CELL_ACTIVATABLE, + DV_MULTIPLE, DV_NO_HEADER, EVT_DATAVIEW_SELECTION_CHANGED, + wxEVT_DATAVIEW_SELECTION_CHANGED +) + +from traits.api import Enum, Instance, observe, provides + +from pyface.data_view.i_data_view_widget import ( + IDataViewWidget, MDataViewWidget +) +from pyface.data_view.data_view_errors import DataViewGetError +from pyface.ui.wx.layout_widget import LayoutWidget +from .data_view_model import DataViewModel + + +logger = logging.getLogger(__name__) + + +# XXX This file is scaffolding and may need to be rewritten + +@provides(IDataViewWidget) +class DataViewWidget(MDataViewWidget, LayoutWidget): + """ The Wx implementation of the DataViewWidget. """ + + #: What can be selected. + selection_type = Enum("row") + + #: How selections are modified. + selection_mode = Enum("extended", "single") + + # Private traits -------------------------------------------------------- + + #: The QAbstractItemModel instance used by the view. This will + #: usually be a DataViewModel subclass. + _item_model = Instance(wxDataViewModel) + + # ------------------------------------------------------------------------ + # IDataViewWidget Interface + # ------------------------------------------------------------------------ + + def _create_item_model(self): + """ Create the DataViewItemModel which wraps the data model. """ + self._item_model = DataViewModel(self.data_model) + + def _get_control_header_visible(self): + """ Method to get the control's header visibility. """ + return not self.control.GetWindowStyleFlag() & DV_NO_HEADER + + def _set_control_header_visible(self, header_visible): + """ Method to set the control's header visibility. """ + old_visible = self._get_control_header_visible() + if header_visible != old_visible: + self.control.ToggleWindowStyle(DV_NO_HEADER) + + def _get_control_selection_type(self): + """ Toolkit specific method to get the selection type. """ + return "row" + + def _set_control_selection_type(self, selection_type): + """ Toolkit specific method to change the selection type. """ + if selection_type != "row": + warnings.warn( + "{!r} selection_type not supported in Wx".format( + selection_type + ), + RuntimeWarning, + ) + + def _get_control_selection_mode(self): + """ Toolkit specific method to get the selection mode. """ + if self.control.GetWindowStyleFlag() & DV_MULTIPLE: + return "extended" + else: + return "single" + + def _set_control_selection_mode(self, selection_mode): + """ Toolkit specific method to change the selection mode. """ + if selection_mode not in {'extended', 'single'}: + warnings.warn( + "{!r} selection_mode not supported in Wx".format( + selection_mode + ), + RuntimeWarning, + ) + return + old_mode = self._get_control_selection_mode() + if selection_mode != old_mode: + self.control.ToggleWindowStyle(DV_MULTIPLE) + + def _get_control_selection(self): + """ Toolkit specific method to get the selection. """ + return [ + (self._item_model._to_row_index(item), ()) + for item in self.control.GetSelections() + ] + + def _set_control_selection(self, selection): + """ Toolkit specific method to change the selection. """ + wx_selection = DataViewItemArray() + for row, column in selection: + item = self._item_model._to_item(row) + wx_selection.append(item) + self.control.SetSelections(wx_selection) + if wx.VERSION >= (4, 1): + if len(wx_selection) > 0: + item = wx_selection[-1] + else: + # a dummy item because we have nothing specific + item = self._item_model._to_item((0,)) + event = DataViewEvent( + wxEVT_DATAVIEW_SELECTION_CHANGED, + self.control, + item, + ) + else: + event = DataViewEvent( + wxEVT_DATAVIEW_SELECTION_CHANGED, + ) + wx.PostEvent(self.control, event) + + def _observe_control_selection(self, remove=False): + """ Toolkit specific method to watch for changes in the selection. """ + if remove: + self.control.Unbind( + EVT_DATAVIEW_SELECTION_CHANGED, + handler=self._update_selection, + ) + else: + self.control.Bind( + EVT_DATAVIEW_SELECTION_CHANGED, + self._update_selection, + ) + + # ------------------------------------------------------------------------ + # Widget Interface + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + """ Create the DataViewWidget's toolkit control. """ + self._create_item_model() + + control = DataViewCtrl(parent) + control.AssociateModel(self._item_model) + # required for wxPython refcounting system + self._item_model.DecRef() + + # create columns for view + value_type = self._item_model.model.get_value_type([], []) + try: + text = value_type.get_text(self._item_model.model, [], []) + except DataViewGetError: + text = '' + except Exception: + # unexpected error, log and raise + logger.exception("get header data failed: column ()") + raise + control.AppendTextColumn(text, 0, mode=DATAVIEW_CELL_ACTIVATABLE) + + for column in range(self._item_model.GetColumnCount()-1): + value_type = self._item_model.model.get_value_type([], [column]) + try: + text = value_type.get_text(self._item_model.model, [], [column]) + except DataViewGetError: + text = '' + except Exception: + # unexpected error, log and raise + logger.exception( + "get header data failed: column (%r,)", + column, + ) + raise + + control.AppendTextColumn( + text, + column+1, + mode=DATAVIEW_CELL_EDITABLE, + ) + return control + + def destroy(self): + """ Perform any actions required to destroy the control. """ + super().destroy() + # ensure that we release the reference to the item model + self._item_model = None + + # ------------------------------------------------------------------------ + # Private methods + # ------------------------------------------------------------------------ + + # Trait observers + + @observe('data_model', dispatch='ui') + def update_item_model(self, event): + if self._item_model is not None: + self._item_model.model = event.new diff -Nru python-pyface-6.1.2/pyface/ui/wx/data_view/data_wrapper.py python-pyface-7.4.0/pyface/ui/wx/data_view/data_wrapper.py --- python-pyface-6.1.2/pyface/ui/wx/data_view/data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/data_view/data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,84 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from wx import CustomDataObject, DataFormat, DataObject, DataObjectComposite + +from traits.api import Instance, provides + +from pyface.data_view.i_data_wrapper import IDataWrapper, MDataWrapper + + +@provides(IDataWrapper) +class DataWrapper(MDataWrapper): + """ WxPython implementaton of IDataWrapper. + + This wraps a DataObjectComposite which is assumed to contain a collection + of CustomDataObjects that store data associated by mimetype. Any other + DataObjects in the DataObjectComposite are ignored. + """ + + #: We always have a a composite data object with custom data objects in it + toolkit_data = Instance( + DataObjectComposite, + args=(), + allow_none=False, + ) + + def mimetypes(self): + """ Return a set of mimetypes holding data. + + Returns + ------- + mimetypes : set of str + The set of mimetypes currently storing data in the toolkit data + object. + """ + return { + wx_format.GetId() + for wx_format in self.toolkit_data.GetAllFormats() + } + + def get_mimedata(self, mimetype): + """ Get raw data for the given media type. + + Parameters + ---------- + mimetype : str + The mime media type to be extracted. + + Returns + ------- + mimedata : bytes + The mime media data as bytes. + """ + wx_format = DataFormat(mimetype) + if self.toolkit_data.IsSupported(wx_format): + data_object = self.toolkit_data.GetObject(wx_format) + if isinstance(data_object, CustomDataObject): + return bytes(data_object.GetData()) + return None + + def set_mimedata(self, mimetype, raw_data): + """ Set raw data for the given media type. + + Parameters + ---------- + mimetype : str + The mime media type to be extracted. + mimedata : bytes + The mime media data encoded as bytes.. + """ + wx_format = DataFormat(mimetype) + if self.toolkit_data.IsSupported(wx_format, dir=DataObject.Set): + data_object = self.toolkit_data.GetObject(wx_format) + else: + data_object = CustomDataObject(wx_format) + self.toolkit_data.Add(data_object) + data_object.SetData(raw_data) diff -Nru python-pyface-6.1.2/pyface/ui/wx/data_view/tests/test_data_wrapper.py python-pyface-7.4.0/pyface/ui/wx/data_view/tests/test_data_wrapper.py --- python-pyface-6.1.2/pyface/ui/wx/data_view/tests/test_data_wrapper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/data_view/tests/test_data_wrapper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,54 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + +try: + import wx +except ImportError: + wx_available = False +else: + from pyface.ui.wx.data_view.data_wrapper import DataWrapper + wx_available = True + + +@unittest.skipUnless(wx_available, "Test requires wx") +class TestDataWrapper(unittest.TestCase): + + def test_get_mimedata(self): + toolkit_data = wx.DataObjectComposite() + text_data = wx.CustomDataObject(wx.DataFormat('text/plain')) + text_data.SetData(b'hello world') + toolkit_data.Add(text_data) + data_wrapper = DataWrapper(toolkit_data=toolkit_data) + self.assertEqual(data_wrapper.mimetypes(), {'text/plain'}) + self.assertEqual(data_wrapper.get_mimedata('text/plain'), b'hello world') + + def test_set_mimedata(self): + data_wrapper = DataWrapper() + toolkit_data = data_wrapper.toolkit_data + data_wrapper.set_mimedata('text/plain', b'hello world') + self.assertEqual(data_wrapper.mimetypes(), {'text/plain'}) + wx_format = wx.DataFormat('text/plain') + self.assertTrue(toolkit_data.IsSupported(wx_format)) + text_data = toolkit_data.GetObject(wx_format) + self.assertEqual(text_data.GetData(), b'hello world') + + def test_ignore_non_custom(self): + toolkit_data = wx.DataObjectComposite() + html_data = wx.HTMLDataObject() + html_data.SetHTML("hello world") + toolkit_data.Add(html_data) + text_data = wx.CustomDataObject(wx.DataFormat('text/plain')) + text_data.SetData(b'hello world') + toolkit_data.Add(text_data) + data_wrapper = DataWrapper(toolkit_data=toolkit_data) + self.assertTrue('text/plain' in data_wrapper.mimetypes()) + self.assertEqual(data_wrapper.get_mimedata('text/plain'), b'hello world') diff -Nru python-pyface-6.1.2/pyface/ui/wx/dialog.py python-pyface-7.4.0/pyface/ui/wx/dialog.py --- python-pyface-6.1.2/pyface/ui/wx/dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,25 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. + import sys import wx -# Enthought library imports. -from traits.api import Bool, Enum, Int, provides, Str, Unicode -# Local imports. +from traits.api import Bool, Enum, Int, provides, Str + + from pyface.i_dialog import IDialog, MDialog from pyface.constant import OK, CANCEL, YES, NO from .window import Window @@ -32,15 +27,15 @@ # Map wx dialog related constants to the pyface equivalents. _RESULT_MAP = { - wx.ID_OK : OK, - wx.ID_CANCEL : CANCEL, - wx.ID_YES : YES, - wx.ID_NO : NO, - wx.ID_CLOSE : CANCEL, + wx.ID_OK: OK, + wx.ID_CANCEL: CANCEL, + wx.ID_YES: YES, + wx.ID_NO: NO, + wx.ID_CLOSE: CANCEL, # There seems to be a bug in wx.SingleChoiceDialog that allows it to return # 0 when it is closed via the window (closing it via the buttons works just # fine). - 0 : CANCEL + 0: CANCEL, } @@ -50,30 +45,29 @@ interface for the API documentation. """ + # 'IDialog' interface -------------------------------------------------# - #### 'IDialog' interface ################################################## - - cancel_label = Unicode + cancel_label = Str() - help_id = Str + help_id = Str() - help_label = Unicode + help_label = Str() - ok_label = Unicode + ok_label = Str() resizeable = Bool(True) return_code = Int(OK) - style = Enum('modal', 'nonmodal') + style = Enum("modal", "nonmodal") - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------# - title = Unicode("Dialog") + title = Str("Dialog") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_buttons(self, parent): sizer = wx.StdDialogButtonSizer() @@ -86,7 +80,7 @@ self._wx_ok = ok = wx.Button(parent, wx.ID_OK, label) ok.SetDefault() - wx.EVT_BUTTON(parent, wx.ID_OK, self._wx_on_ok) + parent.Bind(wx.EVT_BUTTON, self._wx_on_ok, id=wx.ID_OK) sizer.AddButton(ok) # The 'Cancel' button. @@ -96,7 +90,7 @@ label = "Cancel" self._wx_cancel = cancel = wx.Button(parent, wx.ID_CANCEL, label) - wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel) + parent.Bind(wx.EVT_BUTTON, self._wx_on_cancel, id=wx.ID_CANCEL) sizer.AddButton(cancel) # The 'Help' button. @@ -107,7 +101,7 @@ label = "Help" help = wx.Button(parent, wx.ID_HELP, label) - wx.EVT_BUTTON(parent, wx.ID_HELP, self._wx_on_help) + parent.Bind(wx.EVT_BUTTON, self._wx_on_help, id=wx.ID_HELP) sizer.AddButton(help) sizer.Realize() @@ -142,15 +136,15 @@ return panel def _show_modal(self): - if sys.platform == 'darwin': + if sys.platform == "darwin": # Calling Show(False) is needed on the Mac for the modal dialog # to show up at all. self.control.Show(False) return _RESULT_MAP[self.control.ShowModal()] - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): style = wx.DEFAULT_DIALOG_STYLE | wx.CLIP_CHILDREN @@ -158,10 +152,11 @@ if self.resizeable: style |= wx.RESIZE_BORDER - return wx.Dialog(parent, -1, self.title, self.position, self.size, - style) + return wx.Dialog( + parent, -1, self.title, self.position, self.size, style + ) - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _wx_on_ok(self, event): """ Called when the 'OK' button is pressed. """ diff -Nru python-pyface-6.1.2/pyface/ui/wx/directory_dialog.py python-pyface-7.4.0/pyface/ui/wx/directory_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/directory_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/directory_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,26 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Bool, provides, Unicode -# Local imports. +from traits.api import Bool, provides, Str + + from pyface.i_directory_dialog import IDirectoryDialog, MDirectoryDialog from .dialog import Dialog -import six @provides(IDirectoryDialog) @@ -35,43 +29,42 @@ IDirectoryDialog interface for the API documentation. """ + # 'IDirectoryDialog' interface ----------------------------------------- - #### 'IDirectoryDialog' interface ######################################### + default_path = Str() - default_path = Unicode - - message = Unicode + message = Str() new_directory = Bool(True) - path = Unicode + path = Str() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In wx this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): # Get the path of the chosen directory. - self.path = six.text_type(self.control.GetPath()) + self.path = str(self.control.GetPath()) # Let the window close as normal. - super(DirectoryDialog, self).close() + super().close() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # The default style. - style = wx.OPEN + style = wx.FD_OPEN # Create the wx style depending on which buttons are required etc. if self.new_directory: @@ -83,7 +76,6 @@ message = "Choose a directory" # Create the actual dialog. - return wx.DirDialog(parent, message=message, - defaultPath=self.default_path, style=style) - -#### EOF ###################################################################### + return wx.DirDialog( + parent, message=message, defaultPath=self.default_path, style=style + ) diff -Nru python-pyface-6.1.2/pyface/ui/wx/expandable_header.py python-pyface-7.4.0/pyface/ui/wx/expandable_header.py --- python-pyface-6.1.2/pyface/ui/wx/expandable_header.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/expandable_header.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,88 +1,110 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" A header for an entry in a collection of expandables. The header -provides a visual indicator of the current state, a text label, and a -'remove' button. """ -from __future__ import absolute_import +# Thanks for using Enthought open source! + +""" A header for an entry in a collection of expandables. + +The header provides a visual indicator of the current state, a text label, +and a 'remove' button. +""" + +import warnings -# Major package imports. import wx -# Enthought library imports. -from traits.api import Instance, Event, Str, Bool +from traits.api import Event, Str, Bool -# local imports from pyface.wx.util.font_helper import new_font_like +from pyface.ui_traits import Image from .image_resource import ImageResource from .widget import Widget class ExpandableHeader(Widget): - """ A header for an entry in a collection of expandables. The header - provides a visual indicator of the current state, a text label, and a - 'remove' button. """ - - # The title of the panel. - title = Str('Panel') - - # The carat image to show when the panel is collapsed. - collapsed_carat_image = Instance(ImageResource, ImageResource('carat_closed')) - # The carat image to show when the panel is expanded. - expanded_carat_image = Instance(ImageResource, ImageResource('carat_open')) - # The backing header image when the mouse is elsewhere - header_bar_image = Instance(ImageResource, - ImageResource('panel_gradient')) - # The backing header image when the mouse is over - header_mouseover_image = Instance(ImageResource, - ImageResource('panel_gradient_over')) + """ A header for an entry in a collection of expandables. + + The header provides a visual indicator of the current state, a text label, + and a 'remove' button. + """ + + #: The title of the panel. + title = Str("Panel") + + #: The carat image to show when the panel is collapsed. + collapsed_carat_image = Image(ImageResource("carat_closed")) + + #: The carat image to show when the panel is expanded. + expanded_carat_image = Image(ImageResource("carat_open")) + + #: The backing header image when the mouse is elsewhere + #: This is not used and deprecated. + header_bar_image = Image(ImageResource("panel_gradient")) - # The carat image to show when the panel is expanded. - remove_image = Instance(ImageResource, ImageResource('close')) + #: The backing header image when the mouse is over + #: This is not used and deprecated. + header_mouseover_image = Image(ImageResource("panel_gradient_over")) - # Represents the current state of the button. True means pressed. + #: The image to use for the close button. + #: This is not used and deprecated. + remove_image = Image(ImageResource("close")) + + #: Represents the current state of the panel. True means expanded. state = Bool(False) - #### Events #### + # Events ---- - # The panel has been expanded or collapsed - panel_expanded = Event + #: The panel has been expanded or collapsed + panel_expanded = Event() + #: The panel has been closed + panel_closed = Event() _CARAT_X = 4 - _CARAT_Y = 2 + _CARAT_Y = 4 _TEXT_Y = 0 _TEXT_X_OFFSET = 10 - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, container, **traits): + def __init__(self, parent=None, container=None, **traits): """ Creates the panel. """ + if container is not None: + warnings.warn( + "the container parameter is deprecated and will be " + "removed in a future Pyface release", + DeprecationWarning, + ) + self.observe( + lambda event: container.remove_panel(event.new), + "panel_closed", + ) + create = traits.pop("create", True) + # Base class constructor. - super(ExpandableHeader, self).__init__(**traits) + super().__init__(parent=parent, **traits) # Create the toolkit-specific control that represents the widget. - self.control = self._create_control(parent) - - self._container = container - return + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ @@ -94,45 +116,27 @@ expanded_carat = self.expanded_carat_image.create_image() self._expanded_bmp = expanded_carat.ConvertToBitmap() - header_bar = self.header_bar_image.create_image() - self._header_bmp = header_bar.ConvertToBitmap() - - header_bar_over = self.header_mouseover_image.create_image() - self._header_mouseover_bmp = header_bar_over.ConvertToBitmap() - - self._background_bmp = self._header_bmp - - close_image = self.remove_image.create_image() - self._remove_bmp = close_image.ConvertToBitmap() - # create our panel and initialize it appropriately sizer = wx.BoxSizer(wx.VERTICAL) - panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN) + panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN | wx.BORDER_SIMPLE) panel.SetSizer(sizer) panel.SetAutoLayout(True) - # needed on GTK systems for EVT_ERASE_BACKGROUND to work - panel.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) - # create the remove button - remove = wx.BitmapButton(panel, -1, self._remove_bmp, style=0, - pos=(-1, 3)) + remove = wx.BitmapButton.NewCloseButton(panel, -1) sizer.Add(remove, 0, wx.ALIGN_RIGHT, 5) # Create a suitable font. - self._font = new_font_like(wx.NORMAL_FONT, - point_size=wx.NORMAL_FONT.GetPointSize()- 1) + self._font = new_font_like( + wx.NORMAL_FONT, point_size=wx.NORMAL_FONT.GetPointSize() - 1 + ) height = self._get_preferred_height(parent, self.title, self._font) - panel.SetSize((-1, height)) - - wx.EVT_ERASE_BACKGROUND(panel, self._on_erase_background) - wx.EVT_ENTER_WINDOW(panel, self._on_enter_leave) - wx.EVT_LEAVE_WINDOW(panel, self._on_enter_leave) - wx.EVT_LEFT_DOWN(panel, self._on_down) - wx.EVT_RIGHT_DOWN(panel, self._on_down) + panel.SetMinSize((-1, height+2)) - wx.EVT_BUTTON(panel, remove.GetId(), self._on_remove) + panel.Bind(wx.EVT_PAINT, self._on_paint) + panel.Bind(wx.EVT_LEFT_DOWN, self._on_down) + panel.Bind(wx.EVT_BUTTON, self._on_remove) return panel @@ -142,11 +146,11 @@ dc = wx.MemoryDC() dc.SetFont(font) - text_w, text_h = dc.GetTextExtent(text) - text_h = text_h + self._TEXT_Y + metrics = dc.GetFontMetrics() + text_h = metrics.height + 2 * self._TEXT_Y - # add in width of buttons - carat_h = self._collapsed_bmp.GetHeight() + self._CARAT_Y + # add in height of buttons + carat_h = self._collapsed_bmp.GetHeight() + 2 * self._CARAT_Y return max(text_h, carat_h) @@ -160,77 +164,38 @@ dc.DrawBitmap(bmp, self._CARAT_X, self._CARAT_Y, True) - return - - def _tile_background_image(self, dc, width, height): - """ Tiles the background image. """ - - w = self._background_bmp.GetWidth() - h = self._background_bmp.GetHeight() - - x = 0 - while x < width: - y = 0 - while y < height: - dc.DrawBitmap(self._background_bmp, x, y) - - y = y + h - - x = x + w - - return - def _draw_title(self, dc): """ Draws the text label for the header. """ dc.SetFont(self._font) # Render the text. - dc.DrawText(self.title, self._carat_w + self._TEXT_X_OFFSET, - self._TEXT_Y) + dc.DrawText( + self.title, self._carat_w + self._TEXT_X_OFFSET, self._TEXT_Y + ) def _draw(self, dc): """ Draws the control. """ - size = self.control.GetClientSize() - - # Tile the background image. - self._tile_background_image(dc, size.width, size.height) - + # Draw the title text self._draw_title(dc) # Draw the carat button self._draw_carat_button(dc) - return - - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_erase_background(self, event): + def _on_paint(self, event): """ Called when the background of the panel is erased. """ - #print 'ImageButton._on_erase_background' - dc = event.GetDC() + # print('ImageButton._on_erase_background') + dc = wx.PaintDC(self.control) self._draw(dc) - return - - def _on_enter_leave(self, event): - """ Called when button is pressed. """ - - #print 'ExpandableHeader._on_enter_leave' - if event.Entering(): - self._background_bmp = self._header_mouseover_bmp - else: - self._background_bmp = self._header_bmp - - self.control.Refresh() - event.Skip() def _on_down(self, event): """ Called when button is pressed. """ - #print 'ImageButton._on_down' self.state = not self.state self.control.Refresh() @@ -240,5 +205,4 @@ def _on_remove(self, event): """ Called when remove button is pressed. """ - - self._container.remove_panel(self.title) + self.panel_closed = self diff -Nru python-pyface-6.1.2/pyface/ui/wx/expandable_panel.py python-pyface-7.4.0/pyface/ui/wx/expandable_panel.py --- python-pyface-6.1.2/pyface/ui/wx/expandable_panel.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/expandable_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,63 +1,64 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A Layered panel. """ -from __future__ import absolute_import -# Major package imports. +import warnings + import wx -from traits.api import Instance +from traits.api import Dict, Str -# Local imports. +from pyface.ui_traits import Image from .expandable_header import ExpandableHeader from .image_resource import ImageResource -from .widget import Widget +from .layout_widget import LayoutWidget -class ExpandablePanel(Widget): +class ExpandablePanel(LayoutWidget): """ An expandable panel. """ # The default style. STYLE = wx.CLIP_CHILDREN - collapsed_image = Instance(ImageResource, ImageResource('mycarat1')) - expanded_image = Instance(ImageResource, ImageResource('mycarat2')) + collapsed_image = Image(ImageResource("mycarat1")) + expanded_image = Image(ImageResource("mycarat2")) - ########################################################################### + _layers = Dict(Str) + + _headers = Dict(Str) + + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, **traits): + def __init__(self, parent=None, **traits): """ Creates a new LayeredPanel. """ + create = traits.pop("create", True) + # Base class constructor. - super(ExpandablePanel, self).__init__(**traits) + super().__init__(parent=parent, **traits) # Create the toolkit-specific control that represents the widget. - self.control = self._create_control(parent) - - # The layers in the panel. - # - # { str name : wx.Window layer } - self._layers = {} - self._headers = {} - - return - - ########################################################################### + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + # ------------------------------------------------------------------------ # 'Expandale' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_panel(self, name, layer): """ Adds a layer with the specified name. @@ -68,7 +69,7 @@ """ parent = self.control - sizer = self.control.GetSizer() + sizer = self.control.GetSizer() # Add the heading text. header = self._create_header(parent, text=name) @@ -95,18 +96,16 @@ sizer = self.control.GetSizer() panel = self._layers[name] header = self._headers[name] - sizer.Remove(panel) + # sizer.Remove(panel) panel.Destroy() - sizer.Remove(header) + # sizer.Remove(header) header.Destroy() sizer.Layout() - return - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ @@ -127,11 +126,13 @@ panel.SetAutoLayout(True) # Add the panel header. - heading = ExpandableHeader(panel, self, - title = text) + heading = ExpandableHeader(panel, title=text, create=False) + heading.create() sizer.Add(heading.control, 1, wx.EXPAND) - heading.on_trait_change(self._on_button, 'panel_expanded') + # connect observers + heading.observe(self._on_button, "panel_expanded") + heading.observe(self._on_panel_closed, "panel_closed") # Resize the panel to match the sizer's minimum size. sizer.Fit(panel) @@ -141,12 +142,12 @@ return panel - #### wx event handlers #################################################### + # event handlers ---------------------------------------------------- def _on_button(self, event): """ called when one of the expand/contract buttons is pressed. """ - header = event + header = event.new name = header.title visible = header.state @@ -155,6 +156,13 @@ sizer.Layout() # fixme: Errrr, maybe we can NOT do this! - w, h = self.control.GetSize() - self.control.SetSize((w+1, h+1)) + w, h = self.control.GetSize().Get() + self.control.SetSize((w + 1, h + 1)) self.control.SetSize((w, h)) + + def _on_panel_closed(self, event): + """ Called when the close button is clicked in a header. """ + + header = event.new + name = header.title + self.remove_panel(name) diff -Nru python-pyface-6.1.2/pyface/ui/wx/fields/combo_field.py python-pyface-7.4.0/pyface/ui/wx/fields/combo_field.py --- python-pyface-6.1.2/pyface/ui/wx/fields/combo_field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/fields/combo_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,15 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The Wx-specific implementation of the combo field class """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import wx diff -Nru python-pyface-6.1.2/pyface/ui/wx/fields/field.py python-pyface-7.4.0/pyface/ui/wx/fields/field.py --- python-pyface-6.1.2/pyface/ui/wx/fields/field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/fields/field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,88 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" The Wx-specific implementation of the text field class """ +# Thanks for using Enthought open source! -from __future__ import absolute_import, print_function, unicode_literals +""" The Wx-specific implementation of the text field class """ -from traits.api import Any, Instance, Unicode, provides -import wx +from traits.api import Any, provides from pyface.fields.i_field import IField, MField -from pyface.ui.wx.widget import Widget +from pyface.ui.wx.layout_widget import LayoutWidget @provides(IField) -class Field(MField, Widget): - """ The Wxspecific implementation of the field class +class Field(MField, LayoutWidget): + """ The Wx-specific implementation of the field class This is an abstract class which is not meant to be instantiated. """ #: The value held by the field. - value = Any - - #: A tooltip for the field. - tooltip = Unicode - - #: An optional context menu for the field. - context_menu = Instance('pyface.action.menu_manager.MenuManager') - - # ------------------------------------------------------------------------ - # IField interface - # ------------------------------------------------------------------------ - - def _initialize_control(self): - """ Perform any toolkit-specific initialization for the control. """ - self.control.SetToolTipString(self.tooltip) - self.control.Enable(self.enabled) - self.control.Show(self.visible) - - # ------------------------------------------------------------------------ - # IWidget interface - # ------------------------------------------------------------------------ - - def _create(self): - super(Field, self)._create() - self._add_event_listeners() - - def destroy(self): - self._remove_event_listeners() - super(Field, self).destroy() - - # ------------------------------------------------------------------------ - # Private interface - # ------------------------------------------------------------------------ - - def _get_control_tooltip(self): - """ Toolkit specific method to get the control's tooltip. """ - return self.control.GetToolTipString() - - def _set_control_tooltip(self, tooltip): - """ Toolkit specific method to set the control's tooltip. """ - self.control.SetToolTipString(tooltip) - - def _observe_control_context_menu(self, remove=False): - """ Toolkit specific method to change the control menu observer. """ - if remove: - self.control.Unbind(wx.EVT_CONTEXT_MENU, - handler=self._handle_context_menu) - else: - self.control.Bind(wx.EVT_CONTEXT_MENU, self._handle_context_menu) - - def _handle_control_context_menu(self, event): - """ Signal handler for displaying context menu. """ - if self.control is not None and self.context_menu is not None: - menu = self.context_menu.create_menu(self.control) - self.control.PopupMenu(menu) + value = Any() diff -Nru python-pyface-6.1.2/pyface/ui/wx/fields/spin_field.py python-pyface-7.4.0/pyface/ui/wx/fields/spin_field.py --- python-pyface-6.1.2/pyface/ui/wx/fields/spin_field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/fields/spin_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,11 @@ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! # # Author: Enthought, Inc. @@ -12,9 +13,6 @@ """ The Wx-specific implementation of the spin field class """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals -) import wx diff -Nru python-pyface-6.1.2/pyface/ui/wx/fields/text_field.py python-pyface-7.4.0/pyface/ui/wx/fields/text_field.py --- python-pyface-6.1.2/pyface/ui/wx/fields/text_field.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/fields/text_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,15 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017-19, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The Wx-specific implementation of the text field class """ -from __future__ import print_function, absolute_import import wx @@ -35,7 +31,7 @@ """ Create the toolkit-specific control that represents the widget. """ style = wx.TE_PROCESS_ENTER - if self.echo == 'password': + if self.echo == "password": style |= wx.TE_PASSWORD control = wx.TextCtrl(parent, -1, value=self.value, style=style) diff -Nru python-pyface-6.1.2/pyface/ui/wx/fields/time_field.py python-pyface-7.4.0/pyface/ui/wx/fields/time_field.py --- python-pyface-6.1.2/pyface/ui/wx/fields/time_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/fields/time_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,67 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The Wx-specific implementation of the time field class """ + +from datetime import time + +import wx.adv + +from traits.api import provides + +from pyface.fields.i_time_field import ITimeField, MTimeField +from .field import Field + + +@provides(ITimeField) +class TimeField(MTimeField, Field): + """ The Wx-specific implementation of the time field class """ + + # ------------------------------------------------------------------------ + # IWidget interface + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + + control = wx.adv.TimePickerCtrl(parent) + return control + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _get_control_value(self): + """ Toolkit specific method to get the control's value. """ + return time(*self.control.GetTime()) + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + self.control.SetTime(value.hour, value.minute, value.second) + wxdatetime = wx.DateTime.Now() + wxdatetime.SetHour(value.hour) + wxdatetime.SetMinute(value.minute) + wxdatetime.SetSecond(value.second) + event = wx.adv.DateEvent( + self.control, + wxdatetime, + wx.adv.EVT_TIME_CHANGED.typeId + ) + wx.PostEvent(self.control.GetEventHandler(), event) + + def _observe_control_value(self, remove=False): + """ Toolkit specific method to change the control value observer. """ + if remove: + self.control.Unbind( + wx.adv.EVT_TIME_CHANGED, + handler=self._update_value + ) + else: + self.control.Bind(wx.adv.EVT_TIME_CHANGED, self._update_value) diff -Nru python-pyface-6.1.2/pyface/ui/wx/fields/toggle_field.py python-pyface-7.4.0/pyface/ui/wx/fields/toggle_field.py --- python-pyface-6.1.2/pyface/ui/wx/fields/toggle_field.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/fields/toggle_field.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,125 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The Wx-specific implementation of the toggle field class """ + +import wx + +from traits.api import provides + +from pyface.fields.i_toggle_field import IToggleField, MToggleField +from .field import Field + + +@provides(IToggleField) +class ToggleField(MToggleField, Field): + """ The Wx-specific implementation of the toggle field class """ + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + # Toolkit control interface --------------------------------------------- + + def _get_control_value(self): + """ Toolkit specific method to get the control's value. """ + return self.control.GetValue() + + def _get_control_text(self): + """ Toolkit specific method to get the control's text. """ + return self.control.GetLabel() + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + self.control.SetValue(value) + + def _set_control_text(self, text): + """ Toolkit specific method to set the control's text. """ + self.control.SetLabel(text) + + def _set_control_icon(self, icon): + """ Toolkit specific method to set the control's icon. """ + # don't support icons on Wx for now + pass + + +class CheckBoxField(ToggleField): + """ The Wx-specific implementation of the checkbox class """ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + control = wx.CheckBox(parent) + return control + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + super()._set_control_value(value) + event = wx.CommandEvent(wx.EVT_CHECKBOX.typeId, self.control.GetId()) + event.SetInt(value) + wx.PostEvent(self.control.GetEventHandler(), event) + + def _observe_control_value(self, remove=False): + """ Toolkit specific method to change the control value observer. """ + if remove: + self.control.Unbind(wx.EVT_CHECKBOX, handler=self._update_value) + else: + self.control.Bind(wx.EVT_CHECKBOX, self._update_value) + + +class RadioButtonField(ToggleField): + """ The Wx-specific implementation of the radio button class + + This is intended to be used in groups, and shouldn't be used by itself. + """ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + control = wx.RadioButton(parent) + return control + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + super()._set_control_value(value) + event = wx.CommandEvent(wx.EVT_RADIOBUTTON.typeId, self.control.GetId()) + event.SetInt(value) + wx.PostEvent(self.control.GetEventHandler(), event) + + def _observe_control_value(self, remove=False): + """ Toolkit specific method to change the control value observer. """ + if remove: + self.control.Unbind(wx.EVT_RADIOBUTTON, handler=self._update_value) + else: + self.control.Bind(wx.EVT_RADIOBUTTON, self._update_value) + + +class ToggleButtonField(ToggleField): + """ The Wx-specific implementation of the toggle button class """ + + def _create_control(self, parent): + """ Create the toolkit-specific control that represents the widget. """ + control = wx.ToggleButton(parent) + return control + + def _set_control_value(self, value): + """ Toolkit specific method to set the control's value. """ + super()._set_control_value(value) + event = wx.CommandEvent(wx.EVT_TOGGLEBUTTON.typeId, self.control.GetId()) + event.SetInt(value) + wx.PostEvent(self.control.GetEventHandler(), event) + + def _observe_control_value(self, remove=False): + """ Toolkit specific method to change the control value observer. """ + if remove: + self.control.Unbind( + wx.EVT_TOGGLEBUTTON, + handler=self._update_value, + ) + else: + self.control.Bind(wx.EVT_TOGGLEBUTTON, self._update_value) diff -Nru python-pyface-6.1.2/pyface/ui/wx/file_dialog.py python-pyface-7.4.0/pyface/ui/wx/file_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/file_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/file_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,35 +1,29 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. + import os -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Enum, Int, List, provides, Unicode -# Local imports. +from traits.api import Enum, Int, List, provides, Str + + from pyface.i_file_dialog import IFileDialog, MFileDialog from .dialog import Dialog -import six @provides(IFileDialog) @@ -38,46 +32,45 @@ IFileDialog interface for the API documentation. """ + # 'IFileDialog' interface ---------------------------------------------# - #### 'IFileDialog' interface ############################################## + action = Enum("open", "open files", "save as") - action = Enum('open', 'open files', 'save as') + default_directory = Str() - default_directory = Unicode + default_filename = Str() - default_filename = Unicode + default_path = Str() - default_path = Unicode + directory = Str() - directory = Unicode + filename = Str() - filename = Unicode + path = Str() - path = Unicode + paths = List(Str) - paths = List(Unicode) - - wildcard = Unicode + wildcard = Str() wildcard_index = Int(0) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In wx this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): # Get the path of the chosen directory. - self.path = six.text_type(self.control.GetPath()) + self.path = str(self.control.GetPath()) # Work around wx bug throwing exception on cancel of file dialog - if len(self.path)>0: + if len(self.path) > 0: self.paths = self.control.GetPaths() else: self.paths = [] @@ -88,46 +81,54 @@ # Get the index of the selected filter. self.wildcard_index = self.control.GetFilterIndex() # Let the window close as normal. - super(FileDialog, self).close() + super().close() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # If the caller provided a default path instead of a default directory # and filename, split the path into it directory and filename # components. - if len(self.default_path) != 0 and len(self.default_directory) == 0 \ - and len(self.default_filename) == 0: - default_directory, default_filename = os.path.split(self.default_path) + if ( + len(self.default_path) != 0 + and len(self.default_directory) == 0 + and len(self.default_filename) == 0 + ): + default_directory, default_filename = os.path.split( + self.default_path + ) else: default_directory = self.default_directory default_filename = self.default_filename - if self.action == 'open': + if self.action == "open": style = wx.FD_OPEN - elif self.action == 'open files': + elif self.action == "open files": style = wx.FD_OPEN | wx.FD_MULTIPLE else: style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT # Create the actual dialog. - dialog = wx.FileDialog(parent, self.title, defaultDir=default_directory, - defaultFile=default_filename, style=style, - wildcard=self.wildcard.rstrip('|')) + dialog = wx.FileDialog( + parent, + self.title, + defaultDir=default_directory, + defaultFile=default_filename, + style=style, + wildcard=self.wildcard.rstrip("|"), + ) dialog.SetFilterIndex(self.wildcard_index) return dialog - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _wildcard_default(self): """ Return the default wildcard. """ return self.WILDCARD_ALL - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/font_dialog.py python-pyface-7.4.0/pyface/ui/wx/font_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/font_dialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/font_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,63 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The interface for a dialog that allows the user to select a font. """ + +import wx + +from traits.api import provides + +from pyface.font import Font +from pyface.ui_traits import PyfaceFont +from pyface.i_font_dialog import IFontDialog +from .dialog import Dialog + +# The WxPython version in a convenient to compare form. +wx_version = tuple(int(x) for x in wx.__version__.split('.')[:3]) + + +@provides(IFontDialog) +class FontDialog(Dialog): + """ A dialog for selecting fonts. + """ + + # 'IFontDialog' interface ---------------------------------------------- + + #: The font in the dialog. + font = PyfaceFont() + + # ------------------------------------------------------------------------ + # 'IDialog' interface. + # ------------------------------------------------------------------------ + + def _create_contents(self, parent): + # In wx this is a canned dialog. + pass + + # ------------------------------------------------------------------------ + # 'IWindow' interface. + # ------------------------------------------------------------------------ + + def close(self): + font_data = self.control.GetFontData() + wx_font = font_data.GetChosenFont() + self.font = Font.from_toolkit(wx_font) + super(FontDialog, self).close() + + # ------------------------------------------------------------------------ + # 'IWidget' interface. + # ------------------------------------------------------------------------ + + def _create_control(self, parent): + wx_font = self.font.to_toolkit() + data = wx.FontData() + data.SetInitialFont(wx_font) + dialog = wx.FontDialog(parent, data) + return dialog diff -Nru python-pyface-6.1.2/pyface/ui/wx/font.py python-pyface-7.4.0/pyface/ui/wx/font.py --- python-pyface-6.1.2/pyface/ui/wx/font.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/font.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,182 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" +Font conversion utilities + +This module provides facilities for converting between pyface Font objects +and Wx Font objects, trying to keep as much similarity as possible between +them. +""" + +import wx + +# font weight and size features changed in wxPython 4.1/wxWidgets 3.1 +wx_python_4_1 = (wx.VERSION >= (4, 1)) + + +wx_family_to_generic_family = { + wx.FONTFAMILY_DEFAULT: 'default', + wx.FONTFAMILY_DECORATIVE: 'fantasy', + wx.FONTFAMILY_ROMAN: 'serif', + wx.FONTFAMILY_SCRIPT: 'cursive', + wx.FONTFAMILY_SWISS: 'sans-serif', + wx.FONTFAMILY_MODERN: 'monospace', + wx.FONTFAMILY_TELETYPE: 'typewriter', +} +generic_family_to_wx_family = { + 'default': wx.FONTFAMILY_DEFAULT, + 'fantasy': wx.FONTFAMILY_DECORATIVE, + 'decorative': wx.FONTFAMILY_DECORATIVE, + 'serif': wx.FONTFAMILY_ROMAN, + 'roman': wx.FONTFAMILY_ROMAN, + 'cursive': wx.FONTFAMILY_SCRIPT, + 'script': wx.FONTFAMILY_SCRIPT, + 'sans-serif': wx.FONTFAMILY_SWISS, + 'swiss': wx.FONTFAMILY_SWISS, + 'monospace': wx.FONTFAMILY_MODERN, + 'modern': wx.FONTFAMILY_MODERN, + 'typewriter': wx.FONTFAMILY_TELETYPE, + 'teletype': wx.FONTFAMILY_TELETYPE, +} + +if wx_python_4_1: + weight_to_wx_weight = { + 100: wx.FONTWEIGHT_THIN, + 200: wx.FONTWEIGHT_EXTRALIGHT, + 300: wx.FONTWEIGHT_LIGHT, + 400: wx.FONTWEIGHT_NORMAL, + 500: wx.FONTWEIGHT_MEDIUM, + 600: wx.FONTWEIGHT_SEMIBOLD, + 700: wx.FONTWEIGHT_BOLD, + 800: wx.FONTWEIGHT_EXTRABOLD, + 900: wx.FONTWEIGHT_HEAVY, + 1000: wx.FONTWEIGHT_EXTRAHEAVY, + } + wx_weight_to_weight = { + wx.FONTWEIGHT_THIN: 'thin', + wx.FONTWEIGHT_EXTRALIGHT: 'extra-light', + wx.FONTWEIGHT_LIGHT: 'light', + wx.FONTWEIGHT_NORMAL: 'normal', + wx.FONTWEIGHT_MEDIUM: 'medium', + wx.FONTWEIGHT_SEMIBOLD: 'semibold', + wx.FONTWEIGHT_BOLD: 'bold', + wx.FONTWEIGHT_EXTRABOLD: 'extra-bold', + wx.FONTWEIGHT_HEAVY: 'heavy', + wx.FONTWEIGHT_EXTRAHEAVY: 'extra-heavy', + wx.FONTWEIGHT_MAX: 'extra-heavy', + } +else: + weight_to_wx_weight = { + 100: wx.FONTWEIGHT_LIGHT, + 200: wx.FONTWEIGHT_LIGHT, + 300: wx.FONTWEIGHT_LIGHT, + 400: wx.FONTWEIGHT_NORMAL, + 500: wx.FONTWEIGHT_NORMAL, + 600: wx.FONTWEIGHT_BOLD, + 700: wx.FONTWEIGHT_BOLD, + 800: wx.FONTWEIGHT_BOLD, + 900: wx.FONTWEIGHT_MAX, + 1000: wx.FONTWEIGHT_MAX, + } + wx_weight_to_weight = { + wx.FONTWEIGHT_LIGHT: 'light', + wx.FONTWEIGHT_NORMAL: 'normal', + wx.FONTWEIGHT_BOLD: 'bold', + wx.FONTWEIGHT_MAX: 'extra-heavy', + } + +style_to_wx_style = { + 'normal': wx.FONTSTYLE_NORMAL, + 'oblique': wx.FONTSTYLE_SLANT, + 'italic': wx.FONTSTYLE_ITALIC, +} +wx_style_to_style = {value: key for key, value in style_to_wx_style.items()} + + +def font_to_toolkit_font(font): + """ Convert a Pyface font to a wx.font Font. + + Wx fonts have no notion of stretch values or small-caps or overline + variants, so these are ignored when converting. + + Parameters + ---------- + font : pyface.font.Font + The Pyface font to convert. + + Returns + ------- + wx_font : wx.font.Font + The best matching wx font. + """ + size = font.size + for family in font.family: + if family in generic_family_to_wx_family: + default_family = generic_family_to_wx_family[family] + break + else: + default_family = wx.FONTFAMILY_DEFAULT + weight = weight_to_wx_weight[font.weight_] + style = style_to_wx_style[font.style] + underline = ('underline' in font.decorations) + + # get a default font candidate + wx_font = wx.Font(size, default_family, style, weight, underline) + for face in font.family: + # don't try to match generic family + if face in generic_family_to_wx_family: + break + wx_font = wx.Font( + size, default_family, style, weight, underline, face) + # we have a match, so stop + if wx_font.GetFaceName().lower() == face.lower(): + break + + wx_font.SetStrikethrough('strikethrough' in font.decorations) + return wx_font + + +def toolkit_font_to_properties(toolkit_font): + """ Convert a Wx Font to a dictionary of font properties. + + Parameters + ---------- + toolkit_font : wx.font.Font + The Wx font to convert. + + Returns + ------- + properties : dict + Font properties suitable for use in creating a Pyface Font. + """ + family = wx_family_to_generic_family[toolkit_font.GetFamily()] + face = toolkit_font.GetFaceName() + if wx_python_4_1: + size = toolkit_font.GetFractionalPointSize() + else: + size = toolkit_font.GetPointSize() + style = wx_style_to_style[toolkit_font.GetStyle()] + weight = wx_weight_to_weight[toolkit_font.GetWeight()] + decorations = set() + if toolkit_font.GetUnderlined(): + decorations.add('underline') + if toolkit_font.GetStrikethrough(): + decorations.add('strikethrough') + + return { + 'family': [face, family], + 'size': size, + 'weight': weight, + 'stretch': 'normal', + 'style': style, + 'variants': set(), + 'decorations': decorations, + } diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/api.py python-pyface-7.4.0/pyface/ui/wx/grid/api.py --- python-pyface-6.1.2/pyface/ui/wx/grid/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,22 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from __future__ import absolute_import +# Thanks for using Enthought open source! + from .grid import Grid from .grid_model import GridModel, GridSortEvent from .composite_grid_model import CompositeGridModel from .inverted_grid_model import InvertedGridModel from .simple_grid_model import SimpleGridModel, GridRow, GridColumn -from .trait_grid_model import TraitGridModel, TraitGridColumn, \ - TraitGridSelection +from .trait_grid_model import ( + TraitGridModel, + TraitGridColumn, + TraitGridSelection, +) from .grid_cell_renderer import GridCellRenderer - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/checkbox_image_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/checkbox_image_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/checkbox_image_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/checkbox_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,41 +1,36 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2006, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A renderer which displays a checked-box for a True value and an unchecked box for a false value. """ -# Enthought-library imports + from pyface.image_resource import ImageResource -# local imports + from .mapped_grid_cell_image_renderer import MappedGridCellImageRenderer -checked_image_map = { True: ImageResource('checked'), - False: ImageResource('unchecked'), - } +checked_image_map = { + True: ImageResource("checked"), + False: ImageResource("unchecked"), +} -class CheckboxImageRenderer(MappedGridCellImageRenderer): - def __init__(self, display_text = False): +class CheckboxImageRenderer(MappedGridCellImageRenderer): + def __init__(self, display_text=False): text_map = None if display_text: - text_map = { True: 'True', False: 'False' } + text_map = {True: "True", False: "False"} # Base-class constructor - super(CheckboxImageRenderer, self).__init__(checked_image_map, - text_map) + super().__init__(checked_image_map, text_map) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/checkbox_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/checkbox_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/checkbox_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/checkbox_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,34 +1,32 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2006, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + +import logging -# local imports from .checkbox_image_renderer import CheckboxImageRenderer from .grid_cell_renderer import GridCellRenderer -class CheckboxRenderer(GridCellRenderer): +logger = logging.getLogger(__name__) + + +class CheckboxRenderer(GridCellRenderer): def __init__(self, **traits): # base-class constructor - super(CheckboxRenderer, self).__init__(**traits) + super().__init__(**traits) # initialize the renderer, if it hasn't already been initialized if self.renderer is None: self.renderer = CheckboxImageRenderer() - return - def on_left_click(self, grid, row, col): """ Toggles the value. """ @@ -36,9 +34,7 @@ try: grid.model.set_value(row, col, not value) - except: - pass + except Exception: + logger.exception("Can't set cell value") return True - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/combobox_focus_handler.py python-pyface-7.4.0/pyface/ui/wx/grid/combobox_focus_handler.py --- python-pyface-6.1.2/pyface/ui/wx/grid/combobox_focus_handler.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/combobox_focus_handler.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,151 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Workaround for combobox focus problem in wx 2.6. """ - -# Major package imports -import wx - -#------------------------------------------------------------------------------- -# Constants: -#------------------------------------------------------------------------------- - -# Mapping from key code to key event handler names: -Handlers = { - wx.WXK_LEFT: '_left_key', - wx.WXK_RIGHT: '_right_key', - wx.WXK_UP: '_up_key', - wx.WXK_DOWN: '_down_key', - wx.WXK_ESCAPE: '_escape_key' -} - -#------------------------------------------------------------------------------- -# 'ComboboxFocusHandler' class: -#------------------------------------------------------------------------------- - -class ComboboxFocusHandler(wx.EvtHandler): - - def __init__(self, grid): - wx.EvtHandler.__init__(self) - - self._grid = grid - wx.EVT_KEY_DOWN(self, self._on_key) - - def _on_key(self, evt): - """ Called when a key is pressed. """ - getattr( self, Handlers.get( evt.GetKeyCode(), '_ignore_key' ))( evt ) - -#-- Key Event Handlers -------------------------------------------------------- - - def _ignore_key ( self, evt ): - evt.Skip() - - def _escape_key ( self, evt ): - self._grid.DisableCellEditControl() - - def _left_key ( self, evt ): - if not (evt.ControlDown() or evt.AltDown()): - evt.Skip() - return - - grid, row, col, rows, cols = self._grid_info() - - grid._no_reset_row = True - - first = True - while first or (not self._edit_cell( row, col )): - col -= 1 - if col < 0: - col = cols - 1 - row -= 1 - if row < 0: - if not first: - break - - row = rows - 1 - - first = False - - def _right_key ( self, evt ): - if not (evt.ControlDown() or evt.AltDown()): - evt.Skip() - return - - grid, row, col, rows, cols = self._grid_info() - - grid._no_reset_row = True - - first = True - while first or (not self._edit_cell( row, col )): - col += 1 - if col >= cols: - col = 0 - row += 1 - if row >= rows: - if not first: - break - - row = 0 - - first = False - - def _up_key ( self, evt ): - if not (evt.ControlDown() or evt.AltDown()): - evt.Skip() - return - - grid, row, col, rows, cols = self._grid_info() - - grid._no_reset_col = True - - row -= 1 - if row < 0: - row = rows - 1 - - self._edit_cell( row, col ) - - def _down_key ( self, evt ): - if not (evt.ControlDown() or evt.AltDown()): - evt.Skip() - return - - grid, row, col, rows, cols = self._grid_info() - - grid._no_reset_col = True - - row += 1 - if row >= rows: - row = 0 - - self._edit_cell( row, col ) - -#-- Private Methods ----------------------------------------------------------- - - def _grid_info ( self ): - g = self._grid - return ( g, g.GetGridCursorRow(), g.GetGridCursorCol(), - g.GetNumberRows(), g.GetNumberCols() ) - - def _edit_cell ( self, row, col ): - grid = self._grid - grid.DisableCellEditControl() - grid.SetGridCursor( row, col ) - if not grid.CanEnableCellControl(): - return False - - grid.EnableCellEditControl() - grid.MakeCellVisible( row, col ) - - return True - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/composite_grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/composite_grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/composite_grid_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/composite_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,52 +1,47 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + + +from traits.api import Dict, Instance, List, Union -# Enthought library imports. -from traits.api import Dict, List, Trait -# local imports from .grid_model import GridModel, GridRow + class CompositeGridModel(GridModel): """ A CompositeGridModel is a model whose underlying data is a collection of other grid models. """ # The models this model is comprised of. - data = List(GridModel) + data = List(Instance(GridModel)) # The rows in the model. - rows = Trait(None, None, List(GridRow)) + rows = Union(None, List(Instance(GridRow))) # The cached data indexes. - _data_index = Dict + _data_index = Dict() - ######################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ######################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Create a CompositeGridModel object. """ # Base class constructor - super(CompositeGridModel, self).__init__(**traits) + super().__init__(**traits) self._row_count = None - return - - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_column_count(self): """ Return the number of columns for this table. """ @@ -179,7 +174,6 @@ return model.get_type(row, new_col) - def get_value(self, row, col): """ Return the value stored in the table at (row, col). """ model, new_col = self._resolve_column_index(col) @@ -214,7 +208,6 @@ return coords - # fixme: this context menu stuff is going in here for now, but it # seems like this is really more of a view piece than a model piece. # this is how the tree control does it, however, so we're duplicating @@ -294,9 +287,9 @@ return model.get_cell_valignment(row, new_col) - ######################################################################### + # ------------------------------------------------------------------------ # protected 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def _delete_rows(self, pos, num_rows): """ Implementation method for delete_rows. Should return the number of rows that were deleted. """ @@ -323,16 +316,16 @@ model._set_value(row, new_col, value) return 0 - ######################################################################### + # ------------------------------------------------------------------------ # private interface - ######################################################################### + # ------------------------------------------------------------------------ def _resolve_column_index(self, index): """ Resolves a column index into the correct model and adjusted index. Returns the target model and the corrected index. """ real_index = index - cached = None #self._data_index.get(index) + cached = None # self._data_index.get(index) if cached is not None: model, col_index = cached else: @@ -357,8 +350,6 @@ self._data_index.clear() - return - def _data_items_changed(self): """ Called when the members of the data trait have changed. @@ -366,7 +357,3 @@ results of the column lookups is wrong and needs to be invalidated. """ self._data_index.clear() - - return - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/edit_image_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/edit_image_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/edit_image_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/edit_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,28 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + -# ETS imports from pyface.image_resource import ImageResource # Local import from .grid_cell_image_renderer import GridCellImageRenderer + class EditImageRenderer(GridCellImageRenderer): - image = ImageResource('table_edit') + image = ImageResource("table_edit") def __init__(self, **kw): - super(EditImageRenderer, self).__init__(self, **kw) + super().__init__(self, **kw) def get_image_for_cell(self, grid, row, col): """ returns the image resource for the table_edit bitmap """ @@ -32,7 +29,7 @@ obj = grid.GetTable().model.get_rows_drag_value([row])[0] - if (not hasattr(obj, 'editable')) or obj.editable: + if (not hasattr(obj, "editable")) or obj.editable: return self.image return None diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/edit_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/edit_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/edit_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/edit_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,28 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + from .edit_image_renderer import EditImageRenderer from .grid_cell_renderer import GridCellRenderer -class EditRenderer(GridCellRenderer): +class EditRenderer(GridCellRenderer): def __init__(self, **traits): # base-class constructor - super(EditRenderer, self).__init__(**traits) + super().__init__(**traits) # initialize the renderer, if it hasn't already been initialized if self.renderer is None: self.renderer = EditImageRenderer() - return - def on_left_click(self, grid, row, col): """ Calls edit_traits on the object represented by the row. """ @@ -36,5 +31,5 @@ # allow editting if the obj does not have an editable trait # or if the editable trait is True - if (not hasattr(obj, 'editable')) or obj.editable: - obj.edit_traits(kind='live') + if (not hasattr(obj, "editable")) or obj.editable: + obj.edit_traits(kind="live") diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/grid_cell_image_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/grid_cell_image_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/grid_cell_image_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/grid_cell_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,40 +1,38 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A renderer which will display a cell-specific image in addition to some text displayed in the same way the standard string renderer normally would. """ -# Major package imports + import wx -from wx.grid import PyGridCellRenderer +from wx.grid import GridCellRenderer from wx.grid import GridCellStringRenderer from wx import SOLID, Brush, Rect, TRANSPARENT_PEN -class GridCellImageRenderer(PyGridCellRenderer): + +class GridCellImageRenderer(GridCellRenderer): """ A renderer which will display a cell-specific image in addition to some text displayed in the same way the standard string renderer normally would. """ - def __init__(self, provider = None): + def __init__(self, provider=None): """ Build a new PyGridCellImageRenderer. 'provider', if provided, should implement get_image_for_cell(row, col) to specify an image to appear in the cell at row, col. """ - PyGridCellRenderer.__init__(self) + GridCellRenderer.__init__(self) # save the string renderer to use for text. self._string_renderer = GridCellStringRenderer() @@ -43,9 +41,9 @@ return - ######################################################################### + # ------------------------------------------------------------------------ # GridCellRenderer interface. - ######################################################################### + # ------------------------------------------------------------------------ def Draw(self, grid, attr, dc, rect, row, col, isSelected): """ Draw the appropriate icon into the specified grid cell. """ @@ -92,18 +90,17 @@ dc.DrawBitmap(bmp, x, y, 1) x += bmp.GetWidth() - if text is not None and text != '': + if text is not None and text != "": width = rect.x + rect.width - x height = rect.y + rect.height - y # draw any text that should be included new_rect = Rect(x, y, width, height) - self._string_renderer.Draw(grid, attr, dc, new_rect, - row, col, isSelected) + self._string_renderer.Draw( + grid, attr, dc, new_rect, row, col, isSelected + ) dc.DestroyClippingRegion() - return - def GetBestSize(self, grid, attr, dc, row, col): """ Determine best size for the cell. """ @@ -115,32 +112,35 @@ else: bmp_size = wx.Size(0, 0) - # find the correct text for this cell text = self._get_text(grid, row, col) if text is not None: - text_size = self._string_renderer.GetBestSize(grid, attr, dc, - row, col) + text_size = self._string_renderer.GetBestSize( + grid, attr, dc, row, col + ) else: text_size = wx.Size(0, 0) - result = wx.Size(bmp_size.width + text_size.width, - max(bmp_size.height, text_size.height)) + result = wx.Size( + bmp_size.width + text_size.width, + max(bmp_size.height, text_size.height), + ) return result def Clone(self): return GridCellImageRenderer(self._provider) - ######################################################################### + # ------------------------------------------------------------------------ # protected 'GridCellIconRenderer' interface. - ######################################################################### + # ------------------------------------------------------------------------ def _get_image(self, grid, row, col): """ Returns the correct bmp for the data at row, col. """ bmp = None - if self._provider is not None and \ - hasattr(self._provider, 'get_image_for_cell'): + if self._provider is not None and hasattr( + self._provider, "get_image_for_cell" + ): # get the image from the specified provider img = self._provider.get_image_for_cell(grid, row, col) if img is not None: @@ -154,14 +154,12 @@ """ Returns the correct text for the data at row, col. """ text = None - if self._provider is not None and \ - hasattr(self._provider, 'get_text_for_cell'): + if self._provider is not None and hasattr( + self._provider, "get_text_for_cell" + ): # get the image from the specified provider text = self._provider.get_text_for_cell(grid, row, col) else: text = grid.GetCellValue(row, col) return text - -#### EOF ###################################################################### - diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/grid_cell_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/grid_cell_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/grid_cell_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/grid_cell_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2006, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + -# Enthought library imports from traits.api import Any, HasTraits + class GridCellRenderer(HasTraits): # The toolkit-specific renderer for this cell. - renderer = Any + renderer = Any() # A handler to be invoked on right-button mouse clicks. def on_right_click(self, grid, row, col): @@ -43,5 +40,3 @@ # Clean-up! def dispose(self): pass - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/grid_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Model for grid views. """ -# Major package imports -# Enthought library imports. -from traits.api import Any, Bool, Event, HasPrivateTraits, HasTraits, \ - Instance, Int, Str, Tuple +from traits.api import ( + Bool, + Event, + HasPrivateTraits, + HasTraits, + Instance, + Int, + Str, + Tuple, +) + # The classes below are part of the table specification. class GridRow(HasTraits): @@ -27,12 +31,14 @@ read_only = Bool(False) # The label for this column - label = Str + label = Str() + # We specify the same info for rows and for columns, but add a GridColumn # name for clarity. GridColumn = GridRow + class GridSortData(HasTraits): """ An event that signals a sorting has taken place. @@ -41,22 +47,25 @@ no sort has been applied (or a previous sort has been unapplied). The reversed flag indicates that the sort has been done in reverse order. """ + index = Int(-1) reversed = Bool(False) + # for backwards compatibility GridSortEvent = GridSortData + class GridModel(HasPrivateTraits): """ Model for grid views. """ - #### 'GridModel' interface ################################################ + # 'GridModel' interface ------------------------------------------------ # Fire when the structure of the underlying grid model has changed. - structure_changed = Event + structure_changed = Event() # Fire when the content of the underlying grid model has changed. - content_changed = Event + content_changed = Event() # Column model is currently sorted on. column_sorted = Instance(GridSortData) @@ -64,68 +73,57 @@ # The cell (row, col) the mouse is currently in. mouse_cell = Tuple(Int, Int) - #### Events #### + # Events ---- # A row was inserted or appended to this model - rows_added = Event + rows_added = Event() # A column was inserted or appended to this model - columns_added = Event + columns_added = Event() # A row sort took place - row_sorted = Event + row_sorted = Event() # Event fired when a cell is clicked on: - click = Event # = (row, column) that was clicked on + click = Event # = (row, column) that was clicked on # Event fired when a cell is double-clicked on: - dclick = Event # = (row, column) that was double-clicked on - - ######################################################################### - # 'object' interface. - ######################################################################### - def __init__(self, **traits): - """ Creates a new grid model. """ - - # Base class constructors. - super(GridModel, self).__init__(**traits) - - return + dclick = Event # = (row, column) that was double-clicked on - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface -- Subclasses MUST override the following - ######################################################################### + # ------------------------------------------------------------------------ def get_column_count(self): """ Return the number of columns for this table. """ - raise NotImplementedError + raise NotImplementedError() def get_column_name(self, index): """ Return the name of the column specified by the (zero-based) index. """ - raise NotImplementedError + raise NotImplementedError() def get_row_count(self): """ Return the number of rows for this table. """ - raise NotImplementedError + raise NotImplementedError() def get_row_name(self, index): """ Return the name of the row specified by the (zero-based) index. """ - raise NotImplementedError + raise NotImplementedError() def get_value(self, row, col): """ Return the value stored in the table at (row, col). """ - raise NotImplementedError + raise NotImplementedError() def is_cell_empty(self, row, col): """ Returns True if the cell at (row, col) has a None value, False otherwise.""" - raise NotImplementedError + raise NotImplementedError() - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface -- Subclasses MAY override the following - ######################################################################### + # ------------------------------------------------------------------------ def get_cols_drag_value(self, cols): """ Return the value to use when the specified columns are dragged or @@ -167,7 +165,7 @@ def no_column_sort(self): """ Turn off any column sorting of the model data. """ - raise NotImplementedError + raise NotImplementedError() def is_column_read_only(self, index): """ Return True if the column specified by the zero-based index @@ -213,7 +211,7 @@ def no_row_sort(self): """ Turn off any row sorting of the model data. """ - raise NotImplementedError + raise NotImplementedError() def is_row_read_only(self, index): """ Return True if the row specified by the zero-based index @@ -222,7 +220,7 @@ def get_type(self, row, col): """ Return the value stored in the table at (row, col). """ - raise NotImplementedError + raise NotImplementedError() def get_cell_drag_value(self, row, col): """ Return the value to use when the specified cell is dragged or @@ -253,7 +251,6 @@ return selection_list - # fixme: this context menu stuff is going in here for now, but it # seems like this is really more of a view piece than a model piece. # this is how the tree control does it, however, so we're duplicating @@ -277,11 +274,18 @@ Note that subclasses should not override this method, but should override the _set_value method instead. """ - #print 'GridModel.set_value row: ', row, ' col: ', col, ' value: ', value - rows_appended = self._set_value(row, col, value) + # grids are passing only strings, this is temp workaraound + if isinstance(value, str): + try: + value = int(value) + except ValueError: + try: + value = float(value) + except ValueError: + value = value + self._set_value(row, col, value) self.fire_content_changed() - return def is_cell_read_only(self, row, col): """ Returns True if the cell at (row, col) is not editable, @@ -319,23 +323,19 @@ or 'center' for center alignment. """ return None - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface -- Subclasses MAY NOT override the following - ######################################################################### + # ------------------------------------------------------------------------ def fire_content_changed(self): """ Fires the appearance changed event. """ - self.content_changed = 'changed' - - return + self.content_changed = "changed" def fire_structure_changed(self): """ Fires the appearance changed event. """ - self.structure_changed = 'changed' - - return + self.structure_changed = "changed" def delete_rows(self, pos, num_rows): """ Removes rows pos through pos + num_rows from the model. @@ -385,11 +385,11 @@ return True - ######################################################################### + # ------------------------------------------------------------------------ # protected 'GridModel' interface -- Subclasses should override these # if they wish to support the # specific actions. - ######################################################################### + # ------------------------------------------------------------------------ def _delete_rows(self, pos, num_rows): """ Implementation method for delete_rows. Should return the number of rows that were deleted. """ @@ -431,5 +431,3 @@ Returns **True** if successful; **False** otherwise. """ return False - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/grid.py python-pyface-7.4.0/pyface/ui/wx/grid/grid.py --- python-pyface-6.1.2/pyface/ui/wx/grid/grid.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/grid.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,55 +1,68 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A grid control with a model/ui architecture. """ -# Major package imports +from os.path import abspath, exists import sys +import warnings + import wx import wx.lib.gridmovers as grid_movers -from os.path import abspath, exists -from wx.grid import Grid as wxGrid -from wx.grid import GridCellAttr, GridCellBoolRenderer, PyGridTableBase -from wx.grid import GridTableMessage, \ - GRIDTABLE_NOTIFY_ROWS_APPENDED, GRIDTABLE_NOTIFY_ROWS_DELETED, \ - GRIDTABLE_NOTIFY_ROWS_INSERTED, GRIDTABLE_NOTIFY_COLS_APPENDED, \ - GRIDTABLE_NOTIFY_COLS_DELETED, GRIDTABLE_NOTIFY_COLS_INSERTED, \ - GRIDTABLE_REQUEST_VIEW_GET_VALUES, GRID_VALUE_STRING -from wx import TheClipboard -# Enthought library imports -from pyface.api import Widget +from wx.grid import ( + Grid as wxGrid, + GridCellAttr, + GridCellEditor, + GridTableBase, + GridTableMessage, + GRIDTABLE_NOTIFY_ROWS_APPENDED, + GRIDTABLE_NOTIFY_ROWS_DELETED, + GRIDTABLE_NOTIFY_ROWS_INSERTED, + GRIDTABLE_NOTIFY_COLS_APPENDED, + GRIDTABLE_NOTIFY_COLS_DELETED, + GRIDTABLE_NOTIFY_COLS_INSERTED, + GRIDTABLE_REQUEST_VIEW_GET_VALUES, + GRID_VALUE_STRING, +) + +from traits.api import ( + Bool, + Enum, + Event, + Instance, + Int, + Undefined, + Union, +) + from pyface.timer.api import do_later -from traits.api import Bool, Color, Enum, Event, Font, Instance, Int, \ - Trait, Undefined -from pyface.wx.drag_and_drop import PythonDropSource, \ - PythonDropTarget, PythonObject +from pyface.ui.wx.layout_widget import LayoutWidget +from pyface.ui_traits import TraitsUIColor as Color, TraitsUIFont as Font +from pyface.wx.drag_and_drop import ( + PythonDropSource, + PythonDropTarget, +) from pyface.wx.drag_and_drop import clipboard as enClipboard, FileDropSource - -# local imports from .grid_model import GridModel -from .combobox_focus_handler import ComboboxFocusHandler -import six # Is this code running on MS Windows? -is_win32 = (sys.platform == 'win32') +is_win32 = sys.platform == "win32" ASCII_C = 67 -class Grid(Widget): + +class Grid(LayoutWidget): """ A grid control with a model/ui architecture. """ - #### 'Grid' interface ##################################################### + # 'Grid' interface ----------------------------------------------------- # The model that provides the data for the grid. model = Instance(GridModel, ()) @@ -76,10 +89,12 @@ default_label_text_color = Color("black") # The color to use for a selection background - selection_bg_color = Trait(wx.Colour(49, 106, 197), None, Color) + selection_bg_color = Union(None, Color, + default_value=wx.Colour(49, 106, 197)) # The color to use for a selection foreground/text - selection_text_color = Trait(wx.Colour(255, 255, 255), None, Color) + selection_text_color = Union(None, Color, + default_value=wx.Colour(255, 255, 255)) # The default font to use for text in cells default_cell_font = Font(None) @@ -91,7 +106,6 @@ default_cell_bg_color = Color("white") # The default background color to use for read-only cells - #default_cell_read_only_color = Trait(Color("linen"), None, Color) default_cell_read_only_color = Color(wx.Colour(248, 247, 241)) # Should the grid be read-only? If this is set to false, individual @@ -99,7 +113,7 @@ read_only = Bool(False) # Selection mode. - selection_mode = Enum('cell', 'rows', 'cols', '') + selection_mode = Enum("cell", "rows", "cols", "") # Sort data when a column header is clicked? allow_column_sort = Bool(True) @@ -119,52 +133,66 @@ # Allow single-click access to cell-editors? edit_on_first_click = Bool(True) - #### Events #### + # Events ---- # A cell has been activated (ie. double-clicked). - cell_activated = Event + cell_activated = Event() # The current selection has changed. - selection_changed = Event + selection_changed = Event() # A drag operation was started on a cell. - cell_begin_drag = Event + cell_begin_drag = Event() # A left-click occurred on a cell. - cell_left_clicked = Event + cell_left_clicked = Event() # A left-click occurred on a cell at specific location # Useful if the cell contains multiple controls though the hit test # is left to the consumer of the event - cell_left_clicked_location = Event + cell_left_clicked_location = Event() # A right-click occurred on a cell. - cell_right_clicked = Event + cell_right_clicked = Event() # protected variables to store the location of the clicked event - _x_clicked = Int - _y_clicked = Int + _x_clicked = Int() + _y_clicked = Int() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, **traits): """ Creates a new grid. 'parent' is the toolkit-specific control that is the grid's parent. """ + create = traits.pop('create', True) # Base class constructors. - super(Grid, self).__init__(**traits) + super().__init__(parent=parent, **traits) + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + def _create_control(self, parent): # Flag set when columns are resizing: self._user_col_size = False # Create the toolkit-specific control. - self.control = self._grid = grid = wxGrid(parent, -1) - grid.grid = self + self._grid = grid = wxGrid(parent, -1) + grid.grid = self + self._moveTo = None + self._edit = False + grid.Bind(wx.EVT_IDLE, self._on_idle) # Set when moving edit cursor: grid._no_reset_col = False grid._no_reset_row = False @@ -207,82 +235,97 @@ # Enable column and row moving: grid_movers.GridColMover(grid) grid_movers.GridRowMover(grid) - grid.Bind(grid_movers.EVT_GRID_COL_MOVE, self._on_col_move, grid) - grid.Bind(grid_movers.EVT_GRID_ROW_MOVE, self._on_row_move, grid) + grid.Bind(grid_movers.EVT_GRID_COL_MOVE, self._on_col_move) + grid.Bind(grid_movers.EVT_GRID_ROW_MOVE, self._on_row_move) - smotc = self.model.on_trait_change - otc = self.on_trait_change - smotc(self._on_model_content_changed, 'content_changed') - smotc(self._on_model_structure_changed, 'structure_changed') - smotc(self._on_row_sort, 'row_sorted') - smotc(self._on_column_sort, 'column_sorted') - otc(self._on_new_model, 'model') + self.model.observe(self._on_model_content_changed, "content_changed") + self.model.observe( + self._on_model_structure_changed, "structure_changed" + ) + self.model.observe(self._on_row_sort, "row_sorted") + self.model.observe(self._on_column_sort, "column_sorted") + self.observe(self._on_new_model, "model") # hook up style trait handlers - note that we have to use # dynamic notification hook-ups because these handlers should # not be called until after the control object is initialized. # static trait notifiers get called when the object inits. - otc(self._on_enable_lines_changed, 'enable_lines') - otc(self._on_grid_line_color_changed, 'grid_line_color') - otc(self._on_default_label_font_changed, 'default_label_font') - otc(self._on_default_label_bg_color_changed, 'default_label_bg_color') - otc(self._on_default_label_text_color_changed, - 'default_label_text_color') - otc(self._on_selection_bg_color_changed, 'selection_bg_color') - otc(self._on_selection_text_color_changed, 'selection_text_color') - otc(self._on_default_cell_font_changed, 'default_cell_font') - otc(self._on_default_cell_text_color_changed, 'default_cell_text_color') - otc(self._on_default_cell_bg_color_changed, 'default_cell_bg_color') - otc(self._on_read_only_changed, 'read_only_changed') - otc(self._on_selection_mode_changed, 'selection_mode') - otc(self._on_column_label_height_changed, 'column_label_height') - otc(self._on_row_label_width_changed, 'row_label_width') - otc(self._on_show_column_headers_changed, 'show_column_headers') - otc(self._on_show_row_headers_changed, 'show_row_headers') + self.observe(self._on_enable_lines_changed, "enable_lines") + self.observe(self._on_grid_line_color_changed, "grid_line_color") + self.observe(self._on_default_label_font_changed, "default_label_font") + self.observe( + self._on_default_label_bg_color_changed, "default_label_bg_color" + ) + self.observe( + self._on_default_label_text_color_changed, + "default_label_text_color", + ) + self.observe(self._on_selection_bg_color_changed, "selection_bg_color") + self.observe( + self._on_selection_text_color_changed, "selection_text_color" + ) + self.observe(self._on_default_cell_font_changed, "default_cell_font") + self.observe( + self._on_default_cell_text_color_changed, "default_cell_text_color" + ) + self.observe( + self._on_default_cell_bg_color_changed, "default_cell_bg_color" + ) + self.observe(self._on_read_only_changed, "read_only") + self.observe(self._on_selection_mode_changed, "selection_mode") + self.observe( + self._on_column_label_height_changed, "column_label_height" + ) + self.observe(self._on_row_label_width_changed, "row_label_width") + self.observe( + self._on_show_column_headers_changed, "show_column_headers" + ) + self.observe(self._on_show_row_headers_changed, "show_row_headers") # Initialize wx handlers: self._notify_select = True - wx.grid.EVT_GRID_SELECT_CELL(grid, self._on_select_cell) - wx.grid.EVT_GRID_RANGE_SELECT(grid, self._on_range_select) - wx.grid.EVT_GRID_COL_SIZE(grid, self._on_col_size) - wx.grid.EVT_GRID_ROW_SIZE(grid, self._on_row_size) + grid.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_select_cell) + grid.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_range_select) + grid.Bind(wx.grid.EVT_GRID_COL_SIZE, self._on_col_size) + grid.Bind(wx.grid.EVT_GRID_ROW_SIZE, self._on_row_size) # This starts the cell editor on a double-click as well as on a second # click: - wx.grid.EVT_GRID_CELL_LEFT_DCLICK(grid, self._on_cell_left_dclick) + grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self._on_cell_left_dclick) # Notify when cells are clicked on: - wx.grid.EVT_GRID_CELL_RIGHT_CLICK(grid, self._on_cell_right_click) - wx.grid.EVT_GRID_CELL_RIGHT_DCLICK(grid, self._on_cell_right_dclick) + grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self._on_cell_right_click) + grid.Bind( + wx.grid.EVT_GRID_CELL_RIGHT_DCLICK, self._on_cell_right_dclick + ) + + grid.Bind( + wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self._on_label_right_click + ) + grid.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self._on_label_left_click) - wx.grid.EVT_GRID_LABEL_RIGHT_CLICK(grid, self._on_label_right_click) - wx.grid.EVT_GRID_LABEL_LEFT_CLICK(grid, self._on_label_left_click) - - #wx.grid.EVT_GRID_EDITOR_CREATED(grid, self._on_editor_created) if is_win32: - wx.grid.EVT_GRID_EDITOR_HIDDEN(grid, self._on_editor_hidden) + grid.Bind(wx.grid.EVT_GRID_EDITOR_HIDDEN, self._on_editor_hidden) # We handle key presses to change the behavior of the and # keys to make manual data entry smoother. - wx.EVT_KEY_DOWN(grid, self._on_key_down) + grid.Bind(wx.EVT_KEY_DOWN, self._on_key_down) # We handle control resize events to adjust column widths - wx.EVT_SIZE(grid, self._on_size) + grid.Bind(wx.EVT_SIZE, self._on_size) # Handle drags: self._corner_window = grid.GetGridCornerLabelWindow() - self._grid_window = gw = grid.GetGridWindow() - self._row_window = rw = grid.GetGridRowLabelWindow() - self._col_window = cw = grid.GetGridColLabelWindow() + self._grid_window = gw = grid.GetGridWindow() + self._row_window = rw = grid.GetGridRowLabelWindow() + self._col_window = cw = grid.GetGridColLabelWindow() # Handle mouse button state changes: self._ignore = False - for window in ( gw, rw, cw ): - wx.EVT_MOTION( window, self._on_grid_motion ) - wx.EVT_LEFT_DOWN( window, self._on_left_down ) - wx.EVT_LEFT_UP( window, self._on_left_up ) - - wx.EVT_PAINT(self._grid_window, self._on_grid_window_paint) + for window in (gw, rw, cw): + window.Bind(wx.EVT_MOTION, self._on_grid_motion) + window.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) + window.Bind(wx.EVT_LEFT_UP, self._on_left_up) # Initialize the row and column models: self.__initialize_rows(self.model) @@ -298,80 +341,122 @@ self.__autosize() self._edit = False - wx.EVT_IDLE(grid, self._on_idle) + grid.Bind(wx.EVT_IDLE, self._on_idle) + return grid def dispose(self): # Remove all wx handlers: grid = self._grid - wx.grid.EVT_GRID_SELECT_CELL( grid, None ) - wx.grid.EVT_GRID_RANGE_SELECT( grid, None ) - wx.grid.EVT_GRID_COL_SIZE( grid, None ) - wx.grid.EVT_GRID_ROW_SIZE( grid, None ) - wx.grid.EVT_GRID_CELL_LEFT_DCLICK( grid, None ) - wx.grid.EVT_GRID_CELL_RIGHT_CLICK( grid, None ) - wx.grid.EVT_GRID_CELL_RIGHT_DCLICK( grid, None ) - wx.grid.EVT_GRID_LABEL_RIGHT_CLICK( grid, None ) - wx.grid.EVT_GRID_LABEL_LEFT_CLICK( grid, None ) - wx.grid.EVT_GRID_EDITOR_CREATED( grid, None ) - if is_win32: - wx.grid.EVT_GRID_EDITOR_HIDDEN( grid, None ) - wx.EVT_KEY_DOWN( grid, None ) - wx.EVT_SIZE( grid, None ) - wx.EVT_PAINT( self._grid_window, None ) - - for window in ( self._grid_window , self._row_window , - self._col_window ): - wx.EVT_MOTION( window, None ) - wx.EVT_LEFT_DOWN( window, None ) - wx.EVT_LEFT_UP( window, None ) - - otc = self.on_trait_change - otc(self._on_enable_lines_changed, 'enable_lines', - remove = True) - otc(self._on_grid_line_color_changed, 'grid_line_color', - remove = True) - otc(self._on_default_label_font_changed, 'default_label_font', - remove = True) - otc(self._on_default_label_bg_color_changed, 'default_label_bg_color', - remove = True) - otc(self._on_default_label_text_color_changed, - 'default_label_text_color', - remove = True) - otc(self._on_selection_bg_color_changed, 'selection_bg_color', - remove = True) - otc(self._on_selection_text_color_changed, 'selection_text_color', - remove = True) - otc(self._on_default_cell_font_changed, 'default_cell_font', - remove = True) - otc(self._on_default_cell_text_color_changed, 'default_cell_text_color', - remove = True) - otc(self._on_default_cell_bg_color_changed, 'default_cell_bg_color', - remove = True) - otc(self._on_read_only_changed, 'read_only_changed', - remove = True) - otc(self._on_selection_mode_changed, 'selection_mode', - remove = True) - otc(self._on_column_label_height_changed, 'column_label_height', - remove = True) - otc(self._on_row_label_width_changed, 'row_label_width', - remove = True) - otc(self._on_show_column_headers_changed, 'show_column_headers', - remove = True) - otc(self._on_show_row_headers_changed, 'show_row_headers', - remove = True) - - # It seems that the grid must be destroyed before disposing of - # _grid_table_base: otherwise, the grid can apparently generate an - # extra _GridTableBase.GetAttr call after _GridTableBase.dispose() - # has been called, leading to headaches and segfaults. - grid.Destroy() + if grid is not None: + grid.Unbind(wx.grid.EVT_GRID_SELECT_CELL) + grid.Unbind(wx.grid.EVT_GRID_RANGE_SELECT) + grid.Unbind(wx.grid.EVT_GRID_COL_SIZE) + grid.Unbind(wx.grid.EVT_GRID_ROW_SIZE) + grid.Unbind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK) + grid.Unbind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK) + grid.Unbind(wx.grid.EVT_GRID_CELL_RIGHT_DCLICK) + grid.Unbind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK) + grid.Unbind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK) + grid.Unbind(wx.grid.EVT_GRID_EDITOR_CREATED) + if is_win32: + grid.Unbind(wx.grid.EVT_GRID_EDITOR_HIDDEN) + grid.Unbind(wx.EVT_KEY_DOWN) + grid.Unbind(wx.EVT_SIZE) + + self._grid_window.Unbind(wx.EVT_PAINT) + + for window in (self._grid_window, self._row_window, self._col_window): + window.Unbind(wx.EVT_MOTION) + window.Unbind(wx.EVT_LEFT_DOWN) + window.Unbind(wx.EVT_LEFT_UP) + + self.model.observe( + self._on_model_content_changed, "content_changed", remove=True + ) + self.model.observe( + self._on_model_structure_changed, "structure_changed", remove=True + ) + self.model.observe(self._on_row_sort, "row_sorted", remove=True) + self.model.observe(self._on_column_sort, "column_sorted", remove=True) + self.observe(self._on_new_model, "model", remove=True) + + self.observe( + self._on_enable_lines_changed, "enable_lines", remove=True + ) + self.observe( + self._on_grid_line_color_changed, "grid_line_color", remove=True + ) + self.observe( + self._on_default_label_font_changed, + "default_label_font", + remove=True, + ) + self.observe( + self._on_default_label_bg_color_changed, + "default_label_bg_color", + remove=True, + ) + self.observe( + self._on_default_label_text_color_changed, + "default_label_text_color", + remove=True, + ) + self.observe( + self._on_selection_bg_color_changed, + "selection_bg_color", + remove=True, + ) + self.observe( + self._on_selection_text_color_changed, + "selection_text_color", + remove=True, + ) + self.observe( + self._on_default_cell_font_changed, + "default_cell_font", + remove=True, + ) + self.observe( + self._on_default_cell_text_color_changed, + "default_cell_text_color", + remove=True, + ) + self.observe( + self._on_default_cell_bg_color_changed, + "default_cell_bg_color", + remove=True, + ) + self.observe( + self._on_read_only_changed, "read_only", remove=True + ) + self.observe( + self._on_selection_mode_changed, "selection_mode", remove=True + ) + self.observe( + self._on_column_label_height_changed, + "column_label_height", + remove=True, + ) + self.observe( + self._on_row_label_width_changed, "row_label_width", remove=True + ) + self.observe( + self._on_show_column_headers_changed, + "show_column_headers", + remove=True, + ) + self.observe( + self._on_show_row_headers_changed, "show_row_headers", remove=True + ) + self._grid_table_base.dispose() + self._grid = None - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_new_model(self): + def _on_new_model(self, event): """ When we get a new model reinitialize grid match to that model. """ self._grid_table_base.model = self.model @@ -385,21 +470,19 @@ # the rows looks like crap. self._grid.AutoSizeColumns(False) - return - - def _on_model_content_changed(self): + def _on_model_content_changed(self, event): """ A notification method called when the data in the underlying model changes. """ self._grid.ForceRefresh() - def _on_model_structure_changed(self, *arg, **kw): + def _on_model_structure_changed(self, event=None): """ A notification method called when the underlying model has changed. Responsible for making sure the view object updates correctly. """ # Disable any active editors in order to prevent a wx crash bug: self._edit = False - grid = self._grid + grid = self._grid # Make sure any current active editor has been disabled: grid.DisableCellEditControl() @@ -410,46 +493,55 @@ should_autosize = False # First check to see if rows have been added or deleted: - row_count = self.model.get_row_count() - delta = row_count - self._row_count + row_count = self.model.get_row_count() + delta = row_count - self._row_count self._row_count = row_count if delta > 0: # Rows were added: - msg = GridTableMessage(self._grid_table_base, - GRIDTABLE_NOTIFY_ROWS_APPENDED, delta) + msg = GridTableMessage( + self._grid_table_base, GRIDTABLE_NOTIFY_ROWS_APPENDED, delta + ) grid.ProcessTableMessage(msg) should_autosize = True elif delta < 0: # Rows were deleted: - msg = GridTableMessage(self._grid_table_base, - GRIDTABLE_NOTIFY_ROWS_DELETED, - row_count, -delta) + msg = GridTableMessage( + self._grid_table_base, + GRIDTABLE_NOTIFY_ROWS_DELETED, + row_count, + -delta, + ) grid.ProcessTableMessage(msg) should_autosize = True # Now check for column changes: - col_count = self.model.get_column_count() - delta = col_count - self._col_count + col_count = self.model.get_column_count() + delta = col_count - self._col_count self._col_count = col_count if delta > 0: # Columns were added: - msg = GridTableMessage(self._grid_table_base, - GRIDTABLE_NOTIFY_COLS_APPENDED, delta) + msg = GridTableMessage( + self._grid_table_base, GRIDTABLE_NOTIFY_COLS_APPENDED, delta + ) grid.ProcessTableMessage(msg) should_autosize = True elif delta < 0: # Columns were deleted: - msg = GridTableMessage(self._grid_table_base, - GRIDTABLE_NOTIFY_COLS_DELETED, - col_count, -delta) + msg = GridTableMessage( + self._grid_table_base, + GRIDTABLE_NOTIFY_COLS_DELETED, + col_count, + -delta, + ) grid.ProcessTableMessage(msg) should_autosize = True # Finally make sure we update for any new values in the table: - msg = GridTableMessage(self._grid_table_base, - GRIDTABLE_REQUEST_VIEW_GET_VALUES) + msg = GridTableMessage( + self._grid_table_base, GRIDTABLE_REQUEST_VIEW_GET_VALUES + ) grid.ProcessTableMessage(msg) if should_autosize: @@ -462,16 +554,16 @@ grid.AdjustScrollbars() self._refresh() - def _on_row_sort(self, evt): + def _on_row_sort(self, event): """ Handles a row_sorted event from the underlying model. """ # First grab the new data out of the event: - if evt.index < 0: + if event.new.index < 0: self._current_sorted_row = None else: - self._current_sorted_row = evt.index + self._current_sorted_row = event.new.index - self._row_sort_reversed = evt.reversed + self._row_sort_reversed = event.new.reversed # Since the label may have changed we may need to autosize again: # fixme: when we change how we represent the sorted column @@ -481,16 +573,16 @@ # Make sure everything updates to reflect the changes: self._on_model_structure_changed() - def _on_column_sort(self, evt): + def _on_column_sort(self, event): """ Handles a column_sorted event from the underlying model. """ # first grab the new data out of the event - if evt.index < 0: + if event.new.index < 0: self._current_sorted_col = None else: - self._current_sorted_col = evt.index + self._current_sorted_col = event.new.index - self._col_sort_reversed = evt.reversed + self._col_sort_reversed = event.new.reversed # since the label may have changed we may need to autosize again # fixme: when we change how we represent the sorted column @@ -500,25 +592,25 @@ # make sure everything updates to reflect the changes self._on_model_structure_changed() - def _on_enable_lines_changed(self): + def _on_enable_lines_changed(self, event=None): """ Handle a change to the enable_lines trait. """ self._grid.EnableGridLines(self.enable_lines) - def _on_grid_line_color_changed(self): + def _on_grid_line_color_changed(self, event=None): """ Handle a change to the enable_lines trait. """ self._grid.SetGridLineColour(self.grid_line_color) - def _on_default_label_font_changed(self): + def _on_default_label_font_changed(self, event=None): """ Handle a change to the default_label_font trait. """ font = self.default_label_font if font is None: font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) - font.SetWeight(wx.FONTWEIGHT_BOLD) + font.SetWeight(wx.BOLD) self._grid.SetLabelFont(font) - def _on_default_label_text_color_changed(self): + def _on_default_label_text_color_changed(self, event=None): """ Handle a change to the default_cell_text_color trait. """ if self.default_label_text_color is not None: @@ -526,7 +618,7 @@ self._grid.SetLabelTextColour(color) self._grid.ForceRefresh() - def _on_default_label_bg_color_changed(self): + def _on_default_label_bg_color_changed(self, event=None): """ Handle a change to the default_cell_text_color trait. """ if self.default_label_bg_color is not None: @@ -534,24 +626,24 @@ self._grid.SetLabelBackgroundColour(color) self._grid.ForceRefresh() - def _on_selection_bg_color_changed(self): + def _on_selection_bg_color_changed(self, event=None): """ Handle a change to the selection_bg_color trait. """ if self.selection_bg_color is not None: self._grid.SetSelectionBackground(self.selection_bg_color) - def _on_selection_text_color_changed(self): + def _on_selection_text_color_changed(self, event=None): """ Handle a change to the selection_text_color trait. """ if self.selection_text_color is not None: self._grid.SetSelectionForeground(self.selection_text_color) - def _on_default_cell_font_changed(self): + def _on_default_cell_font_changed(self, event=None): """ Handle a change to the default_cell_font trait. """ if self.default_cell_font is not None: self._grid.SetDefaultCellFont(self.default_cell_font) self._grid.ForceRefresh() - def _on_default_cell_text_color_changed(self): + def _on_default_cell_text_color_changed(self, event=None): """ Handle a change to the default_cell_text_color trait. """ if self.default_cell_text_color is not None: @@ -559,7 +651,7 @@ self._grid.SetDefaultCellTextColour(color) self._grid.ForceRefresh() - def _on_default_cell_bg_color_changed(self): + def _on_default_cell_bg_color_changed(self, event=None): """ Handle a change to the default_cell_bg_color trait. """ if self.default_cell_bg_color is not None: @@ -567,7 +659,7 @@ self._grid.SetDefaultCellBackgroundColour(color) self._grid.ForceRefresh() - def _on_read_only_changed(self): + def _on_read_only_changed(self, event=None): """ Handle a change to the read_only trait. """ # should the whole grid be read-only? @@ -576,32 +668,32 @@ else: self._grid.EnableEditing(True) - def _on_selection_mode_changed(self): + def _on_selection_mode_changed(self, event=None): """ Handle a change to the selection_mode trait. """ # should we allow individual cells to be selected or only rows # or only columns - if self.selection_mode == 'cell': - self._grid.SetSelectionMode(wxGrid.wxGridSelectCells) - elif self.selection_mode == 'rows': - self._grid.SetSelectionMode(wxGrid.wxGridSelectRows) - elif self.selection_mode == 'cols': - self._grid.SetSelectionMode(wxGrid.wxGridSelectColumns) + if self.selection_mode == "cell": + self._grid.SetSelectionMode(wxGrid.SelectCells) + elif self.selection_mode == "rows": + self._grid.SetSelectionMode(wxGrid.SelectRows) + elif self.selection_mode == "cols": + self._grid.SetSelectionMode(wxGrid.SelectColumns) - def _on_column_label_height_changed(self): + def _on_column_label_height_changed(self, event=None): """ Handle a change to the column_label_height trait. """ # handle setting for height of column labels if self.column_label_height is not None: self._grid.SetColLabelSize(self.column_label_height) - def _on_row_label_width_changed(self): + def _on_row_label_width_changed(self, event=None): """ Handle a change to the row_label_width trait. """ # handle setting for width of row labels if self.row_label_width is not None: self._grid.SetRowLabelSize(self.row_label_width) - def _on_show_column_headers_changed(self): + def _on_show_column_headers_changed(self, event=None): """ Handle a change to the show_column_headers trait. """ if not self.show_column_headers: @@ -609,7 +701,7 @@ else: self._grid.SetColLabelSize(self.column_label_height) - def _on_show_row_headers_changed(self): + def _on_show_row_headers_changed(self, event=None): """ Handle a change to the show_row_headers trait. """ if not self.show_row_headers: @@ -617,9 +709,9 @@ else: self._grid.SetRowLabelSize(self.row_label_width) - ########################################################################### + # ------------------------------------------------------------------------ # 'Grid' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_selection(self): """ Return a list of the currently selected objects. """ @@ -647,11 +739,11 @@ grid.ClearSelection() mode = self.selection_mode - if mode == 'rows': + if mode == "rows": self._select_rows(cells) - elif mode != '': + elif mode != "": for selection in cells: - row, col = max( 0, selection[0] ), max( 0, selection[1] ) + row, col = max(0, selection[0]), max(0, selection[1]) grid.SelectBlock(row, col, row, col, True) grid.EndBatch() @@ -668,21 +760,13 @@ if self._grid.GetGridCursorRow() in indices: self._grid.DisableCellEditControl() - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_size(self, evt): """ Called when the grid is resized. """ self.__autosize() - - # needed to handle problem in wx 2.6 with combobox cell editors - def _on_editor_created(self, evt): - - editor = evt.GetControl() - if editor is not None: - editor.PushEventHandler(ComboboxFocusHandler(self._grid)) - evt.Skip() def _on_editor_hidden(self, evt): @@ -699,33 +783,26 @@ # Windows. control = wx.FindWindowAtPointer() if isinstance(control, wx.Button): - do_later(wx.PostEvent, control, - wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, - control.GetId())) - - def _on_grid_window_paint(self, evt): - - # fixme: this is a total h*ck to get rid of the scrollbars that appear - # on a grid under wx2.6 when it starts up. these appear whether or - # not needed, and disappear as soon as the grid is resized. hopefully - # we will be able to remove this egregious code on some future version - # of wx. - self._grid.SetColSize(0, self._grid.GetColSize(0) + 1) - self._grid.SetColSize(0, self._grid.GetColSize(0) - 1) + do_later( + wx.PostEvent, + control, + wx.CommandEvent( + wx.wxEVT_COMMAND_BUTTON_CLICKED, control.GetId() + ), + ) - evt.Skip() - - def _on_left_down ( self, evt ): + def _on_left_down(self, evt): """ Called when the left mouse button is pressed. """ - grid = self._grid + grid = self._grid self._x_clicked = evt.GetX() self._y_clicked = evt.GetY() - self._ignore = ((grid.XToEdgeOfCol(evt.GetX()) != wx.NOT_FOUND) or - (grid.YToEdgeOfRow(evt.GetY()) != wx.NOT_FOUND)) + self._ignore = (grid.XToEdgeOfCol(evt.GetX()) != wx.NOT_FOUND) or ( + grid.YToEdgeOfRow(evt.GetY()) != wx.NOT_FOUND + ) evt.Skip() - def _on_left_up ( self, evt ): + def _on_left_up(self, evt): """ Called when the left mouse button is released. """ self._ignore = False @@ -737,7 +814,7 @@ evt.Skip() if evt.Dragging() and not evt.ControlDown(): data = self.__get_drag_value() - if isinstance(data, six.string_types): + if isinstance(data, str): file = abspath(data) if exists(file): FileDropSource(self._grid, file) @@ -747,9 +824,9 @@ def _on_grid_motion(self, evt): if evt.GetEventObject() is self._grid_window: - x, y = self._grid.CalcUnscrolledPosition(evt.GetPosition()) - row = self._grid.YToRow(y) - col = self._grid.XToCol(x) + x, y = self._grid.CalcUnscrolledPosition(evt.GetPosition().Get()) + row = self._grid.YToRow(y) + col = self._grid.XToCol(x) # Notify the model that the mouse has moved into the cell at row,col, # only if the row and col are valid. @@ -765,6 +842,7 @@ def _on_select_cell(self, evt): """ Called when the user has moved to another cell. """ row, col = evt.GetRow(), evt.GetCol() + self._moveTo = (row, col) self.cell_left_clicked = self.model.click = (row, col) # Try to find a renderer for this cell: @@ -775,9 +853,10 @@ if renderer is not None: result = renderer.on_left_click(self, row, col) + # print("self._grid.GetParent()", self._grid.GetParent().GetParent().GetParent()) # if the handler didn't tell us to stop further handling then skip if not result: - if (self.selection_mode != '') or (not self.edit_on_first_click): + if (self.selection_mode != "") or (not self.edit_on_first_click): self._grid.SelectBlock(row, col, row, col, evt.ControlDown()) self._edit = True @@ -785,9 +864,14 @@ def _on_range_select(self, evt): if evt.Selecting(): - if (self.selection_mode == 'cell') and evt.ControlDown(): - self._grid.SelectBlock(evt.GetTopRow(), evt.GetLeftCol(), - evt.GetBottomRow(), evt.GetRightCol(), True) + if (self.selection_mode == "cell") and evt.ControlDown(): + self._grid.SelectBlock( + evt.GetTopRow(), + evt.GetLeftCol(), + evt.GetBottomRow(), + evt.GetRightCol(), + True, + ) if self._notify_select: self.__fire_selection_changed() @@ -808,7 +892,7 @@ """ Immediately jumps into editing mode, bypassing the usual select mode of a spreadsheet. See also self.OnSelectCell(). """ - if self._edit == True and self.edit_on_first_click: + if self._edit and self.edit_on_first_click: if self._grid.CanEnableCellControl(): self._grid.EnableCellEditControl() self._edit = False @@ -827,7 +911,7 @@ """ row, col = evt.GetRow(), evt.GetCol() - data = self.model.get_value(row, col) + data = self.model.get_value(row, col) self.cell_activated = data # Tell the model that a cell was double-clicked on: @@ -879,22 +963,20 @@ # get the underlying menu object if menu_manager is not None: controller = None - if type( menu_manager ) is tuple: + if type(menu_manager) is tuple: menu_manager, controller = menu_manager menu = menu_manager.create_menu(self._grid, controller) # if it has anything in it pop it up if menu.GetMenuItemCount() > 0: # Popup the menu (if an action is selected it will be # performed before before 'PopupMenu' returns). - x, y = evt.GetPosition() - self._grid.PopupMenuXY(menu, x - 10, y - 10 ) + x, y = evt.GetPosition().Get() + self._grid.PopupMenu(menu, x - 10, y - 10) self.cell_right_clicked = (row, col) evt.Skip() - return - def _on_label_right_click(self, evt): """ Called when a right click occurred on a label. """ @@ -915,11 +997,11 @@ if menu.GetMenuItemCount() > 0: # Popup the menu (if an action is selected it will be performed # before before 'PopupMenu' returns). - self._grid.PopupMenu(menu, evt.GetPosition()) + self._grid.PopupMenu(menu, evt.GetPosition().Get()) elif col >= 0: - cws = getattr( self, '_cached_widths', None ) - if (cws is not None) and (0 <= col < len( cws )): - cws[ col ] = None + cws = getattr(self, "_cached_widths", None) + if (cws is not None) and (0 <= col < len(cws)): + cws[col] = None self.__autosize() evt.Skip() @@ -932,10 +1014,10 @@ # A row value of -1 means this click happened on a column. # vice versa, a col value of -1 means a row click. if (row == -1) and self.allow_column_sort and evt.ControlDown(): - self._column_sort( col ) + self._column_sort(col) elif (col == -1) and self.allow_row_sort and evt.ControlDown(): - self._row_sort( row ) + self._row_sort(row) evt.Skip() @@ -988,32 +1070,6 @@ # # Don't change the behavior if the key is pressed as this # has meaning to the edit control. - key_code = evt.GetKeyCode() - - if (key_code == wx.WXK_RETURN) and not evt.ControlDown(): - self._move_to_next_cell(evt.ShiftDown()) - - elif (key_code == wx.WXK_TAB) and not evt.ControlDown(): - if evt.ShiftDown(): - # fixme: in a split window the shift tab is being eaten - # by tabbing between the splits - self._move_to_previous_cell() - - else: - self._move_to_next_cell() - - elif key_code == ASCII_C: - data = self.__get_drag_value() - # deposit the data in our singleton clipboard - enClipboard.data = data - - # build a wxCustomDataObject to notify the system clipboard - # that some in-process data is available - data_object = wx.CustomDataObject(PythonObject) - data_object.SetData('dummy') - if TheClipboard.Open(): - TheClipboard.SetData(data_object) - TheClipboard.Close() evt.Skip() @@ -1056,10 +1112,12 @@ # Move to the last column in the previous row. newRow = self._grid.GetGridCursorRow() - 1 if newRow >= 0: - self._grid.SetGridCursor(newRow, - self._grid.GetNumberCols() - 1) - self._grid.MakeCellVisible(newRow, - self._grid.GetNumberCols() - 1) + self._grid.SetGridCursor( + newRow, self._grid.GetNumberCols() - 1 + ) + self._grid.MakeCellVisible( + newRow, self._grid.GetNumberCols() - 1 + ) def _refresh(self): self._grid.GetParent().Layout() @@ -1079,23 +1137,35 @@ if self.model._move_column(frm, to): # Modify the grid: - grid = self._grid - cols = grid.GetNumberCols() - widths = [ grid.GetColSize(i) for i in range( cols ) ] - width = widths[frm] + grid = self._grid + cols = grid.GetNumberCols() + widths = [grid.GetColSize(i) for i in range(cols)] + width = widths[frm] del widths[frm] - to -= (frm < to) - widths.insert( to, width ) + to -= frm < to + widths.insert(to, width) grid.BeginBatch() - grid.ProcessTableMessage( GridTableMessage( self._grid_table_base, - GRIDTABLE_NOTIFY_COLS_DELETED, frm, 1 ) ) - - grid.ProcessTableMessage( GridTableMessage( self._grid_table_base, - GRIDTABLE_NOTIFY_COLS_INSERTED, to, 1 ) ) + grid.ProcessTableMessage( + GridTableMessage( + self._grid_table_base, + GRIDTABLE_NOTIFY_COLS_DELETED, + frm, + 1, + ) + ) + + grid.ProcessTableMessage( + GridTableMessage( + self._grid_table_base, + GRIDTABLE_NOTIFY_COLS_INSERTED, + to, + 1, + ) + ) - [ grid.SetColSize(i, widths[i]) for i in range(min(frm, to), cols) ] + [grid.SetColSize(i, widths[i]) for i in range(min(frm, to), cols)] grid.EndBatch() @@ -1114,30 +1184,42 @@ if self.model._move_row(frm, to): # Notify the grid: - grid = self._grid - rows = grid.GetNumberRows() - heights = [ grid.GetRowSize(i) for i in range( rows ) ] - height = heights[frm] + grid = self._grid + rows = grid.GetNumberRows() + heights = [grid.GetRowSize(i) for i in range(rows)] + height = heights[frm] del heights[frm] - to -= (frm < to) - heights.insert( to, height ) + to -= frm < to + heights.insert(to, height) grid.BeginBatch() - grid.ProcessTableMessage( GridTableMessage( self._grid_table_base, - GRIDTABLE_NOTIFY_ROWS_DELETED, frm, 1 ) ) - - grid.ProcessTableMessage( GridTableMessage( self._grid_table_base, - GRIDTABLE_NOTIFY_ROWS_INSERTED, to, 1 ) ) + grid.ProcessTableMessage( + GridTableMessage( + self._grid_table_base, + GRIDTABLE_NOTIFY_ROWS_DELETED, + frm, + 1, + ) + ) + + grid.ProcessTableMessage( + GridTableMessage( + self._grid_table_base, + GRIDTABLE_NOTIFY_ROWS_INSERTED, + to, + 1, + ) + ) - [ grid.SetRowSize(i, heights[i]) for i in range(min(frm, to), rows)] + [grid.SetRowSize(i, heights[i]) for i in range(min(frm, to), rows)] grid.EndBatch() - ########################################################################### + # ------------------------------------------------------------------------ # PythonDropTarget interface. - ########################################################################### - def wx_dropped_on ( self, x, y, drag_object, drag_result ): + # ------------------------------------------------------------------------ + def wx_dropped_on(self, x, y, drag_object, drag_result): # first resolve the x/y coords into a grid row/col row, col = self.__resolve_grid_coords(x, y) @@ -1145,8 +1227,9 @@ result = wx.DragNone if row != -1 and col != -1: # now ask the model if the target cell can accept this object - valid_target = self.model.is_valid_cell_value(row, col, - drag_object) + valid_target = self.model.is_valid_cell_value( + row, col, drag_object + ) # if this is a valid target then attempt to set the value if valid_target: # find the data @@ -1154,8 +1237,10 @@ # sometimes a 'node' attribute on the clipboard gets set # to a binding. if this happens we want to use it, otherwise # we want to just use the drag_object passed to us - if hasattr(enClipboard, 'node') and \ - enClipboard.node is not None: + if ( + hasattr(enClipboard, "node") + and enClipboard.node is not None + ): data = enClipboard.node # now make sure the value gets set in the model @@ -1164,7 +1249,7 @@ return result - def wx_drag_over ( self, x, y, drag_object, drag_result ): + def wx_drag_over(self, x, y, drag_object, drag_result): # first resolve the x/y coords into a grid row/col row, col = self.__resolve_grid_coords(x, y) @@ -1172,16 +1257,17 @@ result = wx.DragNone if row != -1 and col != -1: # now ask the model if the target cell can accept this object - valid_target = self.model.is_valid_cell_value(row, col, - drag_object) + valid_target = self.model.is_valid_cell_value( + row, col, drag_object + ) if valid_target: result = drag_result return result - ########################################################################### + # ------------------------------------------------------------------------ # private interface. - ########################################################################### + # ------------------------------------------------------------------------ def __initialize_fonts(self): """ Initialize the label fonts. """ @@ -1202,8 +1288,8 @@ if model.is_row_read_only(row): attr = wx.grid.GridCellAttr() attr.SetReadOnly() - #attr.SetRenderer(None) - #attr.SetBackgroundColour('linen') + # attr.SetRenderer(None) + # attr.SetBackgroundColour('linen') self._grid.SetRowAttr(row, attr) def __initialize_columns(self, model): @@ -1214,8 +1300,8 @@ if model.is_column_read_only(column): attr = wx.grid.GridCellAttr() attr.SetReadOnly() - #attr.SetRenderer(None) - #attr.SetBackgroundColour('linen') + # attr.SetRenderer(None) + # attr.SetBackgroundColour('linen') self._grid.SetColAttr(column, attr) def __initialize_counts(self, model): @@ -1236,10 +1322,10 @@ self._current_sorted_col = None self._current_sorted_row = None - self._col_sort_reversed = False - self._row_sort_reversed = False + self._col_sort_reversed = False + self._row_sort_reversed = False - def __initialize_style_settings(self, event=None): + def __initialize_style_settings(self): # make sure all the handlers for traits defining styles get called self._on_enable_lines_changed() @@ -1257,33 +1343,9 @@ def __get_drag_value(self): """ Calculates the drag value based on the current selection. """ - # fixme: The following line seems like a more useful implementation than - # the previous commented out version below, but I am leaving the old - # version in the code for now just in case. If anyone sees this comment - # after 1/1/2009, it should be safe to delete this comment and the - # commented out code below... - return self.model.get_cell_drag_value(self._grid.GetGridCursorRow(), - self._grid.GetGridCursorCol()) - - ###rows, cols = self.__get_selected_rows_and_cols() - ### - ###if len(rows) > 0: - ### rows.sort() - ### value = self.model.get_rows_drag_value(rows) - ### if len(rows) == 1 and len(value) == 1: - ### value = value[0] - ###elif len(cols) > 0: - ### cols.sort() - ### value = self.model.get_cols_drag_value(cols) - ### if len(cols) == 1 and len(value) == 1: - ### value = value[0] - ###else: - ### # our final option -- grab the cell that the cursor is currently in - ### row = self._grid.GetGridCursorRow() - ### col = self._grid.GetGridCursorCol() - ### value = self.model.get_cell_drag_value(row, col) - ### - ###return value + return self.model.get_cell_drag_value( + self._grid.GetGridCursorRow(), self._grid.GetGridCursorCol() + ) def __get_selection(self): """ Returns a list of values for the current selection. """ @@ -1319,16 +1381,14 @@ rows = self._grid.GetSelectedRows() cols = self._grid.GetSelectedCols() - # because wx is retarded we have to check this as well -- why - # the blazes can't they come up with one Q#$%@$#% API to manage - # selections??? makes me want to put the smack on somebody. + # because of wx we have to check this as well # note that all this malarkey is working on the assumption that # only entire rows or entire columns or single cells are selected. top_left = self._grid.GetSelectionBlockTopLeft() bottom_right = self._grid.GetSelectionBlockBottomRight() selection_mode = self._grid.GetSelectionMode() - if selection_mode == wxGrid.wxGridSelectRows: + if selection_mode == wxGrid.SelectRows: # handle rows differently. figure out which rows were # selected. turns out that in this case, wx adds a "block" # per row, so we have to cycle over the list returned by @@ -1338,7 +1398,7 @@ bottom_point = bottom_right[i] for row_index in range(top_point[0], bottom_point[0] + 1): rows.append(row_index) - elif selection_mode == wxGrid.wxGridSelectColumns: + elif selection_mode == wxGrid.SelectColumns: # again, in this case we know that only whole columns can be # selected for i in range(len(top_left)): @@ -1346,7 +1406,7 @@ bottom_point = bottom_right[i] for col_index in range(top_point[1], bottom_point[1] + 1): cols.append(col_index) - elif selection_mode == wxGrid.wxGridSelectCells: + elif selection_mode == wxGrid.SelectCells: # this is the case where the selection_mode is cell, which also # allows complete columns or complete rows to be selected. @@ -1358,10 +1418,12 @@ top_point = top_left[i] bottom_point = bottom_right[i] # precalculate whether this is a row or column select - row_select = top_point[1] == 0 and \ - bottom_point[1] == col_size - 1 - col_select = top_point[0] == 0 and \ - bottom_point[0] == row_size - 1 + row_select = ( + top_point[1] == 0 and bottom_point[1] == col_size - 1 + ) + col_select = ( + top_point[0] == 0 and bottom_point[0] == row_size - 1 + ) if row_select: for row_index in range(top_point[0], bottom_point[0] + 1): @@ -1370,8 +1432,7 @@ for col_index in range(top_point[1], bottom_point[1] + 1): cols.append(top_point[0]) - return ( rows, cols ) - + return (rows, cols) def __fire_selection_changed(self): self.selection_changed = True @@ -1380,7 +1441,7 @@ """ Autosize the grid with appropriate flags. """ model = self.model - grid = self._grid + grid = self._grid if grid is not None and self.autosize: grid.AutoSizeColumns(False) grid.AutoSizeRows(False) @@ -1390,56 +1451,62 @@ grid.BeginBatch() - dx, dy = grid.GetClientSize() - n = model.get_column_count() - pdx = 0 - wdx = 0.0 - widths = [] - cached = getattr( self, '_cached_widths', None ) - current = [ grid.GetColSize( i ) for i in range( n ) ] - if (cached is None) or (len( cached ) != n): - self._cached_widths = cached = [ None ] * n + dx, dy = grid.GetClientSize().Get() + n = model.get_column_count() + pdx = 0 + wdx = 0.0 + widths = [] + cached = getattr(self, "_cached_widths", None) + current = [grid.GetColSize(i) for i in range(n)] + if (cached is None) or (len(cached) != n): + self._cached_widths = cached = [None] * n - for i in range( n ): + for i in range(n): cw = cached[i] - if ((cw is None) or (-cw == current[i]) or + if ( + (cw is None) + or (-cw == current[i]) + or # hack: For some reason wx always seems to adjust column 0 by # 1 pixel from what we set it to (at least on Windows), so we # need to add a little fudge factor just for this column: - ((i == 0) and (abs( current[i] + cw ) <= 1))): - width = model.get_column_size( i ) + ((i == 0) and (abs(current[i] + cw) <= 1)) + ): + width = model.get_column_size(i) + if width is None: + width = 0.0 if width <= 0.0: width = 0.1 if width <= 1.0: wdx += width cached[i] = -1 else: - width = int( width ) - pdx += width + width = int(width) + pdx += width if cw is None: cached[i] = width else: cached[i] = width = current[i] pdx += width - widths.append( width ) + widths.append(width) # The '-1' below adjusts for an off by 1 error in the way the wx.Grid # control determines whether or not it needs a horizontal scroll bar: - adx = max( 0, dx - pdx - 1 ) + adx = max(0, dx - pdx - 1) - for i in range( n ): + for i in range(n): width = cached[i] if width < 0: width = widths[i] if width <= 1.0: - w = max( 30, int( round( (adx * width) / wdx ) ) ) - wdx -= width - width = w - adx -= width + w = max(30, int(round((adx * width) / wdx))) + wdx -= width + width = w + adx -= width cached[i] = -w - grid.SetColSize( i, width ) + grid.SetColSize(i, width) grid.AdjustScrollbars() grid.EndBatch() @@ -1462,7 +1529,7 @@ if self.show_column_headers: y = y - self._grid.GetGridColLabelWindow().GetRect().height - return ( self._grid.YToRow(y), self._grid.XToCol(x) ) + return (self._grid.YToRow(y), self._grid.XToCol(x)) def _select_rows(self, cells): """ Selects all of the rows specified by a list of (row,column) pairs. @@ -1474,7 +1541,7 @@ sb = self._grid.SelectBlock # Extract the rows and sort them: - rows = [ row for row, column in cells ] + rows = [row for row, column in cells] rows.sort() # Now find contiguous ranges of rows, and select the current range @@ -1492,18 +1559,19 @@ if first >= 0: sb(first, 0, last, 0, True) -class _GridTableBase(PyGridTableBase): + +class _GridTableBase(GridTableBase): """ A private adapter for the underlying wx grid implementation. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, model, grid): """ Creates a new table base. """ # Base class constructor. - PyGridTableBase.__init__(self) + GridTableBase.__init__(self) # The Pyface model that provides the data. self.model = model @@ -1515,7 +1583,7 @@ self._col_count = -1 # caches for editors and renderers - self._editor_cache = {} + self._editor_cache = {} self._renderer_cache = {} def dispose(self): @@ -1529,9 +1597,9 @@ renderer.dispose() self._renderer_cache = {} - ########################################################################### - # 'wxPyGridTableBase' interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'wxGridTableBase' interface. + # ------------------------------------------------------------------------ def GetNumberRows(self): """ Return the number of rows in the model. """ @@ -1572,15 +1640,15 @@ if row == self._grid._current_sorted_row: if self._grid._row_sort_reversed: if is_win32: - ulabel = six.text_type(label, 'ascii') + u' \u00ab' - label = ulabel.encode('latin-1') + ulabel = str(label, "ascii") + " \u00ab" + label = ulabel.encode("latin-1") else: - label += ' <<' + label += " <<" elif is_win32: - ulabel = six.text_type(label, 'ascii') + u' \u00bb' - label = ulabel.encode('latin-1') + ulabel = str(label, "ascii") + " \u00bb" + label = ulabel.encode("latin-1") else: - label += ' >>' + label += " >>" return label @@ -1592,15 +1660,15 @@ if col == self._grid._current_sorted_col: if self._grid._col_sort_reversed: if is_win32: - ulabel = six.text_type(label, 'ascii') + u' \u00ab' - label = ulabel.encode('latin-1') + ulabel = str(label, "ascii") + " \u00ab" + label = ulabel.encode("latin-1") else: - label += ' <<' + label += " <<" elif is_win32: - ulabel = six.text_type(label, 'ascii') + u' \u00bb' - label = ulabel.encode('latin-1') + ulabel = str(label, "ascii") + " \u00bb" + label = ulabel.encode("latin-1") else: - label += ' >>' + label += " >>" return label @@ -1617,7 +1685,7 @@ except NotImplementedError: pass - if typename == None: + if typename is None: typename = GRID_VALUE_STRING return typename @@ -1685,7 +1753,7 @@ self._editor_cache[(row, col)] = editor editor._grid_info = (self._grid._grid, row, col) - if editor is not None: + if False: # editor is not None: # Note: We have to increment the reference to keep the # underlying code from destroying our object. editor.IncRef() @@ -1703,14 +1771,19 @@ # look to see if this cell is editable read_only = False if row < rows and col < cols: - read_only = self.model.is_cell_read_only(row, col) or \ - self.model.is_row_read_only(row) or \ - self.model.is_column_read_only(col) + read_only = ( + self.model.is_cell_read_only(row, col) + or self.model.is_row_read_only(row) + or self.model.is_column_read_only(col) + ) result.SetReadOnly(read_only) - if read_only : + if read_only: read_only_color = self._grid.default_cell_read_only_color - if read_only_color is not None and read_only_color is not Undefined: + if ( + read_only_color is not None + and read_only_color is not Undefined + ): result.SetBackgroundColour(read_only_color) # check to see if colors or fonts are specified for this cell @@ -1745,16 +1818,16 @@ halignment = self.model.get_cell_halignment(row, col) valignment = self.model.get_cell_valignment(row, col) if halignment is not None and valignment is not None: - if halignment == 'center': + if halignment == "center": h = wx.ALIGN_CENTRE - elif halignment == 'right': + elif halignment == "right": h = wx.ALIGN_RIGHT else: h = wx.ALIGN_LEFT - if valignment == 'top': + if valignment == "top": v = wx.ALIGN_TOP - elif valignment == 'bottom': + elif valignment == "bottom": v = wx.ALIGN_BOTTOM else: v = wx.ALIGN_CENTRE @@ -1763,28 +1836,24 @@ return result - ########################################################################### + # ------------------------------------------------------------------------ # private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _clear_cache(self): """ Clean out the editor/renderer cache. """ # Dispose of the editors in the cache after a brief delay, so as # to allow completion of the current event: - do_later( self._editor_dispose, list(self._editor_cache.values()) ) + do_later(self._editor_dispose, list(self._editor_cache.values())) - self._editor_cache = {} + self._editor_cache = {} self._renderer_cache = {} - return def _editor_dispose(self, editors): for editor in editors: editor.dispose() -from wx.grid import PyGridCellEditor -class DummyGridCellEditor(PyGridCellEditor): +class DummyGridCellEditor(GridCellEditor): def Show(self, show, attr): return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/images/image_LICENSE.txt python-pyface-7.4.0/pyface/ui/wx/grid/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/ui/wx/grid/images/image_LICENSE.txt 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -GV (Gael Varoquaux) Public Domain N/A -Nuvola LGPL image_LICENSE_Nuvola.txt -OOo LGPL image_LICENSE_OOo.txt - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/grid/images: - checked.png | GV - image_not_found.png | Nuvola - table_edit.png | OOo - unchecked.png | GV diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/__init__.py python-pyface-7.4.0/pyface/ui/wx/grid/__init__.py --- python-pyface-6.1.2/pyface/ui/wx/grid/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ - - +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/inverted_grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/inverted_grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/inverted_grid_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/inverted_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,24 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An adapter model that inverts all of its row/column targets. Use this class with the CompositeGridModel to make models with different orientations match, or use it to visually flip the data without modifying the underlying model's sense of row and column. """ -# Enthought library imports + from traits.api import Instance -# local imports + from .grid_model import GridModel + class InvertedGridModel(GridModel): """ An adapter model that inverts all of its row/column targets. Use this class with the CompositeGridModel to make models with different @@ -17,9 +27,9 @@ model = Instance(GridModel, ()) - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_column_count(self): @@ -116,7 +126,3 @@ def is_cell_editable(self, row, col): return self.model.is_cell_editable(col, row) - - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/mapped_grid_cell_image_renderer.py python-pyface-7.4.0/pyface/ui/wx/grid/mapped_grid_cell_image_renderer.py --- python-pyface-6.1.2/pyface/ui/wx/grid/mapped_grid_cell_image_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/mapped_grid_cell_image_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,35 +1,31 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2006, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A renderer which will display a cell-specific image in addition to some text displayed in the same way the standard string renderer normally would, all data retrieved from specified value maps. """ from .grid_cell_image_renderer import GridCellImageRenderer + class MappedGridCellImageRenderer(GridCellImageRenderer): """ Maps data values to image and text. """ - def __init__(self, image_map = None, text_map = None): + def __init__(self, image_map=None, text_map=None): # Base-class constructor. We pass ourself as the provider - super(MappedGridCellImageRenderer, self).__init__(self) + super().__init__(self) self.image_map = image_map self.text_map = text_map - return - def get_image_for_cell(self, grid, row, col): if self.image_map is None: @@ -68,5 +64,3 @@ # retrieve the unformatted value from the model and return it return model.get_cell_drag_value(row, col) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/simple_grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/simple_grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/simple_grid_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/simple_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A SimpleGridModel simply builds a table from a 2-dimensional list/array containing the data. Optionally users can pass in specifications for rows and columns. By default these are built off the data itself, with row/column labels as the index + 1.""" -# Enthought library imports -from pyface.action.api import Action, Group, MenuManager, Separator -from traits.api import HasTraits, Any, List, Str, Bool, Trait + +from pyface.action.api import Action, Group, MenuManager +from traits.api import Any, Instance, List, Union from pyface.wx.drag_and_drop import clipboard as enClipboard -# local imports + from .grid_model import GridColumn, GridModel, GridRow + class SimpleGridModel(GridModel): """ A SimpleGridModel simply builds a table from a 2-dimensional list/array containing the data. Optionally users can pass in specifications @@ -31,28 +29,17 @@ with row/column labels as the index + 1.""" # A 2-dimensional list/array containing the grid data. - data = Any + data = Any() # The rows in the model. - rows = Trait(None, None, List(GridRow)) + rows = Union(None, List(Instance(GridRow))) # The columns in the model. - columns = Trait(None, None, List(GridColumn)) - - ######################################################################### - # 'object' interface. - ######################################################################### - def __init__(self, **traits): - """ Create a SimpleGridModel object. """ + columns = Union(None, List(Instance(GridColumn))) - # Base class constructor - super(SimpleGridModel, self).__init__(**traits) - - return - - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_column_count(self): """ Return the number of columns for this table. """ @@ -76,7 +63,7 @@ try: name = self.columns[index].label except IndexError: - name = '' + name = "" else: # otherwise return the index plus 1 name = str(index + 1) @@ -185,7 +172,7 @@ except IndexError: pass - return '' + return "" def is_cell_empty(self, row, col): """ Returns True if the cell at (row, col) has a None value, @@ -208,11 +195,8 @@ context menu for this cell.""" context_menu = MenuManager( - Group( - _CopyAction(self, row, col, name='Copy'), - id = 'Group' - ) - ) + Group(_CopyAction(self, row, col, name="Copy"), id="Group") + ) return context_menu @@ -221,9 +205,9 @@ False otherwise. """ return True - ######################################################################### + # ------------------------------------------------------------------------ # protected 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def _set_value(self, row, col, value): """ Sets the value of the cell at (row, col) to value. @@ -250,9 +234,9 @@ return num_rows - ########################################################################### + # ------------------------------------------------------------------------ # private interface. - ########################################################################### + # ------------------------------------------------------------------------ def __get_data_column(self, col): """ Return a 1-d list of data from the column indexed by col. """ @@ -282,12 +266,12 @@ return rowdata + # Private class class _CopyAction(Action): - def __init__(self, model, row, col, **kw): - super(_CopyAction, self).__init__(**kw) + super().__init__(**kw) self._model = model self._row = row self._col = col @@ -298,6 +282,3 @@ # clipboard value = self._model.get_cell_drag_value(self._row, self._col) enClipboard.data = value - - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/tests/composite_grid_model_test_case.py python-pyface-7.4.0/pyface/ui/wx/grid/tests/composite_grid_model_test_case.py --- python-pyface-6.1.2/pyface/ui/wx/grid/tests/composite_grid_model_test_case.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/tests/composite_grid_model_test_case.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ -import unittest - -try: - from pyface.ui.wx.grid.api \ - import CompositeGridModel, GridRow, GridColumn, SimpleGridModel -except ImportError: - wx_available = False -else: - wx_available = True - - -@unittest.skipUnless(wx_available, "Wx is not available") -class CompositeGridModelTestCase( unittest.TestCase ): - - def setUp(self): - - self.model_1 = SimpleGridModel(data=[[1,2],[3,4]], - rows=[GridRow(label='foo'), - GridRow(label='bar')], - columns=[GridColumn(label='cfoo'), - GridColumn(label='cbar')] - ) - self.model_2 = SimpleGridModel(data=[[3,4,5],[6,7,8]], - rows=[GridRow(label='baz'), - GridRow(label='bat')], - columns=[GridColumn(label='cfoo_2'), - GridColumn(label='cbar_2'), - GridColumn(label='cbaz_2')] - ) - - self.model = CompositeGridModel(data=[self.model_1, self.model_2]) - - return - - def test_get_column_count(self): - - column_count_1 = self.model_1.get_column_count() - column_count_2 = self.model_2.get_column_count() - - self.assertEqual(self.model.get_column_count(), - column_count_1 + column_count_2) - - return - - def test_get_row_count(self): - - self.assertEqual(self.model.get_row_count(), 2) - - return - - def test_get_row_name(self): - - # Regardless of the rows specified in the composed models, the - # composite model returns its own rows. - self.assertEqual(self.model.get_row_name(0), '1') - self.assertEqual(self.model.get_row_name(1), '2') - - return - - def test_get_column_name(self): - - self.assertEqual(self.model.get_column_name(0), 'cfoo') - self.assertEqual(self.model.get_column_name(1), 'cbar') - self.assertEqual(self.model.get_column_name(2), 'cfoo_2') - self.assertEqual(self.model.get_column_name(3), 'cbar_2') - self.assertEqual(self.model.get_column_name(4), 'cbaz_2') - - return - - def test_get_value(self): - - self.assertEqual(self.model.get_value(0,0), 1) - self.assertEqual(self.model.get_value(0,1), 2) - self.assertEqual(self.model.get_value(0,2), 3) - self.assertEqual(self.model.get_value(0,3), 4) - self.assertEqual(self.model.get_value(0,4), 5) - self.assertEqual(self.model.get_value(1,0), 3) - self.assertEqual(self.model.get_value(1,1), 4) - self.assertEqual(self.model.get_value(1,2), 6) - self.assertEqual(self.model.get_value(1,3), 7) - self.assertEqual(self.model.get_value(1,4), 8) - - return - - def test_is_cell_empty(self): - - rows = self.model.get_row_count() - columns = self.model.get_column_count() - - self.assertEqual(self.model.is_cell_empty(0,0), False, - "Cell (0,0) should not be empty.") - self.assertEqual(self.model.is_cell_empty(rows,0), True, - "Cell below the table should be empty.") - self.assertEqual(self.model.is_cell_empty(0,columns), True, - "Cell right of the table should be empty.") - self.assertEqual(self.model.is_cell_empty(rows,columns), True, - "Cell below and right of table should be empty.") - - return - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/tests/simple_grid_model_test_case.py python-pyface-7.4.0/pyface/ui/wx/grid/tests/simple_grid_model_test_case.py --- python-pyface-6.1.2/pyface/ui/wx/grid/tests/simple_grid_model_test_case.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/tests/simple_grid_model_test_case.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -import unittest - -try: - from pyface.ui.wx.grid.api \ - import GridRow, GridColumn, SimpleGridModel -except ImportError: - wx_available = False -else: - wx_available = True - - -@unittest.skipUnless(wx_available, "Wx is not available") -class CompositeGridModelTestCase( unittest.TestCase ): - - def setUp(self): - - self.model = SimpleGridModel(data=[[None,2],[3,4]], - rows=[GridRow(label='foo'), - GridRow(label='bar')], - columns=[GridColumn(label='cfoo'), - GridColumn(label='cbar')] - ) - - return - - def test_get_column_count(self): - - self.assertEqual(self.model.get_column_count(), 2) - - return - - def test_get_row_count(self): - - self.assertEqual(self.model.get_row_count(), 2) - - return - - def test_get_row_name(self): - - # Regardless of the rows specified in the composed models, the - # composite model returns its own rows. - self.assertEqual(self.model.get_row_name(0), 'foo') - self.assertEqual(self.model.get_row_name(1), 'bar') - - return - - def test_get_column_name(self): - - self.assertEqual(self.model.get_column_name(0), 'cfoo') - self.assertEqual(self.model.get_column_name(1), 'cbar') - - return - - def test_get_value(self): - - self.assertEqual(self.model.get_value(0,0), None) - self.assertEqual(self.model.get_value(0,1), 2) - self.assertEqual(self.model.get_value(1,0), 3) - self.assertEqual(self.model.get_value(1,1), 4) - - return - - def test_is_cell_empty(self): - - rows = self.model.get_row_count() - columns = self.model.get_column_count() - - self.assertEqual(self.model.is_cell_empty(0,0), True, - "Cell containing None should be empty.") - self.assertEqual(self.model.is_cell_empty(10,10), True, - "Cell outside the range of values should be empty.") - - return - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/tests/test_composite_grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/tests/test_composite_grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/tests/test_composite_grid_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/tests/test_composite_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,113 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +import unittest + +try: + from pyface.ui.wx.grid.api import ( + CompositeGridModel, + GridRow, + GridColumn, + SimpleGridModel, + ) +except ImportError: + wx_available = False +else: + wx_available = True + + +@unittest.skipUnless(wx_available, "Wx is not available") +class CompositeGridModelTestCase(unittest.TestCase): + def setUp(self): + + self.model_1 = SimpleGridModel( + data=[[1, 2], [3, 4]], + rows=[GridRow(label="foo"), GridRow(label="bar")], + columns=[GridColumn(label="cfoo"), GridColumn(label="cbar")], + ) + self.model_2 = SimpleGridModel( + data=[[3, 4, 5], [6, 7, 8]], + rows=[GridRow(label="baz"), GridRow(label="bat")], + columns=[ + GridColumn(label="cfoo_2"), + GridColumn(label="cbar_2"), + GridColumn(label="cbaz_2"), + ], + ) + + self.model = CompositeGridModel(data=[self.model_1, self.model_2]) + + def test_get_column_count(self): + + column_count_1 = self.model_1.get_column_count() + column_count_2 = self.model_2.get_column_count() + + self.assertEqual( + self.model.get_column_count(), column_count_1 + column_count_2 + ) + + def test_get_row_count(self): + + self.assertEqual(self.model.get_row_count(), 2) + + def test_get_row_name(self): + + # Regardless of the rows specified in the composed models, the + # composite model returns its own rows. + self.assertEqual(self.model.get_row_name(0), "1") + self.assertEqual(self.model.get_row_name(1), "2") + + def test_get_column_name(self): + + self.assertEqual(self.model.get_column_name(0), "cfoo") + self.assertEqual(self.model.get_column_name(1), "cbar") + self.assertEqual(self.model.get_column_name(2), "cfoo_2") + self.assertEqual(self.model.get_column_name(3), "cbar_2") + self.assertEqual(self.model.get_column_name(4), "cbaz_2") + + def test_get_value(self): + + self.assertEqual(self.model.get_value(0, 0), 1) + self.assertEqual(self.model.get_value(0, 1), 2) + self.assertEqual(self.model.get_value(0, 2), 3) + self.assertEqual(self.model.get_value(0, 3), 4) + self.assertEqual(self.model.get_value(0, 4), 5) + self.assertEqual(self.model.get_value(1, 0), 3) + self.assertEqual(self.model.get_value(1, 1), 4) + self.assertEqual(self.model.get_value(1, 2), 6) + self.assertEqual(self.model.get_value(1, 3), 7) + self.assertEqual(self.model.get_value(1, 4), 8) + + def test_is_cell_empty(self): + + rows = self.model.get_row_count() + columns = self.model.get_column_count() + + self.assertEqual( + self.model.is_cell_empty(0, 0), + False, + "Cell (0,0) should not be empty.", + ) + self.assertEqual( + self.model.is_cell_empty(rows, 0), + True, + "Cell below the table should be empty.", + ) + self.assertEqual( + self.model.is_cell_empty(0, columns), + True, + "Cell right of the table should be empty.", + ) + self.assertEqual( + self.model.is_cell_empty(rows, columns), + True, + "Cell below and right of table should be empty.", + ) + + return diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/tests/test_simple_grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/tests/test_simple_grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/tests/test_simple_grid_model.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/tests/test_simple_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,73 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +import unittest + +try: + from pyface.ui.wx.grid.api import GridRow, GridColumn, SimpleGridModel +except ImportError: + wx_available = False +else: + wx_available = True + + +@unittest.skipUnless(wx_available, "Wx is not available") +class CompositeGridModelTestCase(unittest.TestCase): + def setUp(self): + + self.model = SimpleGridModel( + data=[[None, 2], [3, 4]], + rows=[GridRow(label="foo"), GridRow(label="bar")], + columns=[GridColumn(label="cfoo"), GridColumn(label="cbar")], + ) + + def test_get_column_count(self): + + self.assertEqual(self.model.get_column_count(), 2) + + def test_get_row_count(self): + + self.assertEqual(self.model.get_row_count(), 2) + + def test_get_row_name(self): + + # Regardless of the rows specified in the composed models, the + # composite model returns its own rows. + self.assertEqual(self.model.get_row_name(0), "foo") + self.assertEqual(self.model.get_row_name(1), "bar") + + def test_get_column_name(self): + + self.assertEqual(self.model.get_column_name(0), "cfoo") + self.assertEqual(self.model.get_column_name(1), "cbar") + + def test_get_value(self): + + self.assertEqual(self.model.get_value(0, 0), None) + self.assertEqual(self.model.get_value(0, 1), 2) + self.assertEqual(self.model.get_value(1, 0), 3) + self.assertEqual(self.model.get_value(1, 1), 4) + + def test_is_cell_empty(self): + + rows = self.model.get_row_count() + columns = self.model.get_column_count() + + self.assertEqual( + self.model.is_cell_empty(0, 0), + True, + "Cell containing None should be empty.", + ) + self.assertEqual( + self.model.is_cell_empty(rows+1, columns+1), + True, + "Cell outside the range of values should be empty.", + ) + + return diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/trait_grid_cell_adapter.py python-pyface-7.4.0/pyface/ui/wx/grid/trait_grid_cell_adapter.py --- python-pyface-6.1.2/pyface/ui/wx/grid/trait_grid_cell_adapter.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/trait_grid_cell_adapter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Adapter class to make trait editor controls work inside of a grid. """ -# Major package imports + import wx -from wx.grid import PyGridCellEditor +from wx.grid import GridCellEditor from wx import SIZE_ALLOW_MINUS_ONE -# Local imports: -from combobox_focus_handler import ComboboxFocusHandler - -wx_28 = (float( wx.__version__[:3] ) >= 2.8) def get_control(control): if isinstance(control, wx.Control): @@ -34,37 +27,42 @@ return None -def push_control(control, grid): - control.PushEventHandler(ComboboxFocusHandler(grid)) - for child_control in control.GetChildren(): - push_control(child_control, grid) -class TraitGridCellAdapter(PyGridCellEditor): +class TraitGridCellAdapter(GridCellEditor): """ Wrap a trait editor as a GridCellEditor object. """ - def __init__(self, trait_editor_factory, obj, name, description, - handler = None, context = None, style = 'simple', - width = -1.0, height = -1.0): + def __init__( + self, + trait_editor_factory, + obj, + name, + description, + handler=None, + context=None, + style="simple", + width=-1.0, + height=-1.0, + ): """ Build a new TraitGridCellAdapter object. """ - PyGridCellEditor.__init__(self) - self._factory = trait_editor_factory - self._style = style - self._width = width - self._height = height - self._editor = None - self._obj = obj - self._name = name + super().__init__() + self._factory = trait_editor_factory + self._style = style + self._width = width + self._height = height + self._editor = None + self._obj = obj + self._name = name self._description = description - self._handler = handler - self._context = context + self._handler = handler + self._context = context def Create(self, parent, id, evtHandler): """ Called to create the control, which must derive from wxControl. """ from traitsui.api import UI, default_handler # If the editor has already been created, ignore the request: - if hasattr( self, '_control' ): + if hasattr(self, "_control"): return handler = self._handler @@ -72,12 +70,12 @@ handler = default_handler() if self._context is None: - ui = UI(handler = handler) + ui = UI(handler=handler) else: context = self._context.copy() - context['table_editor_object'] = context['object'] - context['object'] = self._obj - ui = UI(handler = handler, context = context) + context["table_editor_object"] = context["object"] + context["object"] = self._obj + ui = UI(handler=handler, context=context) # Link the editor's undo history in to the main ui undo history if the # UI object is available: @@ -87,12 +85,10 @@ # make sure the factory knows this is a grid_cell editor factory.is_grid_cell = True - factory_method = getattr(factory, self._style + '_editor') - self._editor = factory_method(ui, - self._obj, - self._name, - self._description, - parent) + factory_method = getattr(factory, self._style + "_editor") + self._editor = factory_method( + ui, self._obj, self._name, self._description, parent + ) # Tell the editor to actually build the editing widget: self._editor.prepare(parent) @@ -101,26 +97,23 @@ self._control = control = self._editor.control # Calculate and save the required editor height: - grid, row, col = getattr(self, '_grid_info', (None, None, None)) - width, height = control.GetBestSize() + grid, row, col = getattr(self, "_grid_info", (None, None, None)) + width, height = control.GetBestSize() - self_height = self._height + self_height = self._height if self_height > 1.0: - height = int( self_height ) + height = int(self_height) elif (self_height >= 0.0) and (grid is not None): - height = int( self_height * grid.GetSize()[1] ) + height = int(self_height * grid.GetSize().Get()[1]) self_width = self._width if self_width > 1.0: - width = int( self_width ) + width = int(self_width) elif (self_width >= 0.0) and (grid is not None): - width = int( self_width * grid.GetSize()[0] ) + width = int(self_width * grid.GetSize().Get()[0]) self._edit_width, self._edit_height = width, height - # Set up the event handler for each window in the cell editor: - push_control(control, grid) - # Set up the first control found within the cell editor as the cell # editor control: control = get_control(control) @@ -134,16 +127,16 @@ """ changed = False edit_width, edit_height = rect.width, rect.height - grid, row, col = getattr(self, '_grid_info', (None, None, None)) + grid, row, col = getattr(self, "_grid_info", (None, None, None)) if (grid is not None) and self._editor.scrollable: edit_width, cur_width = self._edit_width, grid.GetColSize(col) - restore_width = getattr( grid, '_restore_width', None ) + restore_width = getattr(grid, "_restore_width", None) if restore_width is not None: cur_width = restore_width if (edit_width > cur_width) or (restore_width is not None): - edit_width = max( edit_width, cur_width ) + edit_width = max(edit_width, cur_width) grid._restore_width = cur_width grid.SetColSize(col, edit_width + 1 + (col == 0)) changed = True @@ -152,12 +145,12 @@ edit_height, cur_height = self._edit_height, grid.GetRowSize(row) - restore_height = getattr( grid, '_restore_height', None ) + restore_height = getattr(grid, "_restore_height", None) if restore_height is not None: cur_height = restore_height if (edit_height > cur_height) or (restore_height is not None): - edit_height = max( edit_height, cur_height ) + edit_height = max(edit_height, cur_height) grid._restore_height = cur_height grid.SetRowSize(row, edit_height + 1 + (row == 0)) changed = True @@ -167,25 +160,25 @@ if changed: grid.ForceRefresh() - self._control.SetDimensions(rect.x + 1, rect.y + 1, - edit_width, edit_height, - SIZE_ALLOW_MINUS_ONE) + self._control.SetSize( + rect.x + 1, + rect.y + 1, + edit_width, + edit_height, + SIZE_ALLOW_MINUS_ONE, + ) if changed: - grid.MakeCellVisible(grid.GetGridCursorRow(), - grid.GetGridCursorCol()) + grid.MakeCellVisible( + grid.GetGridCursorRow(), grid.GetGridCursorCol() + ) def Show(self, show, attr): """ Show or hide the edit control. You can use the attr (if not None) to set colours or fonts for the control. """ if self.IsCreated(): - if wx_28: - super(TraitGridCellAdapter, self).Show(show, attr) - else: - self.base_Show(show, attr) - - return + super().Show(show, attr) def PaintBackground(self, rect, attr): """ Draws the part of the cell not occupied by the edit control. The @@ -193,7 +186,6 @@ attribute. In this class the edit control fills the whole cell so don't do anything at all in order to reduce flicker. """ - return def BeginEdit(self, row, col, grid): """ Make sure the control is ready to edit. """ @@ -205,21 +197,17 @@ if isinstance(control, wx.TextCtrl): control.SetSelection(-1, -1) - def EndEdit(self, *args): - """ Validate the input data. """ - return True # Pass on all data to ApplyEdit - - def ApplyEdit(self, row, col, grid): + def EndEdit(self, row, col, grid): """ Do anything necessary to complete the editing. """ self._control.Show(False) - changed = False + changed = False grid, row, col = self._grid_info if grid._no_reset_col: grid._no_reset_col = False else: - width = getattr(grid, '_restore_width', None) + width = getattr(grid, "_restore_width", None) if width is not None: del grid._restore_width grid.SetColSize(col, width) @@ -228,7 +216,7 @@ if grid._no_reset_row: grid._no_reset_row = False else: - height = getattr(grid, '_restore_height', None) + height = getattr(grid, "_restore_height", None) if height is not None: del grid._restore_height grid.SetRowSize(row, height) @@ -241,34 +229,33 @@ """ Reset the value in the control back to its starting value. """ # fixme: should we be using the undo history here? - return def StartingKey(self, evt): """ If the editor is enabled by pressing keys on the grid, this will be called to let the editor do something about that first key if desired. """ - return def StartingClick(self): """ If the editor is enabled by clicking on the cell, this method will be called to allow the editor to simulate the click on the control if needed. """ - return def Destroy(self): """ Final cleanup. """ self._editor.dispose() - return def Clone(self): """ Create a new object which is the copy of this one. """ - return TraitGridCellAdapter(self._factory, self._obj, self._name, - self._description, style=self._style) + return TraitGridCellAdapter( + self._factory, + self._obj, + self._name, + self._description, + style=self._style, + ) def dispose(self): if self._editor is not None: self._editor.dispose() - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/grid/trait_grid_model.py python-pyface-7.4.0/pyface/ui/wx/grid/trait_grid_model.py --- python-pyface-6.1.2/pyface/ui/wx/grid/trait_grid_model.py 2019-05-31 13:42:40.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/grid/trait_grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A TraitGridModel builds a grid from a list of traits objects. Each row represents on object, each column one trait from those objects. All the objects must be of the same type. Optionally a user may pass in a list of trait names @@ -18,47 +15,61 @@ list is not passed in, then the first object is inspected and every trait from that object gets a column.""" -# Enthought library imports -from __future__ import print_function -from traits.api import Any, Bool, Callable, Dict, Function, HasTraits, \ - Int, List, Str, Trait, TraitError, Type +from functools import cmp_to_key + +from traits.api import ( + Any, + Bool, + Callable, + Dict, + HasTraits, + Instance, + Int, + List, + Str, + Type, + Union, +) +from traits.observation.api import match -# local imports from .grid_model import GridColumn, GridModel, GridSortEvent from .trait_grid_cell_adapter import TraitGridCellAdapter + # The classes below are part of the table specification. class TraitGridColumn(GridColumn): """ Structure for holding column specifications in a TraitGridModel. """ # The trait name for this column. This takes precedence over method - name = Trait(None, None, Str) + name = Union(None, Str) # A method name to call to get the value for this column - method = Trait(None, None, Str) + method = Union(None, Str) # A method to be used to sort on this column - sorter = Trait(None, None, Callable) + sorter = Callable # A dictionary of formats for the display of different types. If it is # defined as a callable, then that callable must accept a single argument. - formats = Dict(key_trait = Type, value_trait=Trait('', Str, Callable)) + formats = Dict(Type, Union(Str, Callable)) # A name to designate the type of this column - typename = Trait(None, None, Str) + typename = Union(None, Str) # note: context menus should go in here as well? but we need # more info than we have available at this point size = Int(-1) + class TraitGridSelection(HasTraits): """ Structure for holding specification information. """ # The selected object - obj = Trait(HasTraits) + obj = Instance(HasTraits) # The specific trait selected on the object - trait_name = Trait(None, None, Str) + trait_name = Union(None, Str) + # The meat. class TraitGridModel(GridModel): @@ -70,29 +81,29 @@ inspected and every trait from that object gets a column.""" # A 2-dimensional list/array containing the grid data. - data = List()#HasTraits) + data = List(Any) # The column definitions - columns = Trait(None, None, List(Trait(None, Str, TraitGridColumn))) + columns = Union(None, List(Union(None, Str, Instance(TraitGridColumn)))) # The trait to look at to get the row name - row_name_trait = Trait(None, None, Str) + row_name_trait = Union(None, Str) # Allow column sorting? allow_column_sort = Bool(True) # A factory to generate new rows. If this is not None then it must # be a no-argument function. - row_factory = Trait(None, None, Function) + row_factory = Callable - ######################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ######################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Create a TraitGridModel object. """ # Base class constructor - super(TraitGridModel, self).__init__(**traits) + super().__init__(**traits) # if no columns are pass in then create the list of names # from the first trait in the list. if the list is empty, @@ -106,35 +117,31 @@ # we only add traits that aren't events, since events # are write-only for name, trait in self.data[0].traits().items(): - if trait.type != 'event': - self._auto_columns.append(TraitGridColumn(name = name)) + if trait.type != "event": + self._auto_columns.append(TraitGridColumn(name=name)) else: self._auto_columns = [] # attach trait handlers to the list object - self.on_trait_event(self._on_data_changed, 'data') - self.on_trait_event(self._on_data_items_changed, 'data_items') + self.observe(self._on_data_changed, "data") + self.observe(self._on_data_items_changed, "data:items") # attach appropriate trait handlers to objects in the list self.__manage_data_listeners(self.data) # attach a listener to the column definitions so we refresh when # they change - self.on_trait_change(self._on_columns_changed, - 'columns') - self.on_trait_event(self._on_columns_items_changed, - 'columns_items') + self.observe(self._on_columns_changed, "columns") + self.observe(self._on_columns_items_changed, "columns:items") # attach listeners to the column definitions themselves self.__manage_column_listeners(self.columns) # attach a listener to the row_name_trait - self.on_trait_change(self._on_row_name_trait_changed, 'row_name_trait') + self.observe(self._on_row_name_trait_changed, "row_name_trait") - return - - ######################################################################### + # ------------------------------------------------------------------------ # 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_column_count(self): """ Return the number of columns for this table. """ @@ -153,7 +160,7 @@ else: name = col.name except IndexError: - name = '' + name = "" return name @@ -191,8 +198,11 @@ values = [] for obj in self.data: for col in cols: - values.append(TraitGridSelection(obj = obj, - trait_name = self.__get_column_name(col))) + values.append( + TraitGridSelection( + obj=obj, trait_name=self.__get_column_name(col) + ) + ) return values @@ -208,38 +218,24 @@ column = self._auto_columns[col] name = self.__get_column_name(col) # by default we use cmp to sort on the traits - sorter = cmp - if isinstance(column, TraitGridColumn) and \ - column.sorter is not None: - sorter = column.sorter + key = None + if ( + isinstance(column, TraitGridColumn) + and column.sorter is not None + ): + key = cmp_to_key(column.sorter) except IndexError: return - # now sort the data appropriately - def sort_function(a, b): - if hasattr(a, name): - a_trait = getattr(a, name) - else: - a_trait = None - - if hasattr(b, name): - b_trait = getattr(b, name) - else: - b_trait = None + def key_function(a): + trait = getattr(a, name, None) + if key: + return key(trait) - return sorter(a_trait, b_trait) - - self.data.sort(sort_function) - - if reverse: - self.data.reverse() + self.data.sort(key=key_function, reverse=reverse) # now fire an event to tell the grid we're sorted - print('firing sort event') - self.column_sorted = GridSortEvent(index = col, reversed = reverse) - - return - + self.column_sorted = GridSortEvent(index=col, reversed=reverse) def is_column_read_only(self, index): """ Return True if the column specified by the zero-based index @@ -300,7 +296,7 @@ values = [] for row_index in rows: - values.append(TraitGridSelection(obj = self.data[row_index])) + values.append(TraitGridSelection(obj=self.data[row_index])) return values @@ -315,15 +311,15 @@ # print 'TraitGridModel.get_cell_editor row: ', row, ' col: ', col - obj = self.data[row] + obj = self.data[row] trait_name = self.__get_column_name(col) - trait = obj.base_trait(trait_name) + trait = obj.base_trait(trait_name) if trait is None: return None factory = trait.get_editor() - return TraitGridCellAdapter(factory, obj, trait_name, '') + return TraitGridCellAdapter(factory, obj, trait_name, "") def get_cell_drag_value(self, row, col): """ Return the value to use when the specified cell is dragged or @@ -346,7 +342,7 @@ obj = self.data[row] trait_name = self.__get_column_name(col) - return TraitGridSelection(obj = obj, trait_name = trait_name) + return TraitGridSelection(obj=obj, trait_name=trait_name) def resolve_selection(self, selection_list): """ Returns a list of (row, col) grid-cell coordinates that @@ -386,9 +382,12 @@ value = self.get_cell_drag_value(row, col) formats = self.__get_column_formats(col) - if value is not None and formats is not None and \ - type(value) in formats and \ - formats[type(value)] is not None: + if ( + value is not None + and formats is not None + and type(value) in formats + and formats[type(value)] is not None + ): try: format = formats[type(value)] if callable(format): @@ -414,9 +413,9 @@ False otherwise. """ return not self.is_column_read_only(col) - ######################################################################### + # ------------------------------------------------------------------------ # protected 'GridModel' interface. - ######################################################################### + # ------------------------------------------------------------------------ def _insert_rows(self, pos, num_rows): """ Inserts num_rows at pos and fires an event iff a factory method for new rows is defined. Otherwise returns 0. """ @@ -428,7 +427,7 @@ new_data.append(self.row_factory()) count = self._insert_rows_into_model(pos, new_data) - self.rows_added = ('added', pos, new_data) + self.rows_added = ("added", pos, new_data) return count @@ -446,7 +445,7 @@ Raises a ValueError if the value is vetoed or the cell at (row, col) does not exist. """ - #print 'TraitGridModel._set_value: new: ', value + # print 'TraitGridModel._set_value: new: ', value new_rows = 0 # find the column indexed by col @@ -470,9 +469,9 @@ return new_rows - ######################################################################### + # ------------------------------------------------------------------------ # protected interface. - ######################################################################### + # ------------------------------------------------------------------------ def _get_row(self, index): """ Return the object that corresponds to the row at index. Override this to handle very large data sets. """ @@ -500,7 +499,10 @@ # this is the case when an object method is specified value = getattr(row, column.method)() - return value + if value is None: + return None + else: + return str(value) # value def _set_data_on_row(self, row, column, value): """ Retrieve the data specified by column for this row. Attribute @@ -516,9 +518,9 @@ # sometimes the underlying grid gives us 0/1 instead # of True/False. do some conversion here to make that # case worl. - #if type(getattr(row, column)) == bool and \ + # if type(getattr(row, column)) == bool and \ # type(value) != bool: - # convert the value to a boolean + # convert the value to a boolean # value = bool(value) setattr(row, column, value) @@ -527,9 +529,9 @@ # sometimes the underlying grid gives us 0/1 instead # of True/False. do some conversion here to make that # case worl. - #if type(getattr(row, column.name)) == bool and \ + # if type(getattr(row, column.name)) == bool and \ # type(value) != bool: - # convert the value to a boolean + # convert the value to a boolean # value = bool(value) setattr(row, column.name, value) success = True @@ -547,8 +549,6 @@ self.data.insert(pos, data) pos += 1 - return - def _delete_rows_from_model(self, pos, num_rows): """ Delete the specified rows from the model. Override this method to handle very large data sets. """ @@ -556,22 +556,20 @@ return num_rows - ########################################################################### + # ------------------------------------------------------------------------ # trait handlers - ########################################################################### + # ------------------------------------------------------------------------ - def _on_row_name_trait_changed(self, new): + def _on_row_name_trait_changed(self, event): """ Force the grid to refresh when any underlying trait changes. """ self.fire_content_changed() - return - def _on_columns_changed(self, object, name, old, new): + def _on_columns_changed(self, event): """ Force the grid to refresh when any underlying trait changes. """ - self.__manage_column_listeners(old, remove=True) + self.__manage_column_listeners(event.old, remove=True) self.__manage_column_listeners(self.columns) self._auto_columns = self.columns self.fire_structure_changed() - return def _on_columns_items_changed(self, event): """ Force the grid to refresh when any underlying trait changes. """ @@ -579,20 +577,17 @@ self.__manage_column_listeners(event.removed, remove=True) self.__manage_column_listeners(event.added) self.fire_structure_changed() - return - def _on_contained_trait_changed(self, new): + def _on_contained_trait_changed(self, event): """ Force the grid to refresh when any underlying trait changes. """ self.fire_content_changed() - return - def _on_data_changed(self, object, name, old, new): + def _on_data_changed(self, event): """ Force the grid to refresh when the underlying list changes. """ - self.__manage_data_listeners(old, remove=True) + self.__manage_data_listeners(event.old, remove=True) self.__manage_data_listeners(self.data) self.fire_structure_changed() - return def _on_data_items_changed(self, event): """ Force the grid to refresh when the underlying list changes. """ @@ -604,11 +599,10 @@ self.__manage_data_listeners(event.added) self.fire_content_changed() - return - ########################################################################### + # ------------------------------------------------------------------------ # private interface. - ########################################################################### + # ------------------------------------------------------------------------ def __get_data_column(self, col): """ Return a 1-d list of data from the column indexed by col. """ @@ -618,13 +612,16 @@ coldata = [] for row in range(row_count): try: - coldata.append(self.get_value(row, col)) + val = self.get_value(row, col) + if val is None: + coldata.append(None) + else: + coldata.append(val) # self.get_value(row, col)) except IndexError: coldata.append(None) return coldata - def __get_column(self, col): try: @@ -644,7 +641,7 @@ def __get_column_typename(self, col): - name = column = self.__get_column(col) + column = self.__get_column(col) typename = None if isinstance(column, TraitGridColumn): typename = column.typename @@ -688,18 +685,19 @@ # attach appropriate trait handlers to objects in the list if list is not None: for item in list: - item.on_trait_change(self._on_contained_trait_changed, - remove = remove) - return + item.observe( + self._on_contained_trait_changed, + match(lambda name, trait: True), + remove=remove + ) def __manage_column_listeners(self, collist, remove=False): if collist is not None: for col in collist: if isinstance(col, TraitGridColumn): - col.on_trait_change(self._on_columns_changed, - remove = remove) - - return - -#### EOF #################################################################### + col.observe( + self._on_columns_changed, + match(lambda name, trait: True), + remove=remove, + ) diff -Nru python-pyface-6.1.2/pyface/ui/wx/gui.py python-pyface-7.4.0/pyface/ui/wx/gui.py --- python-pyface-6.1.2/pyface/ui/wx/gui.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/gui.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,34 +1,29 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. + import logging import sys -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Bool, HasTraits, provides, Unicode + +from traits.api import Bool, HasTraits, provides, Str from pyface.util.guisupport import start_event_loop_wx -# Local imports. + from pyface.i_gui import IGUI, MGUI @@ -39,18 +34,17 @@ @provides(IGUI) class GUI(MGUI, HasTraits): - - #### 'GUI' interface ###################################################### + # 'GUI' interface -----------------------------------------------------# busy = Bool(False) started = Bool(False) - state_location = Unicode + state_location = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, splash_screen=None): # Display the (optional) splash screen. @@ -59,9 +53,9 @@ if self._splash_screen is not None: self._splash_screen.open() - ########################################################################### + # ------------------------------------------------------------------------ # 'GUI' class interface. - ########################################################################### + # ------------------------------------------------------------------------ @classmethod def invoke_after(cls, millisecs, callable, *args, **kw): @@ -93,9 +87,9 @@ else: GUI._cursor = None - ########################################################################### + # ------------------------------------------------------------------------ # 'GUI' interface. - ########################################################################### + # ------------------------------------------------------------------------ def start_event_loop(self): """ Start the GUI event loop. """ @@ -108,11 +102,13 @@ self.set_trait_after(10, self, "started", True) # A hack to force menus to appear for applications run on Mac OS X. - if sys.platform == 'darwin': + if sys.platform == "darwin": + def _mac_os_x_hack(): f = wx.Frame(None, -1) f.Show(True) f.Close() + self.invoke_later(_mac_os_x_hack) logger.debug("---------- starting GUI event loop ----------") @@ -126,9 +122,9 @@ logger.debug("---------- stopping GUI event loop ----------") wx.GetApp().ExitMainLoop() - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _state_location_default(self): """ The default state location handler. """ @@ -144,5 +140,3 @@ del self._wx_cursor return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/heading_text.py python-pyface-7.4.0/pyface/ui/wx/heading_text.py --- python-pyface-6.1.2/pyface/ui/wx/heading_text.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/heading_text.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,159 +1,56 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. import wx -# Enthought library imports. -from traits.api import Instance, Int, provides, Unicode +from traits.api import provides -# Local imports. from pyface.i_heading_text import IHeadingText, MHeadingText -from pyface.image_resource import ImageResource -from pyface.wx.util.font_helper import new_font_like -from .widget import Widget +from pyface.ui_traits import Image +from .image_resource import ImageResource +from .layout_widget import LayoutWidget @provides(IHeadingText) -class HeadingText(MHeadingText, Widget): - """ The toolkit specific implementation of a HeadingText. See the - IHeadingText interface for the API documentation. +class HeadingText(MHeadingText, LayoutWidget): + """ The Wx-specific implementation of a HeadingText. """ + # 'IHeadingText' interface --------------------------------------------- - #### 'IHeadingText' interface ############################################# - - level = Int(1) - - text = Unicode('Default') - - image = Instance(ImageResource, ImageResource('heading_level_1')) - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, parent, **traits): - """ Creates the panel. """ - - # Base class constructor. - super(HeadingText, self).__init__(**traits) - - # Create the toolkit-specific control that represents the widget. - self.control = self._create_control(parent) + #: Background image. This is deprecated and no-longer used. + image = Image(ImageResource("heading_level_1")) - return - - ########################################################################### - # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'IWidget' interface. + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ + control = wx.StaticText(parent) + return control - # The background image (it is tiled). - image = self.image.create_image() - self._bmp = image.ConvertToBitmap() - - sizer = wx.BoxSizer(wx.VERTICAL) - panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN | wx.SIMPLE_BORDER) - panel.SetSizer(sizer) - panel.SetAutoLayout(True) - - # Create a suitable font. - self._font = new_font_like(wx.NORMAL_FONT, family=wx.SWISS) - - width, height = self._get_preferred_size(self.text, self._font) - panel.SetMinSize((width, height)) - - wx.EVT_PAINT(panel, self._on_paint_background) - wx.EVT_ERASE_BACKGROUND(panel, self._on_erase_background) - - return panel - - def _get_preferred_size(self, text, font): - """ Calculates the preferred size of the widget. """ - - dc = wx.ScreenDC() - - dc.SetFont(font) - width, height = dc.GetTextExtent(text) - - return (width + 10, height + 10) - - def _tile_background_image(self, dc, width, height): - """ Tiles the background image. """ - - w = self._bmp.GetWidth() - h = self._bmp.GetHeight() - - x = 0 - while x < width: - y = 0 - while y < height: - dc.DrawBitmap(self._bmp, x, y) - y = y + h - - x = x + w - - return - - #### Trait event handlers ################################################# - - def _text_changed(self, new): - """ Called when the text is changed. """ - - if self.control is not None: - self.control.Refresh() - - return - - #### wx event handlers #################################################### - - def _on_paint_background(self, event): - """ Called when the background of the panel is painted. """ - - dc = wx.PaintDC(self.control) - size = self.control.GetClientSize() - - # Tile the background image. - self._tile_background_image(dc, size.width, size.height) - - # Render the text. - dc.SetFont(self._font) - dc.DrawText(self.text, 5, 4) - - return - - def _on_erase_background(self, event): - """ Called when the background of the panel is erased. """ - - dc = event.GetDC() - size = self.control.GetClientSize() - - # Tile the background image. - self._tile_background_image(dc, size.width, size.height) - - # Render the text. - dc.SetFont(self._font) - dc.DrawText(self.text, 5, 4) - - return + # ------------------------------------------------------------------------ + # Private interface. + # ------------------------------------------------------------------------ -#### EOF ###################################################################### + def _set_control_text(self, text): + """ Set the text on the toolkit specific widget. """ + # Bold the text. Wx supports a limited subset of HTML for rich text. + text = f"{text}" + self.control.SetLabelMarkup(text) + + def _get_control_text(self): + """ Get the text on the toolkit specific widget. """ + return self.control.GetLabelText() diff -Nru python-pyface-6.1.2/pyface/ui/wx/image_button.py python-pyface-7.4.0/pyface/ui/wx/image_button.py --- python-pyface-6.1.2/pyface/ui/wx/image_button.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/image_button.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,266 +1,285 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! -# -# Author: David C. Morrill +# Thanks for using Enthought open source! # # Description: Image and text-based pyface button/toolbar/radio button control. -# -#------------------------------------------------------------------------------ + """ An image and text-based control that can be used as a normal, radio or toolbar button. """ -from __future__ import absolute_import +import warnings import wx -from numpy import array, fromstring, reshape, ravel, dtype -from traits.api import Str, Range, Enum, Instance, Event, false +from numpy import array, frombuffer, reshape, ravel, dtype + +from traits.api import Any, Bool, Str, Range, Enum, Event -from .widget import Widget -from .image_resource import ImageResource +from pyface.ui_traits import Image, Orientation +from .layout_widget import LayoutWidget -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Constants: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Text color used when a button is disabled: -DisabledTextColor = wx.Colour( 128, 128, 128 ) +DisabledTextColor = wx.Colour(128, 128, 128) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # 'ImageButton' class: -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class ImageButton ( Widget ): + +class ImageButton(LayoutWidget): """ An image and text-based control that can be used as a normal, radio or toolbar button. """ # Pens used to draw the 'selection' marker: _selectedPenDark = wx.Pen( - wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DSHADOW ), 1, - wx.PENSTYLE_SOLID + wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW), 1, wx.SOLID ) _selectedPenLight = wx.Pen( - wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DHIGHLIGHT ), 1, - wx.PENSTYLE_SOLID + wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DHIGHLIGHT), 1, wx.SOLID ) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Trait definitions: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # The image: - image = Instance( ImageResource, allow_none = True ) + image = Image() # The (optional) label: - label = Str + label = Str() # Extra padding to add to both the left and right sides: - width_padding = Range( 0, 31, 7 ) + width_padding = Range(0, 31, 7) # Extra padding to add to both the top and bottom sides: - height_padding = Range( 0, 31, 5 ) + height_padding = Range(0, 31, 5) # Presentation style: - style = Enum( 'button', 'radio', 'toolbar', 'checkbox' ) + style = Enum("button", "radio", "toolbar", "checkbox") # Orientation of the text relative to the image: - orientation = Enum( 'vertical', 'horizontal' ) + orientation = Orientation() # Is the control selected ('radio' or 'checkbox' style)? - selected = false + selected = Bool(False) # Fired when a 'button' or 'toolbar' style control is clicked: - clicked = Event + clicked = Event() + + _image = Any() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Initializes the object: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def __init__ ( self, parent, **traits ): + def __init__(self, parent, **traits): """ Creates a new image control. """ - self._image = None + create = traits.pop("create", True) + + super().__init__(parent=parent, **traits) - super( ImageButton, self ).__init__( **traits ) + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + def _create_control(self, parent): + self._recalc_size() + + control = wx.Window(parent, -1, size=wx.Size(self._dx, self._dy)) + control._owner = self + self._mouse_over = self._button_down = False + # Set up mouse event handlers: + control.Bind(wx.EVT_ENTER_WINDOW, self._on_enter_window) + control.Bind(wx.EVT_LEAVE_WINDOW, self._on_leave_window) + control.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) + control.Bind(wx.EVT_LEFT_UP, self._on_left_up) + control.Bind(wx.EVT_PAINT, self._on_paint) + + return control + + def _recalc_size(self): # Calculate the size of the button: idx = idy = tdx = tdy = 0 if self._image is not None: idx = self._image.GetWidth() idy = self._image.GetHeight() - if self.label != '': + if self.label != "": dc = wx.ScreenDC() - dc.SetFont( wx.NORMAL_FONT ) - tdx, tdy = dc.GetTextExtent( self.label ) + dc.SetFont(wx.NORMAL_FONT) + tdx, tdy = dc.GetTextExtent(self.label) - wp2 = self.width_padding + 2 + wp2 = self.width_padding + 2 hp2 = self.height_padding + 2 - if self.orientation == 'horizontal': + if self.orientation == "horizontal": self._ix = wp2 - spacing = (idx > 0) * (tdx > 0) * 4 + spacing = (idx > 0) * (tdx > 0) * 4 self._tx = self._ix + idx + spacing - dx = idx + tdx + spacing - dy = max( idy, tdy ) - self._iy = hp2 + ((dy - idy) / 2) - self._ty = hp2 + ((dy - tdy) / 2) + dx = idx + tdx + spacing + dy = max(idy, tdy) + self._iy = hp2 + ((dy - idy) // 2) + self._ty = hp2 + ((dy - tdy) // 2) else: self._iy = hp2 - spacing = (idy > 0) * (tdy > 0) * 2 + spacing = (idy > 0) * (tdy > 0) * 2 self._ty = self._iy + idy + spacing - dx = max( idx, tdx ) - dy = idy + tdy + spacing - self._ix = wp2 + ((dx - idx) / 2) - self._tx = wp2 + ((dx - tdx) / 2) + dx = max(idx, tdx) + dy = idy + tdy + spacing + self._ix = wp2 + ((dx - idx) // 2) + self._tx = wp2 + ((dx - tdx) // 2) # Create the toolkit-specific control: - self._dx = dx + wp2 + wp2 - self._dy = dy + hp2 + hp2 - self.control = wx.Window( parent, -1, - size = wx.Size( self._dx, self._dy ) ) - self.control._owner = self - self._mouse_over = self._button_down = False + self._dx = dx + wp2 + wp2 + self._dy = dy + hp2 + hp2 - # Set up mouse event handlers: - wx.EVT_ENTER_WINDOW( self.control, self._on_enter_window ) - wx.EVT_LEAVE_WINDOW( self.control, self._on_leave_window ) - wx.EVT_LEFT_DOWN( self.control, self._on_left_down ) - wx.EVT_LEFT_UP( self.control, self._on_left_up ) - wx.EVT_PAINT( self.control, self._on_paint ) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'image' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _image_changed ( self, image ): + def _image_changed(self, image): self._image = self._mono_image = None if image is not None: - self._img = image.create_image() + self._img = image.create_image() self._image = self._img.ConvertToBitmap() + self._recalc_size() + self.control.SetSize(wx.Size(self._dx, self._dy)) + if self.control is not None: self.control.Refresh() - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Handles the 'selected' trait being changed: - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- - def _selected_changed ( self, selected ): + def _selected_changed(self, selected): """ Handles the 'selected' trait being changed. """ - if selected and (self.style == 'radio'): + if selected and (self.style == "radio"): for control in self.control.GetParent().GetChildren(): - owner = getattr( control, '_owner', None ) - if (isinstance( owner, ImageButton ) and owner.selected and - (owner is not self)): + owner = getattr(control, "_owner", None) + if ( + isinstance(owner, ImageButton) + and owner.selected + and (owner is not self) + ): owner.selected = False break self.control.Refresh() -#-- wx event handlers ---------------------------------------------------------- + # -- wx event handlers ---------------------------------------------------------- - def _on_enter_window ( self, event ): + def _on_enter_window(self, event): """ Called when the mouse enters the widget. """ - if self.style != 'button': + if self.style != "button": self._mouse_over = True self.control.Refresh() - def _on_leave_window ( self, event ): + def _on_leave_window(self, event): """ Called when the mouse leaves the widget. """ if self._mouse_over: self._mouse_over = False self.control.Refresh() - def _on_left_down ( self, event ): + def _on_left_down(self, event): """ Called when the left mouse button goes down on the widget. """ self._button_down = True self.control.CaptureMouse() self.control.Refresh() - def _on_left_up ( self, event ): + def _on_left_up(self, event): """ Called when the left mouse button goes up on the widget. """ control = self.control control.ReleaseMouse() self._button_down = False - wdx, wdy = control.GetClientSizeTuple() - x, y = event.GetX(), event.GetY() + wdx, wdy = control.GetClientSize().Get() + x, y = event.GetX(), event.GetY() control.Refresh() if (0 <= x < wdx) and (0 <= y < wdy): - if self.style == 'radio': + if self.style == "radio": self.selected = True - elif self.style == 'checkbox': + elif self.style == "checkbox": self.selected = not self.selected else: self.clicked = True - def _on_paint ( self, event ): + def _on_paint(self, event): """ Called when the widget needs repainting. """ - wdc = wx.PaintDC( self.control ) - wdx, wdy = self.control.GetClientSizeTuple() - ox = (wdx - self._dx) / 2 - oy = (wdy - self._dy) / 2 + wdc = wx.PaintDC(self.control) + wdx, wdy = self.control.GetClientSize().Get() + ox = (wdx - self._dx) / 2 + oy = (wdy - self._dy) / 2 - disabled = (not self.control.IsEnabled()) + disabled = not self.control.IsEnabled() if self._image is not None: image = self._image if disabled: if self._mono_image is None: - img = self._img - data = reshape(fromstring(img.GetData(), dtype('uint8')), - (-1, 3)) * array([[ 0.297, 0.589, 0.114 ]]) - g = data[ :, 0 ] + data[ :, 1 ] + data[ :, 2 ] - data[ :, 0 ] = data[ :, 1 ] = data[ :, 2 ] = g - img.SetData(ravel(data.astype(dtype('uint8'))).tostring()) + img = self._img + data = reshape( + frombuffer(img.GetData(), dtype("uint8")), (-1, 3) + ) * array([[0.297, 0.589, 0.114]]) + g = data[:, 0] + data[:, 1] + data[:, 2] + data[:, 0] = data[:, 1] = data[:, 2] = g + img.SetData(ravel(data.astype(dtype("uint8"))).tostring()) img.SetMaskColour(0, 0, 0) self._mono_image = img.ConvertToBitmap() - self._img = None + self._img = None image = self._mono_image - wdc.DrawBitmap( image, ox + self._ix, oy + self._iy, True ) + wdc.DrawBitmap(image, ox + self._ix, oy + self._iy, True) - if self.label != '': + if self.label != "": if disabled: - wdc.SetTextForeground( DisabledTextColor ) - wdc.SetFont( wx.NORMAL_FONT ) - wdc.DrawText( self.label, ox + self._tx, oy + self._ty ) - - pens = [ self._selectedPenLight, self._selectedPenDark ] - bd = self._button_down + wdc.SetTextForeground(DisabledTextColor) + wdc.SetFont(wx.NORMAL_FONT) + wdc.DrawText(self.label, ox + self._tx, oy + self._ty) + pens = [self._selectedPenLight, self._selectedPenDark] + bd = self._button_down style = self.style - is_rc = (style in ( 'radio', 'checkbox' )) - if bd or (style == 'button') or (is_rc and self.selected): + is_rc = style in ("radio", "checkbox") + if bd or (style == "button") or (is_rc and self.selected): if is_rc: bd = 1 - bd - wdc.SetBrush( wx.TRANSPARENT_BRUSH ) - wdc.SetPen( pens[ bd ] ) - wdc.DrawLine( 1, 1, wdx - 1, 1 ) - wdc.DrawLine( 1, 1, 1, wdy - 1 ) - wdc.DrawLine( 2, 2, wdx - 2, 2 ) - wdc.DrawLine( 2, 2, 2, wdy - 2 ) - wdc.SetPen( pens[ 1 - bd ] ) - wdc.DrawLine( wdx - 2, 2, wdx - 2, wdy - 1 ) - wdc.DrawLine( 2, wdy - 2, wdx - 2, wdy - 2 ) - wdc.DrawLine( wdx - 3, 3, wdx - 3, wdy - 2 ) - wdc.DrawLine( 3, wdy - 3, wdx - 3, wdy - 3 ) + wdc.SetBrush(wx.TRANSPARENT_BRUSH) + wdc.SetPen(pens[bd]) + wdc.DrawLine(1, 1, wdx - 1, 1) + wdc.DrawLine(1, 1, 1, wdy - 1) + wdc.DrawLine(2, 2, wdx - 2, 2) + wdc.DrawLine(2, 2, 2, wdy - 2) + wdc.SetPen(pens[1 - bd]) + wdc.DrawLine(wdx - 2, 2, wdx - 2, wdy - 1) + wdc.DrawLine(2, wdy - 2, wdx - 2, wdy - 2) + wdc.DrawLine(wdx - 3, 3, wdx - 3, wdy - 2) + wdc.DrawLine(3, wdy - 3, wdx - 3, wdy - 3) elif self._mouse_over and (not self.selected): - wdc.SetBrush( wx.TRANSPARENT_BRUSH ) - wdc.SetPen( pens[ bd ] ) - wdc.DrawLine( 0, 0, wdx, 0 ) - wdc.DrawLine( 0, 1, 0, wdy ) - wdc.SetPen( pens[ 1 - bd ] ) - wdc.DrawLine( wdx - 1, 1, wdx - 1, wdy ) - wdc.DrawLine( 1, wdy - 1, wdx - 1, wdy - 1 ) + wdc.SetBrush(wx.TRANSPARENT_BRUSH) + wdc.SetPen(pens[bd]) + wdc.DrawLine(0, 0, wdx, 0) + wdc.DrawLine(0, 1, 0, wdy) + wdc.SetPen(pens[1 - bd]) + wdc.DrawLine(wdx - 1, 1, wdx - 1, wdy) + wdc.DrawLine(1, wdy - 1, wdx - 1, wdy - 1) diff -Nru python-pyface-6.1.2/pyface/ui/wx/image_cache.py python-pyface-7.4.0/pyface/ui/wx/image_cache.py --- python-pyface-6.1.2/pyface/ui/wx/image_cache.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/image_cache.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,24 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. + import wx -# Enthought library imports. + from traits.api import HasTraits, provides -# Local imports. + from pyface.i_image_cache import IImageCache, MImageCache @@ -33,26 +28,25 @@ IImageCache interface for the API documentation. """ - - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, width, height): self._width = width self._height = height # The images in the cache! - self._images = {} # {filename : wx.Image} + self._images = {} # {filename : wx.Image} # The images in the cache converted to bitmaps. - self._bitmaps = {} # {filename : wx.Bitmap} + self._bitmaps = {} # {filename : wx.Bitmap} return - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageCache' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, filename): # Try the cache first. @@ -66,7 +60,10 @@ image = wx.Image(filename, wx.BITMAP_TYPE_ANY) # We force all images in the cache to be the same size. - if image.GetWidth() != self._width or image.GetHeight() != self._height: + if ( + image.GetWidth() != self._width + or image.GetHeight() != self._height + ): image.Rescale(self._width, self._height) # Add the bitmap to the cache! @@ -92,5 +89,3 @@ self._bitmaps[filename] = bmp return bmp - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/image_list.py python-pyface-7.4.0/pyface/ui/wx/image_list.py --- python-pyface-6.1.2/pyface/ui/wx/image_list.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/image_list.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A cached image list. """ -from __future__ import absolute_import -# Major package imports. + import wx -# Local imports + from .image_resource import ImageResource -import six # fixme: rename to 'CachedImageList'?!? @@ -36,13 +31,13 @@ self._height = height # Cache of the indexes of the images in the list! - self._cache = {} # {filename : index} + self._cache = {} # {filename : index} return - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageList' interface. - ########################################################################### + # ------------------------------------------------------------------------ def GetIndex(self, filename): """ Returns the index of the specified image. @@ -62,7 +57,7 @@ # If the filename is a string then it is the filename of some kind # of image (e.g 'foo.gif', 'image/foo.png' etc). - elif isinstance(filename, six.string_types): + elif isinstance(filename, str): # Load the image from the file. image = wx.Image(filename, wx.BITMAP_TYPE_ANY) @@ -70,7 +65,7 @@ # probably related to a MIME type). else: # Create a bitmap from the icon. - bmp = wx.EmptyBitmap(self._width, self._height) + bmp = wx.Bitmap(self._width, self._height) bmp.CopyFromIcon(filename) # Turn it into an image so that we can scale it. @@ -90,16 +85,17 @@ return index - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _scale(self, image): """ Scales the specified image (if necessary). """ - if image.GetWidth() != self._width or image.GetHeight()!= self._height: + if ( + image.GetWidth() != self._width + or image.GetHeight() != self._height + ): image.Rescale(self._width, self._height) return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/image_resource.py python-pyface-7.4.0/pyface/ui/wx/image_resource.py --- python-pyface-6.1.2/pyface/ui/wx/image_resource.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/image_resource.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,28 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. + import os -# Major package imports. + import wx -# Enthought library imports. + from traits.api import Any, HasTraits, List, Property, provides -from traits.api import Unicode +from traits.api import Str + -# Local imports. from pyface.i_image_resource import IImageResource, MImageResource @@ -37,23 +32,22 @@ IImageResource interface for the API documentation. """ - - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # The resource manager reference for the image. - _ref = Any + _ref = Any() - #### 'ImageResource' interface ############################################ + # 'ImageResource' interface -------------------------------------------- - absolute_path = Property(Unicode) + absolute_path = Property(Str) - name = Unicode + name = Str() - search_path = List + search_path = List() - ########################################################################### - # 'ImageResource' interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'IImage' interface. + # ------------------------------------------------------------------------ def create_bitmap(self, size=None): return self.create_image(size).ConvertToBitmap() @@ -62,14 +56,14 @@ ref = self._get_ref(size) if ref is not None: - icon = wx.Icon(self.absolute_path, wx.BITMAP_TYPE_ICO) + icon = wx.Icon(self.absolute_path, wx.BITMAP_TYPE_ANY) else: image = self._get_image_not_found_image() # We have to convert the image to a bitmap first and then create an # icon from that. bmp = image.ConvertToBitmap() - icon = wx.EmptyIcon() + icon = wx.Icon() icon.CopyFromBitmap(bmp) return icon @@ -90,9 +84,9 @@ size = image.GetSize() return size.Get() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_absolute_path(self): # FIXME: This doesn't quite wotk the new notion of image size. We @@ -106,5 +100,3 @@ absolute_path = self._get_image_not_found().absolute_path return absolute_path - -#### EOF ###################################################################### Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/ui/wx/images/carat_closed.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/ui/wx/images/carat_closed.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/ui/wx/images/carat_open.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/ui/wx/images/carat_open.png differ diff -Nru python-pyface-6.1.2/pyface/ui/wx/images/image_LICENSE.txt python-pyface-7.4.0/pyface/ui/wx/images/image_LICENSE.txt --- python-pyface-6.1.2/pyface/ui/wx/images/image_LICENSE.txt 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/images/image_LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -The icons are mostly derived work from other icons. As such they are -licensed accordingly to the original license: - -Project License File ----------------------------------------------------------------------------- -Enthought BSD 3-Clause LICENSE.txt -GV (Gael Varoquaux) Public Domain N/A - -Unless stated in this file, icons are the work of Enthought, and are -released under a 3 clause BSD license. - -Files and orginal authors: ----------------------------------------------------------------------------- -enthought/pyface/ui/wx/images: - application.ico | Enthought - heading_level_1.png | Enthought - warning.png | GV Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/ui/wx/images/panel_gradient_over.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/ui/wx/images/panel_gradient_over.png differ Binary files /tmp/tmp46omwqdz/xD49OAIrf4/python-pyface-6.1.2/pyface/ui/wx/images/panel_gradient.png and /tmp/tmp46omwqdz/f4HrZzxdNA/python-pyface-7.4.0/pyface/ui/wx/images/panel_gradient.png differ diff -Nru python-pyface-6.1.2/pyface/ui/wx/image_widget.py python-pyface-7.4.0/pyface/ui/wx/image_widget.py --- python-pyface-6.1.2/pyface/ui/wx/image_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/image_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,85 +1,94 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A clickable/draggable widget containing an image. """ -from __future__ import absolute_import -# Major package imports. +import warnings + import wx -# Enthought library imports. from traits.api import Any, Bool, Event -# Local imports. -from .widget import Widget +from .layout_widget import LayoutWidget -class ImageWidget(Widget): +class ImageWidget(LayoutWidget): """ A clickable/draggable widget containing an image. """ - #### 'ImageWidget' interface ############################################## + # 'ImageWidget' interface ---------------------------------------------# # The bitmap. - bitmap = Any + bitmap = Any() # Is the widget selected? selected = Bool(False) - #### Events #### + # Events ---- # A key was pressed while the tree is in focus. - key_pressed = Event + key_pressed = Event() # A node has been activated (ie. double-clicked). - node_activated = Event + node_activated = Event() # A drag operation was started on a node. - node_begin_drag = Event + node_begin_drag = Event() # A (non-leaf) node has been collapsed. - node_collapsed = Event + node_collapsed = Event() # A (non-leaf) node has been expanded. - node_expanded = Event + node_expanded = Event() # A left-click occurred on a node. - node_left_clicked = Event + node_left_clicked = Event() # A right-click occurred on a node. - node_right_clicked = Event + node_right_clicked = Event() - #### Private interface #################################################### + # Private interface ---------------------------------------------------- - _selected = Any + _selected = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__ (self, parent, **traits): + def __init__(self, parent, **traits): """ Creates a new widget. """ - # Base class constructors. - super(ImageWidget, self).__init__(**traits) + create = traits.pop('create', True) + + # Base-class constructors. + super().__init__(parent=parent, **traits) + + # Create the widget! + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + def _create_control(self, parent): # Add some padding around the image. size = (self.bitmap.GetWidth() + 10, self.bitmap.GetHeight() + 10) # Create the toolkit-specific control. self.control = wx.Window(parent, -1, size=size) - self.control.__tag__ = 'hack' + self.control.__tag__ = "hack" - self._mouse_over = False + self._mouse_over = False self._button_down = False # Set up mouse event handlers: @@ -93,22 +102,20 @@ # Pens used to draw the 'selection' marker: # ZZZ: Make these class instances when moved to the wx toolkit code. self._selectedPenDark = wx.Pen( - wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW), 1, - wx.PENSTYLE_SOLID + wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW), 1, wx.SOLID ) self._selectedPenLight = wx.Pen( - wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DHIGHLIGHT), 1, - wx.PENSTYLE_SOLID + wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DHIGHLIGHT), 1, wx.SOLID ) - return + return self.control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- def _bitmap_changed(self, bitmap): """ Called when the widget's bitmap is changed. """ @@ -116,23 +123,21 @@ if self.control is not None: self.control.Refresh() - return - def _selected_changed(self, selected): """ Called when the selected state of the widget is changed. """ if selected: for control in self.GetParent().GetChildren(): - if hasattr(control, '__tag__'): + if hasattr(control, "__tag__"): if control.Selected(): - control.Selected(False) - break + control.Selected(False) + break self.Refresh() return - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_enter_window(self, event): """ Called when the mouse enters the widget. """ @@ -141,8 +146,6 @@ self._mouse_over = True self.Refresh() - return - def _on_leave_window(self, event): """ Called when the mouse leaves the widget. """ @@ -150,21 +153,17 @@ self._mouse_over = False self.Refresh() - return - def _on_left_dclick(self, event): """ Called when the left mouse button is double-clicked. """ - #print 'left dclick' + # print 'left dclick' event.Skip() - return - - def _on_left_down ( self, event = None ): + def _on_left_down(self, event=None): """ Called when the left mouse button goes down on the widget. """ - #print 'left down' + # print 'left down' if self._selected is not None: self.CaptureMouse() @@ -173,12 +172,10 @@ event.Skip() - return - - def _on_left_up ( self, event = None ): + def _on_left_up(self, event=None): """ Called when the left mouse button goes up on the widget. """ - #print 'left up' + # print 'left up' need_refresh = self._button_down if need_refresh: @@ -186,12 +183,12 @@ self._button_down = False if self._selected is not None: - wdx, wdy = self.GetClientSizeTuple() - x = event.GetX() - y = event.GetY() + wdx, wdy = self.GetClientSize().Get() + x = event.GetX() + y = event.GetY() if (0 <= x < wdx) and (0 <= y < wdy): if self._selected != -1: - self.Selected( True ) + self.Selected(True) elif need_refresh: self.Refresh() @@ -202,42 +199,38 @@ event.Skip() - return - - def _on_paint ( self, event = None ): + def _on_paint(self, event=None): """ Called when the widget needs repainting. """ - wdc = wx.PaintDC( self.control ) - wdx, wdy = self.control.GetClientSizeTuple() - bitmap = self.bitmap - bdx = bitmap.GetWidth() - bdy = bitmap.GetHeight() - wdc.DrawBitmap( bitmap, (wdx - bdx) / 2, (wdy - bdy) / 2, True ) + wdc = wx.PaintDC(self.control) + wdx, wdy = self.control.GetClientSize().Get() + bitmap = self.bitmap + bdx = bitmap.GetWidth() + bdy = bitmap.GetHeight() + wdc.DrawBitmap(bitmap, (wdx - bdx) // 2, (wdy - bdy) // 2, True) - pens = [ self._selectedPenLight, self._selectedPenDark ] - bd = self._button_down + pens = [self._selectedPenLight, self._selectedPenDark] + bd = self._button_down if self._mouse_over: - wdc.SetBrush( wx.TRANSPARENT_BRUSH ) - wdc.SetPen( pens[ bd ] ) - wdc.DrawLine( 0, 0, wdx, 0 ) - wdc.DrawLine( 0, 1, 0, wdy ) - wdc.SetPen( pens[ 1 - bd ] ) - wdc.DrawLine( wdx - 1, 1, wdx - 1, wdy ) - wdc.DrawLine( 1, wdy - 1, wdx - 1, wdy - 1 ) - - if self._selected == True: - wdc.SetBrush( wx.TRANSPARENT_BRUSH ) - wdc.SetPen( pens[ bd ] ) - wdc.DrawLine( 1, 1, wdx - 1, 1 ) - wdc.DrawLine( 1, 1, 1, wdy - 1 ) - wdc.DrawLine( 2, 2, wdx - 2, 2 ) - wdc.DrawLine( 2, 2, 2, wdy - 2 ) - wdc.SetPen( pens[ 1 - bd ] ) - wdc.DrawLine( wdx - 2, 2, wdx - 2, wdy - 1 ) - wdc.DrawLine( 2, wdy - 2, wdx - 2, wdy - 2 ) - wdc.DrawLine( wdx - 3, 3, wdx - 3, wdy - 2 ) - wdc.DrawLine( 3, wdy - 3, wdx - 3, wdy - 3 ) + wdc.SetBrush(wx.TRANSPARENT_BRUSH) + wdc.SetPen(pens[bd]) + wdc.DrawLine(0, 0, wdx, 0) + wdc.DrawLine(0, 1, 0, wdy) + wdc.SetPen(pens[1 - bd]) + wdc.DrawLine(wdx - 1, 1, wdx - 1, wdy) + wdc.DrawLine(1, wdy - 1, wdx - 1, wdy - 1) + + if self._selected: + wdc.SetBrush(wx.TRANSPARENT_BRUSH) + wdc.SetPen(pens[bd]) + wdc.DrawLine(1, 1, wdx - 1, 1) + wdc.DrawLine(1, 1, 1, wdy - 1) + wdc.DrawLine(2, 2, wdx - 2, 2) + wdc.DrawLine(2, 2, 2, wdy - 2) + wdc.SetPen(pens[1 - bd]) + wdc.DrawLine(wdx - 2, 2, wdx - 2, wdy - 1) + wdc.DrawLine(2, wdy - 2, wdx - 2, wdy - 2) + wdc.DrawLine(wdx - 3, 3, wdx - 3, wdy - 2) + wdc.DrawLine(3, wdy - 3, wdx - 3, wdy - 3) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/__init__.py python-pyface-7.4.0/pyface/ui/wx/__init__.py --- python-pyface-6.1.2/pyface/ui/wx/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,9 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/wx/init.py python-pyface-7.4.0/pyface/ui/wx/init.py --- python-pyface-6.1.2/pyface/ui/wx/init.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/init.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, Riverbank Computing Limited -# All rights reserved. -# -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2007 Riverbank Computing Limited +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -#------------------------------------------------------------------------------ +# +# Thanks for using Enthought open source! import wx @@ -18,14 +16,6 @@ from pyface.base_toolkit import Toolkit from .gui import GUI - -# Check the version number is late enough. -if wx.VERSION < (2, 8): - raise RuntimeError( - "Need wx version 2.8 or higher, but got %s" % str(wx.VERSION) - ) - - # It's possible that it has already been initialised. _app = wx.GetApp() if _app is None: @@ -39,7 +29,7 @@ # create the toolkit object -toolkit_object = Toolkit('pyface', 'wx', 'pyface.ui.wx') +toolkit_object = Toolkit("pyface", "wx", "pyface.ui.wx") # ensure that Traits has a UI handler appropriate for the toolkit. diff -Nru python-pyface-6.1.2/pyface/ui/wx/ipython_widget.py python-pyface-7.4.0/pyface/ui/wx/ipython_widget.py --- python-pyface-6.1.2/pyface/ui/wx/ipython_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/ipython_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,36 +1,31 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The wx-backend Pyface widget for an embedded IPython shell. """ -# Standard library imports. -from __future__ import print_function -import six.moves.builtins + +import builtins import codeop import re import sys +import warnings + -# System library imports import IPython from IPython.frontend.wx.wx_frontend import WxController from IPython.kernel.core.interpreter import Interpreter import wx -# Enthought library imports. + from apptools.io.file import File as EnthoughtFile from pyface.i_python_shell import IPythonShell from pyface.key_pressed_event import KeyPressedEvent @@ -38,12 +33,11 @@ from traits.util.clean_strings import python_name from pyface.wx.drag_and_drop import PythonDropTarget -# Local imports. + from .widget import Widget -import six # Constants. -IPYTHON_VERSION = tuple(map(int, IPython.Release.version_base.split('.'))) +IPYTHON_VERSION = tuple(map(int, IPython.Release.version_base.split("."))) class IPythonController(WxController): @@ -56,6 +50,7 @@ # Add a magic to clear the screen def cls(args): self.ClearAll() + self.ipython0.magic_cls = cls @@ -66,7 +61,7 @@ def execute_command(self, command, hidden=False): # XXX: Overriden to fix bug where executing a hidden command still # causes the prompt number to increase. - super(IPython010Controller, self).execute_command(command, hidden) + super().execute_command(command, hidden) if hidden: self.shell.current_cell_number -= 1 @@ -78,7 +73,7 @@ # In the parent class, this is a property that expects the # container to be a frame, thus it fails when modified. # The title of the IPython windows (not displayed in Envisage) - title = Str + title = Str() # Cached value of the banner for the IPython shell. # NOTE: The WxController object (declared in wx_frontend module) contains @@ -96,7 +91,7 @@ """ if self._banner is None: # 'ipython0' gets set in the __init__ method of the base class. - if hasattr(self, 'ipython0'): + if hasattr(self, "ipython0"): self._banner = self.ipython0.BANNER return self._banner @@ -105,33 +100,34 @@ banner = property(_get_banner, _set_banner) - def __init__(self, *args, **kwargs): # This is a hack to avoid the IPython exception hook to trigger # on exceptions (https://bugs.launchpad.net/bugs/337105) # XXX: This is horrible: module-level monkey patching -> side # effects. from IPython import iplib + iplib.InteractiveShell.isthreaded = True # Suppress all key input, to avoid waiting def my_rawinput(x=None): - return '\n' - old_rawinput = six.moves.builtins.raw_input - six.moves.builtins.raw_input = my_rawinput + return "\n" + + old_rawinput = builtins.raw_input + builtins.raw_input = my_rawinput IPythonController.__init__(self, *args, **kwargs) - six.moves.builtins.raw_input = old_rawinput + builtins.raw_input = old_rawinput # XXX: This is bugware for IPython bug: # https://bugs.launchpad.net/ipython/+bug/270998 # Fix all the magics with no docstrings: for funcname in dir(self.ipython0): - if not funcname.startswith('magic'): + if not funcname.startswith("magic"): continue func = getattr(self.ipython0, funcname) try: if func.__doc__ is None: - func.__doc__ = '' + func.__doc__ = "" except AttributeError: """ Avoid "attribute '__doc__' of 'instancemethod' objects is not writable". @@ -145,9 +141,8 @@ """ completion_text = self._get_completion_text(line) - suggestion, completions = super(IPython09Controller, self).complete( \ - completion_text) - new_line = line[:-len(completion_text)] + suggestion + suggestion, completions = super().complete(completion_text) + new_line = line[: -len(completion_text)] + suggestion return new_line, completions def is_complete(self, string): @@ -165,14 +160,15 @@ # FIXME: There has to be a nicer way to do this. Th code is # identical to the base class implementation, except for the if .. # statement on line 146. - if string in ('', '\n'): + if string in ("", "\n"): # Prefiltering, eg through ipython0, may return an empty # string although some operations have been accomplished. We # thus want to consider an empty string as a complete # statement. return True - elif ( len(self.input_buffer.split('\n'))>2 - and not re.findall(r"\n[\t ]*\n[\t ]*$", string)): + elif len(self.input_buffer.split("\n")) > 2 and not re.findall( + r"\n[\t ]*\n[\t ]*$", string + ): return False else: self.capture_output() @@ -181,12 +177,14 @@ # complete (except if '\' was used). # This should probably be done in a different place (like # maybe 'prefilter_input' method? For now, this works. - clean_string = string.rstrip('\n') - if not clean_string.endswith('\\'): clean_string +='\n\n' - is_complete = codeop.compile_command(clean_string, - "", "exec") + clean_string = string.rstrip("\n") + if not clean_string.endswith("\\"): + clean_string += "\n\n" + is_complete = codeop.compile_command( + clean_string, "", "exec" + ) self.release_output() - except Exception as e: + except Exception: # XXX: Hack: return True so that the # code gets executed and the error captured. is_complete = True @@ -207,23 +205,26 @@ # XXX: we are not storing the input buffer previous to the # execution, as this forces us to run the execution # input_buffer a yield, which is not good. - ##current_buffer = self.shell.control.input_buffer + # #current_buffer = self.shell.control.input_buffer command = command.rstrip() - if len(command.split('\n')) > 1: + if len(command.split("\n")) > 1: # The input command is several lines long, we need to # force the execution to happen - command += '\n' + command += "\n" cleaned_command = self.prefilter_input(command) self.input_buffer = command # Do not use wx.Yield() (aka GUI.process_events()) to avoid # recursive yields. self.ProcessEvent(wx.PaintEvent()) - self.write('\n') - if not self.is_complete(cleaned_command + '\n'): + self.write("\n") + if not self.is_complete(cleaned_command + "\n"): self._colorize_input_buffer() - self.render_error('Incomplete or invalid input') - self.new_prompt(self.input_prompt_template.substitute( - number=(self.last_result['number'] + 1))) + self.render_error("Incomplete or invalid input") + self.new_prompt( + self.input_prompt_template.substitute( + number=(self.last_result["number"] + 1) + ) + ) return False self._on_enter() return True @@ -232,8 +233,11 @@ """ Empty completely the widget. """ self.ClearAll() - self.new_prompt(self.input_prompt_template.substitute( - number=(self.last_result['number'] + 1))) + self.new_prompt( + self.input_prompt_template.substitute( + number=(self.last_result["number"] + 1) + ) + ) def continuation_prompt(self): """Returns the current continuation prompt. @@ -241,9 +245,9 @@ current prompt.""" # This assumes that the prompt is always of the form 'In [#]'. - n = self.last_result['number'] + n = self.last_result["number"] promptstr = "In [%d]" % n - return ("."*len(promptstr) + ':') + return "." * len(promptstr) + ":" def _popup_completion(self, create=False): """ Updates the popup completion menu if it exists. If create is @@ -263,11 +267,12 @@ # I am patching this here instead of in the IPython module, but at some # point, this needs to be merged in. if self.debug: - print("_popup_completion" , self.input_buffer, file=sys.__stdout__) + print("_popup_completion", self.input_buffer, file=sys.__stdout__) line = self.input_buffer - if (self.AutoCompActive() and line and not line[-1] == '.') \ - or create==True: + if create or ( + self.AutoCompActive() and line and not line[-1] == "." + ): suggestion, completions = self.complete(line) if completions: offset = len(self._get_completion_text(line)) @@ -284,7 +289,7 @@ # that in the 'pyreadline' module (modes/basemode.py) where we break at # each delimiter and try to complete the residual line, until we get a # successful list of completions. - expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})' + expression = r"\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})" complete_sep = re.compile(expression) text = complete_sep.split(line)[-1] return text @@ -297,21 +302,23 @@ prompt. """ current_buffer = self.input_buffer - cleaned_buffer = self.prefilter_input(current_buffer.replace( - self.continuation_prompt(), - '')) + cleaned_buffer = self.prefilter_input( + current_buffer.replace(self.continuation_prompt(), "") + ) if self.is_complete(cleaned_buffer): self.execute(cleaned_buffer, raw_string=current_buffer) else: - self.input_buffer = current_buffer + \ - self.continuation_prompt() + \ - self._get_indent_string(current_buffer.replace( - self.continuation_prompt(), - '')[:-1]) - if len(current_buffer.split('\n')) == 2: - self.input_buffer += '\t\t' - if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'): - self.input_buffer += '\t' + self.input_buffer = ( + current_buffer + + self.continuation_prompt() + + self._get_indent_string( + current_buffer.replace(self.continuation_prompt(), "")[:-1] + ) + ) + if len(current_buffer.split("\n")) == 2: + self.input_buffer += "\t\t" + if current_buffer[:-1].split("\n")[-1].rstrip().endswith(":"): + self.input_buffer += "\t" @provides(IPythonShell) @@ -320,35 +327,40 @@ IPythonShell interface for the API documentation. """ + # 'IPythonShell' interface --------------------------------------------- - #### 'IPythonShell' interface ############################################# - - command_executed = Event + command_executed = Event() key_pressed = Event(KeyPressedEvent) - #### 'IPythonWidget' interface ############################################ + # 'IPythonWidget' interface -------------------------------------------- interp = Instance(Interpreter, ()) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ # FIXME v3: Either make this API consistent with other Widget sub-classes # or make it a sub-class of HasTraits. def __init__(self, parent, **traits): """ Creates a new pager. """ + warnings.warn( + "the Wx IPython widget us deprecated and will be removed in a " + "future Pyface version", + PendingDeprecationWarning, + ) + # Base class constructor. - super(IPythonWidget, self).__init__(**traits) + super().__init__(**traits) # Create the toolkit-specific control that represents the widget. self.control = self._create_control(parent) - ########################################################################### + # ------------------------------------------------------------------------ # 'IPythonShell' interface. - ########################################################################### + # ------------------------------------------------------------------------ def interpreter(self): return self.interp @@ -358,12 +370,12 @@ self.command_executed = True def execute_file(self, path, hidden=True): - self.control.execute_command('%run ' + '"%s"' % path, hidden=hidden) + self.control.execute_command("%run " + '"%s"' % path, hidden=hidden) self.command_executed = True - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # Create the controller based on the version of the installed IPython @@ -376,16 +388,16 @@ shell = klass(parent, -1, shell=self.interp) # Listen for key press events. - wx.EVT_CHAR(shell, self._wx_on_char) + shell.Bind(wx.EVT_CHAR, self._wx_on_char) # Enable the shell as a drag and drop target. shell.SetDropTarget(PythonDropTarget(self)) return shell - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonDropTarget' handler interface. - ########################################################################### + # ------------------------------------------------------------------------ def on_drop(self, x, y, obj, default_drag_result): """ Called when a drop occurs on the shell. """ @@ -394,23 +406,29 @@ if isinstance(obj, EnthoughtFile): self.control.write(obj.absolute_path) - elif ( isinstance(obj, list) and len(obj) ==1 - and isinstance(obj[0], EnthoughtFile)): + elif ( + isinstance(obj, list) + and len(obj) == 1 + and isinstance(obj[0], EnthoughtFile) + ): self.control.write(obj[0].absolute_path) else: # Not a file, we'll inject the object in the namespace # If we can't create a valid Python identifier for the name of an # object we use this instead. - name = 'dragged' + name = "dragged" - if hasattr(obj, 'name') \ - and isinstance(obj.name, six.string_types) and len(obj.name) > 0: + if ( + hasattr(obj, "name") + and isinstance(obj.name, str) + and len(obj.name) > 0 + ): py_name = python_name(obj.name) # Make sure that the name is actually a valid Python identifier. try: - if eval(py_name, {py_name : True}): + if eval(py_name, {py_name: True}): name = py_name except: @@ -429,19 +447,19 @@ return wx.DragCopy - ########################################################################### + # ------------------------------------------------------------------------ # Private handler interface. - ########################################################################### + # ------------------------------------------------------------------------ def _wx_on_char(self, event): """ Called whenever a change is made to the text of the document. """ self.key_pressed = KeyPressedEvent( - alt_down = event.AltDown() == 1, - control_down = event.ControlDown() == 1, - shift_down = event.ShiftDown() == 1, - key_code = event.GetKeyCode(), - event = event + alt_down=event.AltDown() == 1, + control_down=event.ControlDown() == 1, + shift_down=event.ShiftDown() == 1, + key_code=event.GetKeyCode(), + event=event, ) # Give other event handlers a chance. diff -Nru python-pyface-6.1.2/pyface/ui/wx/layered_panel.py python-pyface-7.4.0/pyface/ui/wx/layered_panel.py --- python-pyface-6.1.2/pyface/ui/wx/layered_panel.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/layered_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,31 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A Layered panel. """ -from __future__ import absolute_import -# Major package imports. + import wx from wx.lib.scrolledpanel import ScrolledPanel -# Enthought library imports. -from traits.api import Any, Str, Int +from traits.api import Int, provides -# Local imports. -from .widget import Widget +from pyface.i_layered_panel import ILayeredPanel, MLayeredPanel +from .layout_widget import LayoutWidget -class LayeredPanel(Widget): +@provides(ILayeredPanel) +class LayeredPanel(MLayeredPanel, LayoutWidget): """ A Layered panel. A layered panel contains one or more named layers, with only one layer @@ -37,42 +33,14 @@ # The default style. STYLE = wx.CLIP_CHILDREN - #### "Layered Panel' interface ############################################ - - # The toolkit-specific control of the currently displayed layer. - current_layer = Any - - # The name of the currently displayed layer. - current_layer_name = Str - # The minimum for the panel, which is the maximum of the minimum # sizes of the layers min_width = Int(0) min_height = Int(0) - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, parent, **traits): - """ Creates a new LayeredPanel. """ - - # Base class constructor. - super(LayeredPanel, self).__init__(**traits) - - # Create the toolkit-specific control that represents the widget. - self.control = self._create_control(parent) - - # The layers in the panel. - # - # { str name : wx.Window layer } - self._layers = {} - - return - - ########################################################################### + # ------------------------------------------------------------------------ # 'LayeredPanel' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_layer(self, name, layer): """ Adds a layer with the specified name. @@ -135,14 +103,9 @@ return layer - def has_layer(self, name): - """ Does the panel contain a layer with the specified name? """ - - return name in self._layers - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ @@ -162,8 +125,6 @@ sizer.Show(layer, False) sizer.Layout() - return - def _show_layer(self, name, layer): """ Shows the specified layer. """ @@ -175,5 +136,3 @@ self.current_layer_name = name return layer - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/layout_widget.py python-pyface-7.4.0/pyface/ui/wx/layout_widget.py --- python-pyface-6.1.2/pyface/ui/wx/layout_widget.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/layout_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,119 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import wx + +from traits.api import provides + +from pyface.i_layout_item import DEFAULT_SIZE +from pyface.i_layout_widget import ILayoutWidget, MLayoutWidget +from pyface.ui.wx.widget import Widget + + +#: The special "default" size for Wx Size objects. +WX_DEFAULT_SIZE = -1 + + +@provides(ILayoutWidget) +class LayoutWidget(MLayoutWidget, Widget): + """ A widget which can participate as part of a layout. + + This is an abstract class, as Widget._create_control needs to be + implemented at a minimum. + """ + + def _set_control_minimum_size(self, size): + """ Set the minimum size of the control. """ + wx_size = _size_to_wx_size(size) + self.control.SetMinSize(wx_size) + + def _get_control_minimum_size(self): + """ Get the minimum size of the control. """ + wx_size = self.control.GetMinSize() + return _wx_size_to_size(wx_size) + + def _set_control_maximum_size(self, size): + """ Set the maximum size of the control. """ + wx_size = _size_to_wx_size(size) + self.control.SetMaxSize(wx_size) + + def _get_control_maximum_size(self): + """ Get the maximum size of the control. """ + wx_size = self.control.GetMaxSize() + return _wx_size_to_size(wx_size) + + def _set_control_stretch(self, size_policy): + """ Set the stretch factor of the control. + + In Wx the stretch factor is set at layout time and can't be changed + without removing and adding the widget back into a layout. + """ + pass + + def _get_control_stretch(self): + """ Get the stretch factor of the control. + + In Wx the stretch factor is set at layout time and can't be obtained + from the widget itself. As a result, this method simply returns the + current trait value. This method is only used for testing. + """ + return self.stretch + + def _set_control_size_policy(self, size_policy): + """ Set the size policy of the control + + In Wx the size policy is set at layout time and can't be changed + without removing and adding the widget back into a layout. + """ + pass + + def _get_control_size_policy(self): + """ Get the size policy of the control + + In Wx the size policy is set at layout time and can't be obtained from + the widget itself. As a result, this method simply returns the + current trait value. This method is only used for testing. + """ + return self.size_policy + + +def _size_to_wx_size(size): + """ Convert a size tuple to a wx.Size instance. + + Parameters + ---------- + size : tuple of (width, height) + The width and height as a tuple of ints. + + Returns + ------- + wx_size : wx.Size instance + A corresponding wx Size instance. + """ + return wx.Size(*( + x if x != DEFAULT_SIZE else WX_DEFAULT_SIZE + for x in size + )) + + +def _wx_size_to_size(wx_size): + """ Convert a wx.Size instance to a size tuple. + + Parameters + ---------- + wx_size : wx.Size instance + A wx Size instance. + + Returns + ------- + size : tuple of (width, height) + The corresponding width and height as a tuple of ints. + """ + return (wx_size.GetWidth(), wx_size.GetHeight()) diff -Nru python-pyface-6.1.2/pyface/ui/wx/list_box.py python-pyface-7.4.0/pyface/ui/wx/list_box.py --- python-pyface-6.1.2/pyface/ui/wx/list_box.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/list_box.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,25 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A simple list box widget with a model-view architecture. """ -from __future__ import absolute_import -# Major package imports. +import warnings + import wx -# Enthought library imports. from traits.api import Event, Instance, Int -# Local imports. from pyface.list_box_model import ListBoxModel -from .widget import Widget +from .layout_widget import LayoutWidget -class ListBox(Widget): +class ListBox(LayoutWidget): """ A simple list box widget with a model-view architecture. """ # The model that provides the data for the list box. @@ -24,35 +31,46 @@ # Events. # An item has been activated. - item_activated = Event + item_activated = Event() # Default style. STYLE = wx.LB_SINGLE | wx.LB_HSCROLL | wx.LB_NEEDED_SB - - def __init__(self, parent, **traits): + def __init__(self, parent=None, **traits): """ Creates a new list box. """ + create = traits.pop('create', True) + # Base-class constructors. - super(ListBox, self).__init__(**traits) + super().__init__(parent=parent, **traits) # Create the widget! - self._create_control(parent) + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) - # Listen for changes to the model. - self.model.on_trait_change(self._on_model_changed, "list_changed") + def _create(self): + super()._create() - return + self._populate() + + # Listen for changes to the model. + self.model.observe(self._on_model_changed, "list_changed") def dispose(self): - self.model.on_trait_change(self._on_model_changed, "list_changed", - remove = True) + self.model.observe( + self._on_model_changed, "list_changed", remove=True + ) self.model.dispose() - return - ########################################################################### + # ------------------------------------------------------------------------ # 'ListBox' interface. - ########################################################################### + # ------------------------------------------------------------------------ def refresh(self): """ Refreshes the list box. """ @@ -63,11 +81,9 @@ # Populate the list. self._populate() - return - - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_item_selected(self, event): """ Called when an item in the list is selected. """ @@ -76,8 +92,6 @@ self.selection = listbox.GetSelection() - return - def _on_item_activated(self, event): """ Called when an item in the list is activated. """ @@ -87,13 +101,11 @@ # Trait event notification. self.item_activated = index - return - - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ - #### Static ############################################################### + # Static --------------------------------------------------------------- def _selection_changed(self, index): """ Called when the selected item is changed. """ @@ -101,9 +113,7 @@ if index != -1: self.control.SetSelection(index) - return - - #### Dynamic ############################################################## + # Dynamic -------------------------------------------------------------# def _on_model_changed(self, event): """ Called when the model has changed. """ @@ -111,27 +121,27 @@ # For now we just clear out the entire list. self.refresh() - return - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the widget. """ - self.control = wx.ListBox(parent, -1, style = self.STYLE) + control = wx.ListBox(parent, -1, style=self.STYLE) # Wire it up! - wx.EVT_LISTBOX(self.control, self.control.GetId(), - self._on_item_selected) - wx.EVT_LISTBOX_DCLICK(self.control, self.control.GetId(), - self._on_item_activated) + control.Bind( + wx.EVT_LISTBOX, self._on_item_selected, id=self.control.GetId() + ) + control.Bind( + wx.EVT_LISTBOX_DCLICK, + self._on_item_activated, + id=self.control.GetId(), + ) # Populate the list. - self._populate() - - return + return control def _populate(self): """ Populates the list box. """ @@ -139,7 +149,3 @@ for index in range(self.model.get_item_count()): label, item = self.model.get_item_at(index) self.control.Append(label, item) - - return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/mdi_application_window.py python-pyface-7.4.0/pyface/ui/wx/mdi_application_window.py --- python-pyface-6.1.2/pyface/ui/wx/mdi_application_window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/mdi_application_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,32 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ An MDI top-level application window. """ -from __future__ import absolute_import -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Bool, Instance, Int, Tuple -# Local imports. +from traits.api import Bool, Int, Tuple + + +from pyface.ui_traits import Image from .application_window import ApplicationWindow from .image_resource import ImageResource try: - import wx.aui + # import wx.aui + from wx.lib.agw import aui # noqa: F401 + AUI = True -except: +except ImportError: AUI = False @@ -42,10 +41,10 @@ """ - #### 'MDIApplicationWindow' interface ##################################### + # 'MDIApplicationWindow' interface ------------------------------------- # The workarea background image. - background_image = Instance(ImageResource, ImageResource('background')) + background_image = Image(ImageResource("background")) # Should we tile the workarea background image? The alternative is to # scale it. Be warned that scaling the image allows for 'pretty' images, @@ -56,9 +55,9 @@ # UPDATE: wx 2.6.1 does NOT fix this issue. _wx_offset = Tuple(Int, Int) - ########################################################################### + # ------------------------------------------------------------------------ # 'MDIApplicationWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_child_window(self, title=None, is_mdi=True, float=True): """ Create a child window. """ @@ -74,9 +73,9 @@ style = wx.DEFAULT_FRAME_STYLE return wx.Frame(self.control, -1, title, style=style) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create the contents of the MDI window. """ @@ -91,19 +90,21 @@ # Frame events. # # We respond to size events to layout windows around the MDI frame. - wx.EVT_SIZE(self.control, self._on_size) + self.control.Bind(wx.EVT_SIZE, self._on_size) # Client window events. client_window = self.control.GetClientWindow() - wx.EVT_ERASE_BACKGROUND(client_window, self._on_erase_background) - - self._wx_offset = client_window.GetPositionTuple() + client_window.Bind(wx.EVT_ERASE_BACKGROUND, self._on_erase_background) + try: + self._wx_offset = client_window.GetPosition().Get() + except: + self._wx_offset = (0, 0) if AUI: # Let the AUI manager look after the frame. self._aui_manager.SetManagedWindow(self.control) - contents = super(MDIApplicationWindow, self)._create_contents(parent) + contents = super()._create_contents(parent) return contents @@ -111,16 +112,19 @@ """ Create the toolkit-specific control that represents the window. """ control = wx.MDIParentFrame( - parent, -1, self.title, style=wx.DEFAULT_FRAME_STYLE, - size=self.size, pos=self.position + parent, + -1, + self.title, + style=wx.DEFAULT_FRAME_STYLE, + size=self.size, + pos=self.position, ) return control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### - + # ------------------------------------------------------------------------ def _tile_background_image(self, dc, width, height): """ Tiles the background image. """ @@ -137,14 +141,12 @@ x = x + w - return - def _scale_background_image(self, dc, width, height): """ Scales the background image. """ # Scale the image (if necessary). image = self._image - if image.GetWidth() != width or image.GetHeight()!= height: + if image.GetWidth() != width or image.GetHeight() != height: image = self._image.Copy() image.Rescale(width, height) @@ -153,14 +155,13 @@ return - ##### wx event handlers ################################################### + ## wx event handlers --------------------------------------------------- def _on_size(self, event): """ Called when the frame is resized. """ - wx.LayoutAlgorithm().LayoutMDIFrame(self.control) - - return + wx.adv.LayoutAlgorithm().LayoutMDIFrame(self.control) + event.Skip() def _on_erase_background(self, event): """ Called when the background of the MDI client window is erased. """ diff -Nru python-pyface-6.1.2/pyface/ui/wx/message_dialog.py python-pyface-7.4.0/pyface/ui/wx/message_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/message_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/message_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,33 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Enum, provides, Unicode -# Local imports. +from traits.api import Enum, provides, Str + + from pyface.i_message_dialog import IMessageDialog, MMessageDialog from .dialog import Dialog # Map the ETS severity to the corresponding wx standard icon. _SEVERITY_TO_ICON_MAP = { - 'information': wx.ICON_INFORMATION, - 'warning': wx.ICON_WARNING, - 'error': wx.ICON_ERROR + "information": wx.ICON_INFORMATION, + "warning": wx.ICON_WARNING, + "error": wx.ICON_ERROR, } @@ -42,33 +37,35 @@ IMessageDialog interface for the API documentation. """ + # 'IMessageDialog' interface ------------------------------------------- - #### 'IMessageDialog' interface ########################################### + message = Str() - message = Unicode + informative = Str() - informative = Unicode + detail = Str() - detail = Unicode + severity = Enum("information", "warning", "error") - severity = Enum('information', 'warning', 'error') + # unused trait, this functionality is only supported on Qt + text_format = Enum("auto", "plain", "rich") - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): # In wx this is a canned dialog. pass - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # The message. if self.informative: - message = self.message + '\n\n' + self.informative + message = self.message + "\n\n" + self.informative else: message = self.message @@ -76,8 +73,9 @@ if self.resizeable: style |= wx.RESIZE_BORDER - dlg = wx.MessageDialog(parent, message, self.title, style, - self.position) + dlg = wx.MessageDialog( + parent, message, self.title, style, self.position + ) if self.size != (-1, -1): dlg.SetSize(self.size) diff -Nru python-pyface-6.1.2/pyface/ui/wx/multi_toolbar_window.py python-pyface-7.4.0/pyface/ui/wx/multi_toolbar_window.py --- python-pyface-6.1.2/pyface/ui/wx/multi_toolbar_window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/multi_toolbar_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A top-level application window that supports multiple toolbars. """ -from __future__ import absolute_import -# Major package imports. + import wx -# Enthought library imports + from pyface.action.api import ToolBarManager -from traits.api import Trait, TraitDict, TraitEnum, TraitList +from traits.api import Dict, Enum, Instance, List + -# Local imports from .application_window import ApplicationWindow @@ -34,19 +30,18 @@ """ # The toolbars in the order they were added to the window. - _tool_bar_managers = Trait([], TraitList(Trait(ToolBarManager))) + _tool_bar_managers = List(Instance(ToolBarManager)) # Map of toolbar to screen location. - _tool_bar_locations = Trait({}, - TraitDict(Trait(ToolBarManager), - TraitEnum('top', 'bottom', - 'left', 'right'))) + _tool_bar_locations = Dict( + Instance(ToolBarManager), Enum("top", "bottom", "left", "right") + ) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): - panel = super(MultiToolbarWindow, self)._create_contents(parent) + panel = super()._create_contents(parent) self._create_trim_widgets(parent) return panel @@ -65,8 +60,6 @@ # Add the (optional) tool bars. self.sizer = self._create_tool_bars(parent) - return - def _create_tool_bars(self, parent): """ Create the tool bars for this window. """ @@ -79,8 +72,9 @@ for tool_bar_manager in self._tool_bar_managers: location = self._tool_bar_locations[tool_bar_manager] - sizer = self._create_tool_bar(parent, sizer, tool_bar_manager, - location) + sizer = self._create_tool_bar( + parent, sizer, tool_bar_manager, location + ) return sizer @@ -99,12 +93,12 @@ tool_bar = tool_bar_manager.create_tool_bar(parent) - if location == 'top': + if location == "top": child_sizer = wx.BoxSizer(wx.VERTICAL) child_sizer.Add(tool_bar, 0, wx.ALL | wx.ALIGN_LEFT | wx.EXPAND) sizer.Add(child_sizer, 1, wx.ALL | wx.EXPAND) - if location == 'bottom': + if location == "bottom": toolbar_sizer = wx.BoxSizer(wx.VERTICAL) # Add the placeholder for the content before adding the toolbar. @@ -114,12 +108,12 @@ toolbar_sizer.Add(tool_bar, 0, wx.ALL | wx.ALIGN_TOP | wx.EXPAND) sizer.Add(toolbar_sizer, 1, wx.ALL | wx.EXPAND) - if location == 'left': + if location == "left": child_sizer = wx.BoxSizer(wx.HORIZONTAL) child_sizer.Add(tool_bar, 0, wx.ALL | wx.ALIGN_TOP | wx.EXPAND) sizer.Add(child_sizer, 1, wx.ALL | wx.EXPAND) - if location == 'right': + if location == "right": toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL) # Add the placeholder for the content before adding the toolbar. @@ -137,11 +131,11 @@ return spacer - ########################################################################### + # ------------------------------------------------------------------------ # Public MultiToolbarWindow interface - ########################################################################### + # ------------------------------------------------------------------------ - def add_tool_bar(self, tool_bar_manager, location='top'): + def add_tool_bar(self, tool_bar_manager, location="top"): """ Add a toolbar in the specified location. Valid locations are 'top', 'bottom', 'left', and 'right' @@ -149,5 +143,3 @@ self._tool_bar_managers.append(tool_bar_manager) self._tool_bar_locations[tool_bar_manager] = location - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/pil_image.py python-pyface-7.4.0/pyface/ui/wx/pil_image.py --- python-pyface-6.1.2/pyface/ui/wx/pil_image.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/pil_image.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,52 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import wx + +from traits.api import provides + +from pyface.i_pil_image import IPILImage, MPILImage +from pyface.ui.wx.util.image_helpers import resize_image + + +@provides(IPILImage) +class PILImage(MPILImage): + """ The toolkit specific implementation of a PILImage. + """ + + # ------------------------------------------------------------------------ + # 'IImage' interface. + # ------------------------------------------------------------------------ + + def create_image(self, size=None): + """ Creates a Wx image for this image. + + Parameters + ---------- + size : (int, int) or None + The desired size as a (width, height) tuple, or None if wanting + default image size. + + Returns + ------- + image : wx.Image + The toolkit image corresponding to the image and the specified + size. + """ + image = self.image + wx_image = wx.EmptyImage(self.image.size[0], self.image.size[1]) + wx_image.SetData(image.convert("RGB").tobytes()) + if image.mode == "RGBA": + wx_image.InitAlpha() + wx_image.SetAlpha(image.getchannel("A").tobytes()) + if size is not None: + return resize_image(wx_image, size) + else: + return wx_image diff -Nru python-pyface-6.1.2/pyface/ui/wx/preference/preference_dialog.py python-pyface-7.4.0/pyface/ui/wx/preference/preference_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/preference/preference_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/preference/preference_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,64 +1,61 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2015, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The preference dialog. """ -# Major package imports. import wx -# Enthought library imports. + from traits.api import Any, Dict, Float, Instance, Str -# Local imports. + from pyface.preference.preference_node import PreferenceNode from pyface.ui.wx.heading_text import HeadingText from pyface.ui.wx.layered_panel import LayeredPanel from pyface.ui.wx.split_dialog import SplitDialog from pyface.ui.wx.viewer.tree_viewer import TreeViewer -from pyface.viewer.default_tree_content_provider import DefaultTreeContentProvider -from pyface.wx.util.font_helper import new_font_like +from pyface.viewer.default_tree_content_provider import ( + DefaultTreeContentProvider, +) class PreferenceDialog(SplitDialog): """ The preference dialog. """ - #### 'Dialog' interface ################################################### + # 'Dialog' interface --------------------------------------------------- # The dialog title. - title = Str('Preferences') + title = Str("Preferences") - #### 'SplitDialog' interface ############################################## + # 'SplitDialog' interface ---------------------------------------------# # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.25) - #### 'PreferenceDialog' interface ######################################### + # 'PreferenceDialog' interface ----------------------------------------- # The root of the preference hierarchy. root = Instance(PreferenceNode) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # The preference pages in the dialog (they are created lazily). - _pages = Dict + _pages = Dict() # The current visible preference page. - _current_page = Any + _current_page = Any() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Dialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_buttons(self, parent): """ Creates the buttons. """ @@ -68,14 +65,14 @@ # 'Done' button. done = wx.Button(parent, wx.ID_OK, "Done") done.SetDefault() - wx.EVT_BUTTON(parent, wx.ID_OK, self._wx_on_ok) + parent.Bind(wx.EVT_BUTTON, self._wx_on_ok, wx.ID_OK) sizer.Add(done) return sizer - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'SplitDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_lhs(self, parent): """ Creates the panel containing the preference page tree. """ @@ -91,17 +88,28 @@ panel.SetAutoLayout(True) # The 'pretty' title bar ;^) - self.__title = HeadingText(panel) + self.__title = HeadingText(parent=panel, create=False) + self.__title.create() sizer.Add( - self.__title.control, 0, - wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5 + self.__title.control, + 0, + wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT, + 5, ) # The preference page of the node currently selected in the tree. - self._layered_panel = LayeredPanel(panel, min_width=-1, min_height=-1) + self._layered_panel = LayeredPanel( + parent=panel, + create=False, + min_width=-1, + min_height=-1, + ) + self._layered_panel.create() sizer.Add( - self._layered_panel.control, 1, - wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 5 + self._layered_panel.control, + 1, + wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, + 5, ) # The 'Restore Defaults' button etc. @@ -117,22 +125,22 @@ return panel - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_tree(self, parent): """ Creates the preference page tree. """ tree_viewer = TreeViewer( parent, - input = self.root, - show_images = False, - show_root = False, - content_provider = DefaultTreeContentProvider() + input=self.root, + show_images=False, + show_root=False, + content_provider=DefaultTreeContentProvider(), ) - tree_viewer.on_trait_change(self._on_selection_changed, 'selection') + tree_viewer.observe(self._on_selection_changed, "selection") return tree_viewer.control @@ -147,19 +155,19 @@ # 'Help' button. Comes first so 'Restore Defaults' doesn't jump around. self._help = help = wx.Button(parent, -1, "Help") - wx.EVT_BUTTON(parent, help.GetId(), self._on_help) + parent.Bind(wx.EVT_BUTTON, self._on_help, help.GetId()) sizer.Add(help, 0, wx.RIGHT, 5) # 'Restore Defaults' button. restore = wx.Button(parent, -1, "Restore Defaults") - wx.EVT_BUTTON(parent, restore.GetId(), self._on_restore_defaults) + parent.Bind(wx.EVT_BUTTON, self._on_restore_defaults, restore.GetId()) sizer.Add(restore) return sizer - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_restore_defaults(self, event): """ Called when the 'Restore Defaults' button is pressed. """ @@ -167,8 +175,6 @@ page = self._pages[self._layered_panel.current_layer_name] page.restore_defaults() - return - def _on_help(self, event): """ Called when the 'Help' button is pressed. """ @@ -177,13 +183,13 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_selection_changed(self, selection): + def _on_selection_changed(self, event): """ Called when a node in the tree is selected. """ - + selection = event.new if len(selection) > 0: # The tree is in single selection mode. node = selection[0] @@ -197,7 +203,7 @@ self._button_sizer.Show(self._help, False) # Show the selected preference page. - layered_panel = self._layered_panel + layered_panel = self._layered_panel parent = self._layered_panel.control # If we haven't yet displayed the node's preference page during the @@ -211,5 +217,3 @@ self.__title.text = node.name return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/progress_dialog.py python-pyface-7.4.0/pyface/ui/wx/progress_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/progress_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/progress_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,30 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A simple progress bar intended to run in the UI thread """ +import warnings + import wx import time -# Enthought library imports -from traits.api import Bool, Enum, Instance, Int, Property, provides, Str +from traits.api import Bool, Instance, Int, Property, Str -# Local imports -from pyface.i_progress_dialog import IProgressDialog, MProgressDialog +from pyface.i_progress_dialog import MProgressDialog +from pyface.ui_traits import Orientation from .widget import Widget from .window import Window + class ProgressBar(Widget): """ A simple progress bar dialog intended to run in the UI thread """ @@ -38,26 +35,51 @@ control = Instance(wx.Gauge) #: The orientation of the progress bar. - direction = Enum('horizontal', 'horizontal', 'vertical') + direction = Orientation("horizontal") #: The maximum value for the progress bar. - _max = Int + _max = Int() - def __init__(self, parent, minimum=0, maximum=100, direction='horizontal', - size=(200, -1)): + def __init__( + self, + parent, + minimum=0, + maximum=100, + direction="horizontal", + size=(200, -1), + **traits, + ): """ Constructs a progress bar which can be put into a panel, or optionaly, its own window """ - self._max = maximum - self.parent = parent + create = traits.pop("create", True) + + # XXX minimum is ignored - it either should be deprecated or supported + super().__init__( + parent=parent, + _max=maximum, + direction=direction, + size=size, + **traits, + ) + + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + def _create_control(self, parent): style = wx.GA_HORIZONTAL - if direction == "vertical": + if self.direction == "vertical": style = wx.GA_VERTICAL - self.control = wx.Gauge(parent, -1, maximum, style=style, size=size) + return wx.Gauge(parent, -1, self._max, style=style, size=self.size) def update(self, value): """ Update the progress bar to the desired value. """ @@ -83,16 +105,16 @@ progress_bar = Instance(ProgressBar) #: The window title - title = Str + title = Str() #: The text message to display in the dialog message = Property() #: The minimum value of the progress range - min = Int + min = Int() #: The minimum value of the progress range - max = Int + max = Int() #: The margin around the progress bar margin = Int(5) @@ -116,7 +138,7 @@ dialog_size = Instance(wx.Size) # Label for the 'cancel' button - cancel_button_label = Str('Cancel') + cancel_button_label = Str("Cancel") #: The widget showing the message text _message_control = Instance(wx.StaticText) @@ -131,22 +153,22 @@ _remaining_control = Instance(wx.StaticText) def __init__(self, *args, **kw): - if 'message' in kw: - self._message_text = kw.pop('message') + if "message" in kw: + self._message_text = kw.pop("message") # initialize the start time in case some tries updating # before open() is called self._start_time = 0 - super(ProgressDialog, self).__init__( *args, **kw) + super().__init__(*args, **kw) - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # IWindow Interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def open(self): """ Opens the window. """ - super(ProgressDialog, self).open() + super().open() self._start_time = time.time() wx.GetApp().Yield(True) @@ -159,11 +181,11 @@ if self._message_control is not None: self._message_control = None - super(ProgressDialog, self).close() + super().close() - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # IProgressDialog Interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def change_message(self, value): """ Change the displayed message in the progress dialog @@ -181,7 +203,9 @@ self._message_control.Update() msg_control_size = self._message_control.GetSize() - self.dialog_size.x = max(self.dialog_size.x, msg_control_size.x + 2*self.margin) + self.dialog_size.x = max( + self.dialog_size.x, msg_control_size.x + 2 * self.margin + ) if self.control is not None: self.control.SetClientSize(self.dialog_size) @@ -218,20 +242,17 @@ self.control.Raise() if self.max > 0: - percent = (float(value) - self.min)/(self.max - self.min) + percent = (float(value) - self.min) / (self.max - self.min) if self.show_time and (percent != 0): current_time = time.time() elapsed = current_time - self._start_time - estimated = elapsed/percent + estimated = elapsed / percent remaining = estimated - elapsed - self._set_time_label(elapsed, - self._elapsed_control) - self._set_time_label(estimated, - self._estimated_control) - self._set_time_label(remaining, - self._remaining_control) + self._set_time_label(elapsed, self._elapsed_control) + self._set_time_label(estimated, self._estimated_control) + self._set_time_label(remaining, self._remaining_control) if self.show_percent: self._percent_control = "%3f" % ((percent * 100) % 1) @@ -246,9 +267,9 @@ return (not self._user_cancelled, False) - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Private Interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def _on_cancel(self, event): self._user_cancelled = True @@ -259,8 +280,8 @@ return self.close() def _set_time_label(self, value, control): - hours = value / 3600 - minutes = (value % 3600) / 60 + hours = value // 3600 + minutes = (value % 3600) // 60 seconds = value % 60 label = "%u:%02u:%02u" % (hours, minutes, seconds) @@ -275,16 +296,19 @@ sizer = wx.BoxSizer(wx.HORIZONTAL) self._cancel = None - if self.can_cancel == True: + if self.can_cancel: # 'Cancel' button. - self._cancel = cancel = wx.Button(dialog, wx.ID_CANCEL, - self.cancel_button_label) - wx.EVT_BUTTON(dialog, wx.ID_CANCEL, self._on_cancel) + self._cancel = cancel = wx.Button( + dialog, wx.ID_CANCEL, self.cancel_button_label + ) + dialog.Bind(wx.EVT_BUTTON, self._on_cancel, id=wx.ID_CANCEL) sizer.Add(cancel, 0, wx.LEFT, 10) button_size = cancel.GetSize() - self.dialog_size.x = max(self.dialog_size.x, button_size.x + 2*self.margin) - self.dialog_size.y += button_size.y + 2*self.margin + self.dialog_size.x = max( + self.dialog_size.x, button_size.x + 2 * self.margin + ) + self.dialog_size.y += button_size.y + 2 * self.margin parent_sizer.Add(sizer, 0, wx.ALIGN_RIGHT | wx.ALL, self.margin) @@ -295,41 +319,59 @@ local_sizer.Add(dummy, 1, wx.ALIGN_LEFT) local_sizer.Add(label, 1, wx.ALIGN_LEFT | wx.ALIGN_RIGHT, self.margin) - parent_sizer.Add(local_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, self.margin) + parent_sizer.Add( + local_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, self.margin + ) return label def _create_gauge(self, dialog, parent_sizer): - self.progress_bar = ProgressBar(dialog, self.min, self.max) - parent_sizer.Add(self.progress_bar.control, 0, wx.CENTER | wx.ALL, self.margin) + self.progress_bar = ProgressBar(dialog, self.min, self.max, create=False) + self.progress_bar.create() + parent_sizer.Add( + self.progress_bar.control, 0, wx.CENTER | wx.ALL, self.margin + ) horiz_spacer = 50 progress_bar_size = self.progress_bar.control.GetSize() - self.dialog_size.x = max(self.dialog_size.x, progress_bar_size.x + 2*self.margin + horiz_spacer) - self.dialog_size.y += progress_bar_size.y + 2*self.margin + self.dialog_size.x = max( + self.dialog_size.x, + progress_bar_size.x + 2 * self.margin + horiz_spacer, + ) + self.dialog_size.y += progress_bar_size.y + 2 * self.margin def _create_message(self, dialog, parent_sizer): self._message_control = wx.StaticText(dialog, -1, self.message) - parent_sizer.Add(self._message_control, 0, wx.LEFT | wx.TOP, self.margin) + parent_sizer.Add( + self._message_control, 0, wx.LEFT | wx.TOP, self.margin + ) msg_control_size = self._message_control.GetSize() - self.dialog_size.x = max(self.dialog_size.x, msg_control_size.x + 2*self.margin) - self.dialog_size.y += msg_control_size.y + 2*self.margin + self.dialog_size.x = max( + self.dialog_size.x, msg_control_size.x + 2 * self.margin + ) + self.dialog_size.y += msg_control_size.y + 2 * self.margin def _create_percent(self, dialog, parent_sizer): if not self.show_percent: return - raise NotImplementedError + raise NotImplementedError() def _create_timer(self, dialog, parent_sizer): if not self.show_time: return - self._elapsed_control = self._create_label(dialog, parent_sizer, "Elapsed time : ") - self._estimated_control = self._create_label(dialog, parent_sizer, "Estimated time : ") - self._remaining_control = self._create_label(dialog, parent_sizer, "Remaining time : ") + self._elapsed_control = self._create_label( + dialog, parent_sizer, "Elapsed time : " + ) + self._estimated_control = self._create_label( + dialog, parent_sizer, "Estimated time : " + ) + self._remaining_control = self._create_label( + dialog, parent_sizer, "Remaining time : " + ) elapsed_size = self._elapsed_control.GetSize() estimated_size = self._estimated_control.GetSize() @@ -339,8 +381,10 @@ timer_size.x = max(elapsed_size.x, estimated_size.x, remaining_size.x) timer_size.y = elapsed_size.y + estimated_size.y + remaining_size.y - self.dialog_size.x = max(self.dialog_size.x, timer_size.x + 2*self.margin) - self.dialog_size.y += timer_size.y + 2*self.margin + self.dialog_size.x = max( + self.dialog_size.x, timer_size.x + 2 * self.margin + ) + self.dialog_size.y += timer_size.y + 2 * self.margin def _create_control(self, parent): """ Creates the window contents. @@ -349,15 +393,22 @@ just create an empty panel. """ - style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_NO_WINDOW_MENU | wx.CLIP_CHILDREN - - dialog = wx.Frame(parent, -1, self.title, style=style, size=self.size, - pos=self.position) + style = ( + wx.DEFAULT_FRAME_STYLE | wx.FRAME_NO_WINDOW_MENU | wx.CLIP_CHILDREN + ) + + dialog = wx.Frame( + parent, + -1, + self.title, + style=style, + size=self.size, + pos=self.position, + ) sizer = wx.BoxSizer(wx.VERTICAL) dialog.SetSizer(sizer) dialog.SetAutoLayout(True) - dialog.SetBackgroundColour(wx.NullColour) self.dialog_size = wx.Size() diff -Nru python-pyface-6.1.2/pyface/ui/wx/python_editor.py python-pyface-7.4.0/pyface/ui/wx/python_editor.py --- python-pyface-6.1.2/pyface/ui/wx/python_editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/python_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,74 +1,78 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Major package imports. +import warnings + import wx.stc -# Enthought library imports. -from traits.api import Bool, Event, provides, Unicode -# Local imports. +from traits.api import Bool, Event, provides, Str + + from pyface.i_python_editor import IPythonEditor, MPythonEditor from pyface.key_pressed_event import KeyPressedEvent from pyface.wx.python_stc import PythonSTC, faces -from .widget import Widget +from .layout_widget import LayoutWidget @provides(IPythonEditor) -class PythonEditor(MPythonEditor, Widget): +class PythonEditor(MPythonEditor, LayoutWidget): """ The toolkit specific implementation of a PythonEditor. See the IPythonEditor interface for the API documentation. """ - - #### 'IPythonEditor' interface ############################################ + # 'IPythonEditor' interface -------------------------------------------- dirty = Bool(False) - path = Unicode + path = Str() show_line_numbers = Bool(True) - #### Events #### + # Events ---- - changed = Event + changed = Event() key_pressed = Event(KeyPressedEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def __init__(self, parent, **traits): + def __init__(self, parent=None, **traits): """ Creates a new pager. """ + create = traits.pop("create", True) + # Base class constructor. - super(PythonEditor, self).__init__(**traits) + super().__init__(parent=parent, **traits) - # Create the toolkit-specific control that represents the widget. - self.control = self._create_control(parent) + if create: + # Create the widget's toolkit-specific control. + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) return - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def load(self, path=None): """ Loads the contents of the editor. """ @@ -78,75 +82,48 @@ # We will have no path for a new script. if len(path) > 0: - f = open(self.path, 'r') + f = open(self.path, "r") text = f.read() f.close() else: - text = '' + text = "" self.control.SetText(text) self.dirty = False - return - def save(self, path=None): """ Saves the contents of the editor. """ if path is None: path = self.path - f = open(path, 'w') + f = open(path, "w") f.write(self.control.GetText()) f.close() self.dirty = False - return - def set_style(self, n, fore, back): self.control.StyleSetForeground(n, fore) - #self.StyleSetBackground(n, '#c0c0c0') - #self.StyleSetBackground(n, '#ffffff') self.control.StyleSetBackground(n, back) self.control.StyleSetFaceName(n, "courier new") - self.control.StyleSetSize(n, faces['size']) - - #self.StyleSetForeground(n, "#f0f0f0") - ##self.StyleSetBackground(n, "#000000") - #self.StyleSetFaceName(n, "courier new") - #self.StyleSetSize(n, 20) - #self.StyleSetUnderline(n, 1) - #self.StyleSetItalic(n, 1) - #self.StyleSetBold(n, 1) - #StyleClearAll - #StyleResetDefault - #StyleSetCase - #StyleSetChangeable - #StyleSetCharacterSet - #StyleSetEOLFilled - #StyleSetFont - #StyleSetFontAttr - #StyleSetHotSpot - #StyleSetSpec --- batch - #StyleSetVisible - - return + self.control.StyleSetSize(n, faces["size"]) def select_line(self, lineno): """ Selects the specified line. """ start = self.control.PositionFromLine(lineno) - end = self.control.GetLineEndPosition(lineno) + end = self.control.GetLineEndPosition(lineno) self.control.SetSelection(start, end) return - ########################################################################### + # ------------------------------------------------------------------------ # Trait handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _path_changed(self): """ Handle a change to path. """ @@ -155,9 +132,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the toolkit-specific control for the widget. """ @@ -188,22 +165,22 @@ stc.SetIndent(4) # Line ending mode. - stc.SetEOLMode(wx.stc.STC_EOL_LF) # Unix - #self.SetEOLMode(wx.stc.STC_EOL_CR) # Apple Mac - #self.SetEOLMode(wx.stc.STC_EOL_CRLF) # Windows + stc.SetEOLMode(wx.stc.STC_EOL_LF) # Unix + # self.SetEOLMode(wx.stc.STC_EOL_CR) # Apple Mac + # self.SetEOLMode(wx.stc.STC_EOL_CRLF) # Windows - ########################################## + # ------------------------------------------------------------------------ # Global styles for all languages. - ########################################## + # ------------------------------------------------------------------------ self.set_style(wx.stc.STC_STYLE_DEFAULT, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_CONTROLCHAR, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_BRACELIGHT, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_BRACEBAD, "#000000", "#ffffff") - ########################################## + # ------------------------------------------------------------------------ # Python styles. - ########################################## + # ------------------------------------------------------------------------ # White space self.set_style(wx.stc.STC_P_DEFAULT, "#000000", "#ffffff") @@ -247,29 +224,38 @@ # End of line where string is not closed self.set_style(wx.stc.STC_P_STRINGEOL, "#000000", "#ffffff") - ########################################## + # ------------------------------------------------------------------------ # Events. - ########################################## + # ------------------------------------------------------------------------ # By default, the will fire EVT_STC_CHANGE evented for all mask values # (STC_MODEVENTMASKALL). This generates too many events. - stc.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT | - wx.stc.STC_MOD_DELETETEXT | - wx.stc.STC_PERFORMED_UNDO | - wx.stc.STC_PERFORMED_REDO) + stc.SetModEventMask( + wx.stc.STC_MOD_INSERTTEXT + | wx.stc.STC_MOD_DELETETEXT + | wx.stc.STC_PERFORMED_UNDO + | wx.stc.STC_PERFORMED_REDO + ) # Listen for changes to the file. - wx.stc.EVT_STC_CHANGE(stc, stc.GetId(), self._on_stc_changed) + stc.Bind(wx.stc.EVT_STC_CHANGE, self._on_stc_changed) # Listen for key press events. - wx.EVT_CHAR(stc, self._on_char) + stc.Bind(wx.EVT_CHAR, self._on_char) # Load the editor's contents. self.load() return stc - #### wx event handlers #################################################### + def destroy(self): + """ Destroy the toolkit control. """ + if self.control is not None: + self.control.Unbind(wx.stc.EVT_STC_CHANGE) + self.control.Unbind(wx.EVT_CHAR) + super().destroy() + + # wx event handlers ---------------------------------------------------- def _on_stc_changed(self, event): """ Called whenever a change is made to the text of the document. """ @@ -280,22 +266,18 @@ # Give other event handlers a chance. event.Skip() - return - def _on_char(self, event): """ Called whenever a change is made to the text of the document. """ self.key_pressed = KeyPressedEvent( - alt_down = event.m_altDown == 1, - control_down = event.m_controlDown == 1, - shift_down = event.m_shiftDown == 1, - key_code = event.m_keyCode, - event = event + alt_down=event.altDown, + control_down=event.controlDown, + shift_down=event.shiftDown, + key_code=event.KeyCode, + event=event, ) # Give other event handlers a chance. event.Skip() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/python_shell.py python-pyface-7.4.0/pyface/ui/wx/python_shell.py --- python-pyface-6.1.2/pyface/ui/wx/python_shell.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/python_shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,75 +1,79 @@ -# Copyright (c) 2005-18, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. +# Thanks for using Enthought open source! """ Enthought pyface package component """ -# Standard library imports. -import six.moves.builtins + +import builtins import os import sys import types +import warnings + -# Major package imports. from wx.py.shell import Shell as PyShellBase import wx -# Enthought library imports. + from traits.api import Event, provides -# Private Enthought library imports. + from traits.util.clean_strings import python_name from pyface.wx.drag_and_drop import PythonDropTarget -# Local imports. + from pyface.i_python_shell import IPythonShell, MPythonShell from pyface.key_pressed_event import KeyPressedEvent -from .widget import Widget -import six +from .layout_widget import LayoutWidget @provides(IPythonShell) -class PythonShell(MPythonShell, Widget): +class PythonShell(MPythonShell, LayoutWidget): """ The toolkit specific implementation of a PythonShell. See the IPythonShell interface for the API documentation. """ - #### 'IPythonShell' interface ############################################# + # 'IPythonShell' interface --------------------------------------------- - command_executed = Event + command_executed = Event() key_pressed = Event(KeyPressedEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ # FIXME v3: Either make this API consistent with other Widget sub-classes # or make it a sub-class of HasTraits. - def __init__(self, parent, **traits): + def __init__(self, parent=None, **traits): """ Creates a new pager. """ - # Base class constructor. - super(PythonShell, self).__init__(**traits) + create = traits.pop("create", True) - # Create the toolkit-specific control that represents the widget. - self.control = self._create_control(parent) + # Base class constructor. + super().__init__(parent=parent, **traits) - # Set up to be notified whenever a Python statement is executed: - self.control.handlers.append(self._on_command_executed) + if create: + # Create the toolkit-specific control that represents the widget. + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) - ########################################################################### + # ------------------------------------------------------------------------ # 'IPythonShell' interface. - ########################################################################### + # ------------------------------------------------------------------------ def interpreter(self): return self.control.interp @@ -88,19 +92,19 @@ filename = os.path.basename(path) # Run in a fresh, empty namespace - main_mod = types.ModuleType('__main__') + main_mod = types.ModuleType("__main__") prog_ns = main_mod.__dict__ - prog_ns['__file__'] = filename - prog_ns['__nonzero__'] = lambda: True + prog_ns["__file__"] = filename + prog_ns["__nonzero__"] = lambda: True # Make sure that the running script gets a proper sys.argv as if it # were run from a system shell. save_argv = sys.argv - sys.argv = [ filename ] + sys.argv = [filename] # Make sure that the running script thinks it is the main module - save_main = sys.modules['__main__'] - sys.modules['__main__'] = main_mod + save_main = sys.modules["__main__"] + sys.modules["__main__"] = main_mod # Redirect sys.std* to control or null old_stdin = sys.stdin @@ -117,22 +121,22 @@ self.control.clearCommand() self.control.write('# Executing "%s"\n' % path) - execfile(path, prog_ns, prog_ns) + exec(open(path).read(), prog_ns, prog_ns) if not hidden: self.control.prompt() finally: # Ensure key global stuctures are restored sys.argv = save_argv - sys.modules['__main__'] = save_main + sys.modules["__main__"] = save_main sys.stdin = old_stdin sys.stdout = old_stdout sys.stderr = old_stderr # Update the interpreter with the new namespace - del prog_ns['__name__'] - del prog_ns['__file__'] - del prog_ns['__nonzero__'] + del prog_ns["__name__"] + del prog_ns["__file__"] + del prog_ns["__nonzero__"] self.interpreter().locals.update(prog_ns) def get_history(self): @@ -162,39 +166,45 @@ self.control.history = list(history) self.control.historyIndex = history_index - ########################################################################### + # ------------------------------------------------------------------------ # 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): shell = PyShell(parent, -1) # Listen for key press events. - wx.EVT_CHAR(shell, self._wx_on_char) + shell.Bind(wx.EVT_CHAR, self._wx_on_char) # Enable the shell as a drag and drop target. shell.SetDropTarget(PythonDropTarget(self)) + # Set up to be notified whenever a Python statement is executed: + shell.handlers.append(self._on_command_executed) + return shell - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonDropTarget' handler interface. - ########################################################################### + # ------------------------------------------------------------------------ def on_drop(self, x, y, obj, default_drag_result): """ Called when a drop occurs on the shell. """ # If we can't create a valid Python identifier for the name of an # object we use this instead. - name = 'dragged' + name = "dragged" - if hasattr(obj, 'name') \ - and isinstance(obj.name, six.string_types) and len(obj.name) > 0: + if ( + hasattr(obj, "name") + and isinstance(obj.name, str) + and len(obj.name) > 0 + ): py_name = python_name(obj.name) # Make sure that the name is actually a valid Python identifier. try: - if eval(py_name, {py_name : True}): + if eval(py_name, {py_name: True}): name = py_name except: @@ -212,9 +222,9 @@ """ Always returns wx.DragCopy to indicate we will be doing a copy.""" return wx.DragCopy - ########################################################################### + # ------------------------------------------------------------------------ # Private handler interface. - ########################################################################### + # ------------------------------------------------------------------------ def _wx_on_char(self, event): """ Called whenever a change is made to the text of the document. """ @@ -224,19 +234,19 @@ if event.AltDown() and event.GetKeyCode() == 317: zoom = self.shell.control.GetZoom() if zoom != 20: - self.control.SetZoom(zoom+1) + self.control.SetZoom(zoom + 1) elif event.AltDown() and event.GetKeyCode() == 319: zoom = self.shell.control.GetZoom() if zoom != -10: - self.control.SetZoom(zoom-1) + self.control.SetZoom(zoom - 1) self.key_pressed = KeyPressedEvent( - alt_down = event.AltDown() == 1, - control_down = event.ControlDown() == 1, - shift_down = event.ShiftDown() == 1, - key_code = event.GetKeyCode(), - event = event + alt_down=event.AltDown() == 1, + control_down=event.ControlDown() == 1, + shift_down=event.ShiftDown() == 1, + key_code=event.GetKeyCode(), + event=event, ) # Give other event handlers a chance. @@ -244,18 +254,37 @@ class PyShell(PyShellBase): - - def __init__(self, parent, id=-1, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=wx.CLIP_CHILDREN, - introText='', locals=None, InterpClass=None, *args, **kwds): - self.handlers=[] + def __init__( + self, + parent, + id=-1, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.CLIP_CHILDREN, + introText="", + locals=None, + InterpClass=None, + *args, + **kwds + ): + self.handlers = [] # save a reference to the original raw_input() function since # wx.py.shell dosent reassign it back to the original on destruction - self.raw_input = six.moves.builtins.raw_input + self.raw_input = input - super(PyShell,self).__init__(parent, id, pos, size, style, introText, - locals, InterpClass, *args, **kwds) + super().__init__( + parent, + id, + pos, + size, + style, + introText, + locals, + InterpClass, + *args, + **kwds + ) def hidden_push(self, command): """ Send a command to the interpreter for execution without adding @@ -288,21 +317,41 @@ self.redirectStdout(False) self.redirectStderr(False) self.redirectStdin(False) - six.moves.builtins.raw_input = self.raw_input + builtins.raw_input = self.raw_input self.destroy() - super(PyShellBase, self).Destroy() + super().Destroy() -class _NullIO: +class _NullIO(object): """ A portable /dev/null for use with PythonShell.execute_file. """ - def tell(self): return 0 - def read(self, n = -1): return "" - def readline(self, length = None): return "" - def readlines(self): return [] - def write(self, s): pass - def writelines(self, list): pass - def isatty(self): return 0 - def flush(self): pass - def close(self): pass - def seek(self, pos, mode = 0): pass + + def tell(self): + return 0 + + def read(self, n=-1): + return "" + + def readline(self, length=None): + return "" + + def readlines(self): + return [] + + def write(self, s): + pass + + def writelines(self, list): + pass + + def isatty(self): + return 0 + + def flush(self): + pass + + def close(self): + pass + + def seek(self, pos, mode=0): + pass diff -Nru python-pyface-6.1.2/pyface/ui/wx/resource_manager.py python-pyface-7.4.0/pyface/ui/wx/resource_manager.py --- python-pyface-6.1.2/pyface/ui/wx/resource_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/resource_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,43 +1,35 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. + import os import tempfile +from io import BytesIO -from six.moves import cStringIO as StringIO -# Major package imports. import wx -# Enthought library imports. -from pyface.resource.api import ResourceFactory -from traits.api import Undefined +from pyface.resource.api import ResourceFactory class PyfaceResourceFactory(ResourceFactory): """ The implementation of a shared resource manager. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ResourceFactory' toolkit interface. - ########################################################################### + # ------------------------------------------------------------------------ def image_from_file(self, filename): """ Creates an image from the data in the specified filename. """ @@ -48,12 +40,7 @@ def image_from_data(self, data, filename=None): """ Creates an image from the specified data. """ - try: - return wx.ImageFromStream(StringIO(data)) - except: - # wx.ImageFromStream is only in wx 2.8 or later(?) - if filename is Undefined: - return None + return wx.Image(BytesIO(data)) handle = None if filename is None: @@ -62,7 +49,7 @@ handle, filename = tempfile.mkstemp() # Write it out... - tf = open(filename, 'wb') + tf = open(filename, "wb") tf.write(data) tf.close() @@ -75,5 +62,3 @@ os.unlink(filename) return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/single_choice_dialog.py python-pyface-7.4.0/pyface/ui/wx/single_choice_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/single_choice_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/single_choice_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,23 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2016, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A dialog that allows the user to chose a single item from a list. """ import wx from traits.api import Any, Bool, List, Str, provides -from pyface.i_single_choice_dialog import ISingleChoiceDialog, MSingleChoiceDialog +from pyface.i_single_choice_dialog import ( + ISingleChoiceDialog, + MSingleChoiceDialog, +) from .dialog import Dialog @@ -27,7 +25,7 @@ class SingleChoiceDialog(MSingleChoiceDialog, Dialog): """ A dialog that allows the user to chose a single item from a list. """ - #### 'ISingleChoiceDialog' interface ###################################### + # 'ISingleChoiceDialog' interface -------------------------------------# #: Whether or not the dialog can be cancelled (Wx Only). cancel = Bool(True) @@ -36,26 +34,26 @@ choices = List(Any) #: The object chosen, if any. - choice = Any + choice = Any() #: An optional attribute to use for the name of each object in the dialog. - name_attribute = Str + name_attribute = Str() #: The message to display to the user. - message = Str + message = Str() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Creates the window contents. """ # In this case, wx does it all for us in 'wx.SingleChoiceDialog' pass - ########################################################################### + # ------------------------------------------------------------------------ # 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): """ Closes the window. """ @@ -65,11 +63,11 @@ self.choice = self.choices[self.control.GetSelection()] # Let the window close as normal. - super(SingleChoiceDialog, self).close() + super().close() - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Create the toolkit-specific control that represents the window. """ @@ -86,7 +84,7 @@ self.title, self._choice_strings(), style, - self.position + self.position, ) if self.size != (-1, -1): diff -Nru python-pyface-6.1.2/pyface/ui/wx/splash_screen.py python-pyface-7.4.0/pyface/ui/wx/splash_screen.py --- python-pyface-6.1.2/pyface/ui/wx/splash_screen.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/splash_screen.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,37 +1,33 @@ - -#------------------------------------------------------------------------------ -# -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Thanks for using Enthought open source! +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. + from logging import DEBUG -# Major package imports. + import wx +import wx.adv + + +from traits.api import Any, Bool, Int, provides +from traits.api import Tuple, Str -# Enthought library imports. -from traits.api import Any, Bool, Font, Instance, Int, provides -from traits.api import Tuple, Unicode -# Local imports. from pyface.i_splash_screen import ISplashScreen, MSplashScreen -from pyface.image_resource import ImageResource +from pyface.ui_traits import Image from pyface.wx.util.font_helper import new_font_like +from .image_resource import ImageResource from .window import Window @@ -41,36 +37,35 @@ ISplashScreen interface for the API documentation. """ + # 'ISplashScreen' interface -------------------------------------------- - #### 'ISplashScreen' interface ############################################ - - image = Instance(ImageResource, ImageResource('splash')) + image = Image(ImageResource("splash")) log_level = Int(DEBUG) show_log_messages = Bool(True) - text = Unicode + text = Str() - text_color = Any + text_color = Any() - text_font = Any + text_font = Any() - text_location = Tuple(5, 5) + text_location = Tuple(5, 5) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_control(self, parent): # Get the splash screen image. image = self.image.create_image() - splash_screen = wx.SplashScreen( + splash_screen = wx.adv.SplashScreen( # The bitmap to display on the splash screen. image.ConvertToBitmap(), # Splash Style. - wx.SPLASH_NO_TIMEOUT | wx.SPLASH_CENTRE_ON_SCREEN, + wx.adv.SPLASH_NO_TIMEOUT | wx.adv.SPLASH_CENTRE_ON_SCREEN, # Timeout in milliseconds (we don't currently timeout!). 0, # The parent of the splash screen. @@ -78,7 +73,7 @@ # wx Id. -1, # Window style. - style = wx.SIMPLE_BORDER | wx.FRAME_NO_TASKBAR + style=wx.SIMPLE_BORDER | wx.FRAME_NO_TASKBAR, ) # By default we create a font slightly bigger and slightly more italic @@ -86,18 +81,18 @@ # handler for 'EVT_PAINT'. self._wx_default_text_font = new_font_like( wx.NORMAL_FONT, - point_size = wx.NORMAL_FONT.GetPointSize() + 1, - style = wx.ITALIC + point_size=wx.NORMAL_FONT.GetPointSize() + 1, + style=wx.ITALIC, ) # This allows us to write status text on the splash screen. - wx.EVT_PAINT(splash_screen, self._on_paint) + splash_screen.Bind(wx.EVT_PAINT, self._on_paint) return splash_screen - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _text_changed(self): """ Called when the splash screen text has been changed. """ @@ -113,7 +108,7 @@ if self.control is not None: # Get the window that the splash image is drawn in. - window = self.control.GetSplashWindow() + window = self.control # .GetSplashWindow() dc = wx.PaintDC(window) @@ -125,7 +120,7 @@ dc.SetFont(text_font) if self.text_color is None: - text_color = 'black' + text_color = "black" else: text_color = self.text_color @@ -136,5 +131,3 @@ # Let the normal wx paint handling do its stuff. event.Skip() - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/split_widget.py python-pyface-7.4.0/pyface/ui/wx/split_widget.py --- python-pyface-6.1.2/pyface/ui/wx/split_widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/split_widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,52 +1,33 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Mix-in class for split widgets. """ -# Major package imports. import wx -# Enthought library imports. -from traits.api import Callable, Enum, Float, HasTraits, provides +from traits.api import provides -# Local imports. from pyface.i_split_widget import ISplitWidget, MSplitWidget @provides(ISplitWidget) -class SplitWidget(MSplitWidget, HasTraits): +class SplitWidget(MSplitWidget): """ The toolkit specific implementation of a SplitWidget. See the ISPlitWidget interface for the API documentation. """ - - #### 'ISplitWidget' interface ############################################# - - direction = Enum('vertical', 'vertical', 'horizontal') - - ratio = Float(0.5) - - lhs = Callable - - rhs = Callable - - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'ISplitWidget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_splitter(self, parent): """ Create the toolkit-specific control that represents the widget. """ @@ -75,7 +56,7 @@ # # fixme: Notice that on the initial split, we DON'T specify the split # ratio. If we do then sadly, wx won't let us move the sash 8^() - if self.direction == 'vertical': + if self.direction == "vertical": splitter.SplitVertically(lhs, rhs) else: @@ -83,7 +64,7 @@ # We respond to the FIRST size event to make sure that the split ratio # is correct when the splitter is laid out in its parent. - wx.EVT_SIZE(splitter, self._on_size) + splitter.Bind(wx.EVT_SIZE, self._on_size) return splitter @@ -92,13 +73,8 @@ if self.lhs is not None: lhs = self.lhs(parent) - if hasattr(wx, 'WindowPtr'): - if not isinstance(lhs, (wx.Window, wx.WindowPtr)): - lhs = lhs.control - else: - # wx 2.8 did away with WindowPtr - if not isinstance(lhs, wx.Window): - lhs = lhs.control + if not isinstance(lhs, wx.Window): + lhs = lhs.control else: # Dummy implementation - override! @@ -113,13 +89,8 @@ if self.rhs is not None: rhs = self.rhs(parent) - if hasattr(wx, 'WindowPtr'): - if not isinstance(rhs, (wx.Window, wx.WindowPtr)): - rhs = rhs.control - else: - # wx 2.8 did away with WindowPtr - if not isinstance(rhs, wx.Window): - rhs = rhs.control + if not isinstance(rhs, wx.Window): + rhs = rhs.control else: # Dummy implementation - override! @@ -129,20 +100,20 @@ return rhs - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_size(self, event): """ Called when the frame is resized. """ splitter = event.GetEventObject() - width, height = splitter.GetSize() + width, height = splitter.GetSize().Get() # Make sure that the split ratio is correct. - if self.direction == 'vertical': + if self.direction == "vertical": position = int(width * self.ratio) else: @@ -152,8 +123,6 @@ # Since we only care about the FIRST size event, remove ourselves as # a listener. - #wx.EVT_SIZE(splitter, None) + # splitter.Unbind(wx.EVT_SIZE) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/system_metrics.py python-pyface-7.4.0/pyface/ui/wx/system_metrics.py --- python-pyface-6.1.2/pyface/ui/wx/system_metrics.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/system_metrics.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,32 +1,27 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Standard library imports. + import sys -# Major package imports. + import wx -# Enthought library imports. + from traits.api import HasTraits, Int, Property, provides, Tuple -# Local imports. + from pyface.i_system_metrics import ISystemMetrics, MSystemMetrics @@ -36,8 +31,7 @@ ISystemMetrics interface for the API documentation. """ - - #### 'ISystemMetrics' interface ########################################### + # 'ISystemMetrics' interface ------------------------------------------- screen_width = Property(Int) @@ -45,9 +39,9 @@ dialog_background_color = Property(Tuple) - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_screen_width(self): return wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X) @@ -56,12 +50,10 @@ return wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) def _get_dialog_background_color(self): - if sys.platform == 'darwin': + if sys.platform == "darwin": # wx lies. color = wx.Colour(232, 232, 232) else: color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR).Get() - return (color[0]/255., color[1]/255., color[2]/255.) - -#### EOF ###################################################################### + return (color[0] / 255.0, color[1] / 255.0, color[2] / 255.0) diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/advanced_editor_area_pane.py python-pyface-7.4.0/pyface/ui/wx/tasks/advanced_editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/advanced_editor_area_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/advanced_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,23 @@ -# Standard library imports. -import sys +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -# Enthought library imports. from traits.api import provides -# Local imports. + from pyface.tasks.i_advanced_editor_area_pane import IAdvancedEditorAreaPane -from editor_area_pane import EditorAreaPane +from .editor_area_pane import EditorAreaPane -############################################################################### +# ---------------------------------------------------------------------------- # 'AdvancedEditorAreaPane' class. -############################################################################### +# ---------------------------------------------------------------------------- + @provides(IAdvancedEditorAreaPane) class AdvancedEditorAreaPane(EditorAreaPane): @@ -18,5 +25,5 @@ See the IAdvancedEditorAreaPane interface for API documentation. """ - + # No additional functionality over the standard EditorAreaPane in wx yet. diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/dock_pane.py python-pyface-7.4.0/pyface/ui/wx/tasks/dock_pane.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/dock_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/dock_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,41 @@ -# Standard library imports -from contextlib import contextmanager +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import logging -# Enthought library imports. + from pyface.tasks.i_dock_pane import IDockPane, MDockPane -from traits.api import Bool, on_trait_change, Property, provides, Tuple, Str, Int +from traits.api import ( + Bool, + observe, + Property, + provides, + Tuple, + Str, + Int, +) + -# System library imports. import wx from pyface.wx.aui import aui -# Local imports. + from .task_pane import TaskPane # Constants. -AREA_MAP = { 'left' : aui.AUI_DOCK_LEFT, - 'right' : aui.AUI_DOCK_RIGHT, - 'top' : aui.AUI_DOCK_TOP, - 'bottom' : aui.AUI_DOCK_BOTTOM } +AREA_MAP = { + "left": aui.AUI_DOCK_LEFT, + "right": aui.AUI_DOCK_RIGHT, + "top": aui.AUI_DOCK_TOP, + "bottom": aui.AUI_DOCK_BOTTOM, +} INVERSE_AREA_MAP = dict((int(v), k) for k, v in AREA_MAP.items()) # Logging @@ -30,9 +48,9 @@ See the IDockPane interface for API documentation. """ - + # Keep a reference to the Aui pane name in order to update dock state - pane_name = Str + pane_name = Str() # Whether the title bar of the pane is currently visible. caption_visible = Bool(True) @@ -41,17 +59,17 @@ # number. This is a way to isolate panes dock_layer = Int(0) - #### 'IDockPane' interface ################################################ + # 'IDockPane' interface ------------------------------------------------ size = Property(Tuple) - #### Protected traits ##################################################### + # Protected traits ----------------------------------------------------- _receiving = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ @classmethod def get_hierarchy(cls, parent, indent=""): @@ -65,7 +83,7 @@ """ # wx doesn't need a wrapper control, so the contents become the control self.control = self.create_contents(parent) - + # hide the pane till the task gets activated, whereupon it will take # its visibility from the task state self.control.Hide() @@ -73,8 +91,11 @@ # Set the widget's object name. This important for AUI Manager state # saving. Use the task ID and the pane ID to avoid collisions when a # pane is present in multiple tasks attached to the same window. - self.pane_name = self.task.id + ':' + self.id - logger.debug("dock_pane.create: %s HIERARCHY:\n%s" % (self.pane_name, self.get_hierarchy(parent, " "))) + self.pane_name = self.task.id + ":" + self.id + logger.debug( + "dock_pane.create: %s HIERARCHY:\n%s" + % (self.pane_name, self.get_hierarchy(parent, " ")) + ) def get_new_info(self): info = aui.AuiPaneInfo().Name(self.pane_name).DestroyOnClose(False) @@ -87,25 +108,30 @@ self.update_dock_title(info) self.update_floating(info) self.update_visible(info) - + return info - + def add_to_manager(self, row=None, pos=None, tabify_pane=None): info = self.get_new_info() if tabify_pane is not None: target = tabify_pane.get_pane_info() - logger.debug("dock_pane.add_to_manager: Tabify! %s onto %s" % (self.pane_name, target.name)) + logger.debug( + "dock_pane.add_to_manager: Tabify! %s onto %s" + % (self.pane_name, target.name) + ) else: target = None if row is not None: info.Row(row) if pos is not None: info.Position(pos) - self.task.window._aui_manager.AddPane(self.control, info, target=target) - + self.task.window._aui_manager.AddPane( + self.control, info, target=target + ) + def validate_traits_from_pane_info(self): """ Sync traits from the AUI pane info. - + Useful after perspective restore to make sure e.g. visibility state is set correctly. """ @@ -118,7 +144,7 @@ if self.control is not None: logger.debug("Destroying %s" % self.control) self.task.window._aui_manager.DetachPane(self.control) - + # Some containers (e.g. TraitsDockPane) will destroy the control # before we get here (e.g. traitsui.ui.UI.finish by way of # TraitsDockPane.destroy), so check to see if it's already been @@ -129,28 +155,28 @@ self.control.Destroy() self.control = None - ########################################################################### + # ------------------------------------------------------------------------ # 'IDockPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ - return wx.Window(parent, name=self.task.id + ':' + self.id) + return wx.Window(parent, name=self.task.id + ":" + self.id) - #### Trait property getters/setters ####################################### + # Trait property getters/setters --------------------------------------- def _get_size(self): if self.control is not None: - return self.control.GetSizeTuple() + return self.control.GetSize().Get() return (-1, -1) - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ def get_pane_info(self): info = self.task.window._aui_manager.GetPane(self.pane_name) return info - + def commit_layout(self, layout=True): if layout: self.task.window._aui_manager.Update() @@ -167,10 +193,12 @@ def update_dock_area(self, info): info.Direction(AREA_MAP[self.dock_area]) - logger.debug("info: dock_area=%s dir=%s" % (self.dock_area, info.dock_direction)) + logger.debug( + "info: dock_area=%s dir=%s" % (self.dock_area, info.dock_direction) + ) - @on_trait_change('dock_area') - def _set_dock_area(self): + @observe("dock_area") + def _set_dock_area(self, event): logger.debug("trait change: dock_area") if self.control is not None: info = self.get_pane_info() @@ -184,8 +212,8 @@ info.CaptionVisible(self.caption_visible) info.Layer(self.dock_layer) - @on_trait_change('closable,floatable,movable,caption_visible,dock_layer') - def _set_dock_features(self): + @observe("closable,floatable,movable,caption_visible,dock_layer") + def _set_dock_features(self, event): if self.control is not None: info = self.get_pane_info() self.update_dock_features(info) @@ -194,8 +222,8 @@ def update_dock_title(self, info): info.Caption(self.name) - @on_trait_change('name') - def _set_dock_title(self): + @observe("name") + def _set_dock_title(self, event): if self.control is not None: info = self.get_pane_info() self.update_dock_title(info) @@ -209,8 +237,8 @@ else: info.Dock() - @on_trait_change('floating') - def _set_floating(self): + @observe("floating") + def _set_floating(self, event): if self.control is not None: info = self.get_pane_info() self.update_floating(info) @@ -222,9 +250,12 @@ else: info.Hide() - @on_trait_change('visible') - def _set_visible(self): - logger.debug("_set_visible %s on pane=%s, control=%s" % (self.visible, self.pane_name, self.control)) + @observe("visible") + def _set_visible(self, event): + logger.debug( + "_set_visible %s on pane=%s, control=%s" + % (self.visible, self.pane_name, self.control) + ) if self.control is not None: info = self.get_pane_info() self.update_visible(info) diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/editor_area_pane.py python-pyface-7.4.0/pyface/ui/wx/tasks/editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/editor_area_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,34 @@ -# Standard library imports. -import sys +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import logging -# Enthought library imports. -from pyface.tasks.i_editor_area_pane import IEditorAreaPane, \ - MEditorAreaPane -from traits.api import on_trait_change, provides -# System library imports. +from pyface.tasks.i_editor_area_pane import IEditorAreaPane, MEditorAreaPane +from traits.api import observe, provides + + import wx from pyface.wx.aui import aui, PyfaceAuiNotebook -# Local imports. + from .task_pane import TaskPane # Logging logger = logging.getLogger(__name__) -############################################################################### +# ---------------------------------------------------------------------------- # 'EditorAreaPane' class. -############################################################################### +# ---------------------------------------------------------------------------- + @provides(IEditorAreaPane) class EditorAreaPane(TaskPane, MEditorAreaPane): @@ -29,11 +37,16 @@ See the IEditorAreaPane interface for API documentation. """ - style = aui.AUI_NB_WINDOWLIST_BUTTON|aui.AUI_NB_TAB_MOVE|aui.AUI_NB_SCROLL_BUTTONS|aui.AUI_NB_CLOSE_ON_ACTIVE_TAB + style = ( + aui.AUI_NB_WINDOWLIST_BUTTON + | aui.AUI_NB_TAB_MOVE + | aui.AUI_NB_SCROLL_BUTTONS + | aui.AUI_NB_CLOSE_ON_ACTIVE_TAB + ) - ########################################################################### + # ------------------------------------------------------------------------ # 'TaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -44,7 +57,9 @@ self.control = control = PyfaceAuiNotebook(parent, agwStyle=self.style) # Connect to the widget's signals. - control.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self._update_active_editor) + control.Bind( + aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self._update_active_editor + ) control.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self._close_requested) def destroy(self): @@ -53,18 +68,18 @@ for editor in self.editors: self.remove_editor(editor) - super(EditorAreaPane, self).destroy() + super().destroy() - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditorAreaPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activates the specified editor in the pane. """ index = self.control.GetPageIndex(editor.control) self.control.SetSelection(index) - + def add_editor(self, editor): """ Adds an editor to the pane. """ @@ -77,7 +92,7 @@ except AttributeError: pass self.editors.append(editor) - self._update_tab_bar() + self._update_tab_bar(event=None) # The EVT_AUINOTEBOOK_PAGE_CHANGED event is not sent when the first # editor is added. @@ -93,22 +108,22 @@ self.control.RemovePage(index) editor.destroy() editor.editor_area = None - self._update_tab_bar() + self._update_tab_bar(event=None) if not self.editors: self.active_editor = None - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_label(self, editor): """ Return a tab label for an editor. """ label = editor.name if editor.dirty: - label = '*' + label + label = "*" + label if not label: - label = " " # bug in agw that fails on empty label + label = " " # bug in agw that fails on empty label return label def _get_editor_with_control(self, control): @@ -119,25 +134,27 @@ return editor return None - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - @on_trait_change('editors:[dirty, name]') - def _update_label(self, editor, name, new): + @observe("editors:items:[dirty, name]") + def _update_label(self, event): + editor = event.object index = self.control.GetPageIndex(editor.control) self.control.SetPageText(index, self._get_label(editor)) - @on_trait_change('editors:tooltip') - def _update_tooltip(self, editor, name, new): + @observe("editors:items:tooltip") + def _update_tooltip(self, event): + editor = event.object self.control.SetPageToolTip(editor.control, editor.tooltip) - #### Signal handlers ###################################################### + # Signal handlers -----------------------------------------------------# def _close_requested(self, evt): index = evt.GetSelection() logger.debug("_close_requested: index=%d" % index) control = self.control.GetPage(index) editor = self._get_editor_with_control(control) - + # Veto the event even though we are going to delete the tab, otherwise # the notebook will delete the editor wx control and the call to # editor.close() will fail. IEditorAreaPane.remove_editor() needs @@ -145,7 +162,7 @@ # editors. evt.Veto() editor.close() - + def _update_active_editor(self, evt): index = evt.GetSelection() logger.debug("index=%d" % index) @@ -156,8 +173,7 @@ control = self.control.GetPage(index) self.active_editor = self._get_editor_with_control(control) - @on_trait_change('hide_tab_bar') - def _update_tab_bar(self): + @observe("hide_tab_bar") + def _update_tab_bar(self, event): if self.control is not None: - visible = self.control.GetPageCount() > 1 if self.hide_tab_bar else True - pass # Can't actually hide the tab bar on wx.aui + pass # Can't actually hide the tab bar on wx.aui diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/editor.py python-pyface-7.4.0/pyface/ui/wx/tasks/editor.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.tasks.i_editor import IEditor, MEditor from traits.api import Bool, Property, provides -# System library imports. + import wx @@ -13,14 +22,13 @@ See the IEditor interface for API documentation. """ - - #### 'IEditor' interface ################################################## + # 'IEditor' interface -------------------------------------------------# has_focus = Property(Bool) - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -35,9 +43,9 @@ self.control.Destroy() self.control = None - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_has_focus(self): if self.control is not None: diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/main_window_layout.py python-pyface-7.4.0/pyface/ui/wx/tasks/main_window_layout.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/main_window_layout.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/main_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,31 @@ -# Standard library imports. -from itertools import combinations +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import logging -# Enthought library imports. -from traits.api import Any, HasTraits -# Local imports. -from dock_pane import AREA_MAP, INVERSE_AREA_MAP -from pyface.tasks.task_layout import LayoutContainer, PaneItem, Tabbed, \ - Splitter, HSplitter, VSplitter +from traits.api import HasTraits + + +from .dock_pane import AREA_MAP, INVERSE_AREA_MAP +from pyface.tasks.task_layout import ( + PaneItem, + Tabbed, + Splitter, +) # row/col orientation for AUI -ORIENTATION_NEEDS_NEW_ROW = { - 'horizontal' : { 'top': False, 'bottom': False, 'left': True, 'right': True}, - 'vertical': { 'top': True, 'bottom': True, 'left': False, 'right': False}, - } +ORIENTATION_NEEDS_NEW_ROW = { + "horizontal": {"top": False, "bottom": False, "left": True, "right": True}, + "vertical": {"top": True, "bottom": True, "left": False, "right": False}, +} # Logging. @@ -25,9 +36,9 @@ """ A class for applying declarative layouts to an AUI managed window. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_layout(self, layout, window): """ Get the layout by adding sublayouts to the specified DockLayout. @@ -40,7 +51,7 @@ """ Applies a DockLayout to the window. """ logger.debug("set_layout: %s" % layout) - + if hasattr(layout, "perspective"): self._set_layout_from_aui(layout, window) return @@ -60,17 +71,25 @@ for dock_pane in self.state.dock_panes: info = mgr.GetPane(dock_pane.pane_name) if not info.IsOk(): - logger.debug("_add_dock_panes: managing pane %s" % dock_pane.pane_name) + logger.debug( + "_add_dock_panes: managing pane %s" % dock_pane.pane_name + ) dock_pane.add_to_manager() else: - logger.debug("_add_dock_panes: arleady managed pane: %s" % dock_pane.pane_name) - + logger.debug( + "_add_dock_panes: arleady managed pane: %s" + % dock_pane.pane_name + ) + def _set_layout_from_aui(self, layout, window): # The central pane will have already been added, but we need to add all # of the dock panes to the manager before the call to LoadPerspective logger.debug("_set_layout_from_aui: using saved perspective") self._add_dock_panes(window) - logger.debug("_set_layout_from_aui: restoring perspective %s" % layout.perspective) + logger.debug( + "_set_layout_from_aui: restoring perspective %s" + % layout.perspective + ) window._aui_manager.LoadPerspective(layout.perspective) for dock_pane in self.state.dock_panes: logger.debug("validating dock pane traits for %s" % dock_pane.id) @@ -86,7 +105,7 @@ # only be split horizontally and within each horizontal split can be # split vertically. logger.debug("set_layout_for_area: %s" % INVERSE_AREA_MAP[direction]) - + if isinstance(layout, PaneItem): dock_pane = self._get_dock_pane(layout) if dock_pane is None: @@ -95,7 +114,7 @@ logger.debug("layout size (%d,%d)" % (layout.width, layout.height)) dock_pane.add_to_manager(row=row, pos=pos) dock_pane.visible = True - + elif isinstance(layout, Tabbed): active_pane = first_pane = None for item in layout.items: @@ -119,7 +138,9 @@ elif isinstance(layout, Splitter): dock_area = INVERSE_AREA_MAP[direction] - needs_new_row = ORIENTATION_NEEDS_NEW_ROW[layout.orientation][dock_area] + needs_new_row = ORIENTATION_NEEDS_NEW_ROW[layout.orientation][ + dock_area + ] if needs_new_row: if row is None: row = 0 @@ -133,23 +154,23 @@ for i, item in enumerate(layout.items): self.set_layout_for_area(item, direction, row, pos) pos += 1 - + else: raise MainWindowLayoutError("Unknown layout item %r" % layout) - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' abstract interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_dock_widget(self, pane): """ Returns the QDockWidget associated with a PaneItem. """ - raise NotImplementedError + raise NotImplementedError() def _get_pane(self, dock_widget): """ Returns a PaneItem for a QDockWidget. """ - raise NotImplementedError + raise NotImplementedError() def _get_dock_pane(self, pane): """ Returns the DockPane associated with a PaneItem. @@ -164,4 +185,5 @@ """ Exception raised when a malformed LayoutItem is passed to the MainWindowLayout. """ + pass diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/split_editor_area_pane.py python-pyface-7.4.0/pyface/ui/wx/tasks/split_editor_area_pane.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/split_editor_area_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/split_editor_area_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,23 @@ -# Standard library imports. -import sys +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -# Enthought library imports. from traits.api import provides -# Local imports. + from pyface.tasks.i_editor_area_pane import IEditorAreaPane -from editor_area_pane import EditorAreaPane +from .editor_area_pane import EditorAreaPane -############################################################################### +# ---------------------------------------------------------------------------- # 'AdvancedEditorAreaPane' class. -############################################################################### +# ---------------------------------------------------------------------------- + @provides(IEditorAreaPane) class SplitEditorAreaPane(EditorAreaPane): @@ -18,5 +25,5 @@ See the IAdvancedEditorAreaPane interface for API documentation. """ - + # No additional functionality over the standard EditorAreaPane in wx yet. diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/task_pane.py python-pyface-7.4.0/pyface/ui/wx/tasks/task_pane.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/task_pane.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/task_pane.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,12 +1,22 @@ -# Enthought library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from pyface.tasks.i_task_pane import ITaskPane, MTaskPane from traits.api import provides -# System library imports. + import wx # Logging import logging + logger = logging.getLogger(__name__) @@ -17,10 +27,9 @@ See the ITaskPane interface for API documentation. """ - - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskPane' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create(self, parent): """ Create and set the toolkit-specific control that represents the @@ -34,7 +43,7 @@ if self.control is not None: logger.debug("Destroying %s" % self.control) self.task.window._aui_manager.DetachPane(self.control) - + self.control.Hide() self.control.Destroy() self.control = None diff -Nru python-pyface-6.1.2/pyface/ui/wx/tasks/task_window_backend.py python-pyface-7.4.0/pyface/ui/wx/tasks/task_window_backend.py --- python-pyface-6.1.2/pyface/ui/wx/tasks/task_window_backend.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tasks/task_window_backend.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,23 @@ -# Standard library imports. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import logging -# System library imports. -import wx + from pyface.wx.aui import aui -# Enthought library imports. + from traits.api import Instance, List, Str -# Local imports. -from main_window_layout import MainWindowLayout + +from .main_window_layout import MainWindowLayout from pyface.tasks.i_task_window_backend import MTaskWindowBackend from pyface.tasks.task_layout import PaneItem, TaskLayout @@ -20,7 +28,9 @@ class AUILayout(TaskLayout): """ The layout for a main window's dock area using AUI Perspectives """ - perspective = Str + + perspective = Str() + class TaskWindowBackend(MTaskWindowBackend): """ The toolkit-specific implementation of a TaskWindowBackend. @@ -28,20 +38,22 @@ See the ITaskWindowBackend interface for API documentation. """ - #### Private interface #################################################### + # Private interface ---------------------------------------------------- _main_window_layout = Instance(MainWindowLayout) - ########################################################################### + # ------------------------------------------------------------------------ # 'ITaskWindowBackend' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_contents(self, parent): """ Create and return the TaskWindow's contents. """ # No extra control needed for wx (it's all handled by the AUIManager in # the ApplicationWindow) but we do need to handle some events here - self.window._aui_manager.Bind(aui.EVT_AUI_PANE_CLOSE, self._pane_close_requested) + self.window._aui_manager.Bind( + aui.EVT_AUI_PANE_CLOSE, self._pane_close_requested + ) def destroy(self): """ Destroy the backend. @@ -57,16 +69,16 @@ # Now hide its controls. self.window._aui_manager.DetachPane(state.central_pane.control) state.central_pane.control.Hide() - + for dock_pane in state.dock_panes: logger.debug("hiding dock pane %s" % dock_pane.id) self.window._aui_manager.DetachPane(dock_pane.control) dock_pane.control.Hide() - + # Remove any tabbed notebooks left over after all the panes have been # removed self.window._aui_manager.UpdateNotebook() - + # Remove any still-left over stuff (i.e. toolbars) for info in self.window._aui_manager.GetAllPanes(): logger.debug("hiding remaining pane: %s" % info.name) @@ -79,14 +91,22 @@ specified TaskState. """ # Show the central pane. - info = aui.AuiPaneInfo().Caption('Central').Dockable(False).Floatable(False).Name('Central').CentrePane().Maximize() + info = ( + aui.AuiPaneInfo() + .Caption("Central") + .Dockable(False) + .Floatable(False) + .Name("Central") + .CentrePane() + .Maximize() + ) logger.debug("adding central pane to %s" % self.window) self.window._aui_manager.AddPane(state.central_pane.control, info) self.window._aui_manager.Update() # Show the dock panes. self._layout_state(state) - + def get_toolbars(self, task=None): if task is None: state = self.window._active_state @@ -97,13 +117,13 @@ info = self.window._aui_manager.GetPane(tool_bar_manager.id) toolbars.append(info) return toolbars - + def show_toolbars(self, toolbars): for info in toolbars: info.Show() self.window._aui_manager.Update() - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_layout(self): """ Returns a TaskLayout for the current state of the window. @@ -123,35 +143,37 @@ self._layout_state(self.window._active_state) self.window._aui_manager.Update() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _layout_state(self, state): """ Layout the dock panes in the specified TaskState using its TaskLayout. """ -# # Assign the window's corners to the appropriate dock areas. -# for name, corner in CORNER_MAP.iteritems(): -# area = getattr(state.layout, name + '_corner') -# self.control.setCorner(corner, AREA_MAP[area]) + # # Assign the window's corners to the appropriate dock areas. + # for name, corner in CORNER_MAP.iteritems(): + # area = getattr(state.layout, name + '_corner') + # self.control.setCorner(corner, AREA_MAP[area]) # Add all panes in the TaskLayout. self._main_window_layout.state = state self._main_window_layout.set_layout(state.layout, self.window) - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def __main_window_layout_default(self): return TaskWindowLayout() - #### Signal handlers ###################################################### + # Signal handlers -----------------------------------------------------# def _pane_close_requested(self, evt): pane = evt.GetPane() logger.debug("_pane_close_requested: pane=%s" % pane.name) for dock_pane in self.window.dock_panes: - logger.debug("_pane_close_requested: checking pane=%s" % dock_pane.pane_name) + logger.debug( + "_pane_close_requested: checking pane=%s" % dock_pane.pane_name + ) if dock_pane.pane_name == pane.name: logger.debug("_pane_close_requested: FOUND PANE!!!!!!") dock_pane.visible = False @@ -159,7 +181,7 @@ def _focus_changed_signal(self, old, new): if self.window.active_task: - panes = [ self.window.central_pane ] + self.window.dock_panes + panes = [self.window.central_pane] + self.window.dock_panes for pane in panes: if new and pane.control.isAncestorOf(new): pane.has_focus = True @@ -171,14 +193,14 @@ """ A MainWindowLayout for a TaskWindow. """ - #### 'TaskWindowLayout' interface ######################################### + # 'TaskWindowLayout' interface ----------------------------------------- - consumed = List - state = Instance('pyface.tasks.task_window.TaskState') + consumed = List() + state = Instance("pyface.tasks.task_window.TaskState") - ########################################################################### + # ------------------------------------------------------------------------ # 'MainWindowLayout' abstract interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_dock_widget(self, pane): """ Returns the control associated with a PaneItem. diff -Nru python-pyface-6.1.2/pyface/ui/wx/tests/bad_import.py python-pyface-7.4.0/pyface/ui/wx/tests/bad_import.py --- python-pyface-6.1.2/pyface/ui/wx/tests/bad_import.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tests/bad_import.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! # This is used to test what happens when there is an unrelated import error # when importing a toolkit object -raise ImportError('No module named nonexistent') +raise ImportError("No module named nonexistent") diff -Nru python-pyface-6.1.2/pyface/ui/wx/timer/do_later.py python-pyface-7.4.0/pyface/ui/wx/timer/do_later.py --- python-pyface-6.1.2/pyface/ui/wx/timer/do_later.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/timer/do_later.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,16 @@ -# Copyright (c) 2018, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! +# Thanks for using Enthought open source! """ DoLaterTimer class Provided for backward compatibility. """ -from pyface.timer.do_later import DoLaterTimer \ No newline at end of file +from pyface.timer.do_later import DoLaterTimer # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/ui/wx/timer/__init__.py python-pyface-7.4.0/pyface/ui/wx/timer/__init__.py --- python-pyface-6.1.2/pyface/ui/wx/timer/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/timer/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,9 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/wx/timer/timer.py python-pyface-7.4.0/pyface/ui/wx/timer/timer.py --- python-pyface-6.1.2/pyface/ui/wx/timer/timer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/timer/timer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,25 @@ -# Copyright (c) 2006-18, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! -# -# Author: Prabhu Ramachandran +# Thanks for using Enthought open source! """A `wx.Timer` subclass that invokes a specified callback periodically. """ import wx -from traits.api import Bool, Instance, Property +from traits.api import Instance from pyface.timer.i_timer import BaseTimer class CallbackTimer(wx.Timer): def __init__(self, timer): - super(CallbackTimer, self).__init__() + super().__init__() self.timer = timer def Notify(self): diff -Nru python-pyface-6.1.2/pyface/ui/wx/tree/tree.py python-pyface-7.4.0/pyface/ui/wx/tree/tree.py --- python-pyface-6.1.2/pyface/ui/wx/tree/tree.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/tree/tree.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,36 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A tree control with a model/ui architecture. """ -# Standard library imports. import logging import os +import warnings -# Major package imports. import wx -# Enthought library imports. -from traits.api import Any, Bool, Callable, Enum, Event, Instance -from traits.api import List, Property, Callable, Str, Trait, Tuple -# Local imports. +from traits.api import ( + Any, Bool, Callable, Enum, Event, Instance, Int, List, Property, Str, + Tuple, +) + + from pyface.filter import Filter from pyface.key_pressed_event import KeyPressedEvent from pyface.sorter import Sorter from pyface.tree.tree_model import TreeModel from pyface.ui.wx.gui import GUI from pyface.ui.wx.image_list import ImageList -from pyface.ui.wx.widget import Widget +from pyface.ui.wx.layout_widget import LayoutWidget from pyface.wx.drag_and_drop import PythonDropSource, PythonDropTarget @@ -48,46 +46,44 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, tree, parent, wxid, style): """ Creates a new tree. """ # Base class constructor. - wx.TreeCtrl.__init__(self, parent, wxid, style=style) + super().__init__(parent, wxid, style=style) # The tree that we are the toolkit-specific delegate for. self._tree = tree - return - - def __del__(self): + def Destroy(self): """ Destructor. """ # Stop listenting to the model! self._tree._remove_model_listeners(self._tree.model) - return + super().Destroy() -class Tree(Widget): +class Tree(LayoutWidget): """ A tree control with a model/ui architecture. """ # The default tree style. STYLE = wx.TR_EDIT_LABELS | wx.TR_HAS_BUTTONS | wx.CLIP_CHILDREN - #### 'Tree' interface ##################################################### + # 'Tree' interface ----------------------------------------------------- # The tree's filters (empty if no filtering is required). - filters = List(Filter) + filters = List(Instance(Filter)) # Mode for lines connecting tree nodes which emphasize hierarchy: # 'appearance' - only on when lines look good, # 'on' - always on, 'off' - always off # NOTE: on and off are ignored in favor of show_lines for now - lines_mode = Enum('appearance', 'on', 'off') + lines_mode = Enum("appearance", "on", "off") # The model that provides the data for the tree. model = Instance(TreeModel, ()) @@ -97,10 +93,10 @@ root = Property(Any) # The objects currently selected in the tree. - selection = List + selection = List(Any) # Selection mode. - selection_mode = Enum('single', 'extended') + selection_mode = Enum("single", "extended") # Should an image be shown for each node? show_images = Bool(True) @@ -114,55 +110,58 @@ # The tree's sorter (None if no sorting is required). sorter = Instance(Sorter) - #### Events #### + # Events ---- # A right-click occurred on the control (not a node!). - control_right_clicked = Event#(Point) + control_right_clicked = Event # (Point) # A key was pressed while the tree has focus. - key_pressed = Event(KeyPressedEvent) + key_pressed = Event(Instance(KeyPressedEvent)) # A node has been activated (ie. double-clicked). - node_activated = Event#(Any) + node_activated = Event # (Any) # A drag operation was started on a node. - node_begin_drag = Event#(Any) + node_begin_drag = Event # (Any) # A (non-leaf) node has been collapsed. - node_collapsed = Event#(Any) + node_collapsed = Event # (Any) # A (non-leaf) node has been expanded. - node_expanded = Event#(Any) + node_expanded = Event # (Any) # A left-click occurred on a node. # # Tuple(node, point). - node_left_clicked = Event#(Tuple) + node_left_clicked = Event # (Tuple) # A right-click occurred on a node. # # Tuple(node, point) - node_right_clicked = Event#(Tuple) + node_right_clicked = Event # (Tuple) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # A name to distinguish the tree for debugging! # # fixme: This turns out to be kinda useful... Should 'Widget' have a name # trait? - _name = Str('Anonymous tree') + _name = Str("Anonymous tree") # An optional callback to detect the end of a label edit. This is # useful because the callback will be invoked even if the node label was # not actually changed. - _label_edit_callback = Trait(None, Callable, None) + _label_edit_callback = Callable # Flag for allowing selection events to be ignored _ignore_selection_events = Bool(False) - ########################################################################### + # The size of the icons in the tree. + _image_size = Tuple(Int, Int) + + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, image_size=(16, 16), **traits): """ Creates a new tree. @@ -173,48 +172,57 @@ specifies the size of the images (if required) displayed in the tree. """ + create = traits.pop('create', True) # Base class constructors. - super(Tree, self).__init__(**traits) + super().__init__(parent=parent, _image_size=image_size, **traits) - # Get our wx Id. - wxid = wx.NewId() + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + def _create_control(self, parent): # Create the toolkit-specific control. - self.control = tree = _Tree(self, parent, wxid,style=self._get_style()) + self.control = tree = _Tree( + self, parent, wxid=wx.ID_ANY, style=self._get_style() + ) # Wire up the wx tree events. - wx.EVT_CHAR(tree, self._on_char) - wx.EVT_LEFT_DOWN(tree, self._on_left_down) + tree.Bind(wx.EVT_CHAR, self._on_char) + tree.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) # fixme: This is not technically correct as context menus etc should # appear on a right up (or right click). Unfortunately, if we # change this to 'EVT_RIGHT_UP' wx does not fire the event unless the # right mouse button is double clicked 8^() Sad, but true! - wx.EVT_RIGHT_DOWN(tree, self._on_right_down) + tree.Bind(wx.EVT_RIGHT_DOWN, self._on_right_down) # fixme: This is not technically correct as we would really like to use # 'EVT_TREE_ITEM_ACTIVATED'. Unfortunately, (in 2.6 at least), it # throws an exception when the 'Enter' key is pressed as the wx tree # item Id in the event seems to be invalid. It also seems to cause # any child frames that my be created in response to the event to # appear *behind* the parent window, which is, errrr, not great ;^) - wx.EVT_LEFT_DCLICK(tree, self._on_tree_item_activated) - #wx.EVT_TREE_ITEM_ACTIVATED(tree, wxid, self._on_tree_item_activated) - wx.EVT_TREE_ITEM_COLLAPSING(tree, wxid, self._on_tree_item_collapsing) - wx.EVT_TREE_ITEM_COLLAPSED(tree, wxid, self._on_tree_item_collapsed) - wx.EVT_TREE_ITEM_EXPANDING(tree, wxid, self._on_tree_item_expanding) - wx.EVT_TREE_ITEM_EXPANDED(tree, wxid, self._on_tree_item_expanded) - wx.EVT_TREE_BEGIN_LABEL_EDIT(tree, wxid,self._on_tree_begin_label_edit) - wx.EVT_TREE_END_LABEL_EDIT(tree, wxid, self._on_tree_end_label_edit) - wx.EVT_TREE_BEGIN_DRAG(tree, wxid, self._on_tree_begin_drag) - wx.EVT_TREE_SEL_CHANGED(tree, wxid, self._on_tree_sel_changed) - wx.EVT_TREE_DELETE_ITEM(tree, wxid, self._on_tree_delete_item) + tree.Bind(wx.EVT_LEFT_DCLICK, self._on_tree_item_activated) + tree.Bind(wx.EVT_TREE_ITEM_COLLAPSING, self._on_tree_item_collapsing) + tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self._on_tree_item_collapsed) + tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self._on_tree_item_expanding) + tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self._on_tree_item_expanded) + tree.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self._on_tree_begin_label_edit) + tree.Bind(wx.EVT_TREE_END_LABEL_EDIT, self._on_tree_end_label_edit) + tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self._on_tree_begin_drag) + tree.Bind(wx.EVT_TREE_SEL_CHANGED, self._on_tree_sel_changed) + tree.Bind(wx.EVT_TREE_DELETE_ITEM, self._on_tree_delete_item) # Enable the tree as a drag and drop target. - self.control.SetDropTarget(PythonDropTarget(self)) + tree.SetDropTarget(PythonDropTarget(self)) # The image list is a wxPython-ism that caches all images used in the # control. - self._image_list = ImageList(image_size[0], image_size[1]) + self._image_list = ImageList(*self._image_size) if self.show_images: tree.AssignImageList(self._image_list) @@ -227,14 +235,13 @@ # Listen for changes to the model. self._add_model_listeners(self.model) + return tree - return - - ########################################################################### + # ------------------------------------------------------------------------ # 'Tree' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Properties ########################################################### + # Properties ----------------------------------------------------------- def _get_root(self): """ Returns the root node of the tree. """ @@ -248,7 +255,7 @@ return - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def collapse(self, node): """ Collapses the specified node. """ @@ -257,8 +264,6 @@ if wxid is not None: self.control.Collapse(wxid) - return - def edit_label(self, node, callback=None): """ Edits the label of the specified node. @@ -274,8 +279,6 @@ self._label_edit_callback = callback self.control.EditLabel(wxid) - return - def expand(self, node): """ Expands the specified node. """ @@ -283,8 +286,6 @@ if wxid is not None: self.control.Expand(wxid) - return - def expand_all(self): """ Expands every node in the tree. """ @@ -295,8 +296,6 @@ for child in self._get_children(self.root): self._expand_item(self._get_wxid(child)) - return - def get_parent(self, node): """ Returns the parent of a node. @@ -313,7 +312,7 @@ # The item data is a tuple. The first element indicates whether or # not we have already populated the item with its children. The # second element is the actual item data. - populated, parent = self.control.GetPyData(pid) + populated, parent = self.control.GetItemData(pid) else: parent = None @@ -361,7 +360,7 @@ if pid is not None: # Delete all of the node's children and re-add them. self.control.DeleteChildren(pid) - self.control.SetPyData(pid, (False, node)) + self.control.SetItemData(pid, (False, node)) # Does the node have any children? has_children = self._has_children(node) @@ -382,8 +381,6 @@ self.control.Expand(pid) - return - def select(self, node): """ Selects the specified node. """ @@ -391,11 +388,9 @@ if wxid is not None: self.control.SelectItem(wxid) - return - def set_selection(self, list): """ Selects the specified list of nodes. """ - logger.debug('Setting selection to [%s] within Tree [%s]', list, self) + logger.debug("Setting selection to [%s] within Tree [%s]", list, self) # Update the control to reflect the target list by unselecting # everything and then selecting each item in the list. During this @@ -406,16 +401,16 @@ try: self.select(node) except: - logger.exception('Unable to select node [%s]', node) + logger.exception("Unable to select node [%s]", node) self._ignore_selection_events = False # Update our selection to reflect the final selection state. self.selection = self._get_selection() - ########################################################################### + # ------------------------------------------------------------------------ # 'PythonDropTarget' interface. - ########################################################################### + # ------------------------------------------------------------------------ def on_drag_over(self, x, y, obj, default_drag_result): """ Called when a node is dragged over the tree. """ @@ -442,9 +437,9 @@ return default_drag_result - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_wxid(self, node): """ Returns the wxid for the specified node. @@ -468,8 +463,6 @@ self._node_to_id_map[key] = wxid - return - def _remove_wxid(self, node): """ Removes the wxid for the specified node. """ @@ -483,9 +476,7 @@ except KeyError: # fixme: No, really, this is a serious one... How do we get in this # situation. It came up when using the canvas stuff... - logger.warn('removing node: %s' % str(node)) - - return + logger.warning("removing node: %s" % str(node)) def _get_style(self): """ Returns the wx style flags for creating the tree control. """ @@ -496,7 +487,7 @@ # Turn lines off for appearance on *nix. # ...for now, show_lines determines if lines are on or off, but # eventually lines_mode may eliminate the need for show_lines - if self.lines_mode == 'appearance' and os.name == 'posix': + if self.lines_mode == "appearance" and os.name == "posix": self.show_lines = False if not self.show_lines: @@ -508,7 +499,7 @@ # image on non-leaf nodes at the root level 8^() style = style | wx.TR_HIDE_ROOT | wx.TR_LINES_AT_ROOT - if self.selection_mode != 'single': + if self.selection_mode != "single": style = style | wx.TR_MULTIPLE | wx.TR_EXTENDED return style @@ -517,44 +508,57 @@ """ Adds listeners for model changes. """ # Listen for changes to the model. - model.on_trait_change(self._on_root_changed, 'root') - model.on_trait_change(self._on_nodes_changed, 'nodes_changed') - model.on_trait_change(self._on_nodes_inserted, 'nodes_inserted') - model.on_trait_change(self._on_nodes_removed, 'nodes_removed') - model.on_trait_change(self._on_nodes_replaced, 'nodes_replaced') - model.on_trait_change(self._on_structure_changed, 'structure_changed') - - return + model.observe(self._on_root_changed, "root") + model.observe(self._on_nodes_changed, "nodes_changed") + model.observe(self._on_nodes_inserted, "nodes_inserted") + model.observe(self._on_nodes_removed, "nodes_removed") + model.observe(self._on_nodes_replaced, "nodes_replaced") + model.observe(self._on_structure_changed, "structure_changed") def _remove_model_listeners(self, model): """ Removes listeners for model changes. """ # Unhook the model event listeners. - model.on_trait_change( - self._on_root_changed, 'root', remove=True - ) + model.observe(self._on_root_changed, "root", remove=True) - model.on_trait_change( - self._on_nodes_changed, 'nodes_changed', remove=True - ) + model.observe(self._on_nodes_changed, "nodes_changed", remove=True) - model.on_trait_change( - self._on_nodes_inserted, 'nodes_inserted', remove=True - ) + model.observe(self._on_nodes_inserted, "nodes_inserted", remove=True) - model.on_trait_change( - self._on_nodes_removed, 'nodes_removed', remove=True - ) + model.observe(self._on_nodes_removed, "nodes_removed", remove=True) - model.on_trait_change( - self._on_nodes_replaced, 'nodes_replaced', remove=True - ) + model.observe(self._on_nodes_replaced, "nodes_replaced", remove=True) - model.on_trait_change( - self._on_structure_changed, 'structure_changed', remove=True + model.observe( + self._on_structure_changed, "structure_changed", remove=True ) - return + # unwire the wx tree events. + tree = self.control + tree.Unbind(wx.EVT_CHAR) + tree.Unbind(wx.EVT_LEFT_DOWN) + # fixme: This is not technically correct as context menus etc should + # appear on a right up (or right click). Unfortunately, if we + # change this to 'EVT_RIGHT_UP' wx does not fire the event unless the + # right mouse button is double clicked 8^() Sad, but true! + tree.Unbind(wx.EVT_RIGHT_DOWN) + # fixme: This is not technically correct as we would really like to use + # 'EVT_TREE_ITEM_ACTIVATED'. Unfortunately, (in 2.6 at least), it + # throws an exception when the 'Enter' key is pressed as the wx tree + # item Id in the event seems to be invalid. It also seems to cause + # any child frames that my be created in response to the event to + # appear *behind* the parent window, which is, errrr, not great ;^) + tree.Unbind(wx.EVT_LEFT_DCLICK) + tree.Unbind(wx.EVT_TREE_ITEM_ACTIVATED) + tree.Unbind(wx.EVT_TREE_ITEM_COLLAPSING) + tree.Unbind(wx.EVT_TREE_ITEM_COLLAPSED) + tree.Unbind(wx.EVT_TREE_ITEM_EXPANDING) + tree.Unbind(wx.EVT_TREE_ITEM_EXPANDED) + tree.Unbind(wx.EVT_TREE_BEGIN_LABEL_EDIT) + tree.Unbind(wx.EVT_TREE_END_LABEL_EDIT) + tree.Unbind(wx.EVT_TREE_BEGIN_DRAG) + tree.Unbind(wx.EVT_TREE_SEL_CHANGED) + tree.Unbind(wx.EVT_TREE_DELETE_ITEM) def _add_root_node(self, node): """ Adds the root node. """ @@ -584,7 +588,7 @@ # element is the actual item data (which in our case is an arbitrary # Python object provided by the tree model). if self.show_root: - self.control.SetPyData(wxid, (not self.show_root, node)) + self.control.SetItemData(wxid, (not self.show_root, node)) # Make sure that we can find the node's Id. self._set_wxid(node, wxid) @@ -593,8 +597,6 @@ if self.show_root: self.control.Expand(wxid) - return - def _add_node(self, pid, node): """ Adds 'node' as a child of the node identified by 'pid'. @@ -620,13 +622,11 @@ # we have already populated the item with its children. The second # element is the actual item data (which in our case is an arbitrary # Python object provided by the tree model). - self.control.SetPyData(wxid, (False, node)) + self.control.SetItemData(wxid, (False, node)) # Make sure that we can find the node's Id. self._set_wxid(node, wxid) - return - def _insert_node(self, pid, node, index): """ Inserts 'node' as a child of the node identified by 'pid'. @@ -639,7 +639,7 @@ text = self._get_text(node) # Add the node. - wxid = self.control.InsertItemBefore( + wxid = self.control.Sizer.InsertBefore( pid, index, text, image_index, image_index ) @@ -654,13 +654,11 @@ # we have already populated the item with its children. The second # element is the actual item data (which in our case is an arbitrary # Python object provided by the tree model). - self.control.SetPyData(wxid, (False, node)) + self.control.SetItemData(wxid, (False, node)) # Make sure that we can find the node's Id. self._set_wxid(node, wxid) - return - def _remove_node(self, wxid, node): """ Removes a node from the tree. """ @@ -669,9 +667,7 @@ # Remove the reference to the item's data. self._remove_wxid(node) - self.control.SetPyData(wxid, None) - - return + self.control.SetItemData(wxid, None) def _update_node(self, wxid, node): """ Updates the image and text of the specified node. """ @@ -685,8 +681,6 @@ text = self._get_text(node) self.control.SetItemText(wxid, text) - return - def _has_children(self, node): """ Returns True if a node has children. """ @@ -756,7 +750,7 @@ text = self.model.get_text(node) if text is None: - text = '' + text = "" return text @@ -778,16 +772,18 @@ if (wxid is None) or (not wxid.IsOk()): wxid, flags = self.control.HitTest(point) - # Warning: On GTK we have to check the flags before we call 'GetPyData' + # Warning: On GTK we have to check the flags before we call 'GetItemData' # because if we call it when the hit test returns 'nowhere' it will # barf (on Windows it simply returns 'None' 8^() if flags & wx.TREE_HITTEST_NOWHERE: data = None - elif flags & wx.TREE_HITTEST_ONITEMICON \ - or flags & wx.TREE_HITTEST_ONITEMLABEL: + elif ( + flags & wx.TREE_HITTEST_ONITEMICON + or flags & wx.TREE_HITTEST_ONITEMLABEL + ): - data = self.control.GetPyData(wxid) + data = self.control.GetItemData(wxid) # fixme: Not sure why 'TREE_HITTEST_NOWHERE' doesn't catch everything! else: @@ -800,7 +796,7 @@ selection = [] for wxid in self.control.GetSelections(): - data = self.control.GetPyData(wxid) + data = self.control.GetItemData(wxid) if data is not None: populated, node = data selection.append(self.model.get_selection_value(node)) @@ -819,11 +815,11 @@ return - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_root_changed(self, root): + def _on_root_changed(self, event): """ Called when the root of the model has changed. """ - + root = event.new # Delete everything... if self.control is not None: self.control.DeleteAllItems() @@ -834,26 +830,21 @@ if root is not None: self._add_root_node(root) - return - def _on_nodes_changed(self, event): """ Called when nodes have been changed. """ - - self._update_node(self._get_wxid(event.node), event.node) - - for child in event.children: + node_event = event.new + self._update_node(self._get_wxid(node_event.node), node_event.node) + for child in node_event.children: cid = self._get_wxid(child) if cid is not None: self._update_node(cid, child) - return - def _on_nodes_inserted(self, event): """ Called when nodes have been inserted. """ - - parent = event.node - children = event.children - index = event.index + node_event = event.new + parent = node_event.node + children = node_event.children + index = node_event.index # Has the node actually appeared in the tree yet? pid = self._get_wxid(parent) @@ -862,7 +853,7 @@ # not we have already populated the item with its children. The # second element is the actual item data. if self.show_root or parent is not self.root: - populated, node = self.control.GetPyData(pid) + populated, node = self.control.GetItemData(pid) else: populated = True @@ -885,7 +876,7 @@ # The element is now populated! if self.show_root or parent is not self.root: - self.control.SetPyData(pid, (True, parent)) + self.control.SetItemData(pid, (True, parent)) # Does the node have any children now? has_children = self.control.GetChildrenCount(pid) > 0 @@ -895,18 +886,15 @@ if not self.is_expanded(parent): self.expand(parent) - return - def _on_nodes_removed(self, event): """ Called when nodes have been removed. """ - - parent = event.node - children = event.children + node_event = event.new + parent = node_event.node # Has the node actually appeared in the tree yet? pid = self._get_wxid(parent) if pid is not None: - for child in event.children: + for child in node_event.children: cid = self._get_wxid(child) if cid is not None: self.control.Delete(cid) @@ -915,12 +903,11 @@ has_children = self.control.GetChildrenCount(pid) > 0 self.control.SetItemHasChildren(pid, has_children) - return - def _on_nodes_replaced(self, event): """ Called when nodes have been replaced. """ - - for old_child, new_child in zip(event.old_children, event.children): + node_event = event.new + old_new_children = zip(node_event.old_children, node_event.children) + for old_child, new_child in old_new_children: cid = self._get_wxid(old_child) if cid is not None: # Remove listeners from the old node. @@ -939,7 +926,7 @@ # children. The second element is the actual item data (which # in our case is an arbitrary Python object provided by the # tree model). - self.control.SetPyData(cid, (False, new_child)) + self.control.SetItemData(cid, (False, new_child)) # Remove the old node from the node to Id map. self._remove_wxid(old_child) @@ -957,31 +944,28 @@ # Update the tree's selection (in case the old node that was replaced # was selected, the selection should now include the new node). self.selection = self._get_selection() - return def _on_structure_changed(self, event): """ Called when the structure of a node has changed drastically. """ - - self.refresh(event.node) + node_event = event.new + self.refresh(node_event.node) return - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_char(self, event): """ Called when a key is pressed when the tree has focus. """ self.key_pressed = KeyPressedEvent( - alt_down = event.m_altDown == 1, - control_down = event.m_controlDown == 1, - shift_down = event.m_shiftDown == 1, - key_code = event.m_keyCode + alt_down=event.altDown, + control_down=event.controlDown, + shift_down=event.shiftDown, + key_code=event.KeyCode, ) event.Skip() - return - def _on_left_down(self, event): """ Called when the left mouse button is clicked on the tree. """ @@ -1002,8 +986,6 @@ # Give other event handlers a chance. event.Skip() - return - def _on_right_down(self, event): """ Called when the right mouse button is clicked on the tree. """ @@ -1023,16 +1005,14 @@ # Give other event handlers a chance. event.Skip() - return - def _on_tree_item_activated(self, event): """ Called when a tree item is activated (i.e., double clicked). """ # fixme: See the comment where the events are wired up for more # information. -## # Which item was activated? -## wxid = event.GetItem() + ## # Which item was activated? + ## wxid = event.GetItem() # Which item was activated. point = event.GetPosition() @@ -1041,13 +1021,11 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Trait event notiification. self.node_activated = node - return - def _on_tree_item_collapsing(self, event): """ Called when a tree item is about to collapse. """ @@ -1057,14 +1035,12 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Give the model a chance to veto the collapse. if not self.model.is_collapsible(node): event.Veto() - return - def _on_tree_item_collapsed(self, event): """ Called when a tree item has been collapsed. """ @@ -1074,7 +1050,7 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Make sure that the item's 'closed' icon is displayed etc. self._update_node(wxid, node) @@ -1082,8 +1058,6 @@ # Trait event notification. self.node_collapsed = node - return - def _on_tree_item_expanding(self, event): """ Called when a tree item is about to expand. """ @@ -1093,7 +1067,7 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Give the model a chance to veto the expansion. if self.model.is_expandable(node): @@ -1104,13 +1078,11 @@ self._add_node(wxid, child) # The element is now populated! - self.control.SetPyData(wxid, (True, node)) + self.control.SetItemData(wxid, (True, node)) else: event.Veto() - return - def _on_tree_item_expanded(self, event): """ Called when a tree item has been expanded. """ @@ -1120,7 +1092,7 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Make sure that the node's 'open' icon is displayed etc. self._update_node(wxid, node) @@ -1128,8 +1100,6 @@ # Trait event notification. self.node_expanded = node - return - def _on_tree_begin_label_edit(self, event): """ Called when the user has started editing an item's label. """ @@ -1138,14 +1108,12 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Give the model a chance to veto the edit. if not self.model.is_editable(node): event.Veto() - return - def _on_tree_end_label_edit(self, event): """ Called when the user has finished editing am item's label. """ @@ -1154,15 +1122,19 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, node = self.control.GetPyData(wxid) + populated, node = self.control.GetItemData(wxid) # Give the model a chance to veto the edit. label = event.GetLabel() # Making sure the new label is not an empty string - if label is not None and len(label) > 0 and \ - self.model.can_set_text(node, label): + if ( + label is not None + and len(label) > 0 + and self.model.can_set_text(node, label) + ): + def end_label_edit(): """ Called to complete the label edit. """ @@ -1191,19 +1163,17 @@ if self._label_edit_callback is not None: self._label_edit_callback(self, node, label) - return - def _on_tree_begin_drag(self, event): """ Called when a drag operation is starting on a tree item. """ # Get the node, its id and the point where the event occurred. data, wxid, flags, point = self._unpack_event(event, event.GetItem()) - if point == (0,0): + if point == (0, 0): # Apply workaround for GTK. point = self.point_left_clicked wxid, flags = self.HitTest(point) - data = self.control.GetPyData(wxid) + data = self.control.GetItemData(wxid) if data is not None: populated, node = data @@ -1218,6 +1188,7 @@ # be dragged and our drag and drop mechanism should allow # extendable ways to extract the actual data. from pyface.wx.drag_and_drop import clipboard + clipboard.node = [node] # Make sure that the tree selection is updated before we start @@ -1242,9 +1213,8 @@ """ Callback invoked when a drag/drop operation has completed. """ from pyface.wx.drag_and_drop import clipboard - clipboard.node = None - return + clipboard.node = None def _on_tree_sel_changed(self, event): """ Called when the selection is changed. """ @@ -1256,22 +1226,27 @@ # Trait notification. self.selection = self._get_selection() - return - def _on_tree_delete_item(self, event): """ Called when a tree item is being been deleted. """ # Which item is being deleted? wxid = event.GetItem() - # Check if GetPyData() returned a valid to tuple to unpack + # Check if GetItemData() returned a valid to tuple to unpack # ...if so, remove the node from the tree, otherwise just return # # fixme: Whoever addeed this code (and the comment above) didn't say # when this was occurring. This is method is called in response to a wx # event to delete an item and hence the item data should never be None # surely?!? Was it happening just on one platform?!? - data = self.control.GetPyData(wxid) + if self.control is None: + return + try: + data = self.control.GetItemData(wxid) + except Exception: + # most likely control is in the process of being destroyed + data = None + if data is not None: # The item data is a tuple. The first element indicates whether or # not we have already populated the item with its children. The @@ -1280,7 +1255,3 @@ # Remove the node. self._remove_node(wxid, node) - - return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/util/image_helpers.py python-pyface-7.4.0/pyface/ui/wx/util/image_helpers.py --- python-pyface-6.1.2/pyface/ui/wx/util/image_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/util/image_helpers.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,174 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Helper functions for working with images + +This module provides helper functions for converting between numpy arrays +and Qt wx.Images, as well as between the various image types in a standardized +way. +""" + +from enum import IntEnum + +import wx + + +class ScaleMode(IntEnum): + fast = wx.IMAGE_QUALITY_NORMAL + smooth = wx.IMAGE_QUALITY_HIGH + + +class AspectRatio(IntEnum): + ignore = 0 + keep_constrain = 1 + keep_expand = 2 + + +def image_to_bitmap(image): + """ Convert a wx.Image to a wx.Bitmap. + + Parameters + ---------- + image : wx.Image + The wx.Image to convert. + + Return + ------ + bitmap : wx.Bitmap + The corresponding wx.Bitmap. + """ + return image.ConvertToBitmap() + + +def bitmap_to_image(bitmap): + """ Convert a wx.Bitmap to a wx.Image. + + Parameters + ---------- + bitmap : wx.Bitmap + The wx.Bitmap to convert. + + Return + ------ + image : wx.Image + The corresponding wx.Image. + """ + return bitmap.ConvertToImage() + + +def bitmap_to_icon(bitmap): + """ Convert a wx.Bitmap to a wx.Icon. + + Parameters + ---------- + bitmap : wx.Bitmap + The wx.Bitmap to convert. + + Return + ------ + icon : wx.Icon + The corresponding wx.Icon. + """ + return wx.Icon(bitmap) + + +def resize_image(image, size, aspect_ratio=AspectRatio.ignore, + mode=ScaleMode.fast): + """ Resize a toolkit image to the given size. """ + image_size = image.GetSize() + width, height = _get_size_for_aspect_ratio(image_size, size, aspect_ratio) + return image.Scale(width, height, mode) + + +def resize_bitmap(bitmap, size, aspect_ratio=AspectRatio.ignore, + mode=ScaleMode.fast): + """ Resize a toolkit bitmap to the given size. """ + image = bitmap_to_image(bitmap) + image = resize_image(image, size, aspect_ratio, mode) + return image_to_bitmap(image) + + +def image_to_array(image): + """ Convert a wx.Image to a numpy array. + + This copies the data returned from wx. + + Parameters + ---------- + image : wx.Image + The wx.Image that we want to extract the values from. The format must + be either RGB32 or ARGB32. + + Return + ------ + array : ndarray + An N x M x 4 array of unsigned 8-bit ints as RGBA values. + """ + import numpy as np + + width, height = image.GetSize() + rgb_data = np.array(image.GetData(), dtype='uint8').reshape(width, height, 3) + if image.HasAlpha(): + alpha = np.array(image.GetAlpha(), dtype='uint8').reshape(width, height) + else: + alpha = np.full((width, height), 0xff, dtype='uint8') + array = np.empty(shape=(width, height, 4), dtype='uint8') + array[:, :, :3] = rgb_data + array[:, :, 3] = alpha + return array + + +def array_to_image(array): + """ Convert a numpy array to a wx.Image. + + This copies the data before passing it to wx. + + Parameters + ---------- + array : ndarray + An N x M x {3, 4} array of unsigned 8-bit ints. The image + format is assumed to be RGB or RGBA, based on the shape. + + Return + ------ + image : wx.Image + The wx.Image created from the data. + """ + if array.ndim != 3: + raise ValueError("Array must be either RGB or RGBA values.") + + height, width, channels = array.shape + + if channels == 3: + image = wx.Image(width, height, array.tobytes()) + elif channels == 4: + image = wx.Image( + width, + height, + array[..., :3].tobytes(), + array[..., 3].tobytes(), + ) + else: + raise ValueError("Array must be either RGB or RGBA values.") + + return image + + +def _get_size_for_aspect_ratio(image_size, size, aspect_ratio): + width, height = size + image_width, image_height = image_size + if aspect_ratio != AspectRatio.ignore: + if aspect_ratio == AspectRatio.keep_constrain: + scale = min(width/image_width, height/image_height) + else: + scale = max(width/image_width, height/image_height) + width = int(round(scale * image_width)) + height = int(round(scale * image_height)) + return (width, height) diff -Nru python-pyface-6.1.2/pyface/ui/wx/util/tests/test_image_helpers.py python-pyface-7.4.0/pyface/ui/wx/util/tests/test_image_helpers.py --- python-pyface-6.1.2/pyface/ui/wx/util/tests/test_image_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/util/tests/test_image_helpers.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,202 @@ +# Copyright (c) 2005-2022, Enthought Inc. +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! + +import unittest + +import wx + +from traits.testing.optional_dependencies import numpy as np, requires_numpy +from ..image_helpers import ( + bitmap_to_icon, bitmap_to_image, image_to_array, image_to_bitmap, + array_to_image, AspectRatio, ScaleMode, resize_image, resize_bitmap, +) + + +class TestImageHelpers(unittest.TestCase): + + def test_image_to_bitmap(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + + wxbitmap = image_to_bitmap(wximage) + + self.assertIsInstance(wxbitmap, wx.Bitmap) + + def test_bitmap_to_image(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wxbitmap = wximage.ConvertToBitmap() + + wximage = bitmap_to_image(wxbitmap) + + self.assertIsInstance(wximage, wx.Image) + + def test_bitmap_to_icon(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wxbitmap = wximage.ConvertToBitmap() + + wximage = bitmap_to_icon(wxbitmap) + + self.assertIsInstance(wximage, wx.Icon) + + def test_resize_image(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + + wximage = resize_image(wximage, (128, 128)) + + self.assertIsInstance(wximage, wx.Image) + self.assertEqual(wximage.GetWidth(), 128) + self.assertEqual(wximage.GetHeight(), 128) + + def test_resize_image_smooth(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + + wximage = resize_image(wximage, (128, 128), mode=ScaleMode.smooth) + + self.assertIsInstance(wximage, wx.Image) + self.assertEqual(wximage.GetWidth(), 128) + self.assertEqual(wximage.GetHeight(), 128) + + def test_resize_image_constrain(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + + wximage = resize_image(wximage, (128, 128), AspectRatio.keep_constrain) + + self.assertIsInstance(wximage, wx.Image) + self.assertEqual(wximage.GetWidth(), 64) + self.assertEqual(wximage.GetHeight(), 128) + + def test_resize_image_expand(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + + wximage = resize_image(wximage, (128, 128), AspectRatio.keep_expand) + + self.assertIsInstance(wximage, wx.Image) + self.assertEqual(wximage.GetWidth(), 128) + self.assertEqual(wximage.GetHeight(), 256) + + def test_resize_bitmap(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wxbitmap = wximage.ConvertToBitmap() + + wxbitmap = resize_bitmap(wxbitmap, (128, 128)) + + self.assertIsInstance(wxbitmap, wx.Bitmap) + self.assertEqual(wxbitmap.GetWidth(), 128) + self.assertEqual(wxbitmap.GetHeight(), 128) + + def test_resize_bitmap_smooth(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wxbitmap = wximage.ConvertToBitmap() + + wxbitmap = resize_bitmap(wxbitmap, (128, 128), mode=ScaleMode.smooth) + + self.assertIsInstance(wxbitmap, wx.Bitmap) + self.assertEqual(wxbitmap.GetWidth(), 128) + self.assertEqual(wxbitmap.GetHeight(), 128) + + def test_resize_bitmap_constrain(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wxbitmap = wximage.ConvertToBitmap() + + wxbitmap = resize_bitmap(wxbitmap, (128, 128), AspectRatio.keep_constrain) + + self.assertIsInstance(wxbitmap, wx.Bitmap) + self.assertEqual(wxbitmap.GetWidth(), 64) + self.assertEqual(wxbitmap.GetHeight(), 128) + + def test_resize_bitmap_expand(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wxbitmap = wximage.ConvertToBitmap() + + wxbitmap = resize_bitmap(wxbitmap, (128, 128), AspectRatio.keep_expand) + + self.assertIsInstance(wxbitmap, wx.Bitmap) + self.assertEqual(wxbitmap.GetWidth(), 128) + self.assertEqual(wxbitmap.GetHeight(), 256) + + +@requires_numpy +class TestArrayImageHelpers(unittest.TestCase): + + def test_image_to_array_rgb(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + + array = image_to_array(wximage) + + self.assertEqual(array.shape, (32, 64, 4)) + self.assertEqual(array.dtype, np.dtype('uint8')) + self.assertTrue(np.all(array[:, :, 3] == 0xff)) + self.assertTrue(np.all(array[:, :, 0] == 0x44)) + self.assertTrue(np.all(array[:, :, 1] == 0x88)) + self.assertTrue(np.all(array[:, :, 2] == 0xcc)) + + def test_image_to_array_rgba(self): + wximage = wx.Image(32, 64) + wximage.SetRGB(wx.Rect(0, 0, 32, 64), 0x44, 0x88, 0xcc) + wximage.InitAlpha() + wximage.SetAlpha(np.full((32*64,), 0xee, dtype='uint8')) + + array = image_to_array(wximage) + + self.assertEqual(array.shape, (32, 64, 4)) + self.assertEqual(array.dtype, np.dtype('uint8')) + self.assertTrue(np.all(array[:, :, 0] == 0x44)) + self.assertTrue(np.all(array[:, :, 1] == 0x88)) + self.assertTrue(np.all(array[:, :, 2] == 0xcc)) + self.assertTrue(np.all(array[:, :, 3] == 0xee)) + + def test_array_to_image_rgb(self): + array = np.empty((64, 32, 3), dtype='uint8') + array[:, :, 0] = 0x44 + array[:, :, 1] = 0x88 + array[:, :, 2] = 0xcc + + wximage = array_to_image(array) + + self.assertEqual(wximage.GetWidth(), 32) + self.assertEqual(wximage.GetHeight(), 64) + self.assertFalse(wximage.HasAlpha()) + + def test_array_to_image_rgba(self): + array = np.empty((64, 32, 4), dtype='uint8') + array[:, :, 0] = 0x44 + array[:, :, 1] = 0x88 + array[:, :, 2] = 0xcc + array[:, :, 3] = 0xee + + wximage = array_to_image(array) + + self.assertEqual(wximage.GetWidth(), 32) + self.assertEqual(wximage.GetHeight(), 64) + self.assertTrue(wximage.HasAlpha()) + + def test_array_to_image_bad_channels(self): + array = np.empty((64, 32, 2), dtype='uint8') + array[:, :, 0] = 0x44 + array[:, :, 1] = 0x88 + + with self.assertRaises(ValueError): + array_to_image(array) + + def test_array_to_image_bad_ndim(self): + array = np.full((64, 32), 0x44, dtype='uint8') + + with self.assertRaises(ValueError): + array_to_image(array) diff -Nru python-pyface-6.1.2/pyface/ui/wx/viewer/table_viewer.py python-pyface-7.4.0/pyface/ui/wx/viewer/table_viewer.py --- python-pyface-6.1.2/pyface/ui/wx/viewer/table_viewer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/viewer/table_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,32 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A viewer for tabular data. """ +import warnings -# Major package imports. import wx -# Enthought library imports. -from traits.api import Color, Event, Instance, Trait +from traits.api import Event, Instance, Int, Tuple -# Local imports. from pyface.ui.wx.image_list import ImageList +from pyface.ui_traits import TraitsUIColor as Color from pyface.viewer.content_viewer import ContentViewer from pyface.viewer.table_column_provider import TableColumnProvider from pyface.viewer.table_content_provider import TableContentProvider from pyface.viewer.table_label_provider import TableLabelProvider +version_4_0 = (wx.VERSION < (4, 1)) + + class TableViewer(ContentViewer): """ A viewer for tabular data. """ @@ -36,25 +35,27 @@ # The label provider provides, err, the labels for the items in the table # (a label can have text and/or an image). - label_provider = Instance(TableLabelProvider, factory = TableLabelProvider) + label_provider = Instance(TableLabelProvider, ()) # The column provider provides information about the columns in the table # (column headers, width etc). - column_provider=Trait(TableColumnProvider(),Instance(TableColumnProvider)) + column_provider = Instance(TableColumnProvider, ()) # The colours used to render odd and even numbered rows. even_row_background = Color("white") - odd_row_background = Color((245, 245, 255)) + odd_row_background = Color((245, 245, 255)) # A row has been selected. - row_selected = Event + row_selected = Event() # A row has been activated. - row_activated = Event + row_activated = Event() # A drag operation was started on a node. - row_begin_drag = Event + row_begin_drag = Event() + # The size of the icons in the table. + _image_size = Tuple(Int, Int) def __init__(self, parent, image_size=(16, 16), **traits): """ Creates a new table viewer. @@ -65,47 +66,53 @@ specifies the size of the images (if any) displayed in the table. """ + create = traits.pop('create', True) - # Base-class constructor. - super(TableViewer, self).__init__(**traits) + # Base class constructors. + super().__init__(parent=parent, _image_size=image_size, **traits) - # Create the toolkit-specific control. - self.control = table = _Table(parent, image_size, self) + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) - # Get our actual id. - wxid = table.GetId() + def _create_control(self, parent): + # Create the toolkit-specific control. + self.control = table = _Table(parent, self._image_size, self) # Table events. - wx.EVT_LIST_ITEM_SELECTED(table, wxid, self._on_item_selected) - wx.EVT_LIST_ITEM_ACTIVATED(table, wxid, self._on_item_activated) - wx.EVT_LIST_BEGIN_DRAG(table, wxid, self._on_list_begin_drag) - wx.EVT_LIST_BEGIN_RDRAG(table, wxid, self._on_list_begin_rdrag) + table.Bind(wx.EVT_LIST_ITEM_SELECTED, self._on_item_selected) + table.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_item_activated) + table.Bind(wx.EVT_LIST_BEGIN_DRAG, self._on_list_begin_drag) + table.Bind(wx.EVT_LIST_BEGIN_RDRAG, self._on_list_begin_rdrag) - wx.EVT_LIST_BEGIN_LABEL_EDIT( - table, wxid, self._on_list_begin_label_edit + table.Bind( + wx.EVT_LIST_BEGIN_LABEL_EDIT, self._on_list_begin_label_edit ) - wx.EVT_LIST_END_LABEL_EDIT( - table, wxid, self._on_list_end_label_edit - ) + table.Bind(wx.EVT_LIST_END_LABEL_EDIT, self._on_list_end_label_edit) # fixme: Bug[732104] indicates that this event does not get fired # in a virtual list control (it *does* get fired in a regular list # control 8^(). - wx.EVT_LIST_ITEM_DESELECTED(table, wxid, self._on_item_deselected) + table.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._on_item_deselected) # Create the widget! self._create_widget(parent) # We use a dynamic handler instead of a static handler here, as we # don't want to react if the input is set in the constructor. - self.on_trait_change(self._on_input_changed, 'input') + self.observe(self._on_input_changed, "input") - return + return table - ########################################################################### + # ------------------------------------------------------------------------ # 'TableViewer' interface. - ########################################################################### + # ------------------------------------------------------------------------ def select_row(self, row): """ Select the specified row. """ @@ -127,30 +134,30 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_input_changed(self, obj, trait_name, old, new): + def _on_input_changed(self, event): """ Called when the input is changed. """ # Update the table contents. self._update_contents() - if old is None: + if event.old is None: self._update_column_widths() return - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_item_selected(self, event): """ Called when an item in the list is selected. """ # Get the index of the row that was selected (nice wx interface huh?!). - row = event.m_itemIndex + row = event.Index # Trait event notification. self.row_selected = row @@ -165,55 +172,45 @@ # Trait event notification. self.row_selected = -1 - return - def _on_item_activated(self, event): """ Called when an item in the list is activated. """ # Get the index of the row that was activated (nice wx interface!). - row = event.m_itemIndex + row = event.Index # Trait event notification. self.row_activated = row - return - def _on_list_begin_drag(self, event=None, is_rdrag=False): """ Called when a drag operation is starting on a list item. """ # Trait notification. self.row_begin_drag = event.GetIndex() - return - def _on_list_begin_rdrag(self, event=None): """ Called when a drag operation is starting on a list item. """ self._on_list_begin_drag(event, True) - return - def _on_list_begin_label_edit(self, event=None): """ Called when a label edit is started. """ event.Veto() - return - def _on_list_end_label_edit(self, event=None): """ Called when a label edit is completed. """ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ FORMAT_MAP = { - 'left' : wx.LIST_FORMAT_LEFT, - 'right' : wx.LIST_FORMAT_RIGHT, - 'center' : wx.LIST_FORMAT_CENTRE, - 'centre' : wx.LIST_FORMAT_CENTRE + "left": wx.LIST_FORMAT_LEFT, + "right": wx.LIST_FORMAT_RIGHT, + "center": wx.LIST_FORMAT_CENTRE, + "centre": wx.LIST_FORMAT_CENTRE, } def _create_widget(self, parent): @@ -232,21 +229,19 @@ alignment = self.column_provider.get_alignment(self, index) info.m_format = self.FORMAT_MAP.get(alignment, wx.LIST_FORMAT_LEFT) - self.control.InsertColumnInfo(index, info) + self.control.InsertColumn(index, info) # # Update the table contents and the column widths. self._update_contents() self._update_column_widths() - return - def _update_contents(self): """ Updates the table content. """ self._elements = [] if self.input is not None: # Filtering... - for element in self.content_provider.get_elements(self.input): + for element in self.content_provider.get_elements(self.input): for filter in self.filters: if not filter.select(self, self.input, element): break @@ -261,8 +256,6 @@ # Setting this causes a refresh! self.control.SetItemCount(len(self._elements)) - return - def _update_column_widths(self): """ Updates the column widths. """ @@ -275,8 +268,6 @@ self.control.SetColumnWidth(column, width) - return - def _get_column_width(self, column): """ Return an appropriate width for the specified column. """ @@ -295,13 +286,20 @@ return width -class _Table(wx.ListCtrl): +class _Table(wx.ListCtrl): # (ULC.UltimateListCtrl):# """ The wx control that we use to implement the table viewer. """ # Default style. - STYLE = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES | wx.STATIC_BORDER \ - | wx.LC_SINGLE_SEL | wx.LC_VIRTUAL | wx.LC_EDIT_LABELS \ - | wx.CLIP_CHILDREN + STYLE = ( + wx.LC_REPORT + | wx.LC_HRULES + | wx.LC_VRULES + | wx.STATIC_BORDER + | wx.LC_SINGLE_SEL + | wx.LC_VIRTUAL + | wx.LC_EDIT_LABELS + | wx.CLIP_CHILDREN + ) def __init__(self, parent, image_size, viewer): """ Creates a new table viewer. @@ -325,21 +323,27 @@ # Set up attributes to show alternate rows with a different background # colour. - self._even_row_attribute = wx.ListItemAttr() + if version_4_0: + self._even_row_attribute = wx.ListItemAttr() + else: + self._even_row_attribute = wx.ItemAttr() self._even_row_attribute.SetBackgroundColour( self._viewer.even_row_background ) - self._odd_row_attribute = wx.ListItemAttr() + if version_4_0: + self._odd_row_attribute = wx.ListItemAttr() + else: + self._odd_row_attribute = wx.ItemAttr() self._odd_row_attribute.SetBackgroundColour( self._viewer.odd_row_background ) return - ########################################################################### + # ------------------------------------------------------------------------ # Virtual 'ListCtrl' interface. - ########################################################################### + # ------------------------------------------------------------------------ def OnGetItemText(self, row, column_index): """ Returns the text for the specified CELL. """ @@ -375,5 +379,3 @@ attribute = self._odd_row_attribute return attribute - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/viewer/tree_viewer.py python-pyface-7.4.0/pyface/ui/wx/viewer/tree_viewer.py --- python-pyface-6.1.2/pyface/ui/wx/viewer/tree_viewer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/viewer/tree_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A viewer based on a tree control. """ +import warnings -# Major package imports. -from __future__ import print_function import wx -# Enthought library imports. -from traits.api import Any, Bool, Enum, Event, Instance, List +from traits.api import Bool, Enum, Event, Instance, Int, List, Tuple -# Local imports. from pyface.ui.wx.image_list import ImageList from pyface.viewer.content_viewer import ContentViewer from pyface.viewer.tree_content_provider import TreeContentProvider @@ -35,7 +29,7 @@ # The default tree style. STYLE = wx.TR_EDIT_LABELS | wx.TR_HAS_BUTTONS | wx.CLIP_CHILDREN - #### 'TreeViewer' interface ############################################### + # 'TreeViewer' interface ----------------------------------------------- # The content provider provides the actual tree data. content_provider = Instance(TreeContentProvider) @@ -45,10 +39,10 @@ label_provider = Instance(TreeLabelProvider, ()) # Selection mode (must be either of 'single' or 'extended'). - selection_mode = Enum('single', 'extended') + selection_mode = Enum("single", "extended") # The currently selected elements. - selection = List + selection = List() # Should an image be shown for each element? show_images = Bool(True) @@ -56,32 +50,35 @@ # Should the root of the tree be shown? show_root = Bool(True) - #### Events #### + # Events ---- # An element has been activated (ie. double-clicked). - element_activated = Event + element_activated = Event() # A drag operation was started on an element. - element_begin_drag = Event + element_begin_drag = Event() # An element that has children has been collapsed. - element_collapsed = Event + element_collapsed = Event() # An element that has children has been expanded. - element_expanded = Event + element_expanded = Event() # A left-click occurred on an element. - element_left_clicked = Event + element_left_clicked = Event() # A right-click occurred on an element. - element_right_clicked = Event + element_right_clicked = Event() # A key was pressed while the tree is in focus. - key_pressed = Event + key_pressed = Event() - ########################################################################### + # The size of the icons in the tree. + _image_size = Tuple(Int, Int) + + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, parent, image_size=(16, 16), **traits): """ Creates a new tree viewer. @@ -92,33 +89,42 @@ specifies the size of the label images (if any) displayed in the tree. """ + create = traits.pop('create', True) + + # Base class constructors. + super().__init__(parent=parent, _image_size=image_size, **traits) - # Base class constructor. - super(TreeViewer, self).__init__(**traits) + if create: + self.create() + warnings.warn( + "automatic widget creation is deprecated and will be removed " + "in a future Pyface version, use create=False and explicitly " + "call create() for future behaviour", + PendingDeprecationWarning, + ) + + def _create_control(self, parent): # Create the toolkit-specific control. self.control = tree = wx.TreeCtrl(parent, -1, style=self._get_style()) - # Get our actual Id. - wxid = tree.GetId() - # Wire up the wx tree events. - wx.EVT_CHAR(tree, self._on_char) - wx.EVT_LEFT_DOWN(tree, self._on_left_down) - wx.EVT_RIGHT_DOWN(tree, self._on_right_down) - wx.EVT_TREE_ITEM_ACTIVATED(tree, wxid, self._on_tree_item_activated) - wx.EVT_TREE_ITEM_COLLAPSED(tree, wxid, self._on_tree_item_collapsed) - wx.EVT_TREE_ITEM_COLLAPSING(tree, wxid, self._on_tree_item_collapsing) - wx.EVT_TREE_ITEM_EXPANDED(tree, wxid, self._on_tree_item_expanded) - wx.EVT_TREE_ITEM_EXPANDING(tree, wxid, self._on_tree_item_expanding) - wx.EVT_TREE_BEGIN_LABEL_EDIT(tree, wxid,self._on_tree_begin_label_edit) - wx.EVT_TREE_END_LABEL_EDIT(tree, wxid, self._on_tree_end_label_edit) - wx.EVT_TREE_BEGIN_DRAG(tree, wxid, self._on_tree_begin_drag) - wx.EVT_TREE_SEL_CHANGED(tree, wxid, self._on_tree_sel_changed) + tree.Bind(wx.EVT_CHAR, self._on_char) + tree.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) + tree.Bind(wx.EVT_RIGHT_DOWN, self._on_right_down) + tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self._on_tree_item_activated) + tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self._on_tree_item_collapsed) + tree.Bind(wx.EVT_TREE_ITEM_COLLAPSING, self._on_tree_item_collapsing) + tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self._on_tree_item_expanded) + tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self._on_tree_item_expanding) + tree.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self._on_tree_begin_label_edit) + tree.Bind(wx.EVT_TREE_END_LABEL_EDIT, self._on_tree_end_label_edit) + tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self._on_tree_begin_drag) + tree.Bind(wx.EVT_TREE_SEL_CHANGED, self._on_tree_sel_changed) # The image list is a wxPython-ism that caches all images used in the # control. - self._image_list = ImageList(image_size[0], image_size[1]) + self._image_list = ImageList(*self._image_size) if self.show_images: tree.AssignImageList(self._image_list) @@ -129,11 +135,11 @@ if self.input is not None: self._add_element(None, self.input) - return + return tree - ########################################################################### + # ------------------------------------------------------------------------ # 'TreeViewer' interface. - ########################################################################### + # ------------------------------------------------------------------------ def is_expanded(self, element): """ Returns True if the element is expanded, otherwise False. """ @@ -174,12 +180,12 @@ # The item data is a tuple. The first element indicates whether or # not we have already populated the item with its children. The # second element is the actual item data. - populated, element = self.control.GetPyData(pid) + populated, element = self.control.GetItemData(pid) # fixme: We should find a cleaner way other than deleting all of # the element's children and re-adding them! self._delete_children(pid) - self.control.SetPyData(pid, (False, element)) + self.control.SetItemData(pid, (False, element)) # Does the element have any children? has_children = self.content_provider.has_children(element) @@ -189,9 +195,7 @@ self.control.Expand(pid) else: - print('**** pid is None!!! ****') - - return + print("**** pid is None!!! ****") def update(self, element): """ Update the tree starting from the specified element. @@ -211,9 +215,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_style(self): """ Returns the wx style flags for creating the tree control. """ @@ -224,7 +228,7 @@ if not self.show_root: style = style | wx.TR_HIDE_ROOT | wx.TR_LINES_AT_ROOT - if self.selection_mode != 'single': + if self.selection_mode != "single": style = style | wx.TR_MULTIPLE | wx.TR_EXTENDED return style @@ -263,10 +267,10 @@ # element is the actual item data. if pid is None: if self.show_root: - self.control.SetPyData(wxid, (False, element)) + self.control.SetItemData(wxid, (False, element)) else: - self.control.SetPyData(wxid, (False, element)) + self.control.SetItemData(wxid, (False, element)) # Make sure that we can find the element's Id later. self._element_to_id_map[self._get_key(element)] = wxid @@ -275,8 +279,6 @@ if pid is None and self.show_root: self.control.Expand(wxid) - return - def _get_image_index(self, element): """ Returns the tree item image index for an element. """ @@ -306,7 +308,7 @@ text = self.label_provider.get_text(self, element) if text is None: - text = '' + text = "" return text @@ -326,8 +328,6 @@ has_children = self.content_provider.has_children(element) self.control.SetItemHasChildren(wxid, has_children) - return - def _unpack_event(self, event): """ Unpacks the event to see whether a tree element was involved. """ @@ -339,14 +339,14 @@ wxid, flags = self.control.HitTest(point) - # Warning: On GTK we have to check the flags before we call 'GetPyData' + # Warning: On GTK we have to check the flags before we call 'GetItemData' # because if we call it when the hit test returns 'nowhere' it will # barf (on Windows it simply returns 'None' 8^() if flags & wx.TREE_HITTEST_NOWHERE: data = None else: - data = self.control.GetPyData(wxid) + data = self.control.GetItemData(wxid) return data, wxid, flags, point @@ -355,7 +355,7 @@ elements = [] for wxid in self.control.GetSelections(): - data = self.control.GetPyData(wxid) + data = self.control.GetItemData(wxid) if data is not None: populated, element = data elements.append(element) @@ -380,9 +380,9 @@ self._delete_children(cid) # Remove the reference to the item's data. - populated, element = self.control.GetPyData(cid) + populated, element = self.control.GetItemData(cid) del self._element_to_id_map[self._get_key(element)] - self.control.SetPyData(cid, None) + self.control.SetItemData(cid, None) # Next! (cid, cookie) = self.control.GetNextChild(pid, cookie) @@ -391,7 +391,7 @@ return - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- def _input_changed(self): """ Called when the tree's input has been changed. """ @@ -406,8 +406,6 @@ if self.input is not None: self._add_element(None, self.input) - return - def _element_begin_drag_changed(self, element): """ Called when a drag is started on a element. """ @@ -419,7 +417,7 @@ return - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_right_down(self, event): """ Called when the right mouse button is clicked on the tree. """ @@ -436,8 +434,6 @@ # Give other event handlers a chance. event.Skip() - return - def _on_left_down(self, event): """ Called when the left mouse button is clicked on the tree. """ @@ -458,8 +454,6 @@ # Give other event handlers a chance. event.Skip() - return - def _on_tree_item_expanding(self, event): """ Called when a tree item is about to expand. """ @@ -469,7 +463,7 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Give the label provider a chance to veto the expansion. if self.label_provider.is_expandable(self, element): @@ -491,13 +485,11 @@ self._add_element(wxid, child) # The element is now populated! - self.control.SetPyData(wxid, (True, element)) + self.control.SetItemData(wxid, (True, element)) else: event.Veto() - return - def _on_tree_item_expanded(self, event): """ Called when a tree item has been expanded. """ @@ -507,7 +499,7 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Make sure that the element's 'open' icon is displayed etc. self._refresh_element(wxid, element) @@ -515,8 +507,6 @@ # Trait notification. self.element_expanded = element - return - def _on_tree_item_collapsing(self, event): """ Called when a tree item is about to collapse. """ @@ -526,14 +516,12 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Give the label provider a chance to veto the collapse. if not self.label_provider.is_collapsible(self, element): event.Veto() - return - def _on_tree_item_collapsed(self, event): """ Called when a tree item has been collapsed. """ @@ -543,7 +531,7 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Make sure that the element's 'closed' icon is displayed etc. self._refresh_element(wxid, element) @@ -551,8 +539,6 @@ # Trait notification. self.element_collapsed = element - return - def _on_tree_item_activated(self, event): """ Called when a tree item is activated (i.e., double clicked). """ @@ -562,32 +548,28 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Trait notification. self.element_activated = element - return - def _on_tree_sel_changed(self, event): """ Called when the selection is changed. """ # Trait notification. self.selection = self._get_selection() - return - def _on_tree_begin_drag(self, event): """ Called when a drag operation is starting on a tree item. """ # Get the element, its id and the point where the event occurred. data, wxid, flags, point = self._unpack_event(event) - if point == (0,0): + if point == (0, 0): # Apply workaround. point = self._point_left_clicked wxid, flags = self.control.HitTest(point) - data = self.control.GetPyData(wxid) + data = self.control.GetItemData(wxid) if data is not None: populated, element = data @@ -595,8 +577,6 @@ # Trait notification. self.element_begin_drag = element - return - def _on_tree_begin_label_edit(self, event): """ Called when the user has started editing an item's label. """ @@ -605,14 +585,12 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Give the label provider a chance to veto the edit. if not self.label_provider.is_editable(self, element): event.Veto() - return - def _on_tree_end_label_edit(self, event): """ Called when the user has finished editing an item's label. """ @@ -621,15 +599,13 @@ # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. - populated, element = self.control.GetPyData(wxid) + populated, element = self.control.GetItemData(wxid) # Give the label provider a chance to veto the edit. label = event.GetLabel() if not self.label_provider.set_text(self, element, label): event.Veto() - return - def _on_char(self, event): """ Called when a key is pressed when the tree has focus. """ @@ -637,5 +613,3 @@ self.key_pressed = event.GetKeyCode() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/widget.py python-pyface-7.4.0/pyface/ui/wx/widget.py --- python-pyface-6.1.2/pyface/ui/wx/widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,21 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, provides +import wx + +from traits.api import Any, Bool, HasTraits, Instance, Str, provides -# Local imports. from pyface.i_widget import IWidget, MWidget @@ -33,10 +28,10 @@ # 'IWidget' interface ---------------------------------------------------- #: The toolkit specific control that represents the widget. - control = Any + control = Any() #: The control's optional parent control. - parent = Any + parent = Any() #: Whether or not the control is visible visible = Bool(True) @@ -44,6 +39,12 @@ #: Whether or not the control is enabled enabled = Bool(True) + #: A tooltip for the widget. + tooltip = Str() + + #: An optional context menu for the widget. + context_menu = Instance("pyface.action.menu_manager.MenuManager") + # ------------------------------------------------------------------------ # 'IWidget' interface. # ------------------------------------------------------------------------ @@ -72,7 +73,56 @@ if self.control is not None: self.control.Enable(enabled) + def focus(self): + """ Set the keyboard focus to this widget. + """ + if self.control is not None: + self.control.SetFocus() + + def has_focus(self): + """ Does the widget currently have keyboard focus? + + Returns + ------- + focus_state : bool + Whether or not the widget has keyboard focus. + """ + return ( + self.control is not None + and self.control.HasFocus() + ) + def destroy(self): if self.control is not None: - self.control.Destroy() - self.control = None + control = self.control + super().destroy() + control.Destroy() + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _get_control_tooltip(self): + """ Toolkit specific method to get the control's tooltip. """ + return self.control.GetToolTipText() + + def _set_control_tooltip(self, tooltip): + """ Toolkit specific method to set the control's tooltip. """ + self.control.SetToolTip(tooltip) + + def _observe_control_context_menu(self, remove=False): + """ Toolkit specific method to change the control menu observer. """ + if remove: + self.control.Unbind( + wx.EVT_CONTEXT_MENU, handler=self._handle_control_context_menu + ) + else: + self.control.Bind( + wx.EVT_CONTEXT_MENU, self._handle_control_context_menu + ) + + def _handle_control_context_menu(self, event): + """ Signal handler for displaying context menu. """ + if self.control is not None and self.context_menu is not None: + menu = self.context_menu.create_menu(self.control) + self.control.PopupMenu(menu) diff -Nru python-pyface-6.1.2/pyface/ui/wx/window.py python-pyface-7.4.0/pyface/ui/wx/window.py --- python-pyface-6.1.2/pyface/ui/wx/window.py 2019-06-14 11:44:07.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,22 @@ -# Copyright (c) 2005-18, Enthought, Inc. -# All rights reserved. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Thanks for using Enthought open source! +# Thanks for using Enthought open source! """ Enthought pyface package component """ -# Major package imports. + import wx -# Enthought library imports. -from traits.api import Event, Property, Tuple, Unicode, VetoableEvent, provides -# Local imports. +from traits.api import Event, Property, Tuple, Str, VetoableEvent, provides + + from pyface.i_window import IWindow, MWindow from pyface.key_pressed_event import KeyPressedEvent from .system_metrics import SystemMetrics @@ -35,33 +35,35 @@ size = Property(Tuple) - title = Unicode + title = Str() # Window Events ---------------------------------------------------------- #: The window has been opened. - opened = Event + opened = Event() #: The window is about to open. - opening = VetoableEvent + opening = VetoableEvent() #: The window has been activated. - activated = Event + activated = Event() #: The window has been closed. - closed = Event + closed = Event() #: The window is about to be closed. - closing = VetoableEvent + closing = VetoableEvent() #: The window has been deactivated. - deactivated = Event + deactivated = Event() #: A key was pressed while the window had focus. # FIXME v3: This smells of a hack. What's so special about key presses? - # FIXME v3: Unicode + # FIXME v3: Str key_pressed = Event(KeyPressedEvent) + size = Property(Tuple) + # Private interface ------------------------------------------------------ # Shadow trait for position. @@ -86,6 +88,7 @@ # ------------------------------------------------------------------------- def _add_event_listeners(self): + super()._add_event_listeners() self.control.Bind(wx.EVT_ACTIVATE, self._wx_on_activate) self.control.Bind(wx.EVT_SHOW, self._wx_on_show) self.control.Bind(wx.EVT_CLOSE, self._wx_on_close) @@ -100,16 +103,16 @@ def _create_control(self, parent): # create a basic window control - style = wx.DEFAULT_FRAME_STYLE \ - | wx.FRAME_NO_WINDOW_MENU \ - | wx.CLIP_CHILDREN + style = ( + wx.DEFAULT_FRAME_STYLE | wx.FRAME_NO_WINDOW_MENU | wx.CLIP_CHILDREN + ) control = wx.Frame( parent, -1, self.title, style=style, size=self.size, - pos=self.position + pos=self.position, ) control.SetBackgroundColour(SystemMetrics().dialog_background_color) control.Enable(self.enabled) @@ -137,7 +140,7 @@ old = self._position self._position = position - self.trait_property_changed('position', old, position) + self.trait_property_changed("position", old, position) def _get_size(self): """ Property getter for size. """ @@ -153,7 +156,7 @@ old = self._size self._size = size - self.trait_property_changed('size', old, size) + self.trait_property_changed("size", old, size) def _title_changed(self, title): """ Static trait change handler. """ @@ -195,8 +198,13 @@ # call event.GetPosition directly, but that would be wrong. The pixel # reported by that call is the pixel just below the window menu and # just right of the Windows-drawn border. - self._position = event.GetEventObject().GetPositionTuple() + try: + self._position = ( + event.GetEventObject().GetPosition().Get() + ) # Sizer.GetPosition().Get() + except: + pass event.Skip() def _wx_on_control_size(self, event): @@ -214,11 +222,11 @@ """ Called when a key is pressed when the tree has focus. """ self.key_pressed = KeyPressedEvent( - alt_down=event.m_altDown == 1, - control_down=event.m_controlDown == 1, - shift_down=event.m_shiftDown == 1, - key_code=event.m_keyCode, - event=event + alt_down=event.altDown, + control_down=event.controlDown, + shift_down=event.shiftDown, + key_code=event.KeyCode, + event=event, ) event.Skip() diff -Nru python-pyface-6.1.2/pyface/ui/wx/wizard/__init__.py python-pyface-7.4.0/pyface/ui/wx/wizard/__init__.py --- python-pyface-6.1.2/pyface/ui/wx/wizard/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/wizard/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,9 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/wx/wizard/wizard_page.py python-pyface-7.4.0/pyface/ui/wx/wizard/wizard_page.py --- python-pyface-6.1.2/pyface/ui/wx/wizard/wizard_page.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/wizard/wizard_page.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A page in a wizard. """ -# Major package imports. import wx -# Enthought library imports. -from traits.api import Bool, HasTraits, provides, Str, Tuple, Unicode + +from traits.api import Bool, HasTraits, provides, Str, Tuple from pyface.api import HeadingText from pyface.wizard.i_wizard_page import IWizardPage, MWizardPage @@ -31,26 +27,25 @@ """ + # 'IWizardPage' interface ---------------------------------------------# - #### 'IWizardPage' interface ############################################## - - id = Str + id = Str() - next_id = Str + next_id = Str() last_page = Bool(False) complete = Bool(False) - heading = Unicode + heading = Str() - subheading = Unicode + subheading = Str() - size = Tuple + size = Tuple() - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_page(self, parent): """ Creates the wizard page. """ @@ -77,17 +72,15 @@ return panel - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_page_content(self, parent): """ Creates the actual page content. """ # Dummy implementation - override! panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN) - panel.SetBackgroundColour('yellow') + panel.SetBackgroundColour("yellow") return panel - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/wizard/wizard.py python-pyface-7.4.0/pyface/ui/wx/wizard/wizard.py --- python-pyface-6.1.2/pyface/ui/wx/wizard/wizard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/wizard/wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The base class for all pyface wizards. """ -# Major package imports. import wx -# Enthought library imports. -from traits.api import Bool, Instance, List, Property, provides, Unicode + +from traits.api import Bool, Instance, List, Property, provides, Str from pyface.api import Dialog, LayeredPanel from pyface.wizard.i_wizard import IWizard, MWizard from pyface.wizard.i_wizard_controller import IWizardController @@ -33,8 +29,7 @@ """ - - #### 'IWizard' interface ################################################## + # 'IWizard' interface -------------------------------------------------- pages = Property(List(IWizardPage)) @@ -42,18 +37,23 @@ show_cancel = Bool(True) - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------- + + title = Str("Wizard") - title = Unicode('Wizard') + # private traits ------------------------------------------------------- - ########################################################################### + _layered_panel = Instance(LayeredPanel) + + # ------------------------------------------------------------------------ # Protected 'IDialog' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_dialog_area(self, parent): """ Creates the main content of the dialog. """ - self._layered_panel = panel = LayeredPanel(parent) + self._layered_panel = panel = LayeredPanel(parent=parent, create=False) + panel.create() # fixme: Specific size? panel.control.SetSize((100, 200)) @@ -66,38 +66,38 @@ # 'Back' button. self._back = back = wx.Button(parent, -1, "Back") - wx.EVT_BUTTON(parent, back.GetId(), self._on_back) + parent.Bind(wx.EVT_BUTTON, self._on_back, back) sizer.Add(back, 0) # 'Next' button. self._next = next = wx.Button(parent, -1, "Next") - wx.EVT_BUTTON(parent, next.GetId(), self._on_next) + parent.Bind(wx.EVT_BUTTON, self._on_next, next) sizer.Add(next, 0, wx.LEFT, 5) next.SetDefault() # 'Finish' button. self._finish = finish = wx.Button(parent, wx.ID_OK, "Finish") finish.Enable(self.controller.complete) - wx.EVT_BUTTON(parent, wx.ID_OK, self._wx_on_ok) + parent.Bind(wx.EVT_BUTTON, self._wx_on_ok, finish) sizer.Add(finish, 0, wx.LEFT, 5) # 'Cancel' button. if self.show_cancel: self._cancel = cancel = wx.Button(parent, wx.ID_CANCEL, "Cancel") - wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel) + parent.Bind(wx.EVT_BUTTON, self._wx_on_cancel, cancel) sizer.Add(cancel, 0, wx.LEFT, 10) # 'Help' button. if len(self.help_id) > 0: help = wx.Button(parent, wx.ID_HELP, "Help") - wx.EVT_BUTTON(parent, wx.ID_HELP, self._wx_on_help) + parent.Bind(wx.EVT_BUTTON, self._wx_on_help, help) sizer.Add(help, 0, wx.LEFT, 10) return sizer - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'MWizard' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _show_page(self, page): """ Show the specified page. """ @@ -119,16 +119,14 @@ # page? self.controller.current_page = page - return - - def _update(self): + def _update(self, event): """ Enables/disables buttons depending on the state of the wizard. """ controller = self.controller current_page = controller.current_page is_first_page = controller.is_first_page(current_page) - is_last_page = controller.is_last_page(current_page) + is_last_page = controller.is_last_page(current_page) # 'Next button'. if self._next is not None: @@ -152,9 +150,7 @@ if self._next is not None: self._next.SetDefault() - return - - #### Trait handlers ####################################################### + # Trait handlers ------------------------------------------------------- def _controller_default(self): """ Provide a default controller. """ @@ -173,20 +169,14 @@ self.controller.pages = pages - #### wx event handlers #################################################### + # wx event handlers ---------------------------------------------------- def _on_next(self, event): """ Called when the 'Next' button is pressed. """ self.next() - return - def _on_back(self, event): """ Called when the 'Back' button is pressed. """ self.previous() - - return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/editor.py python-pyface-7.4.0/pyface/ui/wx/workbench/editor.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,18 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Local imports. + from pyface.workbench.i_editor import MEditor @@ -28,9 +23,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_control(self, parent): """ Create the toolkit-specific control that represents the part. """ @@ -51,8 +46,6 @@ self.control.Destroy() self.control = None - return - def set_focus(self): """ Set the focus to the appropriate control in the part. """ @@ -60,5 +53,3 @@ self.control.SetFocus() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/editor_set_structure_handler.py python-pyface-7.4.0/pyface/ui/wx/workbench/editor_set_structure_handler.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/editor_set_structure_handler.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/editor_set_structure_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,21 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The handler used to restore editors. """ -# Standard library imports. + import logging -# Enthought library imports. + from pyface.dock.api import SetStructureHandler @@ -35,9 +30,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, window_layout, editor_mementos): """ Creates a new handler. """ @@ -47,15 +42,15 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'SetStructureHandler' interface. - ########################################################################### + # ------------------------------------------------------------------------ def resolve_id(self, id): """ Resolves an unresolved dock control id. """ window_layout = self.window_layout - window = window_layout.window + window = window_layout.window try: # Get the memento for the editor with this Id. @@ -75,14 +70,14 @@ window.editors.append(editor) except: - logger.warn('could not restore editor [%s]', id) + logger.warning("could not restore editor [%s]", id) control = None return control - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_editor_memento(self, id): """ Return the editor memento for the editor with the specified Id. @@ -93,8 +88,6 @@ editor_memento = self.editor_mementos.get(id) if editor_memento is None: - raise ValueError('no editor memento with Id %s' % id) + raise ValueError("no editor memento with Id %s" % id) return editor_memento - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/__init__.py python-pyface-7.4.0/pyface/ui/wx/workbench/__init__.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,6 +1,9 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/view.py python-pyface-7.4.0/pyface/ui/wx/workbench/view.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,18 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Enthought pyface package component """ -# Enthought library imports. + from traits.api import Bool from pyface.workbench.i_view import MView @@ -34,9 +29,9 @@ # in workbench_window_layout.py. closeable = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_control(self, parent): """ Create the toolkit-specific control that represents the part. """ @@ -57,8 +52,6 @@ self.control.Destroy() self.control = None - return - def set_focus(self): """ Set the focus to the appropriate control in the part. """ @@ -66,5 +59,3 @@ self.control.SetFocus() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/view_set_structure_handler.py python-pyface-7.4.0/pyface/ui/wx/workbench/view_set_structure_handler.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/view_set_structure_handler.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/view_set_structure_handler.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,21 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The handler used to restore views. """ -# Standard library imports. + import logging -# Enthought library imports. + from pyface.dock.api import SetStructureHandler @@ -35,9 +30,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, window_layout): """ Creates a new handler. """ @@ -46,15 +41,15 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'SetStructureHandler' interface. - ########################################################################### + # ------------------------------------------------------------------------ def resolve_id(self, id): """ Resolves an unresolved dock control *id*. """ window_layout = self.window_layout - window = window_layout.window + window = window_layout.window view = window.get_view_by_id(id) if view is not None: @@ -65,9 +60,7 @@ control = window_layout._wx_get_view_control(view) else: - logger.warn('could not restore view [%s]', id) + logger.warning("could not restore view [%s]", id) control = None return control - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/workbench_dock_window.py python-pyface-7.4.0/pyface/ui/wx/workbench/workbench_dock_window.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/workbench_dock_window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/workbench_dock_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,31 +1,27 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Base class for workbench dock windows. """ -# Standard library imports. + import logging -# Enthought library imports. + from pyface.dock.api import DockGroup, DockRegion, DockWindow logger = logging.getLogger(__name__) + class WorkbenchDockWindow(DockWindow): """ Base class for workbench dock windows. @@ -34,9 +30,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'DockWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _right_up(self, event): """ Handles the right mouse button being released. @@ -48,9 +44,9 @@ pass - ########################################################################### + # ------------------------------------------------------------------------ # 'WorkbenchDockWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_control(self, id): """ Activates the dock control with the specified Id. @@ -62,13 +58,11 @@ control = self.get_control(id) if control is not None: - logger.debug('activating control <%s>', id) + logger.debug("activating control <%s>", id) control.activate() else: - logger.debug('no control <%s> to activate', id) - - return + logger.debug("no control <%s> to activate", id) def close_control(self, id): """ Closes the dock control with the specified Id. @@ -80,13 +74,11 @@ control = self.get_control(id) if control is not None: - logger.debug('closing control <%s>', id) + logger.debug("closing control <%s>", id) control.close() else: - logger.debug('no control <%s> to close', id) - - return + logger.debug("no control <%s> to close", id) def get_control(self, id, visible_only=True): """ Returns the dock control with the specified Id. @@ -107,7 +99,7 @@ def get_controls(self, visible_only=True): """ Returns all of the dock controls in the window. """ - sizer = self.control.GetSizer() + sizer = self.control.GetSizer() section = sizer.GetContents() return section.get_controls(visible_only=visible_only) @@ -135,15 +127,13 @@ def reset_regions(self): """ Activates the first dock control in every region. """ - sizer = self.control.GetSizer() + sizer = self.control.GetSizer() section = sizer.GetContents() for region in self.get_regions(section): if len(region.contents) > 0: region.contents[0].activate(layout=False) - return - def set_structure(self, structure, handler=None): """ Sets the window structure. """ @@ -151,5 +141,3 @@ sizer.SetStructure(self.control.GetParent(), structure, handler) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/workbench/workbench_window_layout.py python-pyface-7.4.0/pyface/ui/wx/workbench/workbench_window_layout.py --- python-pyface-6.1.2/pyface/ui/wx/workbench/workbench_window_layout.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/workbench/workbench_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,41 +1,35 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The wx implementation of the workbench window layout interface. """ -# Standard library imports. -import six.moves.cPickle + +import pickle import logging -# Major package imports. + import wx -# Enthought library imports. + from pyface.dock.api import DOCK_BOTTOM, DOCK_LEFT, DOCK_RIGHT from pyface.dock.api import DOCK_TOP from pyface.dock.api import DockControl, DockRegion, DockSection from pyface.dock.api import DockSizer from traits.api import Delegate -# Mixin class imports. -from pyface.workbench.i_workbench_window_layout import \ - MWorkbenchWindowLayout -# Local imports. +from pyface.workbench.i_workbench_window_layout import MWorkbenchWindowLayout + + from .editor_set_structure_handler import EditorSetStructureHandler from .view_set_structure_handler import ViewSetStructureHandler from .workbench_dock_window import WorkbenchDockWindow @@ -46,10 +40,10 @@ # Mapping from view position to the appropriate dock window constant. _POSITION_MAP = { - 'top' : DOCK_TOP, - 'bottom' : DOCK_BOTTOM, - 'left' : DOCK_LEFT, - 'right' : DOCK_RIGHT + "top": DOCK_TOP, + "bottom": DOCK_BOTTOM, + "left": DOCK_LEFT, + "right": DOCK_RIGHT, } @@ -60,13 +54,13 @@ """ - #### 'IWorkbenchWindowLayout' interface ################################### + # 'IWorkbenchWindowLayout' interface ----------------------------------- - editor_area_id = Delegate('window') + editor_area_id = Delegate("window") - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activate an editor. """ @@ -95,7 +89,7 @@ self._wx_add_editor(editor, title) except Exception: - logger.exception('error creating editor control <%s>', editor.id) + logger.exception("error creating editor control <%s>", editor.id) return editor @@ -107,7 +101,7 @@ view.visible = True except Exception: - logger.exception('error creating view control <%s>', view.id) + logger.exception("error creating view control <%s>", view.id) # Even though we caught the exception, it sometimes happens that # the view's control has been created as a child of the application @@ -116,7 +110,7 @@ view.destroy_control() # Additionally, display an error message to the user. - self.window.error('Unable to add view %s' % view.id) + self.window.error("Unable to add view %s" % view.id) return view @@ -140,8 +134,6 @@ self._wx_editor_dock_window.close() self._wx_view_dock_window.close() - return - def create_initial_layout(self, parent): """ Create the initial window layout. """ @@ -159,12 +151,12 @@ # Nest the editor dock window in the view dock window. editor_dock_window_control = DockControl( - id = self.editor_area_id, - name = 'Editors', - control = self._wx_editor_dock_window.control, - style = 'fixed', - width = self.window.editor_area_size[0], - height = self.window.editor_area_size[1], + id=self.editor_area_id, + name="Editors", + control=self._wx_editor_dock_window.control, + style="fixed", + width=self.window.editor_area_size[0], + height=self.window.editor_area_size[1], ) view_dock_window_sizer = DockSizer( @@ -190,8 +182,6 @@ ) dock_control.show(False, layout=True) - return - def hide_view(self, view): """ Hide a view. """ @@ -209,22 +199,16 @@ self._wx_view_dock_window.update_layout() - return - def reset_editors(self): """ Activate the first editor in every group. """ self._wx_editor_dock_window.reset_regions() - return - def reset_views(self): """ Activate the first view in every group. """ self._wx_view_dock_window.reset_regions() - return - def show_editor_area(self): """ Show the editor area. """ @@ -233,8 +217,6 @@ ) dock_control.show(True, layout=True) - return - def show_view(self, view): """ Show a view. """ @@ -245,25 +227,23 @@ dock_control.show(True, layout=True) view.visible = True - return - def is_editor_area_visible(self): dock_control = self._wx_view_dock_window.get_control( self.editor_area_id, visible_only=False ) return dock_control.visible - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_view_memento(self): structure = self._wx_view_dock_window.get_structure() # We always return a clone. - return six.moves.cPickle.loads(six.moves.cPickle.dumps(structure)) + return pickle.loads(pickle.dumps(structure)) def set_view_memento(self, memento): # We always use a clone. - memento = six.moves.cPickle.loads(six.moves.cPickle.dumps(memento)) + memento = pickle.loads(pickle.dumps(memento)) # The handler knows how to resolve view Ids when setting the dock # window structure. @@ -282,8 +262,6 @@ else: view.visible = False - return - def get_editor_memento(self): # Get the layout of the editors. structure = self._wx_editor_dock_window.get_structure() @@ -314,9 +292,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _wx_add_editor(self, editor, title): """ Adds an editor. """ @@ -329,11 +307,11 @@ controls = self._wx_editor_dock_window.get_controls() if len(controls) == 0: # Get a reference to the empty editor section. - sizer = self._wx_editor_dock_window.control.GetSizer() + sizer = self._wx_editor_dock_window.control.GetSizer() section = sizer.GetContents() # Add a region containing the editor dock control. - region = DockRegion(contents=[editor_dock_control]) + region = DockRegion(contents=[editor_dock_control]) section.contents = [region] # Otherwise, add the editor to the same region as the first editor @@ -349,8 +327,6 @@ # resizing the window makes it better!). self._wx_editor_dock_window.update_layout() - return - def _wx_add_view(self, view, position, relative_to, size): """ Adds a view. """ @@ -362,7 +338,7 @@ # Create a dock control that contains the view. dock_control = self._wx_create_view_dock_control(view) - if position == 'with': + if position == "with": # Does the item we are supposed to be positioned 'with' actual # exist? with_item = self._wx_view_dock_window.get_control(relative_to.id) @@ -373,10 +349,12 @@ # Otherwise, just fall back to the 'left' of the editor area. else: - self._wx_add_view_relative(dock_control, None, 'left', size) + self._wx_add_view_relative(dock_control, None, "left", size) else: - self._wx_add_view_relative(dock_control,relative_to,position,size) + self._wx_add_view_relative( + dock_control, relative_to, position, size + ) return @@ -407,30 +385,26 @@ self._wx_set_item_size(dock_control, size) # The parent of a dock control is a dock region. - region = relative_to_item.parent + region = relative_to_item.parent section = region.parent section.add(dock_control, region, _POSITION_MAP[position]) - return - def _wx_add_view_with(self, dock_control, with_obj): """ Adds a view in the same region as another item. """ # Find the item that we are adding the view 'with'. with_item = self._wx_view_dock_window.get_control(with_obj.id) if with_item is None: - raise ValueError('Cannot find item %s' % with_obj) + raise ValueError("Cannot find item %s" % with_obj) # The parent of a dock control is a dock region. with_item.parent.add(dock_control) - return - def _wx_set_item_size(self, dock_control, size): """ Sets the size of a dock control. """ - window_width, window_height = self.window.control.GetSize() - width, height = size + window_width, window_height = self.window.control.GetSize().Get() + width, height = size if width != -1: dock_control.width = int(window_width * width) @@ -438,8 +412,6 @@ if height != -1: dock_control.height = int(window_height * height) - return - def _wx_create_editor_dock_control(self, editor): """ Creates a dock control that contains the specified editor. """ @@ -447,14 +419,14 @@ # Wrap a dock control around it. editor_dock_control = DockControl( - id = editor.id, - name = editor.name, - closeable = True, - control = editor.control, - style = 'tab', + id=editor.id, + name=editor.name, + closeable=True, + control=editor.control, + style="tab", # fixme: Create a subclass of dock control and give it a proper # editor trait! - _editor = editor + _editor=editor, ) # Hook up the 'on_close' and trait change handlers etc. @@ -479,17 +451,17 @@ # Wrap a dock control around it. view_dock_control = DockControl( - id = view.id, - name = view.name, + id=view.id, + name=view.name, # fixme: We would like to make views closeable, but closing via the # tab is different than calling show(False, layout=True) on the # control! If we use a close handler can we change that?!? - closeable = closeable, - control = control, - style = view.style_hint, + closeable=closeable, + control=control, + style=view.style_hint, # fixme: Create a subclass of dock control and give it a proper # view trait! - _view = view + _view=view, ) # Hook up the 'on_close' and trait change handlers etc. @@ -535,8 +507,6 @@ # Let the default wx event handling do its thang. event.Skip() - return - def on_kill_focus(event): """ Called when the control gets the focus. """ @@ -547,9 +517,9 @@ return - self._wx_add_focus_listeners(editor.control,on_set_focus,on_kill_focus) - - return + self._wx_add_focus_listeners( + editor.control, on_set_focus, on_kill_focus + ) def _wx_get_view_control(self, view): """ Returns a view's toolkit-specific control. @@ -592,8 +562,6 @@ # Let the default wx event handling do its thang. event.Skip() - return - def on_kill_focus(event): """ Called when the control gets the focus. """ @@ -606,8 +574,6 @@ self._wx_add_focus_listeners(view.control, on_set_focus, on_kill_focus) - return - def _wx_add_focus_listeners(self, control, on_set_focus, on_kill_focus): """ Recursively adds focus listeners to a control. """ @@ -617,18 +583,16 @@ # register event handlers. The exception messages complain that # the passed control is a str object instead of a wx object. if on_set_focus is not None: - #control.Bind(wx.EVT_SET_FOCUS, on_set_focus) - wx.EVT_SET_FOCUS(control, on_set_focus) + # control.Bind(wx.EVT_SET_FOCUS, on_set_focus) + control.Bind(wx.EVT_SET_FOCUS, on_set_focus) if on_kill_focus is not None: - #control.Bind(wx.EVT_KILL_FOCUS, on_kill_focus) - wx.EVT_KILL_FOCUS(control, on_kill_focus) + # control.Bind(wx.EVT_KILL_FOCUS, on_kill_focus) + control.Bind(wx.EVT_KILL_FOCUS, on_kill_focus) for child in control.GetChildren(): self._wx_add_focus_listeners(child, on_set_focus, on_kill_focus) - return - def _wx_initialize_editor_dock_control(self, editor, editor_dock_control): """ Initializes an editor dock control. @@ -649,36 +613,38 @@ editor_dock_control.set_name(editor.name) # fixme: Should we roll the traits UI stuff into the default editor. - if hasattr(editor, 'ui') and editor.ui is not None: + if hasattr(editor, "ui") and editor.ui is not None: from traitsui.dockable_view_element import DockableViewElement + # This makes the control draggable outside of the main window. - #editor_dock_control.export = 'pyface.workbench.editor' + # editor_dock_control.export = 'pyface.workbench.editor' editor_dock_control.dockable = DockableViewElement( should_close=True, ui=editor.ui ) editor_dock_control.on_close = self._wx_on_editor_closed - def on_id_changed(editor, trait_name, old, new): + def on_id_changed(event): + editor = event.object editor_dock_control.id = editor.id return - editor.on_trait_change(on_id_changed, 'id') + editor.observe(on_id_changed, "id") - def on_name_changed(editor, trait_name, old, new): + def on_name_changed(event): + editor = event.object editor_dock_control.set_name(editor.name) return - editor.on_trait_change(on_name_changed, 'name') + editor.observe(on_name_changed, "name") - def on_activated_changed(editor_dock_control, trait_name, old, new): + def on_activated_changed(event): + editor_dock_control = event.object if editor_dock_control._editor is not None: editor_dock_control._editor.set_focus() return - editor_dock_control.on_trait_change(on_activated_changed, 'activated') - - return + editor_dock_control.observe(on_activated_changed, "activated") def _wx_initialize_view_dock_control(self, view, view_dock_control): """ Initializes a view dock control. @@ -700,10 +666,11 @@ view_dock_control.set_name(view.name) # fixme: Should we roll the traits UI stuff into the default editor. - if hasattr(view, 'ui') and view.ui is not None: + if hasattr(view, "ui") and view.ui is not None: from traitsui.dockable_view_element import DockableViewElement + # This makes the control draggable outside of the main window. - #view_dock_control.export = 'pyface.workbench.view' + # view_dock_control.export = 'pyface.workbench.view' # If the ui's 'view' trait has an 'export' field set, pass that on # to the dock control. This makes the control detachable from the @@ -716,63 +683,66 @@ view_dock_control.on_close = self._wx_on_view_closed - def on_id_changed(view, trait_name, old, new): + def on_id_changed(event): + view = event.object view_dock_control.id = view.id return - view.on_trait_change(on_id_changed, 'id') + view.observe(on_id_changed, "id") - def on_name_changed(view, trait_name, old, new): + def on_name_changed(event): + view = event.object view_dock_control.set_name(view.name) return - view.on_trait_change(on_name_changed, 'name') + view.observe(on_name_changed, "name") - def on_activated_changed(view_dock_control, trait_name, old, new): + def on_activated_changed(event): + view_dock_control = event.object if view_dock_control._view is not None: view_dock_control._view.set_focus() return - view_dock_control.on_trait_change(on_activated_changed, 'activated') + view_dock_control.observe(on_activated_changed, "activated") return - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - #### Static #### + # Static ---- def _window_changed(self, old, new): """ Static trait change handler. """ if old is not None: - old.on_trait_change( - self._wx_on_editor_area_size_changed, 'editor_area_size', - remove=True + old.observe( + self._wx_on_editor_area_size_changed, + "editor_area_size", + remove=True, ) - if new is not None: - new.on_trait_change( - self._wx_on_editor_area_size_changed, 'editor_area_size', + new.observe( + self._wx_on_editor_area_size_changed, "editor_area_size" ) - #### Dynamic #### + # Dynamic ---- - def _wx_on_editor_area_size_changed(self, new): + def _wx_on_editor_area_size_changed(self, event): """ Dynamic trait change handler. """ - window_width, window_height = self.window.control.GetSize() + window_width, window_height = self.window.control.GetSize().Get() # Get the dock control that contains the editor dock window. control = self._wx_view_dock_window.get_control(self.editor_area_id) # We actually resize the region that the editor area is in. region = control.parent - region.width = int(new[0] * window_width) - region.height = int(new[1] * window_height) + region.width = int(event.new[0] * window_width) + region.height = int(event.new[1] * window_height) return - #### Dock window handlers ################################################# + # Dock window handlers ------------------------------------------------- # fixme: Should these just fire events that the window listens to? def _wx_on_view_closed(self, dock_control, force): @@ -780,7 +750,7 @@ view = self.window.get_view_by_id(dock_control.id) if view is not None: - logger.debug('workbench destroying view control <%s>', view) + logger.debug("workbench destroying view control <%s>", view) try: view.visible = False @@ -789,7 +759,7 @@ self.view_closed = view except: - logger.exception('error destroying view control <%s>', view) + logger.exception("error destroying view control <%s>", view) return True @@ -799,11 +769,11 @@ dock_control._editor = None editor = self.window.get_editor_by_id(dock_control.id) -## import weakref -## editor_ref = weakref.ref(editor) + ## import weakref + ## editor_ref = weakref.ref(editor) if editor is not None: - logger.debug('workbench destroying editor control <%s>', editor) + logger.debug("workbench destroying editor control <%s>", editor) try: # fixme: We would like this event to be vetoable, but it isn't # just yet (we will need to modify the dock window package). @@ -812,21 +782,21 @@ self.editor_closed = editor except: - logger.exception('error destroying editor control <%s>',editor) + logger.exception( + "error destroying editor control <%s>", editor + ) + + ## import gc + ## gc.collect() + + ## print 'Editor references', len(gc.get_referrers(editor)) + ## for r in gc.get_referrers(editor): + ## print '********************************************' + ## print type(r), id(r), r -## import gc -## gc.collect() + ## del editor + ## gc.collect() -## print 'Editor references', len(gc.get_referrers(editor)) -## for r in gc.get_referrers(editor): -## print '********************************************' -## print type(r), id(r), r - -## del editor -## gc.collect() - -## print 'Is editor gone?', editor_ref() is None, 'ref', editor_ref() + ## print 'Is editor gone?', editor_ref() is None, 'ref', editor_ref() return True - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui/wx/xrc_dialog.py python-pyface-7.4.0/pyface/ui/wx/xrc_dialog.py --- python-pyface-6.1.2/pyface/ui/wx/xrc_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui/wx/xrc_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,47 +1,48 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: Scott Swarts -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """A dialog that is loaded from an XRC resource file. """ -from __future__ import absolute_import -# Standard library imports. + import os.path # Major packages. import wx import wx.xrc -# Enthought library imports + from traits.api import Instance, Str import traits.util.resource -# Local imports. + from .dialog import Dialog -############################################################################## +# ---------------------------------------------------------------------------- # class 'XrcDialog' -############################################################################## +# ---------------------------------------------------------------------------- + class XrcDialog(Dialog): """A dialog that is loaded from an XRC resource file. """ - ########################################################################## + # ------------------------------------------------------------------------ # Traits - ########################################################################## + # ------------------------------------------------------------------------ - ### 'XrcDialog' interface ############################################ + # 'XrcDialog' interface -------------------------------------------- # Path to the xrc file relative to the class's module - xrc_file = Str + xrc_file = Str() # The ID of the dialog in the file id = Str("dialog") @@ -49,18 +50,18 @@ # The resource object resource = Instance(wx.xrc.XmlResource) - ########################################################################## + # ------------------------------------------------------------------------ # 'Dialog' interface - ########################################################################## + # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the dialog and loads it in from the resource file. """ - classpath = traits.util.resource.get_path( self ) - path = os.path.join( classpath, self.xrc_file ) + classpath = traits.util.resource.get_path(self) + path = os.path.join(classpath, self.xrc_file) - self.resource = wx.xrc.XmlResource( path ) + self.resource = wx.xrc.XmlResource(path) return self.resource.LoadDialog(parent, self.id) def _create_contents(self, dialog): @@ -75,21 +76,23 @@ if okbutton is not None: # Change the ID and set the handler okbutton.SetId(wx.ID_OK) - wx.EVT_BUTTON(self.control, okbutton.GetId(), self._on_ok) + self.control.Bind(wx.EVT_BUTTON, okbutton.GetId(), self._on_ok) cancelbutton = self.XRCCTRL("CANCEL") if cancelbutton is not None: # Change the ID and set the handler cancelbutton.SetId(wx.ID_CANCEL) - wx.EVT_BUTTON(self.control, cancelbutton.GetId(), self._on_cancel) + self.control.Bind( + wx.EVT_BUTTON, self._on_cancel, cancelbutton.GetId() + ) helpbutton = self.XRCCTRL("HELP") if helpbutton is not None: - wx.EVT_BUTTON(self.control, helpbutton.GetId(), self._on_help) + self.control.Bind(wx.EVT_BUTTON, self._on_help, helpbutton.GetId()) self._add_handlers() - ########################################################################## + # ------------------------------------------------------------------------ # 'XrcDialog' interface - ########################################################################## + # ------------------------------------------------------------------------ def XRCID(self, name): """ @@ -101,7 +104,7 @@ """ Returns the control with the given name. """ - return self.control.FindWindowById(self.XRCID(name)) + return self.control.Window.FindWindowById(self.XRCID(name)) def set_validator(self, name, validator): """ @@ -109,14 +112,12 @@ """ self.XRCCTRL(name).SetValidator(validator) - ########################################################################## + # ------------------------------------------------------------------------ # 'XrcDialog' protected interface - ########################################################################## + # ------------------------------------------------------------------------ def _add_handlers(self): """ Override to add event handlers. """ return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/ui_traits.py python-pyface-7.4.0/pyface/ui_traits.py --- python-pyface-6.1.2/pyface/ui_traits.py 2019-06-18 11:33:39.000000000 +0000 +++ python-pyface-7.4.0/pyface/ui_traits.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,58 +1,72 @@ -#------------------------------------------------------------------------------ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2016, Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! -# -# Author: Enthought Developers -# -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Defines common traits used within the pyface library. """ +from collections.abc import Sequence import logging -from traits.api import ABCHasStrictTraits, Enum, Range, TraitError, TraitType -from traits.trait_base import get_resource_path try: - from traits.trait_handlers import CALLABLE_AND_ARGS_DEFAULT_VALUE + import numpy as np except ImportError: - CALLABLE_AND_ARGS_DEFAULT_VALUE = 7 -import six + np = None + +from traits.api import ( + ABCHasStrictTraits, + DefaultValue, + Enum, + Range, + TraitError, + TraitFactory, + TraitType, +) +from traits.trait_base import get_resource_path + +from pyface.color import Color +from pyface.font import Font +from pyface.i_image import IImage +from pyface.util.color_parser import ColorParseError +from pyface.util.font_parser import simple_parser, FontParseError logger = logging.getLogger(__name__) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Images -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- +# cache of lookups from string to ImageResource instance image_resource_cache = {} + +# cache of conversions of ImageResource instances to toolkit bitmaps image_bitmap_cache = {} def convert_image(value, level=3): """ Converts a specified value to an ImageResource if possible. """ - if not isinstance( value, six.string_types ): + if not isinstance(value, str): return value key = value - is_pyface_image = value.startswith('@') + is_pyface_image = value.startswith("@") if not is_pyface_image: search_path = get_resource_path(level) - key = '%s[%s]' % (value, search_path) + key = "%s[%s]" % (value, search_path) result = image_resource_cache.get(key) if result is None: if is_pyface_image: try: from .image.image import ImageLibrary + result = ImageLibrary.image_resource(value) except Exception as exc: logger.error("Can't load image resource '%s'." % value) @@ -60,6 +74,7 @@ result = None else: from pyface.image_resource import ImageResource + result = ImageResource(value, search_path=[search_path]) image_resource_cache[key] = result @@ -67,27 +82,32 @@ return result -def convert_bitmap(image_resource): +def convert_bitmap(image): """ Converts an ImageResource to a bitmap using a cache. """ - bitmap = image_bitmap_cache.get(image_resource) - if (bitmap is None) and (image_resource is not None): - image_bitmap_cache[image_resource] = bitmap = \ - image_resource.create_bitmap() + from pyface.i_image_resource import IImageResource + if not isinstance(image, IImageResource): + # don't try to cache non-ImageResource IImages as they may be + # dynamically changing + return image.create_bitmap() + + bitmap = image_bitmap_cache.get(image) + if (bitmap is None) and (image is not None): + image_bitmap_cache[image] = bitmap = image.create_bitmap() return bitmap class Image(TraitType): - """ Defines a trait whose value must be a ImageResource or a string - that can be converted to one. + """ Defines a trait whose value must be a IImage or a string + that can be converted to an IImageResource. """ - # Define the default value for the trait: + #: Define the default value for the trait. default_value = None - # A description of the type of value this trait accepts: - info_text = 'an ImageResource or string that can be used to define one' + #: A description of the type of value this trait accepts. + info_text = "an IImage or string that can be used to define an ImageResource" # noqa: E501 def __init__(self, value=None, **metadata): """ Creates an Image trait. @@ -95,21 +115,19 @@ Parameters ---------- value : string or ImageResource - The default value for the Image, either an ImageResource object, + The default value for the Image, either an IImage object, or a string from which an ImageResource object can be derived. """ - super(Image, self).__init__(convert_image(value), **metadata) + super().__init__(convert_image(value), **metadata) def validate(self, object, name, value): """ Validates that a specified value is valid for this trait. """ - from pyface.i_image_resource import IImageResource - if value is None: return None new_value = convert_image(value, 4) - if isinstance(new_value, IImageResource): + if isinstance(new_value, IImage): return new_value self.error(object, name, value) @@ -118,15 +136,117 @@ """ Returns the default UI editor for the trait. """ from traitsui.editors.api import ImageEditor + return ImageEditor() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- +# Color +# ------------------------------------------------------------------------------- + + +class PyfaceColor(TraitType): + """ A Trait which casts strings and tuples to a Pyface Color value. + """ + + #: The default value should be a tuple (factory, args, kwargs) + default_value_type = DefaultValue.callable_and_args + + def __init__(self, value=None, **metadata): + if value is not None: + color = self.validate(None, None, value) + default_value = (Color, (), {'rgba': color.rgba}) + else: + default_value = (Color, (), {}) + super().__init__(default_value, **metadata) + + def validate(self, object, name, value): + if isinstance(value, Color): + return value + if isinstance(value, str): + try: + return Color.from_str(value) + except ColorParseError: + self.error(object, name, value) + is_array = ( + np is not None + and isinstance(value, (np.ndarray, np.void)) + ) + if is_array or isinstance(value, Sequence): + channels = tuple(value) + if len(channels) == 4: + return Color(rgba=channels) + elif len(channels) == 3: + return Color(rgb=channels) + + self.error(object, name, value) + + def info(self): + return ( + "a Pyface Color, a #-hexadecimal rgb or rgba string, a standard " + "color name, or a sequence of RGBA or RGB values between 0 and 1" + ) + + +# ------------------------------------------------------------------------------- +# Font +# ------------------------------------------------------------------------------- + + +class PyfaceFont(TraitType): + """ A Trait which casts strings to a Pyface Font value. + """ + + #: The default value should be a tuple (factory, args, kwargs) + default_value_type = DefaultValue.callable_and_args + + #: The parser to use when converting text to keyword args. This should + #: accept a string and return a dictionary of Font class trait values (ie. + #: "family", "size", "weight", etc.). + parser = None + + def __init__(self, value=None, *, parser=simple_parser, **metadata): + self.parser = parser + if value is not None: + try: + font = self.validate(None, None, value) + except TraitError: + raise ValueError( + "expected " + self.info() + + f", but got {value!r}" + ) + default_value = ( + Font, + (), + font.trait_get(transient=lambda x: not x), + ) + else: + default_value = (Font, (), {}) + super().__init__(default_value, **metadata) + + def validate(self, object, name, value): + if isinstance(value, Font): + return value + if isinstance(value, str): + try: + return Font(**self.parser(value)) + except FontParseError: + self.error(object, name, value) + + self.error(object, name, value) + + def info(self): + return ( + "a Pyface Font, or a string describing a Pyface Font" + ) + + +# ------------------------------------------------------------------------------- # Borders, Margins and Layout -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -class BaseMB(ABCHasStrictTraits): +class BaseMB(ABCHasStrictTraits): def __init__(self, *args, **traits): """ Map posiitonal arguments to traits. @@ -146,12 +266,14 @@ elif n == 4: left, right, top, bottom = args else: - raise TraitError('0, 1, 2 or 4 arguments expected, but %d ' - 'specified' % n) - traits.update({'left': left, 'right': right, - 'top': top, 'bottom': bottom}) + raise TraitError( + "0, 1, 2 or 4 arguments expected, but %d " "specified" % n + ) + traits.update( + {"left": left, "right": right, "top": top, "bottom": bottom} + ) - super(BaseMB, self).__init__(**traits) + super().__init__(**traits) class Margin(BaseMB): @@ -196,11 +318,13 @@ default_value = Margin(0) # A description of the type of value this trait accepts: - info_text = ('a Margin instance, or an integer in the range from -32 to 32 ' - 'or a tuple with 1, 2 or 4 integers in that range that can be ' - 'used to define one') + info_text = ( + "a Margin instance, or an integer in the range from -32 to 32 " + "or a tuple with 1, 2 or 4 integers in that range that can be " + "used to define one" + ) - def validate (self, object, name, value): + def validate(self, object, name, value): """ Validates that a specified value is valid for this trait. """ if isinstance(value, int): @@ -222,9 +346,10 @@ def get_default_value(self): """ Returns a tuple of the form: (default_value_type, default_value) + which describes the default value for this trait. """ - dv = self.default_value + dv = self.default_value dvt = self.default_value_type if dvt < 0: if isinstance(dv, int): @@ -233,9 +358,9 @@ dv = self.klass(*dv) if not isinstance(dv, self.klass): - return super(HasMargin, self).get_default_value() + return super().get_default_value() - self.default_value_type = dvt = CALLABLE_AND_ARGS_DEFAULT_VALUE + self.default_value_type = dvt = DefaultValue.callable_and_args dv = (self.klass, (), dv.trait_get()) return (dvt, dv) @@ -253,13 +378,56 @@ default_value = Border(0) # A description of the type of value this trait accepts: - info_text = ('a Border instance, or an integer in the range from 0 to 32 ' - 'or a tuple with 1, 2 or 4 integers in that range that can be ' - 'used to define one') + info_text = ( + "a Border instance, or an integer in the range from 0 to 32 " + "or a tuple with 1, 2 or 4 integers in that range that can be " + "used to define one" + ) #: The position of an image relative to its associated text. -Position = Enum('left', 'right', 'above', 'below') +Position = Enum("left", "right", "above", "below") #: The alignment of text within a control. -Alignment = Enum('default', 'left', 'center', 'right') +Alignment = Enum("default", "left", "center", "right") + +#: Whether the orientation of a widget's contents is horizontal or vertical. +Orientation = Enum("vertical", "horizontal") + + +# ------------------------------------------------------------------------------- +# Legacy TraitsUI Color and Font Traits +# ------------------------------------------------------------------------------- + +def TraitsUIColor(*args, **metadata): + """ Returns a trait whose value must be a GUI toolkit-specific color. + + This is copied from the deprecated trait that is in traits.api. It adds + a deferred dependency on TraitsUI. + + This trait will be replaced by native Pyface color traits in Pyface 8.0. + New code should not use this trait. + """ + from traitsui.toolkit_traits import ColorTrait + + return ColorTrait(*args, **metadata) + + +TraitsUIColor = TraitFactory(TraitsUIColor) + + +def TraitsUIFont(*args, **metadata): + """ Returns a trait whose value must be a GUI toolkit-specific font. + + This is copied from the deprecated trait that is in traits.api. It adds + a deferred dependency on TraitsUI. + + This trait will be replaced by native Pyface font traits in Pyface 8.0. + New code should not use this trait. + """ + from traitsui.toolkit_traits import FontTrait + + return FontTrait(*args, **metadata) + + +TraitsUIFont = TraitFactory(TraitsUIFont) diff -Nru python-pyface-6.1.2/pyface/undo/abstract_command.py python-pyface-7.4.0/pyface/undo/abstract_command.py --- python-pyface-6.1.2/pyface/undo/abstract_command.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/abstract_command.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,86 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from traits.api import Any, HasTraits, Str, provides + +# Local imports. +from .i_command import ICommand + + +@provides(ICommand) +class AbstractCommand(HasTraits): + """The AbstractCommand class is an abstract base class that implements the + ICommand interface. + """ + + #### 'ICommand' interface ################################################# + + #: This is the data on which the command operates. + data = Any() + + #: This is the name of the command as it will appear in any GUI element. It + #: may include '&' which will be automatically removed whenever it is + #: inappropriate. + name = Str() + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + """This is called by the command stack to do the command and to return + any value. The command must save any state necessary for the 'redo()' + and 'undo()' methods to work. The class's __init__() must also ensure + that deep copies of any arguments are made if appropriate. It is + guaranteed that this will only ever be called once and that it will be + called before any call to 'redo()' or 'undo()'. + """ + + raise NotImplementedError + + def merge(self, other): + """This is called by the command stack to try and merge another + command with this one. True is returned if the commands were merged. + 'other' is the command that is about to be executed. If the commands + are merged then 'other' will discarded and not placed on the command + stack. A subsequent undo or redo of this modified command must have + the same effect as the two original commands. + """ + + # By default merges never happen. + return False + + def redo(self): + """This is called by the command stack to redo the command. Any + returned value will replace the value that the command stack references + from the original call to 'do()' or previous call to 'redo()'. + """ + + raise NotImplementedError + + def undo(self): + """ This is called by the command stack to undo the command. """ + + raise NotImplementedError diff -Nru python-pyface-6.1.2/pyface/undo/action/abstract_command_stack_action.py python-pyface-7.4.0/pyface/undo/action/abstract_command_stack_action.py --- python-pyface-6.1.2/pyface/undo/action/abstract_command_stack_action.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/abstract_command_stack_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,93 @@ +# (C) Copyright 2008-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from pyface.action.api import Action +from traits.api import Instance + +# Local library imports +from ..i_undo_manager import IUndoManager + + +class AbstractCommandStackAction(Action): + """The abstract base class for all actions that operate on a command + stack. + """ + + #### 'AbstractCommandStackAction' interface ############################### + + #: The undo manager. + undo_manager = Instance(IUndoManager) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Initialise the instance. """ + + super().__init__(**traits) + + self.undo_manager.observe( + self._on_stack_updated, "stack_updated" + ) + + # Update the action to initialise it. + self._update_action() + + ########################################################################### + # 'Action' interface. + ########################################################################### + + def destroy(self): + """Called when the action is no longer required. + + By default this method does nothing, but this would be a great place to + unhook trait listeners etc. + + """ + + self.undo_manager.observe( + self._on_stack_updated, "stack_updated", remove=True + ) + + ########################################################################### + # Protected interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + raise NotImplementedError + + ########################################################################### + # Private interface. + ########################################################################### + + def _on_stack_updated(self, event): + """ Handle changes to the state of a command stack. """ + stack = event.new + # Ignore unless it is the active stack. + if stack is self.undo_manager.active_stack: + self._update_action() diff -Nru python-pyface-6.1.2/pyface/undo/action/api.py python-pyface-7.4.0/pyface/undo/action/api.py --- python-pyface-6.1.2/pyface/undo/action/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,40 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +""" + +API for ``pyface.undo.action``. + +CommandAction and useful subclasses +----------------------------------- + +- :class:`~.CommandAction` +- :class:`~.RedoAction` +- :class:`~.UndoAction` + +""" + +from .command_action import CommandAction +from .redo_action import RedoAction +from .undo_action import UndoAction diff -Nru python-pyface-6.1.2/pyface/undo/action/command_action.py python-pyface-7.4.0/pyface/undo/action/command_action.py --- python-pyface-6.1.2/pyface/undo/action/command_action.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/command_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,68 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from pyface.action.api import Action +from traits.api import Any, Callable, Instance +from ..i_command_stack import ICommandStack + + +class CommandAction(Action): + """The CommandAction class is an Action class that wraps undo/redo + commands. It is only useful for commands that do not take any arguments or + return any result. + """ + + #### 'CommandAction' interface ############################################ + + #: The command to create when the action is performed. + command = Callable() + + #: The command stack onto which the command will be pushed when the action + #: is performed. + command_stack = Instance(ICommandStack) + + #: This is the data on which the command operates. + data = Any() + + ########################################################################### + # 'Action' interface. + ########################################################################### + + def perform(self, event): + """This is reimplemented to push a new command instance onto the + command stack. + """ + + self.command_stack.push(self.command(data=self.data)) + + def _name_default(self): + """ This gets the action name from the command. """ + + if self.command: + name = self.command().name + else: + name = "" + + return name diff -Nru python-pyface-6.1.2/pyface/undo/action/__init__.py python-pyface-7.4.0/pyface/undo/action/__init__.py --- python-pyface-6.1.2/pyface/undo/action/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,9 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/undo/action/redo_action.py python-pyface-7.4.0/pyface/undo/action/redo_action.py --- python-pyface-6.1.2/pyface/undo/action/redo_action.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/redo_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,59 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Local imports. +from .abstract_command_stack_action import AbstractCommandStackAction + + +class RedoAction(AbstractCommandStackAction): + """An action that redos the last command undone of the active command + stack. + """ + + ########################################################################### + # 'Action' interface. + ########################################################################### + + def perform(self, event): + """ Perform the action. """ + + self.undo_manager.redo() + + ########################################################################### + # 'AbstractUndoAction' interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + name = self.undo_manager.redo_name + + if name: + name = "&Redo " + name + self.enabled = True + else: + name = "&Redo" + self.enabled = False + + self.name = name diff -Nru python-pyface-6.1.2/pyface/undo/action/tests/__init__.py python-pyface-7.4.0/pyface/undo/action/tests/__init__.py --- python-pyface-6.1.2/pyface/undo/action/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/tests/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,9 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/undo/action/tests/test_actions.py python-pyface-7.4.0/pyface/undo/action/tests/test_actions.py --- python-pyface-6.1.2/pyface/undo/action/tests/test_actions.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/tests/test_actions.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,51 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + +from pyface.undo.api import CommandStack, UndoManager +from pyface.undo.tests.testing_commands import SimpleCommand + +from pyface.undo.action.api import RedoAction, UndoAction + + +class TestRedoAction(unittest.TestCase): + + def setUp(self): + self.stack = CommandStack() + self.undo_manager = UndoManager() + self.stack.undo_manager = self.undo_manager + self.undo_manager.active_stack = self.stack + + self.command = SimpleCommand() + + def test_update(self): + redo_action = RedoAction(command=self.command, undo_manager=self.undo_manager) + self.stack.push(self.command) + self.undo_manager.undo() + self.assertTrue(redo_action.enabled) + self.assertEqual(redo_action.name, "&Redo Increment by 1") + + +class TestUndoAction(unittest.TestCase): + + def setUp(self): + self.stack = CommandStack() + self.undo_manager = UndoManager() + self.stack.undo_manager = self.undo_manager + self.undo_manager.active_stack = self.stack + + self.command = SimpleCommand() + + def test_update(self): + undo_action = UndoAction(command=self.command, undo_manager=self.undo_manager) + self.stack.push(self.command) + self.assertTrue(undo_action.enabled) + self.assertEqual(undo_action.name, "&Undo Increment by 1") diff -Nru python-pyface-6.1.2/pyface/undo/action/undo_action.py python-pyface-7.4.0/pyface/undo/action/undo_action.py --- python-pyface-6.1.2/pyface/undo/action/undo_action.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/action/undo_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,57 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Local imports. +from .abstract_command_stack_action import AbstractCommandStackAction + + +class UndoAction(AbstractCommandStackAction): + """ An action that undos the last command of the active command stack. """ + + ########################################################################### + # 'Action' interface. + ########################################################################### + + def perform(self, event): + """ Perform the action. """ + + self.undo_manager.undo() + + ########################################################################### + # 'AbstractUndoAction' interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + name = self.undo_manager.undo_name + + if name: + name = "&Undo " + name + self.enabled = True + else: + name = "&Undo" + self.enabled = False + + self.name = name diff -Nru python-pyface-6.1.2/pyface/undo/api.py python-pyface-7.4.0/pyface/undo/api.py --- python-pyface-6.1.2/pyface/undo/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------------ +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +""" + +API for ``pyface.undo``. + +Interfaces and Implementations +------------------------------ + +- :class:`~.ICommand` +- :class:`~.AbstractCommand` +- :class:`~.ICommandStack` +- :class:`~.CommandStack` +- :class:`~.IUndoManager` +- :class:`~.UndoManager` + +""" + +from .abstract_command import AbstractCommand +from .command_stack import CommandStack +from .i_command import ICommand +from .i_command_stack import ICommandStack +from .i_undo_manager import IUndoManager +from .undo_manager import UndoManager diff -Nru python-pyface-6.1.2/pyface/undo/command_stack.py python-pyface-7.4.0/pyface/undo/command_stack.py --- python-pyface-6.1.2/pyface/undo/command_stack.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/command_stack.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,334 @@ +# (C) Copyright 2008-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from traits.api import ( + Bool, + HasTraits, + Instance, + Int, + List, + Property, + Str, + provides, +) + +# Local imports. +from .abstract_command import AbstractCommand +from .i_command import ICommand +from .i_command_stack import ICommandStack +from .i_undo_manager import IUndoManager + + +class _StackEntry(HasTraits): + """ The _StackEntry class is a single entry on a command stack. """ + + #### '_StackEntry' interface ############################################## + + #: Set if the entry corresponds to a clean point on the stack. + clean = Bool(False) + + #: The command instance. + command = Instance(ICommand) + + #: The sequence number of the entry. + sequence_nr = Int() + + +class _MacroCommand(AbstractCommand): + """ The _MacroCommand class is an internal command that handles macros. """ + + #### '_MacroCommand' interface ############################################ + + #: The commands that make up this macro. + macro_commands = List(Instance(ICommand)) + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + """ Invoke the command. """ + + # This is a dummy. + return None + + def merge(self, other): + """ Try and merge a command. """ + + if len(self.macro_commands) == 0: + merged = False + else: + merged = self.macro_commands[-1].merge(other) + + return merged + + def redo(self): + """ Redo the sub-commands. """ + + for cmd in self.macro_commands: + cmd.redo() + + # Macros cannot return values. + return None + + def undo(self): + """ Undo the sub-commands. """ + + for cmd in self.macro_commands: + cmd.undo() + + +@provides(ICommandStack) +class CommandStack(HasTraits): + """The CommandStack class is the default implementation of the + ICommandStack interface. + """ + + #### 'ICommandStack' interface ############################################ + + #: This is the clean state of the stack. Its value changes as commands are + #: undone and redone. It can also be explicity set to mark the current + #: stack position as being clean (when the data is saved to disk for + #: example). + clean = Property(Bool) + + #: This is the name of the command that can be redone. It will be empty if + #: there is no command that can be redone. It is maintained by the undo + #: stack. + redo_name = Property(Str) + + #: This is the undo manager that manages this stack. + undo_manager = Instance(IUndoManager) + + #: This is the name of the command that can be undone. It will be empty if + #: there is no command that can be undone. It is maintained by the undo + #: stack. + undo_name = Property(Str) + + #### Private interface #################################################### + + # The current index into the stack (ie. the last command that was done). + _index = Int(-1) + + # The current macro stack. + _macro_stack = List(Instance(_MacroCommand)) + + # The stack itself. + _stack = List(Instance(_StackEntry)) + + ########################################################################### + # 'ICommandStack' interface. + ########################################################################### + + def begin_macro(self, name): + """This begins a macro by creating an empty command with the given + 'name'. All subsequent calls to 'push()' create commands that will be + children of the empty command until the next call to 'end_macro()'. + Macros may be nested. The stack is disabled (ie. nothing can be undone + or redone) while a macro is being created (ie. while there is an + outstanding 'end_macro()' call). + """ + + command = _MacroCommand(name=name) + self.push(command) + self._macro_stack.append(command) + + def clear(self): + """This clears the stack, without undoing or redoing any commands, and + leaves the stack in a clean state. It is typically used when all + changes to the data have been abandoned. + """ + + self._index = -1 + self._stack = [] + self._macro_stack = [] + + self.undo_manager.stack_updated = self + + def end_macro(self): + """ This ends a macro. """ + + try: + self._macro_stack.pop() + except IndexError: + pass + + def push(self, command): + """This executes a command and saves it on the command stack so that + it can be subsequently undone and redone. 'command' is an instance + that implements the ICommand interface. Its 'do()' method is called + to execute the command. If any value is returned by 'do()' then it is + returned by 'push()'. + """ + + # See if the command can be merged with the previous one. + if len(self._macro_stack) == 0: + if self._index >= 0 and not self._stack[self._index].clean: + merged = self._stack[self._index].command.merge(command) + else: + merged = False + else: + merged = self._macro_stack[-1].merge(command) + + # Increment the global sequence number. + if not merged: + self.undo_manager.sequence_nr += 1 + + # Execute the command. + result = command.do() + + # Update the stack state for a merged command. + if merged: + if len(self._macro_stack) == 0: + # If not in macro mode, remove everything after the current + # command from the stack. + del self._stack[self._index+1:] + self.undo_manager.stack_updated = self + return result + + # Only update the command stack if there is no current macro. + if len(self._macro_stack) == 0: + # Remove everything on the stack after the last command that was + # done. + self._index += 1 + del self._stack[self._index:] + + # Create a new stack entry and add it to the stack. + entry = _StackEntry( + command=command, sequence_nr=self.undo_manager.sequence_nr + ) + + self._stack.append(entry) + self.undo_manager.stack_updated = self + else: + # Add the command to the parent macro command. + self._macro_stack[-1].macro_commands.append(command) + + return result + + def redo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command that was undone is + redone and any result returned. Otherwise commands are redone up to + and including the given 'sequence_nr' and any result of the last of + these is returned. + """ + + # Make sure a redo is valid in the current context. + if self.redo_name == "": + return None + + if sequence_nr == 0: + result = self._redo_one() + else: + result = None + + while self._index + 1 < len(self._stack): + if self._stack[self._index + 1].sequence_nr > sequence_nr: + break + + result = self._redo_one() + + self.undo_manager.stack_updated = self + + return result + + def undo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command is undone. Otherwise + commands are undone up to and including the given 'sequence_nr'. + """ + + # Make sure an undo is valid in the current context. + if self.undo_name == "": + return + + if sequence_nr == 0: + self._undo_one() + else: + while self._index >= 0: + if self._stack[self._index].sequence_nr <= sequence_nr: + break + + self._undo_one() + + self.undo_manager.stack_updated = self + + ########################################################################### + # Private interface. + ########################################################################### + + def _redo_one(self): + """ Redo the command at the current index and return the result. """ + + self._index += 1 + entry = self._stack[self._index] + + return entry.command.redo() + + def _undo_one(self): + """ Undo the command at the current index. """ + + entry = self._stack[self._index] + self._index -= 1 + + entry.command.undo() + + def _get_clean(self): + """ Get the clean state of the stack. """ + + if self._index >= 0: + clean = self._stack[self._index].clean + else: + clean = True + + return clean + + def _set_clean(self, clean): + """ Set the clean state of the stack. """ + + if self._index >= 0: + self._stack[self._index].clean = clean + + def _get_redo_name(self): + """ Get the name of the redo command, if any. """ + + redo_name = "" + + if len(self._macro_stack) == 0 and self._index + 1 < len(self._stack): + redo_name = self._stack[self._index + 1].command.name.replace( + "&", "" + ) + + return redo_name + + def _get_undo_name(self): + """ Get the name of the undo command, if any. """ + + undo_name = "" + + if len(self._macro_stack) == 0 and self._index >= 0: + command = self._stack[self._index].command + undo_name = command.name.replace("&", "") + + return undo_name diff -Nru python-pyface-6.1.2/pyface/undo/i_command.py python-pyface-7.4.0/pyface/undo/i_command.py --- python-pyface-6.1.2/pyface/undo/i_command.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/i_command.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,75 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + + +# Enthought library imports. +from traits.api import Any, Interface, Str + + +class ICommand(Interface): + """The command interface. The state of the data can be changed by passing + an instance that implements this interface to the 'push()' method of a + command stack along with any arguments. + """ + + #### 'ICommand' interface ################################################# + + #: This is the data on which the command operates. + data = Any() + + #: This is the name of the command as it will appear in any GUI element. It + #: may include '&' which will be automatically removed whenever it is + #: inappropriate. + name = Str() + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + + def do(self): + """This is called by the command stack to do the command and to return + any value. The command must save any state necessary for the 'redo()' + and 'undo()' methods to work. The class's __init__() must also ensure + that deep copies of any arguments are made if appropriate. It is + guaranteed that this will only ever be called once and that it will be + called before any call to 'redo()' or 'undo()'. + """ + + def merge(self, other): + """This is called by the command stack to try and merge another + command with this one. True is returned if the commands were merged. + 'other' is the command that is about to be executed. If the commands + are merged then 'other' will discarded and not placed on the command + stack. A subsequent undo or redo of this modified command must have + the same effect as the two original commands. + """ + + def redo(self): + """This is called by the command stack to redo the command. Any + returned value will replace the value that the command stack references + from the original call to 'do()' or previous call to 'redo()'. + """ + + def undo(self): + """ This is called by the command stack to undo the command. """ diff -Nru python-pyface-6.1.2/pyface/undo/i_command_stack.py python-pyface-7.4.0/pyface/undo/i_command_stack.py --- python-pyface-6.1.2/pyface/undo/i_command_stack.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/i_command_stack.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,101 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2007, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from traits.api import Bool, Instance, Interface, Str + +# Local imports. +from .i_undo_manager import IUndoManager + + +class ICommandStack(Interface): + """The command stack interface. A command stack is responsible for + managing the changes to a data model and recording those changes so that + they can be undone or redone. + """ + + #### 'ICommandStack' interface ############################################ + + #: This is the clean state of the stack. Its value changes as commands are + #: undone and redone. It can also be explicity set to mark the current + #: stack position as being clean (when the data is saved to disk for + #: example). + clean = Bool() + + #: This is the name of the command that can be redone. It will be empty if + #: there is no command that can be redone. It is maintained by the undo + #: stack. + redo_name = Str() + + #: This is the undo manager that manages this stack. + undo_manager = Instance(IUndoManager) + + #: This is the name of the command that can be undone. It will be empty if + #: there is no command that can be undone. It is maintained by the undo + #: stack. + undo_name = Str() + + ########################################################################### + # 'ICommandStack' interface. + ########################################################################### + + def begin_macro(self, name): + """This begins a macro by creating an empty command with the given + 'name'. The commands passed to all subsequent calls to 'push()' will + be contained in the macro until the next call to 'end_macro()'. Macros + may be nested. The stack is disabled (ie. nothing can be undone or + redone) while a macro is being created (ie. while there is an + outstanding 'end_macro()' call). + """ + + def clear(self): + """This clears the stack, without undoing or redoing any commands, and + leaves the stack in a clean state. It is typically used when all + changes to the data have been abandoned. + """ + + def end_macro(self): + """ This ends a macro. """ + + def push(self, command): + """This executes a command and saves it on the command stack so that + it can be subsequently undone and redone. 'command' is an instance + that implements the ICommand interface. Its 'do()' method is called + to execute the command. If any value is returned by 'do()' then it is + returned by 'push()'. The command stack will keep a reference to the + result so that it can recognise it as an argument to a subsequent + command (which allows a script to properly save a result needed later). + """ + + def redo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command that was undone is + redone and any result returned. Otherwise commands are redone up to + and including the given 'sequence_nr' and any result of the last of + these is returned. + """ + + def undo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command is undone. Otherwise + commands are undone up to and including the given 'sequence_nr'. + """ diff -Nru python-pyface-6.1.2/pyface/undo/__init__.py python-pyface-7.4.0/pyface/undo/__init__.py --- python-pyface-6.1.2/pyface/undo/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,11 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Supports undoing and scripting application commands. +""" diff -Nru python-pyface-6.1.2/pyface/undo/i_undo_manager.py python-pyface-7.4.0/pyface/undo/i_undo_manager.py --- python-pyface-6.1.2/pyface/undo/i_undo_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/i_undo_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,75 @@ +# (C) Copyright 2007-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from traits.api import Bool, Event, Instance, Int, Interface, Str + + +class IUndoManager(Interface): + """The undo manager interface. An undo manager is responsible for one or + more command stacks. Typically an application would have a single undo + manager. + """ + + #### 'IUndoManager' interface ############################################# + + #: This is the currently active command stack and may be None. Typically it + #: is set when some sort of editor becomes active. + #: IUndoManager and ICommandStack depend on one another, hence we can't + #: directly import ICommandStack and use it here. + active_stack = Instance("pyface.undo.api.ICommandStack") + + #: This reflects the clean state of the currently active command stack. It + #: is intended to support a "document modified" indicator in the GUI. It is + #: maintained by the undo manager. + active_stack_clean = Bool() + + #: This is the name of the command that can be redone. It will be empty if + #: there is no command that can be redone. It is maintained by the undo + #: manager. + redo_name = Str() + + #: This is the sequence number of the next command to be performed. It is + #: incremented immediately before a command is invoked (by its 'do()' + #: method). + sequence_nr = Int() + + #: This event is fired when the index of a command stack changes. Note that + #: it may not be the active stack. + stack_updated = Event(Instance("pyface.undo.api.ICommandStack")) + + #: This is the name of the command that can be undone. It will be empty if + #: there is no command that can be undone. It is maintained by the undo + #: manager. + undo_name = Str() + + ########################################################################### + # 'IUndoManager' interface. + ########################################################################### + + def redo(self): + """ Redo the last undone command of the active command stack. """ + + def undo(self): + """ Undo the last command of the active command stack. """ diff -Nru python-pyface-6.1.2/pyface/undo/tests/__init__.py python-pyface-7.4.0/pyface/undo/tests/__init__.py --- python-pyface-6.1.2/pyface/undo/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/tests/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,9 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/undo/tests/test_command_stack.py python-pyface-7.4.0/pyface/undo/tests/test_command_stack.py --- python-pyface-6.1.2/pyface/undo/tests/test_command_stack.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/tests/test_command_stack.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,258 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from contextlib import contextmanager +import unittest + +from pyface.undo.api import CommandStack, UndoManager +from pyface.undo.tests.testing_commands import ( + MergeableCommand, SimpleCommand, UnnamedCommand, +) +from traits.testing.api import UnittestTools + + +class TestCommandStack(UnittestTools, unittest.TestCase): + def setUp(self): + self.stack = CommandStack() + undo_manager = UndoManager() + self.stack.undo_manager = undo_manager + + self.command = SimpleCommand() + + # Command pushing tests --------------------------------------------------- + + def test_empty_command_stack(self): + with self.assert_n_commands_pushed(self.stack, 0): + pass + + def test_1_command_pushed(self): + with self.assert_n_commands_pushed(self.stack, 1): + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(self.command) + + def test_n_command_pushed(self): + n = 4 + with self.assert_n_commands_pushed(self.stack, n): + for i in range(n): + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(self.command) + + def test_push_after_undo(self): + with self.assert_n_commands_pushed(self.stack, 1): + self.stack.push(self.command) + self.stack.undo() + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(self.command) + + def test_push_after_n_undo(self): + with self.assert_n_commands_pushed(self.stack, 1): + n = 4 + for i in range(n): + self.stack.push(self.command) + n = 4 + for i in range(n): + self.stack.undo() + + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(self.command) + + # Command merging tests --------------------------------------------------- + + def test_1_merge_command_pushed(self): + self.command = MergeableCommand() + with self.assert_n_commands_pushed(self.stack, 1): + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(self.command) + + def test_n_merge_command_pushed(self): + n = 4 + with self.assert_n_commands_pushed(self.stack, 1): + self.command = MergeableCommand() + self.stack.push(self.command) + for i in range(n): + command = MergeableCommand() + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(command) + self.assertEqual(self.command.amount, n+1) + + def test_merge_after_undo(self): + with self.assert_n_commands_pushed(self.stack, 2): + self.stack.push(self.command) + command = MergeableCommand() + self.stack.push(command) + command = SimpleCommand() + self.stack.push(command) + self.stack.undo() + command = MergeableCommand() + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(command) + + def test_merge_after_clean(self): + with self.assert_n_commands_pushed(self.stack, 2): + command = MergeableCommand() + self.stack.push(command) + self.stack.clean = True + command = MergeableCommand() + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.push(command) + + # Undo/Redo tests --------------------------------------------------------- + + def test_undo_1_command(self): + with self.assert_n_commands_pushed_and_undone(self.stack, 1): + self.stack.push(self.command) + self.assertEqual(self.stack.undo_name, self.command.name) + with self.assertTraitChanges(self.stack.undo_manager, + 'stack_updated', count=1): + self.stack.undo() + + def test_undo_n_command(self): + n = 4 + with self.assert_n_commands_pushed_and_undone(self.stack, n): + for i in range(n): + self.stack.push(self.command) + + for i in range(n): + self.stack.undo() + + def test_undo_redo_sequence_nr(self): + n = 4 + for i in range(n): + self.stack.push(self.command) + self.assertEqual(self.stack._index, 3) + # undo back to the 1st command in the stack + self.stack.undo(1) + self.assertEqual(self.stack._index, 0) + # redo back to the 3rd command in the stack + self.stack.redo(3) + self.assertEqual(self.stack._index, 2) + + def test_undo_unnamed_command(self): + unnamed_command = UnnamedCommand() + with self.assert_n_commands_pushed(self.stack, 1): + self.stack.push(unnamed_command) + + # But the command cannot be undone because it has no name + self.assertEqual(self.stack.undo_name, "") + # This is a no-op + self.stack.undo() + + def test_undo_redo_1_command(self): + with self.assert_n_commands_pushed(self.stack, 1): + self.stack.push(self.command) + self.stack.undo() + self.stack.redo() + + # Macro tests ------------------------------------------------------------- + + def test_define_macro(self): + with self.assert_n_commands_pushed(self.stack, 1): + add_macro(self.stack, num_commands=2) + + def test_undo_macro(self): + with self.assert_n_commands_pushed_and_undone(self.stack, 1): + # The 2 pushes are viewed as 1 command + add_macro(self.stack, num_commands=2) + self.stack.undo() + + # Cleanliness tests ------------------------------------------------------- + + def test_empty_stack_is_clean(self): + self.assertTrue(self.stack.clean) + + def test_non_empty_stack_is_dirty(self): + self.stack.push(self.command) + self.assertFalse(self.stack.clean) + + def test_make_clean(self): + # This makes it dirty by default + self.stack.push(self.command) + # Make the current tip of the stack clean + self.stack.clean = True + self.assertTrue(self.stack.clean) + + def test_make_dirty(self): + # Start from a clean state: + self.stack.push(self.command) + self.stack.clean = True + + self.stack.clean = False + self.assertFalse(self.stack.clean) + + def test_save_push_undo_is_clean(self): + self.stack.push(self.command) + + self.stack.clean = True + self.stack.push(self.command) + self.stack.undo() + self.assertTrue(self.stack.clean) + + def test_save_push_save_undo_is_clean(self): + self.stack.push(self.command) + + self.stack.clean = True + self.stack.push(self.command) + self.stack.clean = True + self.stack.undo() + self.assertTrue(self.stack.clean) + + def test_push_undo_save_redo_is_dirty(self): + self.stack.push(self.command) + self.stack.undo() + self.stack.clean = True + self.stack.redo() + self.assertFalse(self.stack.clean) + + def test_clear(self): + n = 5 + for _ in range(n): + self.stack.push(self.command) + self.stack.clear() + self.assertEqual(self.stack._stack, []) + self.assertTrue(self.stack.clean) + + # Assertion helpers ------------------------------------------------------- + + @contextmanager + def assert_n_commands_pushed(self, stack, n): + current_length = len(stack._stack) + yield + # N commands have been pushed... + self.assertEqual(len(stack._stack), current_length + n) + # ... and the state is at the tip of the stack... + self.assertEqual(stack._index, current_length + n - 1) + + @contextmanager + def assert_n_commands_pushed_and_undone(self, stack, n): + current_length = len(stack._stack) + yield + # N commands have been pushed and then reverted. The stack still + # contains the commands... + self.assertEqual(len(stack._stack), n) + # ... but we are back to the initial (clean) state + self.assertEqual(stack._index, current_length - 1) + + +def add_macro(stack, num_commands=2): + command = SimpleCommand() + stack.begin_macro("Increment n times") + try: + for i in range(num_commands): + stack.push(command) + finally: + stack.end_macro() diff -Nru python-pyface-6.1.2/pyface/undo/tests/testing_commands.py python-pyface-7.4.0/pyface/undo/tests/testing_commands.py --- python-pyface-6.1.2/pyface/undo/tests/testing_commands.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/tests/testing_commands.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,56 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from traits.api import Int +from pyface.undo.api import AbstractCommand + + +class SimpleCommand(AbstractCommand): + """ Simplest command possible operating on an integer. """ + + name = "Increment by 1" + + data = Int() + + def do(self): + self.redo() + + def redo(self): + self.data += 1 + + def undo(self): + self.data -= 1 + + +class UnnamedCommand(SimpleCommand): + name = "" + + +class MergeableCommand(SimpleCommand): + + name = "Increment" + + amount = Int(1) + + def do(self): + self.redo() + + def redo(self): + self.data += self.amount + + def undo(self): + self.data -= self.amount + + def merge(self, other): + if not isinstance(other, MergeableCommand): + return False + self.data += other.amount + self.amount += other.amount + return True diff -Nru python-pyface-6.1.2/pyface/undo/tests/test_undo_manager.py python-pyface-7.4.0/pyface/undo/tests/test_undo_manager.py --- python-pyface-6.1.2/pyface/undo/tests/test_undo_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/tests/test_undo_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,73 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +import unittest + +from traits.testing.api import UnittestTools + +from pyface.undo.api import CommandStack, UndoManager +from pyface.undo.tests.testing_commands import SimpleCommand + + +class TestUndoManager(unittest.TestCase, UnittestTools): + + def setUp(self): + self.stack_a = CommandStack() + self.stack_b = CommandStack() + self.undo_manager = UndoManager() + self.stack_a.undo_manager = self.undo_manager + self.stack_b.undo_manager = self.undo_manager + + self.undo_manager.active_stack = self.stack_a + + self.command = SimpleCommand() + + # Command pushing tests --------------------------------------------------- + + def test_undo(self): + self.assertEqual(self.stack_a._index, -1) + self.stack_a.push(self.command) + self.assertEqual(self.stack_a._index, 0) + with self.assertTraitChanges( + self.undo_manager, 'stack_updated', count=1): + self.undo_manager.undo() + self.assertEqual(self.stack_a._index, -1) + + def test_redo(self): + self.assertEqual(self.stack_a._index, -1) + self.stack_a.push(self.command) + self.undo_manager.undo() + self.assertEqual(self.stack_a._index, -1) + with self.assertTraitChanges( + self.undo_manager, 'stack_updated', count=1): + self.undo_manager.redo() + self.assertEqual(self.stack_a._index, 0) + + def test_change_active_stack(self): + for _ in range(5): + self.stack_a.push(self.command) + self.assertEqual(self.stack_a._index, 4) + self.undo_manager.active_stack = self.stack_b + for _ in range(5): + self.stack_b.push(self.command) + self.assertEqual(self.stack_b._index, 4) + for _ in range(3): + self.undo_manager.undo() + self.undo_manager.redo() + + self.assertEqual(self.stack_a._index, 4) + self.assertEqual(self.stack_b._index, 2) + + def test_active_stack_clean(self): + self.assertTrue(self.undo_manager.active_stack_clean) + self.stack_a.push(self.command) + self.assertFalse(self.undo_manager.active_stack_clean) + self.undo_manager.active_stack = None + self.assertTrue(self.undo_manager.active_stack_clean) diff -Nru python-pyface-6.1.2/pyface/undo/undo_manager.py python-pyface-7.4.0/pyface/undo/undo_manager.py --- python-pyface-6.1.2/pyface/undo/undo_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/undo/undo_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,134 @@ +# (C) Copyright 2008-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# ------------------------------------------------------------------------------ +# Copyright (c) 2008, Riverbank Computing Limited +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in enthought/LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! +# +# Author: Riverbank Computing Limited +# Description: +# ------------------------------------------------------------------------------ + +# Enthought library imports. +from traits.api import ( + Bool, + Event, + HasTraits, + Instance, + Int, + Property, + Str, + provides, + observe, +) + +# Local imports. +from .i_undo_manager import IUndoManager + + +@provides(IUndoManager) +class UndoManager(HasTraits): + """The UndoManager class is the default implementation of the + IUndoManager interface. + """ + + #### 'IUndoManager' interface ############################################# + + #: This is the currently active command stack and may be None. Typically it + #: is set when some sort of editor becomes active. + active_stack = Instance("pyface.undo.api.ICommandStack") + + #: This reflects the clean state of the currently active command stack. It + #: is intended to support a "document modified" indicator in the GUI. It is + #: maintained by the undo manager. + active_stack_clean = Property(Bool) + + #: This is the name of the command that can be redone. It will be empty if + #: there is no command that can be redone. It is maintained by the undo + #: manager. + redo_name = Property(Str) + + #: This is the sequence number of the next command to be performed. It is + #: incremented immediately before a command is invoked (by its 'do()' + #: method). + sequence_nr = Int() + + #: This event is fired when the index of a command stack changes. The value + #: of the event is the stack that has changed. Note that it may not be the + #: active stack. + stack_updated = Event() + + #: This is the name of the command that can be undone. It will be empty if + #: there is no command that can be undone. It is maintained by the undo + #: manager. + undo_name = Property(Str) + + ########################################################################### + # 'IUndoManager' interface. + ########################################################################### + + def redo(self): + """ Redo the last undone command of the active command stack. """ + + if self.active_stack is not None: + self.active_stack.redo() + + def undo(self): + """ Undo the last command of the active command stack. """ + + if self.active_stack is not None: + self.active_stack.undo() + + ########################################################################### + # Private interface. + ########################################################################### + + @observe("active_stack") + def _update_stack_updated(self, event): + """ Handle a different stack becoming active. """ + new = event.new + # Pretend that the stack contents have changed. + self.stack_updated = new + + def _get_active_stack_clean(self): + """ Get the current clean state. """ + + if self.active_stack is None: + active_stack_clean = True + else: + active_stack_clean = self.active_stack.clean + + return active_stack_clean + + def _get_redo_name(self): + """ Get the current redo name. """ + + if self.active_stack is None: + redo_name = "" + else: + redo_name = self.active_stack.redo_name + + return redo_name + + def _get_undo_name(self): + """ Get the current undo name. """ + + if self.active_stack is None: + undo_name = "" + else: + undo_name = self.active_stack.undo_name + + return undo_name diff -Nru python-pyface-6.1.2/pyface/util/color_helpers.py python-pyface-7.4.0/pyface/util/color_helpers.py --- python-pyface-6.1.2/pyface/util/color_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/color_helpers.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,117 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Routines supporting color computations + +Most of what is needed is provided by Python's builtin colorsys module, +but we need a few additional routines for things that are not covered by +that code. +""" + + +def channels_to_ints(channels, maximum=255): + """ Convert an iterable of floating point channel values to integers. + + Values are rounded to the nearest integer, rather than truncated. + + Parameters + ---------- + channels : iterable of float + An iterable of channel values, each value between 0.0 and 1.0, + inclusive. + maximum : int + The maximum value of the integer range. Common values are 15, + 65535 or 255, which is the default. + + Returns + ------- + values : tuple of int + A tuple of values as integers between 0 and max, inclusive. + """ + return tuple(int(round(channel * maximum)) for channel in channels) + + +def ints_to_channels(values, maximum=255): + """ Convert an iterable of integers to floating point channel values. + + Parameters + ---------- + values : tuple of int + An iterable of values as integers between 0 and max, inclusive. + maximum : int + The maximum value of the integer range. Common values are 15, + 65535 or 255, which is the default. + + Returns + ------- + channels : iterable of float + A tuple of channel values, each value between 0.0 and 1.0, + inclusive. + """ + return tuple(value / maximum for value in values) + + +def relative_luminance(rgb): + """ The relative luminance of the color. + + This value is the critical value when comparing colors for contrast when + displayed next to each other, in particular for readability of text. + + Parameters + ---------- + rgb : tuple of red, green, blue values + A tuple of values representing red, green and blue components of + the color, as values from 0.0 to 1.0. + + Returns + ------- + luminance : float + The relative luminance of the color. + + References + ---------- + Web Contrast Accessibility Guidelines + https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + """ + gamma_corrected = [ + x/12.92 if x <= 0.03928 else ((x + 0.055)/1.055)**2.4 + for x in rgb + ] + luminance = ( + 0.2126 * gamma_corrected[0] + + 0.7152 * gamma_corrected[1] + + 0.0722 * gamma_corrected[2] + ) + return luminance + + +def is_dark(rgb): + """ Is the color dark to human perception? + + A color is dark if white contasts better with it according to the WC3 + definition of contrast ratio. This is allows GUI code to choose either + black or white as a contrasting color for things like text on a colored + background. + + Parameters + ---------- + rgb : tuple of red, green, blue values + A tuple of values representing red, green and blue components of + the color, as values from 0.0 to 1.0. + + References + ---------- + Understanding Web Contrast Accessibility Guidelines + https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef + """ + lumininance = relative_luminance(rgb) + black_contrast = (lumininance + 0.05) / 0.05 + white_contrast = 1.05 / (lumininance + 0.05) + return white_contrast > black_contrast diff -Nru python-pyface-6.1.2/pyface/util/color_parser.py python-pyface-7.4.0/pyface/util/color_parser.py --- python-pyface-6.1.2/pyface/util/color_parser.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/color_parser.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,298 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Color string parser for Pyface. + +The ``parse_text`` function allows the creation of Color objects from +CSS-style strings, including all the CSS names colours or hex strings starting +with "#". If there is no match, a ColorParseError is raised. + +A dictionary of named colours is available as the color_table module-level +dictionary. This dictionary holds all CSS colour names, plus a number of +other names such as "transparent". + +Additionally, two utility functions ``channels_to_ints`` and +``ints_to_channels`` are provided to assist in converting between floating +point and integer values. +""" + +import re + +from .color_helpers import ints_to_channels + + +#: A dictionary mapping known color names to rgba tuples. +color_table = { + "aliceblue": (0.941, 0.973, 1.000, 1.0), + "antiquewhite": (0.980, 0.922, 0.843, 1.0), + "aqua": (0.000, 1.000, 1.000, 1.0), + "aquamarine": (0.498, 1.000, 0.831, 1.0), + "azure": (0.941, 1.000, 1.000, 1.0), + "beige": (0.961, 0.961, 0.863, 1.0), + "bisque": (1.000, 0.894, 0.769, 1.0), + "black": (0.000, 0.000, 0.000, 1.0), + "blanchedalmond": (1.000, 0.922, 0.804, 1.0), + "blue": (0.000, 0.000, 1.000, 1.0), + "blueviolet": (0.541, 0.169, 0.886, 1.0), + "brown": (0.647, 0.165, 0.165, 1.0), + "burlywood": (0.871, 0.722, 0.529, 1.0), + "cadetblue": (0.373, 0.620, 0.627, 1.0), + "chartreuse": (0.498, 1.000, 0.000, 1.0), + "chocolate": (0.824, 0.412, 0.118, 1.0), + "coral": (1.000, 0.498, 0.314, 1.0), + "cornflowerblue": (0.392, 0.584, 0.929, 1.0), + "cornsilk": (1.000, 0.973, 0.863, 1.0), + "crimson": (0.863, 0.078, 0.235, 1.0), + "cyan": (0.000, 1.000, 1.000, 1.0), + "darkblue": (0.000, 0.000, 0.545, 1.0), + "darkcyan": (0.000, 0.545, 0.545, 1.0), + "darkgoldenrod": (0.722, 0.525, 0.043, 1.0), + "darkgray": (0.663, 0.663, 0.663, 1.0), + "darkgreen": (0.000, 0.392, 0.000, 1.0), + "darkgrey": (0.663, 0.663, 0.663, 1.0), + "darkkhaki": (0.741, 0.718, 0.420, 1.0), + "darkmagenta": (0.545, 0.000, 0.545, 1.0), + "darkolivegreen": (0.333, 0.420, 0.184, 1.0), + "darkorange": (1.000, 0.549, 0.000, 1.0), + "darkorchid": (0.600, 0.196, 0.800, 1.0), + "darkred": (0.545, 0.000, 0.000, 1.0), + "darksalmon": (0.914, 0.588, 0.478, 1.0), + "darkseagreen": (0.561, 0.737, 0.561, 1.0), + "darkslateblue": (0.282, 0.239, 0.545, 1.0), + "darkslategray": (0.184, 0.310, 0.310, 1.0), + "darkslategrey": (0.184, 0.310, 0.310, 1.0), + "darkturquoise": (0.000, 0.808, 0.820, 1.0), + "darkviolet": (0.580, 0.000, 0.827, 1.0), + "deeppink": (1.000, 0.078, 0.576, 1.0), + "deepskyblue": (0.000, 0.749, 1.000, 1.0), + "dimgray": (0.412, 0.412, 0.412, 1.0), + "dimgrey": (0.412, 0.412, 0.412, 1.0), + "dodgerblue": (0.118, 0.565, 1.000, 1.0), + "firebrick": (0.698, 0.133, 0.133, 1.0), + "floralwhite": (1.000, 0.980, 0.941, 1.0), + "forestgreen": (0.133, 0.545, 0.133, 1.0), + "fuchsia": (1.000, 0.000, 1.000, 1.0), + "gainsboro": (0.863, 0.863, 0.863, 1.0), + "ghostwhite": (0.973, 0.973, 1.000, 1.0), + "gold": (1.000, 0.843, 0.000, 1.0), + "goldenrod": (0.855, 0.647, 0.125, 1.0), + "gray": (0.502, 0.502, 0.502, 1.0), + "green": (0.000, 0.502, 0.000, 1.0), + "greenyellow": (0.678, 1.000, 0.184, 1.0), + "grey": (0.502, 0.502, 0.502, 1.0), + "honeydew": (0.941, 1.000, 0.941, 1.0), + "hotpink": (1.000, 0.412, 0.706, 1.0), + "indianred": (0.804, 0.361, 0.361, 1.0), + "indigo": (0.294, 0.000, 0.510, 1.0), + "ivory": (1.000, 1.000, 0.941, 1.0), + "khaki": (0.941, 0.902, 0.549, 1.0), + "lavender": (0.902, 0.902, 0.980, 1.0), + "lavenderblush": (1.000, 0.941, 0.961, 1.0), + "lawngreen": (0.486, 0.988, 0.000, 1.0), + "lemonchiffon": (1.000, 0.980, 0.804, 1.0), + "lightblue": (0.678, 0.847, 0.902, 1.0), + "lightcoral": (0.941, 0.502, 0.502, 1.0), + "lightcyan": (0.878, 1.000, 1.000, 1.0), + "lightgoldenrodyellow": (0.980, 0.980, 0.824, 1.0), + "lightgray": (0.827, 0.827, 0.827, 1.0), + "lightgreen": (0.565, 0.933, 0.565, 1.0), + "lightgrey": (0.827, 0.827, 0.827, 1.0), + "lightpink": (1.000, 0.714, 0.757, 1.0), + "lightsalmon": (1.000, 0.627, 0.478, 1.0), + "lightseagreen": (0.125, 0.698, 0.667, 1.0), + "lightskyblue": (0.529, 0.808, 0.980, 1.0), + "lightslategray": (0.467, 0.533, 0.600, 1.0), + "lightslategrey": (0.467, 0.533, 0.600, 1.0), + "lightsteelblue": (0.690, 0.769, 0.871, 1.0), + "lightyellow": (1.000, 1.000, 0.878, 1.0), + "lime": (0.000, 1.000, 0.000, 1.0), + "limegreen": (0.196, 0.804, 0.196, 1.0), + "linen": (0.980, 0.941, 0.902, 1.0), + "magenta": (1.000, 0.000, 1.000, 1.0), + "maroon": (0.502, 0.000, 0.000, 1.0), + "mediumaquamarine": (0.400, 0.804, 0.667, 1.0), + "mediumblue": (0.000, 0.000, 0.804, 1.0), + "mediumorchid": (0.729, 0.333, 0.827, 1.0), + "mediumpurple": (0.576, 0.439, 0.859, 1.0), + "mediumseagreen": (0.235, 0.702, 0.443, 1.0), + "mediumslateblue": (0.482, 0.408, 0.933, 1.0), + "mediumspringgreen": (0.000, 0.980, 0.604, 1.0), + "mediumturquoise": (0.282, 0.820, 0.800, 1.0), + "mediumvioletred": (0.780, 0.082, 0.522, 1.0), + "midnightblue": (0.098, 0.098, 0.439, 1.0), + "mintcream": (0.961, 1.000, 0.980, 1.0), + "mistyrose": (1.000, 0.894, 0.882, 1.0), + "moccasin": (1.000, 0.894, 0.710, 1.0), + "navajowhite": (1.000, 0.871, 0.678, 1.0), + "navy": (0.000, 0.000, 0.502, 1.0), + "oldlace": (0.992, 0.961, 0.902, 1.0), + "olive": (0.502, 0.502, 0.000, 1.0), + "olivedrab": (0.420, 0.557, 0.137, 1.0), + "orange": (1.000, 0.647, 0.000, 1.0), + "orangered": (1.000, 0.271, 0.000, 1.0), + "orchid": (0.855, 0.439, 0.839, 1.0), + "palegoldenrod": (0.933, 0.910, 0.667, 1.0), + "palegreen": (0.596, 0.984, 0.596, 1.0), + "paleturquoise": (0.686, 0.933, 0.933, 1.0), + "palevioletred": (0.859, 0.439, 0.576, 1.0), + "papayawhip": (1.000, 0.937, 0.835, 1.0), + "peachpuff": (1.000, 0.855, 0.725, 1.0), + "peru": (0.804, 0.522, 0.247, 1.0), + "pink": (1.000, 0.753, 0.796, 1.0), + "plum": (0.867, 0.627, 0.867, 1.0), + "powderblue": (0.690, 0.878, 0.902, 1.0), + "purple": (0.502, 0.000, 0.502, 1.0), + "red": (1.000, 0.000, 0.000, 1.0), + "rosybrown": (0.737, 0.561, 0.561, 1.0), + "royalblue": (0.255, 0.412, 0.882, 1.0), + "saddlebrown": (0.545, 0.271, 0.075, 1.0), + "salmon": (0.980, 0.502, 0.447, 1.0), + "sandybrown": (0.957, 0.643, 0.376, 1.0), + "seagreen": (0.180, 0.545, 0.341, 1.0), + "seashell": (1.000, 0.961, 0.933, 1.0), + "sienna": (0.627, 0.322, 0.176, 1.0), + "silver": (0.753, 0.753, 0.753, 1.0), + "skyblue": (0.529, 0.808, 0.922, 1.0), + "slateblue": (0.416, 0.353, 0.804, 1.0), + "slategray": (0.439, 0.502, 0.565, 1.0), + "slategrey": (0.439, 0.502, 0.565, 1.0), + "snow": (1.000, 0.980, 0.980, 1.0), + "springgreen": (0.000, 1.000, 0.498, 1.0), + "steelblue": (0.275, 0.510, 0.706, 1.0), + "tan": (0.824, 0.706, 0.549, 1.0), + "teal": (0.000, 0.502, 0.502, 1.0), + "thistle": (0.847, 0.749, 0.847, 1.0), + "tomato": (1.000, 0.388, 0.278, 1.0), + "turquoise": (0.251, 0.878, 0.816, 1.0), + "violet": (0.933, 0.510, 0.933, 1.0), + "wheat": (0.961, 0.871, 0.702, 1.0), + "white": (1.000, 1.000, 1.000, 1.0), + "whitesmoke": (0.961, 0.961, 0.961, 1.0), + "yellow": (1.000, 1.000, 0.000, 1.0), + "yellowgreen": (0.604, 0.804, 0.196, 1.0), + "rebeccapurple": (0.4, 0.2, 0.6, 1.0), + + # Several aliases for transparent + "clear": (0.0, 0.0, 0.0, 0.0), + "transparent": (0.0, 0.0, 0.0, 0.0), + "none": (0.0, 0.0, 0.0, 0.0), +} + +# Translation table for stripping extraneous characters out of names. +ignored = str.maketrans({' ': None, '-': None, '_': None}) + + +def _parse_name(text): + """ Parse a color name. + + Parameters + ---------- + text : str + A string holding a color name, including all CSS color names, plus + any additional names found in pyface.color.color_table. The names + are normalized to lower case and stripped of whitespace, hyphens and + underscores. + + Returns + ------- + result : (space, channels), or None + Either a tuple of the form ('rgba', channels), where channels is a + tuple of 4 floating point values between 0.0 and 1.0, inclusive; + or None if there is no matching color name. + """ + text = text.lower() + text = text.translate(ignored) + if text in color_table: + return 'rgba', color_table[text] + return None + + +def _parse_hex(text): + """ Parse a hex form of a color. + + Parameters + ---------- + text : str + A string holding a hex representation of the color in the form + '#RGB', '#RGBA', '#RRGGBB', '#RRGGBBAA', '#RRRRGGGGBBBB', or + '#RRRRGGGGBBBBAAAA'. + + Returns + ------- + result : (space, channels), or None + Either a tuple of the form (space, channels), where space is one of + 'rgb' or 'rgba' and channels is a tuple of 3 or 4 floating point + values between 0.0 and 1.0, inclusive; or None if no hex + representation could be matched. + """ + text = text.strip() + if re.match("#[0-9a-fA-F]+", text) is None: + return None + text = text[1:] + if len(text) in {3, 4}: + step = 1 + elif len(text) in {6, 8}: + step = 2 + elif len(text) in {12, 16}: + step = 4 + else: + return None + maximum = (1 << 4 * step) - 1 + channels = ints_to_channels( + (int(text[i:i+step], 16) for i in range(0, len(text), step)), + maximum=maximum, + ) + space = 'rgb' if len(channels) == 3 else 'rgba' + return space, channels + + +class ColorParseError(ValueError): + """ An Exception raised when parsing fails. """ + pass + + +def parse_text(text): + """ Parse a text representation of a color. + + Parameters + ---------- + text : str + A string holding the representation of the color. This can be: + + - a color name, including all CSS color names, plus any additional + names found in pyface.color.color_table. The names are normalized + to lower case and stripped of whitespace, hyphens and underscores. + + - a hex representation of the color in the form '#RGB', '#RGBA', + '#RRGGBB', '#RRGGBBAA', '#RRRRGGGGBBBB', or '#RRRRGGGGBBBBAAAA'. + + Returns + ------- + space : str + A string describing the color space for the channels. Will be one of + 'rgb' or 'rgba'. + channels : tuple of floats + The channel values as a tuple of 3 or 4 floating point values between + 0.0 and 1.0, inclusive. + + Raises + ------ + ColorParseError + If the string cannot be converted to a valid color. + """ + result = None + for parser in _parse_hex, _parse_name: + result = parser(text) + if result is not None: + return result + else: + raise ColorParseError( + 'Unable to parse color value in string {!r}'.format(text) + ) diff -Nru python-pyface-6.1.2/pyface/util/event_loop_helper.py python-pyface-7.4.0/pyface/util/event_loop_helper.py --- python-pyface-6.1.2/pyface/util/event_loop_helper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/event_loop_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,17 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The implementation of EventLoopHelper. """ + + +# Import the toolkit specific version. +from pyface.toolkit import toolkit_object + +EventLoopHelper = toolkit_object("util.event_loop_helper:EventLoopHelper") diff -Nru python-pyface-6.1.2/pyface/util/fix_introspect_bug.py python-pyface-7.4.0/pyface/util/fix_introspect_bug.py --- python-pyface-6.1.2/pyface/util/fix_introspect_bug.py 2019-06-18 10:52:41.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/fix_introspect_bug.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Prabhu Ramachandran -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """This module adds a fix for wx.py's introspect module. In order to do code-completion, the function @@ -28,33 +25,41 @@ # Import introspect. from wx.py import introspect -import types -import six # The fixed function. -def getAttributeNames(object, includeMagic=1, includeSingle=1, - includeDouble=1): +def getAttributeNames( + object, includeMagic=1, includeSingle=1, includeDouble=1 +): """Return list of unique attributes, including inherited, for object.""" attributes = [] dict = {} if not introspect.hasattrAlwaysReturnsTrue(object): # Add some attributes that don't always get picked up. - special_attrs = ['__bases__', '__class__', '__dict__', '__name__', - '__closure__', '__code__', '___kwdefaults__', - '__doc__', '__globals__'] - attributes += [attr for attr in special_attrs \ - if hasattr(object, attr)] + special_attrs = [ + "__bases__", + "__class__", + "__dict__", + "__name__", + "__closure__", + "__code__", + "___kwdefaults__", + "__doc__", + "__globals__", + ] + attributes += [attr for attr in special_attrs if hasattr(object, attr)] # For objects that have traits, get all the trait names since # these do not show up in dir(object). - if hasattr(object, 'trait_names'): + if hasattr(object, "trait_names"): try: attributes += object.trait_names() except TypeError: pass if includeMagic: - try: attributes += object._getAttributeNames() - except: pass + try: + attributes += object._getAttributeNames() + except: + pass # Get all attribute names. attrdict = getAllAttributeNames(object) # Store the object's dir. @@ -63,12 +68,14 @@ # This complexity is necessary to avoid accessing all the # attributes of the object. This is very handy for objects # whose attributes are lazily evaluated. - if type(object).__name__ == obj_type_name and technique == 'dir': + if type(object).__name__ == obj_type_name and technique == "dir": attributes += attrlist else: - attributes += [attr for attr in attrlist \ - if attr not in object_dir and \ - hasattr(object, attr)] + attributes += [ + attr + for attr in attrlist + if attr not in object_dir and hasattr(object, attr) + ] # Remove duplicates from the attribute list. for item in attributes: @@ -76,23 +83,26 @@ attributes = list(dict.keys()) # new-style swig wrappings can result in non-string attributes # e.g. ITK http://www.itk.org/ - attributes = [attribute for attribute in attributes \ - if isinstance(attribute, six.string_types)] + attributes = [ + attribute for attribute in attributes if isinstance(attribute, str) + ] attributes.sort(key=lambda x: x.upper()) if not includeSingle: - attributes = filter(lambda item: item[0]!='_' \ - or item[1]=='_', attributes) + attributes = filter( + lambda item: item[0] != "_" or item[1] == "_", attributes + ) if not includeDouble: - attributes = filter(lambda item: item[:2]!='__', attributes) + attributes = filter(lambda item: item[:2] != "__", attributes) return attributes # Replace introspect's version with ours. introspect.getAttributeNames = getAttributeNames + # This is also a modified version of the function which does not use # str(object). -def getAllAttributeNames(object): +def getAllAttributeNames(obj): """Return dict of all attributes, including inherited, for an object. Recursively walk through a class and all base classes. @@ -106,30 +116,30 @@ try: # This could(?) fail if the type is poorly defined without # even a name. - key = type(object).__name__ + key = type(obj).__name__ except: - key = 'anonymous' + key = "anonymous" # Wake up sleepy objects - a hack for ZODB objects in "ghost" state. - wakeupcall = dir(object) + wakeupcall = dir(obj) del wakeupcall # Get attributes available through the normal convention. - attributes = dir(object) - attrdict[(key, 'dir', len(attributes))] = attributes + attributes = dir(obj) + attrdict[(key, "dir", len(attributes))] = attributes # Get attributes from the object's dictionary, if it has one. try: - attributes = list(object.__dict__.keys()) + attributes = list(obj.__dict__.keys()) attributes.sort() except: # Must catch all because object might have __getattr__. pass else: - attrdict[(key, '__dict__', len(attributes))] = attributes + attrdict[(key, "__dict__", len(attributes))] = attributes # For a class instance, get the attributes for the class. try: - klass = object.__class__ + klass = obj.__class__ except: # Must catch all because object might have __getattr__. pass else: - if klass is object: + if klass is obj: # Break a circular reference. This happens with extension # classes. pass @@ -137,14 +147,14 @@ attrdict.update(getAllAttributeNames(klass)) # Also get attributes from any and all parent classes. try: - bases = object.__bases__ + bases = obj.__bases__ except: # Must catch all because object might have __getattr__. pass else: - if isinstance(bases, types.TupleType): + if isinstance(bases, tuple): for base in bases: - if type(base) is types.TypeType: - # Break a circular reference. Happens in Python 2.2. + if isinstance(base, (tuple, object)): + # Break a circular reference. Happens in Python 2.2. & 3.6 (prob others) pass else: attrdict.update(getAllAttributeNames(base)) diff -Nru python-pyface-6.1.2/pyface/util/font_helper.py python-pyface-7.4.0/pyface/util/font_helper.py --- python-pyface-6.1.2/pyface/util/font_helper.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/font_helper.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.font_helper, use pyface.wx.utils.font_helper instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.util.font_helper import * diff -Nru python-pyface-6.1.2/pyface/util/font_parser.py python-pyface-7.4.0/pyface/util/font_parser.py --- python-pyface-6.1.2/pyface/util/font_parser.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/font_parser.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,145 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + + +GENERIC_FAMILIES = { + 'default', 'fantasy', 'decorative', 'serif', 'roman', 'cursive', 'script', + 'sans-serif', 'swiss', 'monospace', 'modern', 'typewriter', 'teletype' +} +WEIGHTS = { + 'thin', 'extra-light', 'light', 'regular', 'medium', 'demi-bold', + 'bold', 'extra-bold', 'heavy', 'extra-heavy' +} +STRETCHES = { + 'ultra-condensed', 'extra-condensed', 'condensed', + 'semi-condensed', 'semi-expanded', 'expanded', 'extra-expanded', + 'ultra-expanded' +} +STYLES = {'italic', 'oblique'} +VARIANTS = {'small-caps'} +DECORATIONS = {'underline', 'strikethrough', 'overline'} +NOISE = {'pt', 'point', 'px', 'family'} + + +class FontParseError(ValueError): + """An exception raised when font parsing fails.""" + pass + + +def simple_parser(description): + """An extremely simple font description parser. + + The parser is simple, and works by splitting the description on whitespace + and examining each resulting token for understood terms: + + Size + The first numeric term is treated as the font size. + + Weight + The following weight terms are accepted: 'thin', 'extra-light', + 'light', 'regular', 'medium', 'demi-bold', 'bold', 'extra-bold', + 'heavy', 'extra-heavy'. + + Stretch + The following stretch terms are accepted: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'semi-expanded', + 'expanded', 'extra-expanded', 'ultra-expanded'. + + Style + The following style terms are accepted: 'italic', 'oblique'. + + Variant + The following variant terms are accepted: 'small-caps'. + + Decorations + The following decoration terms are accepted: 'underline', + 'strikethrough', 'overline'. + + Generic Families + The following generic family terms are accepted: 'default', 'fantasy', + 'decorative', 'serif', 'roman', 'cursive', 'script', 'sans-serif', + 'swiss', 'monospace', 'modern', 'typewriter', 'teletype'. + + In addtion, the parser ignores the terms 'pt', 'point', 'px', and 'family'. + Any remaining terms are combined into the typeface name. There is no + expected order to the terms. + + This parser is roughly compatible with the various ad-hoc parsers in + TraitsUI and Kiva, allowing for the slight differences between them and + adding support for additional options supported by Pyface fonts, such as + stretch and variants. + + Parameters + ---------- + description : str + The font description to be parsed. + + Returns + ------- + properties : dict + Font properties suitable for use in creating a Pyface Font. + + Notes + ----- + This is not a particularly good parser, as it will fail to properly + parse something like "10 pt times new roman" or "14 pt computer modern" + since they have generic font names as part of the font face name. + """ + face = [] + generic_family = "" + size = None + weight = "normal" + stretch = "normal" + style = "normal" + variants = set() + decorations = set() + for word in description.lower().split(): + if word in NOISE: + continue + elif word in GENERIC_FAMILIES: + generic_family = word + elif word in WEIGHTS: + weight = word + elif word in STRETCHES: + stretch = word + elif word in STYLES: + style = word + elif word in VARIANTS: + variants.add(word) + elif word in DECORATIONS: + decorations.add(word) + else: + if size is None: + try: + size = float(word) + continue + except ValueError: + pass + face.append(word) + + family = [] + if face: + family.append(" ".join(face)) + if generic_family: + family.append(generic_family) + if not family: + family = ["default"] + if size is None: + size = 12 + + return { + 'family': family, + 'size': size, + 'weight': weight, + 'stretch': stretch, + 'style': style, + 'variants': variants, + 'decorations': decorations, + } diff -Nru python-pyface-6.1.2/pyface/util/grid/api.py python-pyface-7.4.0/pyface/util/grid/api.py --- python-pyface-6.1.2/pyface/util/grid/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/grid/api.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.grid.api, use pyface.wx.grid.api instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.grid.api import * diff -Nru python-pyface-6.1.2/pyface/util/grid/grid_column.py python-pyface-7.4.0/pyface/util/grid/grid_column.py --- python-pyface-6.1.2/pyface/util/grid/grid_column.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/grid/grid_column.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.grid.grid_column, use pyface.wx.grid.grid_column instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.grid.grid_column import * diff -Nru python-pyface-6.1.2/pyface/util/grid/grid_model.py python-pyface-7.4.0/pyface/util/grid/grid_model.py --- python-pyface-6.1.2/pyface/util/grid/grid_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/grid/grid_model.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.grid.grid_model, use pyface.wx.grid.grid_model instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.grid.grid_model import * diff -Nru python-pyface-6.1.2/pyface/util/grid/grid.py python-pyface-7.4.0/pyface/util/grid/grid.py --- python-pyface-6.1.2/pyface/util/grid/grid.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/grid/grid.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.grid.grid, use pyface.wx.grid.grid instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.grid.grid import * diff -Nru python-pyface-6.1.2/pyface/util/grid/grid_row.py python-pyface-7.4.0/pyface/util/grid/grid_row.py --- python-pyface-6.1.2/pyface/util/grid/grid_row.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/grid/grid_row.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.grid.grid_row, use pyface.wx.grid.grid_row instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.grid.grid_row import * diff -Nru python-pyface-6.1.2/pyface/util/grid/__init__.py python-pyface-7.4.0/pyface/util/grid/__init__.py --- python-pyface-6.1.2/pyface/util/grid/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/grid/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.grid, use pyface.wx.grid instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.grid import * diff -Nru python-pyface-6.1.2/pyface/util/guisupport.py python-pyface-7.4.0/pyface/util/guisupport.py --- python-pyface-6.1.2/pyface/util/guisupport.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/guisupport.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,3 +1,12 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Support for creating GUI apps and starting event loops. @@ -56,43 +65,47 @@ """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Copyright (C) 2008-2010 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Prevent name conflict with local wx package. -from __future__ import absolute_import -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # wx -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def get_app_wx(*args, **kwargs): """Create a new wx app or return an exiting one.""" import wx + app = wx.GetApp() if app is None: - if 'redirect' not in kwargs: - kwargs['redirect'] = False - app = wx.PySimpleApp(*args, **kwargs) + if "redirect" not in kwargs: + kwargs["redirect"] = False + app = wx.App(*args, **kwargs) return app + def is_event_loop_running_wx(app=None): """Is the wx event loop running.""" if app is None: app = get_app_wx() - if hasattr(app, '_in_event_loop'): + if hasattr(app, "_in_event_loop"): return app._in_event_loop else: return app.IsMainLoopRunning() + def start_event_loop_wx(app=None): """Start the wx event loop in a consistent manner.""" if app is None: @@ -104,34 +117,39 @@ else: app._in_event_loop = True -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # qt4 -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def get_app_qt4(*args, **kwargs): """Create a new qt4 app or return an existing one.""" from pyface.qt import QtGui + app = QtGui.QApplication.instance() if app is None: if not args: - args = ([''],) + args = ([""],) app = QtGui.QApplication(*args, **kwargs) return app + def is_event_loop_running_qt4(app=None): """Is the qt4 event loop running.""" if app is None: - app = get_app_qt4(['']) - if hasattr(app, '_in_event_loop'): + app = get_app_qt4([""]) + if hasattr(app, "_in_event_loop"): return app._in_event_loop else: # Does qt4 provide a other way to detect this? return False + def start_event_loop_qt4(app=None): """Start the qt4 event loop in a consistent manner.""" if app is None: - app = get_app_qt4(['']) + app = get_app_qt4([""]) if not is_event_loop_running_qt4(app): app._in_event_loop = True app.exec_() @@ -139,10 +157,11 @@ else: app._in_event_loop = True -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Tk -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # gtk -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- diff -Nru python-pyface-6.1.2/pyface/util/gui_test_assistant.py python-pyface-7.4.0/pyface/util/gui_test_assistant.py --- python-pyface-6.1.2/pyface/util/gui_test_assistant.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/gui_test_assistant.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,17 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The implementation of a Gui Test Assistant. """ + + +# Import the toolkit specific version. +from pyface.toolkit import toolkit_object + +GuiTestAssistant = toolkit_object("util.gui_test_assistant:GuiTestAssistant") diff -Nru python-pyface-6.1.2/pyface/util/id_helper.py python-pyface-7.4.0/pyface/util/id_helper.py --- python-pyface-6.1.2/pyface/util/id_helper.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/id_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005-2013, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Helper functions to automatically generate unique IDs. """ from weakref import WeakKeyDictionary + class _ObjectCounter(object): """ Counts objects. """ @@ -47,6 +45,7 @@ self._objects_registry[obj] = count + 1 return self._objects_registry[obj] + # Global object counter. object_counter = _ObjectCounter() @@ -63,4 +62,4 @@ name = class_.__name__ number = object_counter.next_count(class_) - return name + '_' + str(number) + return name + "_" + str(number) diff -Nru python-pyface-6.1.2/pyface/util/image_helpers.py python-pyface-7.4.0/pyface/util/image_helpers.py --- python-pyface-6.1.2/pyface/util/image_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/image_helpers.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,48 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" Helper functions for working with images + +This module provides helper functions for converting between numpy arrays +and toolkit images, as well as between the various image types in a standardized +way. + +Helper functions +---------------- + +- :data:`~.array_to_image` +- :data:`~.bitmap_to_icon` +- :data:`~.bitmap_to_image` +- :data:`~.image_to_array` +- :data:`~.image_to_bitmap` +- :data:`~.resize_image` +- :data:`~.resize_bitmap` + +Options for resizing images +--------------------------- +- :data:`~.ScaleMode` +- :data:`~.AspectRatio` + +""" + +from pyface.toolkit import toolkit_object + +# Enum types for function arguments +ScaleMode = toolkit_object("util.image_helpers:ScaleMode") +AspectRatio = toolkit_object("util.image_helpers:AspectRatio") + +# Helper functions +array_to_image = toolkit_object("util.image_helpers:array_to_image") +bitmap_to_icon = toolkit_object("util.image_helpers:bitmap_to_icon") +bitmap_to_image = toolkit_object("util.image_helpers:bitmap_to_image") +image_to_array = toolkit_object("util.image_helpers:image_to_array") +image_to_bitmap = toolkit_object("util.image_helpers:image_to_bitmap") +resize_image = toolkit_object("util.image_helpers:resize_image") +resize_bitmap = toolkit_object("util.image_helpers:resize_bitmap") diff -Nru python-pyface-6.1.2/pyface/util/__init__.py python-pyface-7.4.0/pyface/util/__init__.py --- python-pyface-6.1.2/pyface/util/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/util/modal_dialog_tester.py python-pyface-7.4.0/pyface/util/modal_dialog_tester.py --- python-pyface-6.1.2/pyface/util/modal_dialog_tester.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/modal_dialog_tester.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,19 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +""" The implementation of a Modal Dialog Tester. """ + + +# Import the toolkit specific version. +from pyface.toolkit import toolkit_object + +ModalDialogTester = toolkit_object( + "util.modal_dialog_tester:ModalDialogTester" +) diff -Nru python-pyface-6.1.2/pyface/util/_optional_dependencies.py python-pyface-7.4.0/pyface/util/_optional_dependencies.py --- python-pyface-6.1.2/pyface/util/_optional_dependencies.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/_optional_dependencies.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,38 @@ + +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Utilities for handling optional import dependencies.""" + +import contextlib + + +@contextlib.contextmanager +def optional_import(dependency_name, msg, logger): + """ Context manager for capturing ImportError for a particular optional + dependency. If such an error occurs, it will be silenced and a debug + message will be logged. + + Parameters + ---------- + dependency_name : str + Name of the module that may fail to be imported. + If matched, the ImportError will be silenced + msg : str + Log message to be emitted. + logger : Logger + Logger to use for logging messages. + """ + try: + yield + except ImportError as exception: + if exception.name == dependency_name: + logger.debug(msg, exc_info=True) + else: + raise diff -Nru python-pyface-6.1.2/pyface/util/python_stc.py python-pyface-7.4.0/pyface/util/python_stc.py --- python-pyface-6.1.2/pyface/util/python_stc.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/python_stc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.util.python_stc, use pyface.wx.python_stc instead. ' - 'Will be removed in Pyface 7.') - -from pyface.wx.python_stc import * diff -Nru python-pyface-6.1.2/pyface/util/testing.py python-pyface-7.4.0/pyface/util/testing.py --- python-pyface-6.1.2/pyface/util/testing.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/testing.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,74 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from functools import wraps +import re +from unittest import TestSuite + +from packaging.version import Version + +from traits import __version__ as TRAITS_VERSION + + +def filter_tests(test_suite, exclusion_pattern): + filtered_test_suite = TestSuite() + for item in test_suite: + if isinstance(item, TestSuite): + filtered = filter_tests(item, exclusion_pattern) + filtered_test_suite.addTest(filtered) + else: + match = re.search(exclusion_pattern, item.id()) + if match is not None: + skip_msg = "Test excluded via pattern '{}'".format( + exclusion_pattern + ) + setattr(item, 'setUp', lambda: item.skipTest(skip_msg)) + filtered_test_suite.addTest(item) + return filtered_test_suite def has_traitsui(): - """ Is traitsui installed? """ + """ Is traitsui installed and sufficiently recent? """ try: - import traitsui + import traitsui # noqa: F401 except ImportError: return False + from pyface.toolkit import toolkit + if toolkit.toolkit.startswith("qt"): + from pyface.qt import is_qt6 + if is_qt6: + return Version(traitsui.__version__) >= Version("7.4") return True def skip_if_no_traitsui(test): """ Decorator that skips test if traitsui not available """ + @wraps(test) def new_test(self): if has_traitsui(): test(self) else: self.skipTest("Can't import traitsui.") + return new_test + + +def is_traits_version_ge(version): + """ Return true if the traits version is greater than or equal to the + required value. + + Parameters + ---------- + version : str + Version to be parsed. e.g. "6.0" + """ + traits_version = Version(TRAITS_VERSION) + given_version = Version(version) + return traits_version >= given_version diff -Nru python-pyface-6.1.2/pyface/util/tests/test_color_helpers.py python-pyface-7.4.0/pyface/util/tests/test_color_helpers.py --- python-pyface-6.1.2/pyface/util/tests/test_color_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_color_helpers.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,164 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from pyface.util.color_helpers import ( + channels_to_ints, ints_to_channels, is_dark, relative_luminance +) + + +class TestChannelConversion(TestCase): + + def test_ints_to_channels(self): + values = (102, 102, 0, 255) + channels = ints_to_channels(values) + self.assertEqual(channels, (0.4, 0.4, 0.0, 1.0)) + + def test_ints_to_channels_maximum(self): + values = (6, 6, 0, 15) + channels = ints_to_channels(values, maximum=15) + self.assertEqual(channels, (0.4, 0.4, 0.0, 1.0)) + + def test_channels_to_ints(self): + channels = (0.4, 0.4, 0.0, 1.0) + values = channels_to_ints(channels) + self.assertEqual(values, (102, 102, 0, 255)) + + def test_channels_to_ints_maximum(self): + channels = (0.4, 0.4, 0.0, 1.0) + values = channels_to_ints(channels, maximum=15) + self.assertEqual(values, (6, 6, 0, 15)) + + def test_round_trip(self): + """ Test to assert stability of values through round-trips """ + for value in range(256): + with self.subTest(int=value): + result = channels_to_ints(ints_to_channels([value])) + self.assertEqual(result, (value,)) + + def test_round_trip_maximum(self): + """ Test to assert stability of values through round-trips """ + for value in range(65536): + with self.subTest(int=value): + result = channels_to_ints( + ints_to_channels( + [value], + maximum=65535, + ), + maximum=65535, + ) + self.assertEqual(result, (value,)) + + +class TestRelativeLuminance(TestCase): + + def test_white(self): + rgb = (1.0, 1.0, 1.0) + result = relative_luminance(rgb) + self.assertEqual(result, 1.0) + + def test_black(self): + rgb = (0.0, 0.0, 0.0) + result = relative_luminance(rgb) + self.assertEqual(result, 0.0) + + def test_red(self): + rgb = (1.0, 0.0, 0.0) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.2126) + + def test_green(self): + rgb = (0.0, 1.0, 0.0) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.7152) + + def test_blue(self): + rgb = (0.0, 0.0, 1.0) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.0722) + + def test_yellow(self): + rgb = (1.0, 1.0, 0.0) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.2126 + 0.7152) + + def test_cyan(self): + rgb = (0.0, 1.0, 1.0) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.7152 + 0.0722) + + def test_magenta(self): + rgb = (1.0, 0.0, 1.0) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.2126 + 0.0722) + + def test_dark_grey(self): + rgb = (0.01, 0.01, 0.01) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, 0.01/12.92) + + def test_medium_grey(self): + rgb = (0.5, 0.5, 0.5) + result = relative_luminance(rgb) + self.assertAlmostEqual(result, (0.555/1.055)**2.4) + + +class TestIsDark(TestCase): + + def test_white(self): + rgb = (1.0, 1.0, 1.0) + result = is_dark(rgb) + self.assertFalse(result) + + def test_black(self): + rgb = (0.0, 0.0, 0.0) + result = is_dark(rgb) + self.assertTrue(result) + + def test_red(self): + rgb = (1.0, 0.0, 0.0) + result = is_dark(rgb) + self.assertFalse(result) + + def test_green(self): + rgb = (0.0, 1.0, 0.0) + result = is_dark(rgb) + self.assertFalse(result) + + def test_blue(self): + rgb = (0.0, 0.0, 1.0) + result = is_dark(rgb) + self.assertTrue(result) + + def test_yellow(self): + rgb = (1.0, 1.0, 0.0) + result = is_dark(rgb) + self.assertFalse(result) + + def test_cyan(self): + rgb = (0.0, 1.0, 1.0) + result = is_dark(rgb) + self.assertFalse(result) + + def test_magenta(self): + rgb = (1.0, 0.0, 1.0) + result = is_dark(rgb) + self.assertFalse(result) + + def test_dark_grey(self): + rgb = (0.01, 0.01, 0.01) + result = is_dark(rgb) + self.assertTrue(result) + + def test_medium_grey(self): + rgb = (0.5, 0.5, 0.5) + result = is_dark(rgb) + self.assertFalse(result) diff -Nru python-pyface-6.1.2/pyface/util/tests/test_color_parser.py python-pyface-7.4.0/pyface/util/tests/test_color_parser.py --- python-pyface-6.1.2/pyface/util/tests/test_color_parser.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_color_parser.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,89 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from unittest import TestCase + +from ..color_parser import ( + ColorParseError, color_table, parse_text, _parse_hex, _parse_name +) + + +class TestParseHex(TestCase): + + def test_hex_3(self): + space, channels = _parse_hex('#06c') + self.assertEqual(space, 'rgb') + self.assertEqual(channels, (0.0, 0.4, 0.8)) + + def test_hex_4(self): + space, channels = _parse_hex('#06cf') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.0, 0.4, 0.8, 1.0)) + + def test_hex_6(self): + space, channels = _parse_hex('#0066cc') + self.assertEqual(space, 'rgb') + self.assertEqual(channels, (0.0, 0.4, 0.8)) + + def test_hex_8(self): + space, channels = _parse_hex('#0066ccff') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.0, 0.4, 0.8, 1.0)) + + def test_hex_12(self): + space, channels = _parse_hex('#00006666cccc') + self.assertEqual(space, 'rgb') + self.assertEqual(channels, (0.0, 0.4, 0.8)) + + def test_hex_16(self): + space, channels = _parse_hex('#00006666ccccffff') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.0, 0.4, 0.8, 1.0)) + + def test_hex_bad(self): + result = _parse_hex('#0c') + self.assertIsNone(result) + + +class TestParseName(TestCase): + + def test_names(self): + for name, value in color_table.items(): + with self.subTest(color=name): + space, channels = _parse_name(name) + self.assertEqual(space, 'rgba') + self.assertEqual(channels, value) + + def test_name_space(self): + space, channels = _parse_name('rebecca purple') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.4, 0.2, 0.6, 1.0)) + + def test_name_capitals(self): + space, channels = _parse_name('RebeccaPurple') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.4, 0.2, 0.6, 1.0)) + + +class TestParseText(TestCase): + + def test_name(self): + space, channels = parse_text('rebeccapurple') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.4, 0.2, 0.6, 1.0)) + + def test_hex(self): + space, channels = parse_text('#663399ff') + self.assertEqual(space, 'rgba') + self.assertEqual(channels, (0.4, 0.2, 0.6, 1.0)) + + def test_error(self): + with self.assertRaises(ColorParseError): + parse_text('invalidcolorname') diff -Nru python-pyface-6.1.2/pyface/util/tests/test_event_loop_helper.py python-pyface-7.4.0/pyface/util/tests/test_event_loop_helper.py --- python-pyface-6.1.2/pyface/util/tests/test_event_loop_helper.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_event_loop_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,23 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source!from unittest import TestCase + +import unittest + +from pyface.toolkit import toolkit + +is_wx = (toolkit.toolkit == "wx") + + +class TestEventLoopHelper(unittest.TestCase): + + @unittest.skipIf(is_wx, "wx is not supported") + def test_import(self): + from pyface.util.event_loop_helper import EventLoopHelper + self.assertNotEqual(EventLoopHelper.__name__, "Unimplemented") diff -Nru python-pyface-6.1.2/pyface/util/tests/test_font_parser.py python-pyface-7.4.0/pyface/util/tests/test_font_parser.py --- python-pyface-6.1.2/pyface/util/tests/test_font_parser.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_font_parser.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,198 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +from itertools import chain, combinations +from unittest import TestCase + +from ..font_parser import ( + DECORATIONS, + GENERIC_FAMILIES, + NOISE, + STRETCHES, + STYLES, + VARIANTS, + WEIGHTS, + simple_parser, +) + + +class TestSimpleParser(TestCase): + + def test_empty(self): + properties = simple_parser("") + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': "normal", + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_typical(self): + properties = simple_parser( + "10 pt bold condensed italic underline Helvetica sans-serif") + self.assertEqual( + properties, + { + 'family': ["helvetica", "sans-serif"], + 'size': 10.0, + 'weight': "bold", + 'stretch': "condensed", + 'style': "italic", + 'variants': set(), + 'decorations': {"underline"}, + }, + ) + + def test_noise(self): + for noise in NOISE: + with self.subTest(noise=noise): + properties = simple_parser(noise) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': "normal", + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_generic_families(self): + for family in GENERIC_FAMILIES: + with self.subTest(family=family): + properties = simple_parser(family) + self.assertEqual( + properties, + { + 'family': [family], + 'size': 12.0, + 'weight': "normal", + 'stretch': "normal", + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_size(self): + for size in [12, 24, 12.5]: + with self.subTest(size=size): + properties = simple_parser(str(size)) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': size, + 'weight': "normal", + 'stretch': "normal", + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_weight(self): + for weight in WEIGHTS: + with self.subTest(weight=weight): + properties = simple_parser(weight) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': weight, + 'stretch': "normal", + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_stretch(self): + for stretch in STRETCHES: + with self.subTest(stretch=stretch): + properties = simple_parser(stretch) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': stretch, + 'style': "normal", + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_style(self): + for style in STYLES: + with self.subTest(style=style): + properties = simple_parser(style) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': "normal", + 'style': style, + 'variants': set(), + 'decorations': set(), + }, + ) + + def test_variant(self): + for variant in VARIANTS: + with self.subTest(variant=variant): + properties = simple_parser(variant) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': "normal", + 'style': "normal", + 'variants': {variant}, + 'decorations': set(), + }, + ) + + def test_decorations(self): + # get powerset iterator of DECORATIONS + all_decorations = chain.from_iterable( + combinations(DECORATIONS, n) + for n in range(len(DECORATIONS) + 1) + ) + for decorations in all_decorations: + with self.subTest(decorations=decorations): + properties = simple_parser(" ".join(decorations)) + self.assertEqual( + properties, + { + 'family': ["default"], + 'size': 12.0, + 'weight': "normal", + 'stretch': "normal", + 'style': "normal", + 'variants': set(), + 'decorations': set(decorations), + }, + ) diff -Nru python-pyface-6.1.2/pyface/util/tests/test_gui_test_assistant.py python-pyface-7.4.0/pyface/util/tests/test_gui_test_assistant.py --- python-pyface-6.1.2/pyface/util/tests/test_gui_test_assistant.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_gui_test_assistant.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,23 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source!from unittest import TestCase + +import unittest + +from pyface.toolkit import toolkit + +is_wx = (toolkit.toolkit == "wx") + + +class TestGuiTestAssistant(unittest.TestCase): + + @unittest.skipIf(is_wx, "wx is not supported") + def test_import(self): + from pyface.util.gui_test_assistant import GuiTestAssistant + self.assertNotEqual(GuiTestAssistant.__name__, "Unimplemented") diff -Nru python-pyface-6.1.2/pyface/util/tests/test_id_helper.py python-pyface-7.4.0/pyface/util/tests/test_id_helper.py --- python-pyface-6.1.2/pyface/util/tests/test_id_helper.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_id_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,11 +1,13 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Enthought product code -# -# (C) Copyright 2012 Enthought, Inc., Austin, TX -# All right reserved. -# -# This file is confidential and NOT open source. Do not distribute. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # +# Thanks for using Enthought open source! + """ Test the scripting tools. """ @@ -16,7 +18,7 @@ class IDHelperTestCase(unittest.TestCase): """ Test the scripting tools. """ - #### Tests ################################################################ + # Tests ---------------------------------------------------------------- def test_object_counter(self): @@ -30,25 +32,19 @@ foo = Foo() - self.assertEqual(object_counter.get_count(Bogus), 0) + self.assertEqual(object_counter.get_count(Bogus), 0) self.assertEqual(object_counter.next_count(Bogus), 1) self.assertEqual(object_counter.next_count(Bogus), 2) - self.assertEqual(object_counter.get_count(Bogus), 2) - self.assertEqual(object_counter.next_count(foo), 1) + self.assertEqual(object_counter.get_count(Bogus), 2) + self.assertEqual(object_counter.next_count(foo), 1) self.assertEqual(object_counter.next_count(Bogus), 3) def test_get_unique_id(self): - class Bogus(object): pass bogus_1 = Bogus() bogus_2 = Bogus() - self.assertEqual(get_unique_id(bogus_1), 'Bogus_1') - self.assertEqual(get_unique_id(bogus_2), 'Bogus_2') - -if __name__ == '__main__': - unittest.main() - -#### EOF ###################################################################### + self.assertEqual(get_unique_id(bogus_1), "Bogus_1") + self.assertEqual(get_unique_id(bogus_2), "Bogus_2") diff -Nru python-pyface-6.1.2/pyface/util/tests/test_image_helpers.py python-pyface-7.4.0/pyface/util/tests/test_image_helpers.py --- python-pyface-6.1.2/pyface/util/tests/test_image_helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_image_helpers.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,21 @@ +# Copyright (c) 2005-2022, Enthought Inc. +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only +# under the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# Thanks for using Enthought open source! + +import unittest + + +class TestImageHelpers(unittest.TestCase): + + def test_imports(self): + # actual functions are tested in toolkits + from ..image_helpers import ( # noqa: F401 + AspectRatio, ScaleMode, array_to_image, bitmap_to_icon, + bitmap_to_image, image_to_array, image_to_bitmap, + resize_bitmap, resize_image, + ) diff -Nru python-pyface-6.1.2/pyface/util/tests/test_modal_dialog_tester.py python-pyface-7.4.0/pyface/util/tests/test_modal_dialog_tester.py --- python-pyface-6.1.2/pyface/util/tests/test_modal_dialog_tester.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test_modal_dialog_tester.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,23 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source!from unittest import TestCase + +import unittest + +from pyface.toolkit import toolkit + +is_wx = (toolkit.toolkit == "wx") + + +class TestModalDialogTester(unittest.TestCase): + + @unittest.skipIf(is_wx, "wx is not supported") + def test_import(self): + from pyface.util.modal_dialog_tester import ModalDialogTester + self.assertNotEqual(ModalDialogTester.__name__, "Unimplemented") diff -Nru python-pyface-6.1.2/pyface/util/tests/test__optional_dependencies.py python-pyface-7.4.0/pyface/util/tests/test__optional_dependencies.py --- python-pyface-6.1.2/pyface/util/tests/test__optional_dependencies.py 1970-01-01 00:00:00.000000000 +0000 +++ python-pyface-7.4.0/pyface/util/tests/test__optional_dependencies.py 2022-02-01 12:21:15.000000000 +0000 @@ -0,0 +1,38 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! +""" Tests for the _optional_dependencies module """ + +import logging +import unittest + +from pyface.util._optional_dependencies import optional_import + + +class TestOptionalImport(unittest.TestCase): + """ Test optional import context manager """ + + def test_optional_import(self): + # Test excusing dependency and the logging behaviour + logger = logging.getLogger(self.id()) + with self.assertLogs(logger, level="DEBUG") as log_context: + with optional_import( + "random_missing_lib", "fail to import", logger): + # assume this library is not importable. + import random_missing_lib # noqa: F401 + + log, = log_context.output + self.assertIn("fail to import", log) + + def test_optional_import_reraise(self): + # Test if the import error was about something else, reraise + logger = logging.getLogger(self.id()) + with self.assertRaises(ImportError): + with optional_import("some_random_lib", "", logger): + import some_random_missing_lib # noqa: F401 diff -Nru python-pyface-6.1.2/pyface/_version.py python-pyface-7.4.0/pyface/_version.py --- python-pyface-6.1.2/pyface/_version.py 2019-07-22 09:15:04.000000000 +0000 +++ python-pyface-7.4.0/pyface/_version.py 2022-02-02 10:48:06.000000000 +0000 @@ -1,8 +1,25 @@ -# THIS FILE IS GENERATED FROM PYFACE SETUP.PY -version = '6.1.2' -full_version = '6.1.2' +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# THIS FILE IS GENERATED FROM SETUP.PY + +#: The full version of the package, including a development suffix +#: for unreleased versions of the package. +version = '7.4.0' + +#: The full version of the package, same as 'version' +#: Kept for backward compatibility +full_version = version + +#: The Git revision from which this release was made. git_revision = 'Unknown' -is_released = True -if not is_released: - version = full_version +#: Flag whether this is a final release +is_released = True diff -Nru python-pyface-6.1.2/pyface/viewer/api.py python-pyface-7.4.0/pyface/viewer/api.py --- python-pyface-6.1.2/pyface/viewer/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,40 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from __future__ import absolute_import +# Thanks for using Enthought open source! + + +""" + +API for the ``pyface.viewer`` subpacakge. + +- :class:`~.ColumnProvider` +- :class:`~.ContentProvider` +- :class:`~.ContentViewer` +- :class:`~.DefaultTreeContentProvider` +- :class:`~.LabelProvider` +- :class:`~.TableColumnProvider` +- :class:`~.TableContentProvider` +- :class:`~.TableLabelProvider` +- :class:`~.TreeContentProvider` +- :class:`~.TreeLabelProvider` +- :class:`~.TreeItem` +- :class:`~.Viewer` +- :class:`~.ViewerFilter` +- :class:`~.ViewerSorter` + +Note that the following classes are only available in the Wx toolkit at the +moment. + +- :class:`~.TableViewer`. +- :class:`~.TreeViewer`. + +""" from .column_provider import ColumnProvider from .content_provider import ContentProvider diff -Nru python-pyface-6.1.2/pyface/viewer/column_provider.py python-pyface-7.4.0/pyface/viewer/column_provider.py --- python-pyface-6.1.2/pyface/viewer/column_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/column_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Base class for all column providers. """ -# Enthought library imports. from traits.api import HasTraits, Int @@ -26,16 +22,16 @@ """ # The number of columns. - column_count = Int + column_count = Int() - ########################################################################### + # ------------------------------------------------------------------------ # 'TableColumnProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_label(self, viewer, column_index): """ Returns the label for a column. """ - return 'Column %d' % column_index + return "Column %d" % column_index def get_width(self, viewer, column_index): """ Returns the width of a column. @@ -55,6 +51,4 @@ """ - return 'left' - -#### EOF ###################################################################### + return "left" diff -Nru python-pyface-6.1.2/pyface/viewer/content_provider.py python-pyface-7.4.0/pyface/viewer/content_provider.py --- python-pyface-6.1.2/pyface/viewer/content_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/content_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for content providers. """ -# Enthought library imports. from traits.api import HasTraits class ContentProvider(HasTraits): """ Abstract base class for content providers. """ - ######################################################################### + # ------------------------------------------------------------------------ # 'ContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_elements(self, element): """ Returns a list of the elements to display in a viewer. @@ -35,6 +31,4 @@ """ - raise NotImplementedError - -#### EOF #################################################################### + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/viewer/content_viewer.py python-pyface-7.4.0/pyface/viewer/content_viewer.py --- python-pyface-6.1.2/pyface/viewer/content_viewer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/content_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all content viewers. """ -# Enthought library imports. from traits.api import Any, Instance, List -# Local imports. + from .viewer import Viewer from .viewer_filter import ViewerFilter from .viewer_sorter import ViewerSorter @@ -37,26 +33,24 @@ """ # The domain object that is the root of the viewer's data. - input = Any + input = Any() # The content provider provides the data elements for the viewer. # # Derived classes specialize this trait with the specific type of the # content provider that they require (e.g. the tree viewer MUST have a # 'TreeContentProvider'). - content_provider = Any + content_provider = Any() # The label provider provides labels for each element. # # Derived classes specialize this trait with the specific type of the label # provider that they require (e.g. the table viewer MUST have a # 'TableLabelProvider'). - label_provider = Any + label_provider = Any() # The viewer's sorter (None if no sorting is required). sorter = Instance(ViewerSorter) # The viewer's filters. filters = List(ViewerFilter) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/default_tree_content_provider.py python-pyface-7.4.0/pyface/viewer/default_tree_content_provider.py --- python-pyface-6.1.2/pyface/viewer/default_tree_content_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/default_tree_content_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The default tree content provider. """ -# Local imports. from .tree_content_provider import TreeContentProvider +from .tree_item import TreeItem class DefaultTreeContentProvider(TreeContentProvider): """ The default tree content provider. """ - ######################################################################### + # ------------------------------------------------------------------------ # 'TreeContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_parent(self, item): """ Returns the parent of an item. """ @@ -40,9 +37,9 @@ return item.has_children - ######################################################################### + # ------------------------------------------------------------------------ # 'DefaultTreeContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def append(self, parent, child): """ Appends 'child' to the 'parent' item. """ @@ -54,9 +51,6 @@ index, child = parent.insert_before(before, child) - # Trait notification. - #self.items_inserted(parent, [index], [child]) - return (index, child) def insert(self, parent, index, child): @@ -64,29 +58,20 @@ parent.insert(index, child) - # Trait notification. - #self.items_inserted(parent, [index], [child]) - return child def remove(self, parent, child): """ Removes 'child' from the 'parent' item. """ - index = parent.children.index(child) parent.remove(child) - # Trait notification. - #self.items_removed(parent, [index], [child]) - return child - ######################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ######################################################################### + # ------------------------------------------------------------------------ def _create_item(self, **kw): """ Creates a new item. """ return TreeItem(**kw) - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/__init__.py python-pyface-7.4.0/pyface/viewer/__init__.py --- python-pyface-6.1.2/pyface/viewer/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ - +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/viewer/label_provider.py python-pyface-7.4.0/pyface/viewer/label_provider.py --- python-pyface-6.1.2/pyface/viewer/label_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/label_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for label providers. """ -# Enthought library imports. from traits.api import HasTraits @@ -26,9 +22,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'LabelProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, viewer, element): """ Returns the label image for an element. """ @@ -53,5 +49,3 @@ """ Can the label text be changed via the viewer? """ return False - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/table_column_provider.py python-pyface-7.4.0/pyface/viewer/table_column_provider.py --- python-pyface-6.1.2/pyface/viewer/table_column_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/table_column_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -""" Base class for all table column providers. """ +# Thanks for using Enthought open source! +""" Base class for all table column providers. """ -# Enthought library imports. -from traits.api import Int -# Local imports. from .column_provider import ColumnProvider @@ -36,5 +29,3 @@ # 'TreeContentProvider' and 'TreeLabelProvider' instead of some # combination of the specific and generic viewer classes as in JFace. pass - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/table_content_provider.py python-pyface-7.4.0/pyface/viewer/table_content_provider.py --- python-pyface-6.1.2/pyface/viewer/table_content_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/table_content_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for table content providers. """ -# Local imports. from .content_provider import ContentProvider @@ -33,5 +29,3 @@ # 'TreeContentProvider' and 'TreeLabelProvider' instead of some # combination of the specific and generic viewer classes as in JFace. pass - -#### EOF #################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/table_label_provider.py python-pyface-7.4.0/pyface/viewer/table_label_provider.py --- python-pyface-6.1.2/pyface/viewer/table_label_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/table_label_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Base class for all table label providers. """ -# Local imports. from .label_provider import LabelProvider @@ -25,9 +21,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'TableLabelProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, viewer, element, column_index=0): """ Returns the filename of the label image for an element. """ @@ -37,6 +33,4 @@ def get_text(self, viewer, element, column_index=0): """ Returns the label text for an element. """ - return '%s column %d' % (str(element), column_index) - -#### EOF ###################################################################### + return "%s column %d" % (str(element), column_index) diff -Nru python-pyface-6.1.2/pyface/viewer/table_viewer.py python-pyface-7.4.0/pyface/viewer/table_viewer.py --- python-pyface-6.1.2/pyface/viewer/table_viewer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/table_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ A viewer based on a table control. """ # Import the toolkit specific version. -from __future__ import absolute_import + from pyface.toolkit import toolkit_object -TableViewer = toolkit_object('viewer.table_viewer:TableViewer') + +TableViewer = toolkit_object("viewer.table_viewer:TableViewer") diff -Nru python-pyface-6.1.2/pyface/viewer/tree_content_provider.py python-pyface-7.4.0/pyface/viewer/tree_content_provider.py --- python-pyface-6.1.2/pyface/viewer/tree_content_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/tree_content_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for tree content providers. """ -# Local imports. from .content_provider import ContentProvider @@ -25,9 +21,9 @@ """ - ######################################################################### + # ------------------------------------------------------------------------ # 'ContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_elements(self, element): """ Returns a list of the elements to display in a viewer. @@ -39,11 +35,11 @@ """ - return get_children(element) + return self.get_children(element) - ######################################################################### + # ------------------------------------------------------------------------ # 'TreeContentProvider' interface. - ######################################################################### + # ------------------------------------------------------------------------ def get_parent(self, element): """ Returns the parent of an element. @@ -58,11 +54,9 @@ def get_children(self, element): """ Returns the children of an element. """ - raise NotImplementedError + raise NotImplementedError() def has_children(self, element): """ Returns True iff the element has children, otherwise False. """ - raise NotImplementedError - -#### EOF #################################################################### + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/viewer/tree_item.py python-pyface-7.4.0/pyface/viewer/tree_item.py --- python-pyface-6.1.2/pyface/viewer/tree_item.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/tree_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,81 +1,77 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A generic base-class for items in a tree data structure. -An example:- +An example:: -root = TreeItem(data='Root') + root = TreeItem(data='Root') -fruit = TreeItem(data='Fruit') -fruit.append(TreeItem(data='Apple', allows_children=False)) -fruit.append(TreeItem(data='Orange', allows_children=False)) -fruit.append(TreeItem(data='Pear', allows_children=False)) -root.append(fruit) - -veg = TreeItem(data='Veg') -veg.append(TreeItem(data='Carrot', allows_children=False)) -veg.append(TreeItem(data='Cauliflower', allows_children=False)) -veg.append(TreeItem(data='Sprout', allows_children=False)) -root.append(veg) + fruit = TreeItem(data='Fruit') + fruit.append(TreeItem(data='Apple', allows_children=False)) + fruit.append(TreeItem(data='Orange', allows_children=False)) + fruit.append(TreeItem(data='Pear', allows_children=False)) + root.append(fruit) + + veg = TreeItem(data='Veg') + veg.append(TreeItem(data='Carrot', allows_children=False)) + veg.append(TreeItem(data='Cauliflower', allows_children=False)) + veg.append(TreeItem(data='Sprout', allows_children=False)) + root.append(veg) """ -# Enthought library imports. from traits.api import Any, Bool, HasTraits, Instance, List, Property class TreeItem(HasTraits): """ A generic base-class for items in a tree data structure. """ - #### 'TreeItem' interface ################################################# + # 'TreeItem' interface ------------------------------------------------- # Does this item allow children? allows_children = Bool(True) # The item's children. - children = List(Instance('TreeItem')) + children = List(Instance("TreeItem")) # Arbitrary data associated with the item. - data = Any + data = Any() # Does the item have any children? has_children = Property(Bool) # The item's parent. - parent = Instance('TreeItem') + parent = Instance("TreeItem") - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __str__(self): """ Returns the informal string representation of the object. """ if self.data is None: - s = '' + s = "" else: s = str(self.data) return s - ########################################################################### + # ------------------------------------------------------------------------ # 'TreeItem' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Properties ########################################################### + # Properties ----------------------------------------------------------- # has_children def _get_has_children(self): @@ -83,7 +79,7 @@ return len(self.children) != 0 - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def append(self, child): """ Appends a child to this item. @@ -142,5 +138,3 @@ self.insert(index + 1, child) return (index, child) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/tree_label_provider.py python-pyface-7.4.0/pyface/viewer/tree_label_provider.py --- python-pyface-6.1.2/pyface/viewer/tree_label_provider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/tree_label_provider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Base class for all tree label providers. """ -# Local imports. from .label_provider import LabelProvider @@ -26,9 +22,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'LabelProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def set_text(self, viewer, element, text): """ Sets the text representation of a node. @@ -39,9 +35,9 @@ return len(text.strip()) > 0 - ########################################################################### + # ------------------------------------------------------------------------ # 'TreeLabelProvider' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_drag_value(self, viewer, element): """ Get the value that is dragged for an element. @@ -61,5 +57,3 @@ """ Returns True is the node is expandanble, otherwise False. """ return True - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/tree_viewer.py python-pyface-7.4.0/pyface/viewer/tree_viewer.py --- python-pyface-6.1.2/pyface/viewer/tree_viewer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/tree_viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,18 @@ -# Copyright (c) 2017, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt +# # Thanks for using Enthought open source! """ A viewer based on a tree control. """ # Import the toolkit specific version. -from __future__ import absolute_import + from pyface.toolkit import toolkit_object -TreeViewer = toolkit_object('viewer.tree_viewer:TreeViewer') + +TreeViewer = toolkit_object("viewer.tree_viewer:TreeViewer") diff -Nru python-pyface-6.1.2/pyface/viewer/viewer_filter.py python-pyface-7.4.0/pyface/viewer/viewer_filter.py --- python-pyface-6.1.2/pyface/viewer/viewer_filter.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/viewer_filter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all viewer filters. """ -# Enthought library imports. from traits.api import HasTraits class ViewerFilter(HasTraits): """ Abstract base class for all viewer filters. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ViewerFilter' interface. - ########################################################################### + # ------------------------------------------------------------------------ def filter(self, viewer, parent, elements): """ Filters a list of elements. @@ -66,5 +62,3 @@ """ return False - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/viewer.py python-pyface-7.4.0/pyface/viewer/viewer.py --- python-pyface-6.1.2/pyface/viewer/viewer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/viewer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all viewers. """ -# Enthought library imports. -from pyface.widget import Widget +from pyface.layout_widget import LayoutWidget -class Viewer(Widget): +class Viewer(LayoutWidget): """ Abstract base class for all viewers. A viewer is a model-based adapter on some underlying toolkit-specific @@ -27,5 +23,3 @@ """ pass - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/viewer/viewer_sorter.py python-pyface-7.4.0/pyface/viewer/viewer_sorter.py --- python-pyface-6.1.2/pyface/viewer/viewer_sorter.py 2019-05-31 13:42:40.000000000 +0000 +++ python-pyface-7.4.0/pyface/viewer/viewer_sorter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Abstract base class for all viewer sorters. """ -# Enthought library imports. from traits.api import HasTraits class ViewerSorter(HasTraits): """ Abstract base class for all viewer sorters. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'ViewerSorter' interface. - ########################################################################### + # ------------------------------------------------------------------------ def sort(self, viewer, parent, elements): """ Sorts a list of elements IN PLACE. @@ -38,51 +34,33 @@ # This creates a comparison function with the names 'viewer' and # 'parent' bound to the corresponding arguments to this method. - def comparator(element_a, element_b): - """ Comparator. """ - - return self.compare(viewer, parent, element_a, element_b) + def key(element): + """ Key function. """ + return self.key(viewer, parent, element) - elements.sort(comparator) + elements.sort(key=key) return elements - def compare(self, viewer, parent, element_a, element_b): + def key(self, viewer, parent, element): """ Returns the result of comparing two elements. 'viewer' is the viewer that we are sorting elements for. 'parent' is the parent element. - 'element_a' is the the first element to compare. - 'element_b' is the the second element to compare. + 'element' is the the first element being sorted. """ - # Get the category for each element. - category_a = self.category(viewer, parent, element_a) - category_b = self.category(viewer, parent, element_b) - - # If they are not in the same category then return the result of - # comparing the categories. - if category_a != category_b: - result = cmp(category_a, category_b) + # Get the category + category = self.category(viewer, parent, element) + # Get the label + if hasattr(viewer, "label_provider"): + label = viewer.label_provider.get_text(viewer, element) else: - # Get the label text for each element. - # - # fixme: This is a hack until we decide whethwe we like the - # JFace(ish) or Swing(ish) models! - if hasattr(viewer, 'label_provider'): - label_a = viewer.label_provider.get_text(viewer, element_a) - label_b = viewer.label_provider.get_text(viewer, element_b) - - else: - label_a = viewer.node_model.get_text(viewer, element_a) - label_b = viewer.node_model.get_text(viewer, element_b) + label = viewer.node_model.get_text(viewer, element) - # Compare the label text. - result = cmp(label_a, label_b) - - return result + return (category, label) def category(self, viewer, parent, element): """ Returns the category (an integer) for an element. @@ -97,7 +75,6 @@ By default all elements are given the same category (0). """ - return 0 def is_sorter_trait(self, element, trait_name): @@ -112,7 +89,4 @@ By default we return False. """ - return False - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/widget.py python-pyface-7.4.0/pyface/widget.py --- python-pyface-6.1.2/pyface/widget.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/widget.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The base implementation of all pyface widgets. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -Widget = toolkit_object('widget:Widget') -#### EOF ###################################################################### +Widget = toolkit_object("widget:Widget") diff -Nru python-pyface-6.1.2/pyface/window.py python-pyface-7.4.0/pyface/window.py --- python-pyface-6.1.2/pyface/window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The abstract implementation of all pyface top-level windows. """ # Import the toolkit specific version. -from __future__ import absolute_import + from .toolkit import toolkit_object -Window = toolkit_object('window:Window') -#### EOF ###################################################################### +Window = toolkit_object("window:Window") diff -Nru python-pyface-6.1.2/pyface/wizard/api.py python-pyface-7.4.0/pyface/wizard/api.py --- python-pyface-6.1.2/pyface/wizard/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,32 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + +""" + +API for the ``pyface.wizard`` subpackage. + +- :class:`~.WizardPage` +- :class:`~.Wizard` +- :class:`~.WizardController` +- :class:`~.ChainedWizard` +- :class:`~.ChainedWizardController` + +Interfaces +---------- + +- :class:`~.IWizardPage` +- :class:`~.IWizard` +- :class:`~.IWizardController` + +""" + from .i_wizard_page import IWizardPage from .wizard_page import WizardPage diff -Nru python-pyface-6.1.2/pyface/wizard/chained_wizard_controller.py python-pyface-7.4.0/pyface/wizard/chained_wizard_controller.py --- python-pyface-6.1.2/pyface/wizard/chained_wizard_controller.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/chained_wizard_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A wizard controller that can be chained with others. """ -# Enthought library imports. -from traits.api import Instance +from traits.api import Instance, observe + -# Local imports. from .i_wizard_controller import IWizardController from .wizard_controller import WizardController @@ -25,14 +21,14 @@ class ChainedWizardController(WizardController): """ A wizard controller that can be chained with others. """ - #### 'ChainedWizardController' interface ################################## + # 'ChainedWizardController' interface ---------------------------------# # The next chained wizard controller. next_controller = Instance(IWizardController) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_next_page(self, page): """ Returns the next page. """ @@ -67,7 +63,9 @@ previous_page = self._pages[-1] else: - previous_page = self.next_controller.get_previous_page(page) + previous_page = self.next_controller.get_previous_page( + page + ) else: previous_page = None @@ -85,7 +83,7 @@ if page in self._pages: # If page is not this controller's last page, then it cannot be # *the* last page. - if not page is self._pages[-1]: + if page is not self._pages[-1]: is_last = False # Otherwise, it is *the* last page if this controller has no next @@ -120,9 +118,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'ChainedWizardController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_pages(self): """ Returns the pages in the wizard. """ @@ -141,9 +139,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _update(self): """ Checks the completion status of the controller. """ @@ -166,59 +164,53 @@ return - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - #### Static #### + # Static ---- - def _current_page_changed(self, old, new): + @observe("current_page") + def _reset_observers_on_current_page_and_update(self, event): """ Called when the current page is changed. """ - + old, new = event.old, event.new if old is not None: - old.on_trait_change( - self._on_page_complete, 'complete',remove=True + old.observe( + self._on_page_complete, "complete", remove=True ) if new is not None: - new.on_trait_change(self._on_page_complete, 'complete') + new.observe(self._on_page_complete, "complete") if self.next_controller is not None: self.next_controller.current_page = new self._update() - return - - def _next_controller_changed(self, old, new): + @observe("next_controller") + def _reset_observers_on_next_controller_and_update(self, event): """ Called when the next controller is changed. """ - + old, new = event.old, event.new if old is not None: - old.on_trait_change( - self._on_controller_complete, 'complete', remove=True + old.observe( + self._on_controller_complete, "complete", remove=True ) if new is not None: - new.on_trait_change( - self._on_controller_complete, 'complete' - ) + new.observe(self._on_controller_complete, "complete") self._update() return - #### Dynamic #### + # Dynamic ---- - def _on_controller_complete(self, obj, trait_name, old, new): + def _on_controller_complete(self, event): """ Called when the next controller's complete state changes. """ self._update() - return - - def _on_page_complete(self, obj, trait_name, old, new): + def _on_page_complete(self, event): """ Called when the current page is complete. """ self._update() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wizard/chained_wizard.py python-pyface-7.4.0/pyface/wizard/chained_wizard.py --- python-pyface-6.1.2/pyface/wizard/chained_wizard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/chained_wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A wizard model that can be chained with other wizards. """ -# Enthought library imports. -from traits.api import Instance +from traits.api import Instance, observe + -# Local imports. from .i_wizard import IWizard from .wizard import Wizard @@ -25,33 +21,33 @@ class ChainedWizard(Wizard): """ A wizard model that can be chained with other wizards. """ - #### 'ChainedWizard' interface ############################################ + # 'ChainedWizard' interface -------------------------------------------- # The wizard following this wizard in the chain. next_wizard = Instance(IWizard) - ########################################################################### + # ------------------------------------------------------------------------ # 'ChainedWizard' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait handlers. ###################################################### + # Trait handlers. -----------------------------------------------------# def _controller_default(self): """ Provide a default controller. """ - from chained_wizard_controller import ChainedWizardController + from .chained_wizard_controller import ChainedWizardController return ChainedWizardController() - #### Trait event handlers. ################################################ + # Trait event handlers. ------------------------------------------------ - #### Static #### + # Static ---- - def _next_wizard_changed(self, old, new): + @observe("next_wizard") + def _reset_next_controller_and_update(self, event): """ Handle the next wizard being changed. """ - - if new is not None: - self.controller.next_controller = new.controller + if event.new is not None: + self.controller.next_controller = event.new.controller if self.control is not None: # FIXME: Do we need to call _create_buttons? Buttons would have @@ -62,17 +58,13 @@ # self._create_buttons(self.control) self._update() - return - - def _controller_changed(self, old, new): + @observe("controller") + def _reset_traits_on_controller_and_update(self, event): """ handle the controller being changed. """ - - if new is not None and self.next_wizard is not None: + if event.new is not None and self.next_wizard is not None: self.controller.next_controller = self.next_wizard.controller if self.control is not None: self._update() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wizard/__init__.py python-pyface-7.4.0/pyface/wizard/__init__.py --- python-pyface-6.1.2/pyface/wizard/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ - +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/wizard/i_wizard_controller.py python-pyface-7.4.0/pyface/wizard/i_wizard_controller.py --- python-pyface-6.1.2/pyface/wizard/i_wizard_controller.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/i_wizard_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for all pyface wizard controllers. """ -# Enthought library imports. from traits.api import Bool, Interface, Instance, List -# Local imports. + from .i_wizard_page import IWizardPage class IWizardController(Interface): """ The interface for all pyface wizard controllers. """ - #### 'IWizardController' interface ######################################## + # 'IWizardController' interface ---------------------------------------- # The pages under the control of this controller. pages = List(IWizardPage) @@ -35,9 +31,9 @@ # Set if the wizard complete. complete = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_first_page(self): """ Returns the first page in the model. """ @@ -56,5 +52,3 @@ def dispose_pages(self): """ Dispose all the pages. """ - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wizard/i_wizard_page.py python-pyface-7.4.0/pyface/wizard/i_wizard_page.py --- python-pyface-6.1.2/pyface/wizard/i_wizard_page.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/i_wizard_page.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,29 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for a page in a wizard. """ -# Enthought library imports. -from traits.api import Bool, Interface, Str, Tuple, Unicode +from traits.api import Bool, HasTraits, Interface, Str, Tuple class IWizardPage(Interface): """ The interface for a page in a wizard. """ - #### 'IWizardPage' interface ############################################## + # 'IWizardPage' interface ---------------------------------------------# # The unique Id of the page within the wizard. - id = Str + id = Str() # The Id of the next page. - next_id = Str + next_id = Str() # Set if this is the last page of the wizard. It can be ignored for # simple linear wizards. @@ -37,17 +33,17 @@ complete = Bool(False) # The page heading. - heading = Unicode + heading = Str() # The page sub-heading. - subheading = Unicode + subheading = Str() # The size of the page. - size = Tuple + size = Tuple() - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_page(self, parent): """ Creates the wizard page. """ @@ -59,24 +55,24 @@ dispose of the contents of a page. """ - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_page_content(self, parent): """ Creates the actual page content. """ -class MWizardPage(object): +class MWizardPage(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IWizardPage interface. Implements: dispose_page() """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardPage' interface. - ########################################################################### + # ------------------------------------------------------------------------ def dispose_page(self): """ Disposes the wizard page. @@ -86,5 +82,3 @@ """ pass - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wizard/i_wizard.py python-pyface-7.4.0/pyface/wizard/i_wizard.py --- python-pyface-6.1.2/pyface/wizard/i_wizard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/i_wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for all pyface wizards. """ -# Enthought library imports. -from traits.api import Bool, Instance, List, Unicode +from traits.api import Bool, HasTraits, Instance, List, Str from pyface.i_dialog import IDialog -# Local imports. + from .i_wizard_controller import IWizardController from .i_wizard_page import IWizardPage @@ -26,7 +22,7 @@ class IWizard(IDialog): """ The interface for all pyface wizards. """ - #### 'IWizard' interface ################################################## + # 'IWizard' interface -------------------------------------------------# # The pages in the wizard. pages = List(IWizardPage) @@ -38,14 +34,14 @@ # Should the 'Cancel' button be displayed? show_cancel = Bool(True) - #### 'IWindow' interface ################################################## + # 'IWindow' interface -------------------------------------------------# # The dialog title. - title = Unicode('Wizard') + title = Str("Wizard") - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizard' interface. - ########################################################################### + # ------------------------------------------------------------------------ def next(self): """ Advance to the next page in the wizard. """ @@ -54,7 +50,7 @@ """ Return to the previous page in the wizard. """ -class MWizard(object): +class MWizard(HasTraits): """ The mixin class that contains common code for toolkit specific implementations of the IWizard interface. @@ -63,9 +59,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizard' interface. - ########################################################################### + # ------------------------------------------------------------------------ def next(self): """ Advance to the next page in the wizard. """ @@ -73,8 +69,6 @@ page = self.controller.get_next_page(self.controller.current_page) self._show_page(page) - return - def previous(self): """ Return to the previous page in the wizard. """ @@ -83,15 +77,15 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'IWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Creates the window contents. """ # This creates the dialog and button areas. - super(MWizard, self)._create_contents(parent) + super()._create_contents(parent) # Wire up the controller. self._initialize_controller(self.controller) @@ -101,9 +95,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Protected MWizard interface. - ########################################################################### + # ------------------------------------------------------------------------ def _show_page(self, page): """ Show the specified page. """ @@ -115,40 +109,36 @@ # page? self.controller.current_page = page - def _update(self): + def _update(self, event): """ Enables/disables buttons depending on the state of the wizard. """ pass - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _initialize_controller(self, controller): """ Initializes the wizard controller. """ - controller.on_trait_change(self._update, 'complete') + controller.observe(self._update, "complete") - controller.on_trait_change( - self._on_current_page_changed, 'current_page' - ) + controller.observe(self._on_current_page_changed, "current_page") return - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - def _on_current_page_changed(self, obj, trait_name, old, new): + def _on_current_page_changed(self, event): """ Called when the current page is changed. """ - if old is not None: - old.on_trait_change(self._update, 'complete', remove=True) - - if new is not None: - new.on_trait_change(self._update, 'complete') + if event.old is not None: + event.old.observe(self._update, "complete", remove=True) - self._update() + if event.new is not None: + event.new.observe(self._update, "complete") - return + self._update(event=None) def _on_closed_changed(self): """ Called when the wizard is closed. """ @@ -156,5 +146,3 @@ self.controller.dispose_pages() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wizard/simple_wizard_controller.py python-pyface-7.4.0/pyface/wizard/simple_wizard_controller.py --- python-pyface-6.1.2/pyface/wizard/simple_wizard_controller.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/simple_wizard_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ SimpleWizardController is deprecated. Use WizardController instead. """ -# Local imports. from .wizard_controller import WizardController -class SimpleWizardController(WizardController): pass +class SimpleWizardController(WizardController): + pass diff -Nru python-pyface-6.1.2/pyface/wizard/simple_wizard.py python-pyface-7.4.0/pyface/wizard/simple_wizard.py --- python-pyface-6.1.2/pyface/wizard/simple_wizard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/simple_wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ SimpleWizard is deprecated. Use Wizard instead. """ -# Local imports. from .wizard import Wizard diff -Nru python-pyface-6.1.2/pyface/wizard/wizard_controller.py python-pyface-7.4.0/pyface/wizard/wizard_controller.py --- python-pyface-6.1.2/pyface/wizard/wizard_controller.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/wizard_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,23 +1,21 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A wizard controller that has a static list of pages. """ -# Enthought library imports. -from traits.api import Bool, HasTraits, Instance, List, Property, provides +from traits.api import ( + Bool, HasTraits, Instance, List, Property, provides, observe +) + -# Local imports. from .i_wizard_controller import IWizardController from .i_wizard_page import IWizardPage @@ -25,7 +23,8 @@ @provides(IWizardController) class WizardController(HasTraits): """ A wizard controller that has a static list of pages. """ - #### 'IWizardController' interface ######################################## + + # 'IWizardController' interface ---------------------------------------- # The pages under the control of this controller. pages = Property(List(IWizardPage)) @@ -36,14 +35,14 @@ # Set if the wizard is complete. complete = Bool(False) - #### Protected 'IWizardController' interface ############################## + # Protected 'IWizardController' interface -----------------------------# # Shadow trait for the 'pages' property. _pages = List(IWizardPage) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWizardController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_first_page(self): """ Returns the first page. """ @@ -107,9 +106,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'WizardController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_pages(self): """ Returns the pages in the wizard. """ @@ -125,17 +124,19 @@ # If the current page is None (i.e., the current page has # not been set yet), do not set it here. The current page will # get set when the wizard calls _show_page. - if self.current_page is not None and \ - self.current_page not in self._pages: + if ( + self.current_page is not None + and self.current_page not in self._pages + ): self.current_page = self._pages[0] else: self._update() return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _update(self): """ Checks the completion status of the controller. """ @@ -150,30 +151,31 @@ return - #### Trait event handlers ################################################# + # Trait event handlers ------------------------------------------------- - #### Static #### + # Static ---- - def _current_page_changed(self, old, new): + @observe("current_page") + def _reset_observers_on_current_page_and_update(self, event): """ Called when the current page is changed. """ - + old, new = event.old, event.new if old is not None: - old.on_trait_change(self._on_page_complete, 'complete',remove=True) + old.observe( + self._on_page_complete, "complete", remove=True + ) if new is not None: - new.on_trait_change(self._on_page_complete, 'complete') + new.observe(self._on_page_complete, "complete") self._update() return - #### Dynamic #### + # Dynamic ---- - def _on_page_complete(self, obj, trait_name, old, new): + def _on_page_complete(self, event): """ Called when the current page is complete. """ self._update() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wizard/wizard_page.py python-pyface-7.4.0/pyface/wizard/wizard_page.py --- python-pyface-6.1.2/pyface/wizard/wizard_page.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/wizard_page.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a page in a wizard. """ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -WizardPage = toolkit_object('wizard.wizard_page:WizardPage') -#### EOF ###################################################################### +WizardPage = toolkit_object("wizard.wizard_page:WizardPage") diff -Nru python-pyface-6.1.2/pyface/wizard/wizard.py python-pyface-7.4.0/pyface/wizard/wizard.py --- python-pyface-6.1.2/pyface/wizard/wizard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wizard/wizard.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a wizard. """ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -Wizard = toolkit_object('wizard.wizard:Wizard') -#### EOF ###################################################################### +Wizard = toolkit_object("wizard.wizard:Wizard") diff -Nru python-pyface-6.1.2/pyface/workbench/action/action_controller.py python-pyface-7.4.0/pyface/workbench/action/action_controller.py --- python-pyface-6.1.2/pyface/workbench/action/action_controller.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/action_controller.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,18 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The action controller for workbench menu and tool bars. """ -# Enthought library imports. from pyface.action.api import ActionController from pyface.workbench.api import WorkbenchWindow -from traits.api import HasTraits, Instance +from traits.api import Instance class ActionController(ActionController): @@ -17,14 +25,14 @@ """ - #### 'ActionController' interface ######################################### + # 'ActionController' interface ----------------------------------------- # The workbench window that this is the controller for. window = Instance(WorkbenchWindow) - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionController' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, action, event): """ Control an action invocation. """ @@ -33,5 +41,3 @@ event.window = self.window return action.perform(event) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/api.py python-pyface-7.4.0/pyface/workbench/action/api.py --- python-pyface-6.1.2/pyface/workbench/action/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from .menu_bar_manager import MenuBarManager from .tool_bar_manager import ToolBarManager diff -Nru python-pyface-6.1.2/pyface/workbench/action/delete_user_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/delete_user_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/delete_user_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/delete_user_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,50 +1,52 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005-2006 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: David C. Morrill -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ An action that deletes a user perspective. """ -# Enthought library imports. from pyface.api import YES -# Local imports. + from .user_perspective_action import UserPerspectiveAction class DeleteUserPerspectiveAction(UserPerspectiveAction): """ An action that deletes a user perspective. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier (may be None). - id = 'pyface.workbench.action.delete_user_perspective_action' + id = "pyface.workbench.action.delete_user_perspective_action" # The action's name (displayed on menus/tool bar tools etc). - name = 'Delete Perspective' + name = "Delete Perspective" - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Perform the action. """ - window = event.window + window = event.window manager = window.workbench.user_perspective_manager # The perspective to delete. perspective = window.active_perspective # Make sure that the user isn't having second thoughts! - message = 'Are you sure you want to delete the "%s" perspective?' % \ - perspective.name + message = ( + 'Are you sure you want to delete the "%s" perspective?' + % perspective.name + ) - answer = window.confirm(message, title='Confirm Delete') + answer = window.confirm(message, title="Confirm Delete") if answer == YES: # Set the active perspective to be the first remaining perspective. # @@ -61,9 +63,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_next_perspective(self, window): """ Return the first perspective that is not the active one! """ @@ -75,5 +77,3 @@ index = 0 return window.perspectives[index] - -#### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/menu_bar_manager.py python-pyface-7.4.0/pyface/workbench/action/menu_bar_manager.py --- python-pyface-6.1.2/pyface/workbench/action/menu_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/menu_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,33 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The menu bar manager for Envisage workbench windows. """ -# Enthought library imports. from pyface.action.api import MenuBarManager as BaseMenuBarManager from traits.api import Instance -# Local imports. + from .action_controller import ActionController class MenuBarManager(BaseMenuBarManager): """ The menu bar manager for Envisage workbench windows. """ - #### 'MenuBarManager' interface ########################################### + # 'MenuBarManager' interface ------------------------------------------- # The workbench window that we are the menu bar manager for. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - ########################################################################### + # ------------------------------------------------------------------------ # 'MenuBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_menu_bar(self, parent): """ Creates a menu bar representation of the manager. """ @@ -27,10 +35,6 @@ # The controller handles the invocation of every action. controller = ActionController(window=self.window) - menu_bar = super(MenuBarManager, self).create_menu_bar( - parent, controller=controller - ) + menu_bar = super().create_menu_bar(parent, controller=controller) return menu_bar - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/new_user_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/new_user_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/new_user_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/new_user_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,15 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005-2006 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: David C. Morrill -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ An action that creates a new (and empty) user perspective. """ -# Local imports. from .user_perspective_name import UserPerspectiveName from .workbench_action import WorkbenchAction @@ -17,27 +17,27 @@ class NewUserPerspectiveAction(WorkbenchAction): """ An action that creates a new (and empty) user perspective. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier. - id = 'pyface.workbench.action.new_user_perspective_action' + id = "pyface.workbench.action.new_user_perspective_action" # The action's name. - name = 'New Perspective...' + name = "New Perspective..." - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Peform the action. """ - window = event.window + window = event.window manager = window.workbench.user_perspective_manager # Get the details of the new perspective. - upn = UserPerspectiveName(name='User Perspective %d' % manager.next_id) - if upn.edit_traits(view='new_view').result: + upn = UserPerspectiveName(name="User Perspective %d" % manager.next_id) + if upn.edit_traits(view="new_view").result: # Create a new (and empty) user perspective. perspective = manager.create_perspective( upn.name.strip(), upn.show_editor_area @@ -50,5 +50,3 @@ window.active_perspective = perspective return - -#### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/perspective_menu_manager.py python-pyface-7.4.0/pyface/workbench/action/perspective_menu_manager.py --- python-pyface-6.1.2/pyface/workbench/action/perspective_menu_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/perspective_menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,11 +1,19 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The default perspective menu for a workbench window. """ -# Enthought library imports. from pyface.action.api import Group, MenuManager -from traits.api import Instance, List, on_trait_change +from traits.api import Instance, List, observe + -# Local imports. from .delete_user_perspective_action import DeleteUserPerspectiveAction from .new_user_perspective_action import NewUserPerspectiveAction from .rename_user_perspective_action import RenameUserPerspectiveAction @@ -18,27 +26,27 @@ class PerspectiveMenuManager(MenuManager): """ The default perspective menu for a workbench window. """ - #### 'ActionManager' interface ############################################ + # 'ActionManager' interface -------------------------------------------- # All of the groups in the manager. groups = List(Group) # The manager's unique identifier. - id = 'PerspectivesMenu' + id = "PerspectivesMenu" - #### 'MenuManager' interface ############################################## + # 'MenuManager' interface ---------------------------------------------# # The menu manager's name. - name = 'Perspectives' + name = "Perspectives" - #### 'PerspectiveMenuManager' interface ################################### + # 'PerspectiveMenuManager' interface ----------------------------------- # The workbench window that the manager is part of. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _groups_default(self): """ Trait initializer. """ @@ -47,25 +55,21 @@ # Create a group containing the actions that switch to specific # perspectives. self._create_perspective_group(self.window), - # Create a group containing the user perspective create/save/rename # /delete actions. self._create_user_perspective_group(self.window), - # Create a group containing the reset actions. - self._create_reset_perspective_group(self.window) - + self._create_reset_perspective_group(self.window), ] return groups - ########################################################################### + # ------------------------------------------------------------------------ # 'PerspectiveMenuManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ - @on_trait_change('window.perspectives') - @on_trait_change('window.perspectives_items') - def rebuild(self): + @observe("window.perspectives.items") + def rebuild(self, event): """ Rebuild the menu. This is called when user perspectives have been added or removed. @@ -79,16 +83,16 @@ # Resetting the trait allows the initializer to run again (which will # happen just as soon as we fire the 'changed' event). - self.reset_traits(['groups']) + self.reset_traits(["groups"]) # Let the associated menu know that we have changed. self.changed = True return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_perspective_group(self, window): """ Create the actions that switch to specific perspectives. """ @@ -96,7 +100,7 @@ # fixme: Not sure if alphabetic sorting is appropriate in all cases, # but it will do for now! perspectives = window.perspectives[:] - perspectives.sort(key=lambda x:x.name) + perspectives.sort(key=lambda x: x.name) # For each perspective, create an action that sets the active # perspective to it. @@ -117,7 +121,7 @@ NewUserPerspectiveAction(window=window), SaveAsUserPerspectiveAction(window=window), RenameUserPerspectiveAction(window=window), - DeleteUserPerspectiveAction(window=window) + DeleteUserPerspectiveAction(window=window), ) return group @@ -127,9 +131,7 @@ group = Group( ResetActivePerspectiveAction(window=window), - ResetAllPerspectivesAction(window=window) + ResetAllPerspectivesAction(window=window), ) return group - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/rename_user_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/rename_user_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/rename_user_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/rename_user_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,15 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005-2006 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: David C. Morrill -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ An action that renames a user perspective. """ -# Local imports. from .user_perspective_action import UserPerspectiveAction from .user_perspective_name import UserPerspectiveName @@ -17,29 +17,27 @@ class RenameUserPerspectiveAction(UserPerspectiveAction): """ An action that renames a user perspective. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier (may be None). - id = 'pyface.workbench.action.rename_user_perspective_action' + id = "pyface.workbench.action.rename_user_perspective_action" # The action's name (displayed on menus/tool bar tools etc). - name = 'Rename Perspective...' + name = "Rename Perspective..." - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def perform( self, event): + def perform(self, event): """ Perform the action. """ - window = event.window + window = event.window manager = window.workbench.user_perspective_manager # Get the new name. upn = UserPerspectiveName(name=window.active_perspective.name) - if upn.edit_traits(view='rename_view').result: + if upn.edit_traits(view="rename_view").result: manager.rename(window.active_perspective, upn.name.strip()) return - -#### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/reset_active_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/reset_active_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/reset_active_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/reset_active_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,18 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An action that resets the active perspective. """ -# Enthought library imports. from pyface.api import YES -# Local imports. + from .workbench_action import WorkbenchAction @@ -15,17 +23,17 @@ class ResetActivePerspectiveAction(WorkbenchAction): """ An action that resets the active perspective. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier (may be None). - id = 'pyface.workbench.action.reset_active_perspective' + id = "pyface.workbench.action.reset_active_perspective" # The action's name (displayed on menus/tool bar tools etc). - name = 'Reset Perspective' + name = "Reset Perspective" - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Perform the action. """ @@ -36,5 +44,3 @@ window.reset_active_perspective() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/reset_all_perspectives_action.py python-pyface-7.4.0/pyface/workbench/action/reset_all_perspectives_action.py --- python-pyface-6.1.2/pyface/workbench/action/reset_all_perspectives_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/reset_all_perspectives_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,31 +1,39 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An action that resets *all* perspectives. """ -# Enthought library imports. from pyface.api import YES -# Local imports. + from .workbench_action import WorkbenchAction # The message used when confirming the action. -MESSAGE = 'Do you want to reset ALL perspectives to their defaults?' +MESSAGE = "Do you want to reset ALL perspectives to their defaults?" class ResetAllPerspectivesAction(WorkbenchAction): """ An action that resets *all* perspectives. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier (may be None). - id = 'pyface.workbench.action.reset_all_perspectives' + id = "pyface.workbench.action.reset_all_perspectives" # The action's name (displayed on menus/tool bar tools etc). - name = 'Reset All Perspectives' + name = "Reset All Perspectives" - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Perform the action. """ @@ -36,5 +44,3 @@ window.reset_all_perspectives() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/save_as_user_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/save_as_user_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/save_as_user_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/save_as_user_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,15 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005-2006 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: David C. Morrill -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ An action that saves the active perspective as a user perspective. """ -# Local imports. from .user_perspective_name import UserPerspectiveName from .workbench_action import WorkbenchAction @@ -17,30 +17,30 @@ class SaveAsUserPerspectiveAction(WorkbenchAction): """ An action that saves the active perspective as a user perspective. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier. - id = 'pyface.workbench.action.save_as_user_perspective_action' + id = "pyface.workbench.action.save_as_user_perspective_action" # The action's name (displayed on menus/tool bar tools etc). - name = 'Save Perspective As...' + name = "Save Perspective As..." - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Perform the action. """ - window = event.window + window = event.window manager = window.workbench.user_perspective_manager # Get the name of the new perspective. upn = UserPerspectiveName(name=window.active_perspective.name) - if upn.edit_traits(view='save_as_view').result: + if upn.edit_traits(view="save_as_view").result: # Make a clone of the active perspective, but give it the new name. perspective = manager.clone_perspective( - window, window.active_perspective, name=upn.name.strip() + window, window.active_perspective, name=upn.name.strip() ) # Add it to the window... @@ -50,5 +50,3 @@ window.active_perspective = perspective return - -#### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/set_active_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/set_active_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/set_active_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/set_active_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,47 +1,53 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An action that sets the active perspective. """ -# Enthought library imports. from pyface.workbench.api import IPerspective -from traits.api import Delegate, Instance, on_trait_change +from traits.api import Delegate, Instance, observe + -# Local imports. from .workbench_action import WorkbenchAction class SetActivePerspectiveAction(WorkbenchAction): """ An action that sets the active perspective. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # Is the action enabled? - enabled = Delegate('perspective') + enabled = Delegate("perspective") # The action's unique identifier (may be None). - id = Delegate('perspective') + id = Delegate("perspective") # The action's name (displayed on menus/tool bar tools etc). - name = Delegate('perspective') + name = Delegate("perspective") # The action's style. - style = 'radio' + style = "radio" - #### 'SetActivePerspectiveAction' interface ############################### + # 'SetActivePerspectiveAction' interface ------------------------------- # The perspective that we set the active perspective to. perspective = Instance(IPerspective) - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Destroy the action. """ self.window = None - return - def perform(self, event): """ Perform the action. """ @@ -49,19 +55,19 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - @on_trait_change('perspective,window.active_perspective') - def _refresh_checked(self): + @observe("perspective,window.active_perspective") + def _refresh_checked(self, event): """ Refresh the checked state of the action. """ - self.checked = self.perspective is not None \ - and self.window is not None \ - and self.window.active_perspective is not None \ - and self.perspective.id is self.window.active_perspective.id + self.checked = ( + self.perspective is not None + and self.window is not None + and self.window.active_perspective is not None + and self.perspective.id is self.window.active_perspective.id + ) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/setattr_action.py python-pyface-7.4.0/pyface/workbench/action/setattr_action.py --- python-pyface-6.1.2/pyface/workbench/action/setattr_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/setattr_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,30 +1,38 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An action that sets an attribute. """ -# Enthought library imports. from traits.api import Any, Str -# Local imports. + from .workbench_action import WorkbenchAction class SetattrAction(WorkbenchAction): """ An action that sets an attribute. """ - #### 'SetattrAction' interface ############################################ + # 'SetattrAction' interface -------------------------------------------- # The object that we set the attribute on. - obj = Any + obj = Any() # The name of the attribute that we set. - attribute_name = Str + attribute_name = Str() # The value that we set the attribute to. - value = Any + value = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Performs the action. """ @@ -32,5 +40,3 @@ setattr(self.obj, self.attribute_name, self.value) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/show_view_action.py python-pyface-7.4.0/pyface/workbench/action/show_view_action.py --- python-pyface-6.1.2/pyface/workbench/action/show_view_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/show_view_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An action that shows a dialog to allow the user to choose a view. """ -# Local imports. from .view_chooser import ViewChooser from .workbench_action import WorkbenchAction @@ -9,24 +17,24 @@ class ShowViewAction(WorkbenchAction): """ An action that shows a dialog to allow the user to choose a view. """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier (may be None). - id = 'pyface.workbench.action.show_view' + id = "pyface.workbench.action.show_view" # The action's name (displayed on menus/tool bar tools etc). - name = 'Show View' + name = "Show View" - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def perform(self, event): """ Perform the action. """ chooser = ViewChooser(window=self.window) - ui = chooser.edit_traits(parent=self.window.control, kind='livemodal') + ui = chooser.edit_traits(parent=self.window.control, kind="livemodal") # If the user closes the dialog by using the window manager's close button # (e.g. the little [x] in the top corner), ui.result is True, but chooser.view @@ -40,5 +48,3 @@ chooser.view.activate() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/toggle_view_visibility_action.py python-pyface-7.4.0/pyface/workbench/action/toggle_view_visibility_action.py --- python-pyface-6.1.2/pyface/workbench/action/toggle_view_visibility_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/toggle_view_visibility_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,36 +1,44 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An action that toggles a view's visibility (ie. hides/shows it). """ -# Enthought library imports. from pyface.workbench.api import IView from traits.api import Delegate, Instance -# Local imports. + from .workbench_action import WorkbenchAction class ToggleViewVisibilityAction(WorkbenchAction): """ An action that toggles a view's visibility (ie. hides/shows it). """ - #### 'Action' interface ################################################### + # 'Action' interface --------------------------------------------------- # The action's unique identifier (may be None). - id = Delegate('view', modify=True) + id = Delegate("view", modify=True) # The action's name (displayed on menus/tool bar tools etc). - name = Delegate('view', modify=True) + name = Delegate("view", modify=True) # The action's style. - style = 'toggle' + style = "toggle" - #### 'ViewAction' interface ############################################### + # 'ViewAction' interface ----------------------------------------------- # The view that we toggle the visibility for. view = Instance(IView) - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Called when the action is no longer required. """ @@ -38,8 +46,6 @@ if self.view is not None: self._remove_view_listeners(self.view) - return - def perform(self, event): """ Perform the action. """ @@ -47,11 +53,11 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ def _view_changed(self, old, new): """ Static trait change handler. """ @@ -66,32 +72,28 @@ return - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def _add_view_listeners(self, view): """ Add listeners for trait events on a view. """ - view.on_trait_change(self._refresh_checked, 'visible') - view.on_trait_change(self._refresh_checked, 'window') - - return + view.observe(self._refresh_checked, "visible") + view.observe(self._refresh_checked, "window") def _remove_view_listeners(self, view): """ Add listeners for trait events on a view. """ - view.on_trait_change(self._refresh_checked, 'visible', remove=True) - view.on_trait_change(self._refresh_checked, 'window', remove=True) + view.observe(self._refresh_checked, "visible", remove=True) + view.observe(self._refresh_checked, "window", remove=True) - return - - def _refresh_checked(self): + def _refresh_checked(self, event=None): """ Refresh the checked state of the action. """ - self.checked = self.view is not None \ - and self.view.window is not None \ - and self.view.visible - - return + self.checked = ( + self.view is not None + and self.view.window is not None + and self.view.visible + ) def _toggle_view_visibility(self, view): """ Toggle the visibility of a view. """ @@ -103,5 +105,3 @@ view.show() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/tool_bar_manager.py python-pyface-7.4.0/pyface/workbench/action/tool_bar_manager.py --- python-pyface-6.1.2/pyface/workbench/action/tool_bar_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/tool_bar_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,37 +1,43 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The tool bar manager for the Envisage workbench window. """ -# Enthought library imports. import pyface.action.api as pyface from traits.api import Instance -# Local imports. + from .action_controller import ActionController class ToolBarManager(pyface.ToolBarManager): """ The tool bar manager for the Envisage workbench window. """ - #### 'ToolBarManager' interface ########################################### + # 'ToolBarManager' interface ------------------------------------------- # The workbench window that we are the tool bar manager for. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - ########################################################################### + # ------------------------------------------------------------------------ # 'ToolBarManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ - def create_tool_bar(self, parent, controller=None): + def create_tool_bar(self, parent, controller=None, **kwargs): """ Creates a tool bar representation of the manager. """ # The controller handles the invocation of every action. if controller is None: controller = ActionController(window=self.window) - tool_bar = super(ToolBarManager, self).create_tool_bar( - parent, controller=controller + tool_bar = super().create_tool_bar( + parent, controller=controller, **kwargs ) return tool_bar - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/user_perspective_action.py python-pyface-7.4.0/pyface/workbench/action/user_perspective_action.py --- python-pyface-6.1.2/pyface/workbench/action/user_perspective_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/user_perspective_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,18 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005-2006 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: David C. Morrill -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ The base class for user perspective actions. """ -# Enthought library imports. -from traits.api import on_trait_change +from traits.api import observe + -# Local imports. from .workbench_action import WorkbenchAction @@ -24,9 +24,9 @@ """ - ########################################################################### + # ------------------------------------------------------------------------ # 'Action' interface. - ########################################################################### + # ------------------------------------------------------------------------ def destroy(self): """ Destroy the action. """ @@ -36,9 +36,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _is_user_perspective(self, perspective): """ Is the specified perspective a user perspective? """ @@ -46,16 +46,16 @@ # fixme: This seems a bit of a smelly way to make the determinaction! id = perspective.id - return ((id[:19] == '__user_perspective_') and (id[-2:] == '__')) + return (id[:19] == "__user_perspective_") and (id[-2:] == "__") - @on_trait_change('window.active_perspective') - def _refresh_enabled(self): + @observe("window.active_perspective") + def _refresh_enabled(self, event): """ Refresh the enabled state of the action. """ - self.enabled = self.window is not None \ - and self.window.active_perspective is not None \ - and self._is_user_perspective(self.window.active_perspective) + self.enabled = ( + self.window is not None + and self.window.active_perspective is not None + and self._is_user_perspective(self.window.active_perspective) + ) return - -#### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/user_perspective_name.py python-pyface-7.4.0/pyface/workbench/action/user_perspective_name.py --- python-pyface-6.1.2/pyface/workbench/action/user_perspective_name.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/user_perspective_name.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,40 +1,31 @@ -#----------------------------------------------------------------------------- +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. # -# Copyright (c) 2005-2006 by Enthought, Inc. -# All rights reserved. +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt # -# Author: David C. Morrill -# -#----------------------------------------------------------------------------- +# Thanks for using Enthought open source! """ Object with views for naming or renaming a user perspective. """ -# Enthought library imports. -from traits.api import Bool, HasTraits, Trait, TraitError, Constant +from traits.api import Bool, HasTraits, Constant, String from traitsui.api import View, Item, VGroup -import six - - -#### Trait definitions ######################################################## - -def not_empty_string(object, name, value): - """a not-empty string""" - if isinstance(value, six.string_types) and (value.strip() != ''): - return value - raise TraitError +# Trait definitions -------------------------------------------------------- # Define a trait which can not be the empty string: -NotEmptyString = Trait('', not_empty_string) +NotEmptyString = String(minlen=1) class UserPerspectiveName(HasTraits): """ Object with views for naming or renaming a user perspective. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'UserPerspectiveName' interface. - ########################################################################### + # ------------------------------------------------------------------------ # The name of the new user perspective. name = NotEmptyString @@ -43,49 +34,46 @@ show_editor_area = Bool(True) # Help notes when creating a new view. - new_help = Constant("""Note: + new_help = Constant( + """Note: - The new perspective will initially be empty. - Add new views to the perspective by selecting them from the 'View' menu. - Drag the notebook tabs and splitter bars to - arrange the views within the perspective.""") + arrange the views within the perspective.""" + ) - #### Traits views ######################################################### + # Traits views --------------------------------------------------------- new_view = View( VGroup( - VGroup( 'name', 'show_editor_area' ), - VGroup( '_', - Item( 'new_help', - style = 'readonly' ), - show_labels = False - ) + VGroup("name", "show_editor_area"), + VGroup("_", Item("new_help", style="readonly"), show_labels=False), ), - title = 'New User Perspective', - id = 'envisage.workbench.action.' - 'new_user_perspective_action.UserPerspectiveName', - buttons = [ 'OK', 'Cancel' ], - kind = 'livemodal', - width = 300 + title="New User Perspective", + id="envisage.workbench.action." + "new_user_perspective_action.UserPerspectiveName", + buttons=["OK", "Cancel"], + kind="livemodal", + width=300, ) - save_as_view = View( 'name', - title = 'Save User Perspective As', - id = 'envisage.workbench.action.' - 'save_as_user_perspective_action.UserPerspectiveName', - buttons = [ 'OK', 'Cancel' ], - kind = 'livemodal', - width = 300 + save_as_view = View( + "name", + title="Save User Perspective As", + id="envisage.workbench.action." + "save_as_user_perspective_action.UserPerspectiveName", + buttons=["OK", "Cancel"], + kind="livemodal", + width=300, ) - rename_view = View( 'name', - title = 'Rename User Perspective', - id = 'envisage.workbench.action.' - 'rename_user_perspective_action.UserPerspectiveName', - buttons = [ 'OK', 'Cancel' ], - kind = 'livemodal', - width = 300 + rename_view = View( + "name", + title="Rename User Perspective", + id="envisage.workbench.action." + "rename_user_perspective_action.UserPerspectiveName", + buttons=["OK", "Cancel"], + kind="livemodal", + width=300, ) - -#### EOF ##################################################################### - diff -Nru python-pyface-6.1.2/pyface/workbench/action/view_chooser.py python-pyface-7.4.0/pyface/workbench/action/view_chooser.py --- python-pyface-6.1.2/pyface/workbench/action/view_chooser.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/view_chooser.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,30 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A UI that allows the user to choose a view. """ -# Enthought library imports. from pyface.workbench.api import IView, WorkbenchWindow from traits.api import Any, HasTraits, Instance, List, Str from traits.api import TraitError, Undefined from traitsui.api import Item, TreeEditor, TreeNode, View -from traitsui.menu import Action # fixme: Non-api import! +from traitsui.menu import Action # fixme: Non-api import! class Category(HasTraits): """ A view category. """ # The name of the category. - name = Str + name = Str() # The views in the category. - views = List + views = List() class WorkbenchWindowTreeNode(TreeNode): @@ -26,14 +34,14 @@ """ - #### 'TreeNode' interface ################################################# + # 'TreeNode' interface ------------------------------------------------- # List of object classes that the node applies to. node_for = [WorkbenchWindow] - ########################################################################### + # ------------------------------------------------------------------------ # 'TreeNode' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_children(self, object): """ Get the object's children. """ @@ -46,9 +54,9 @@ return categories - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_categories_by_name(self, window): """ Return a dictionary containing all categories keyed by name. """ @@ -97,7 +105,7 @@ else: # fixme: A bit of magic here! Is there a better way to say 'use # the default leaf icon'? - icon = '' + icon = "" return icon @@ -110,78 +118,70 @@ """ # The window that contains the views to choose from. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") # The currently selected tree item (at any point in time this might be # either None, a view category, or a view). - selected = Any + selected = Any() # The selected view (None if the selected item is not a view). view = Instance(IView) - #### Traits UI views ###################################################### + # Traits UI views -----------------------------------------------------# traits_ui_view = View( Item( - name = 'window', - editor = TreeEditor( - nodes = [ + name="window", + editor=TreeEditor( + nodes=[ WorkbenchWindowTreeNode( - auto_open = True, - label = '=Views', - rename = False, - copy = False, - delete = False, - insert = False, - menu = None, + auto_open=True, + label="=Views", + rename=False, + copy=False, + delete=False, + insert=False, + menu=None, ), - TreeNode( - node_for = [Category], - auto_open = True, - children = 'views', - label = 'name', - rename = False, - copy = False, - delete = False, - insert = False, - menu = None, + node_for=[Category], + auto_open=True, + children="views", + label="name", + rename=False, + copy=False, + delete=False, + insert=False, + menu=None, ), - IViewTreeNode( - auto_open = False, - label = 'name', - rename = False, - copy = False, - delete = False, - insert = False, - menu = None, - ) + auto_open=False, + label="name", + rename=False, + copy=False, + delete=False, + insert=False, + menu=None, + ), ], - - editable = False, - hide_root = True, - selected = 'selected', - show_icons = True + editable=False, + hide_root=True, + selected="selected", + show_icons=True, ), - show_label = False + show_label=False, ), - - buttons = [ - Action(name='OK', enabled_when='view is not None'), 'Cancel' - ], - - resizable = True, - style = 'custom', - title = 'Show View', - - width = .2, - height = .4 + buttons=[Action(name="OK", enabled_when="view is not None"), "Cancel"], + resizable=True, + style="custom", + title="Show View", + width=0.2, + height=0.4, ) - ########################################################################### + # ------------------------------------------------------------------------ # 'ViewChooser' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _selected_changed(self, old, new): """ Static trait change handler. """ @@ -195,5 +195,3 @@ self.view = None return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/view_menu_manager.py python-pyface-7.4.0/pyface/workbench/action/view_menu_manager.py --- python-pyface-6.1.2/pyface/workbench/action/view_menu_manager.py 2019-07-20 11:46:59.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/view_menu_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,15 +1,23 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The 'View' menu """ -# Standard library imports. import logging -# Enthought library imports. + from pyface.action.api import Group, MenuManager -from traits.api import Any, Bool, Instance, List, Str, Unicode -from traits.api import on_trait_change +from traits.api import Any, Bool, Instance, List, Str +from traits.api import observe + -# Local imports. from .perspective_menu_manager import PerspectiveMenuManager from .show_view_action import ShowViewAction from .toggle_view_visibility_action import ToggleViewVisibilityAction @@ -26,36 +34,36 @@ """ - #### 'ActionManager' interface ############################################ + # 'ActionManager' interface -------------------------------------------- # All of the groups in the manager. groups = List(Group) # The manager's unique identifier (if it has one). - id = Str('View') + id = Str("View") - #### 'MenuManager' interface ############################################## + # 'MenuManager' interface ---------------------------------------------# # The menu manager's name (if the manager is a sub-menu, this is what its # label will be). - name = Unicode('&View') + name = Str("&View") - #### 'ViewMenuManager' interface ########################################## + # 'ViewMenuManager' interface -----------------------------------------# # Should the perspective menu be shown? show_perspective_menu = Bool(True) # The workbench window that the menu is part of. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - #### 'Private' interface ################################################## + # 'Private' interface -------------------------------------------------# # The group containing the view hide/show actions. - _view_group = Any + _view_group = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'ActionManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _groups_default(self): """ Trait initializer. """ @@ -76,18 +84,15 @@ return groups - ########################################################################### + # ------------------------------------------------------------------------ # 'ViewMenuManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ - @on_trait_change( - 'window.active_perspective,window.active_part,' - 'window.views,window.views_items' - ) - def refresh(self): + @observe("window.active_perspective,window.active_part,window.views.items") + def refresh(self, event): """ Refreshes the checked state of the actions in the menu. """ - logger.debug('refreshing view menu') + logger.debug("refreshing view menu") if self._view_group is not None: self._clear_group(self._view_group) @@ -96,9 +101,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _clear_group(self, group): """ Remove all items in a group. """ @@ -107,13 +112,11 @@ group.destroy() group.clear() - return - def _create_other_group(self, window): """ Creates a group containing the 'Other...' action. """ group = Group() - group.append(ShowViewAction(name='Other...', window=window)) + group.append(ShowViewAction(name="Other...", window=window)) return group @@ -141,5 +144,3 @@ ) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/action/workbench_action.py python-pyface-7.4.0/pyface/workbench/action/workbench_action.py --- python-pyface-6.1.2/pyface/workbench/action/workbench_action.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/action/workbench_action.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Abstract base class for all workbench actions. """ -# Enthought library imports. from pyface.workbench.api import WorkbenchWindow from pyface.action.api import Action from traits.api import Instance @@ -10,11 +18,9 @@ class WorkbenchAction(Action): """ Abstract base class for all workbench actions. """ - #### 'WorkbenchAction' interface ########################################## + # 'WorkbenchAction' interface -----------------------------------------# # The workbench window that the action is in. # # This is set by the framework. window = Instance(WorkbenchWindow) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/api.py python-pyface-7.4.0/pyface/workbench/api.py --- python-pyface-6.1.2/pyface/workbench/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,13 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + from .i_editor import IEditor from .editor import Editor diff -Nru python-pyface-6.1.2/pyface/workbench/debug/api.py python-pyface-7.4.0/pyface/workbench/debug/api.py --- python-pyface-6.1.2/pyface/workbench/debug/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/debug/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,4 +1,12 @@ -from __future__ import absolute_import +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! from .debug_view import DebugView diff -Nru python-pyface-6.1.2/pyface/workbench/debug/debug_view.py python-pyface-7.4.0/pyface/workbench/debug/debug_view.py --- python-pyface-6.1.2/pyface/workbench/debug/debug_view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/debug/debug_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,38 +1,42 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A view containing a main walter canvas. """ -# Enthought library imports. from pyface.workbench.api import View, WorkbenchWindow -from traits.api import HasTraits, Instance, Str, on_trait_change +from traits.api import HasTraits, Instance, Str, observe from traitsui.api import View as TraitsView class DebugViewModel(HasTraits): """ The model for the debug view! """ - #### 'Model' interface #################################################### + # 'Model' interface ---------------------------------------------------- - active_editor = Str - active_part = Str - active_view = Str + active_editor = Str() + active_part = Str() + active_view = Str() window = Instance(WorkbenchWindow) - ########################################################################### + # ------------------------------------------------------------------------ # 'Model' interface. - ########################################################################### + # ------------------------------------------------------------------------ - @on_trait_change( - 'window.active_editor', 'window.active_part', 'window.active_view' - ) - def refresh(self): + @observe("window.active_editor,window.active_part,window.active_view") + def refresh(self, event): """ Refresh the model. """ self.active_editor = self._get_id(self.window.active_editor) - self.active_part = self._get_id(self.window.active_part) - self.active_view = self._get_id(self.window.active_view) - - return + self.active_part = self._get_id(self.window.active_part) + self.active_view = self._get_id(self.window.active_view) def _window_changed(self): """ Window changed! """ @@ -41,15 +45,15 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_id(self, obj): """ Return the Id of an object. """ if obj is None: - id = 'None' + id = "None" else: id = obj.id @@ -60,19 +64,19 @@ class DebugView(View): """ A view containing a main walter canvas. """ - #### 'IWorkbenchPart' interface ########################################### + # 'IWorkbenchPart' interface ------------------------------------------- # The part's name (displayed to the user). - name = 'Debug' + name = "Debug" - #### 'DebugView' interface ################################################ + # 'DebugView' interface ------------------------------------------------ # The model for the debug view! model = Instance(DebugViewModel) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_control(self, parent): """ Creates the toolkit-specific control that represents the view. @@ -84,11 +88,9 @@ self.model = DebugViewModel(window=self.window) ui = self.model.edit_traits( - parent = parent, - kind = 'subpanel', - view = TraitsView('active_part', 'active_editor', 'active_view') + parent=parent, + kind="subpanel", + view=TraitsView("active_part", "active_editor", "active_view"), ) return ui.control - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/editor_manager.py python-pyface-7.4.0/pyface/workbench/editor_manager.py --- python-pyface-6.1.2/pyface/workbench/editor_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/editor_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,12 +1,21 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The default editor manager. """ -# Standard library imports. + import weakref -# Enthought library imports. + from traits.api import HasTraits, Instance, provides -# Local imports. + from .i_editor_manager import IEditorManager from .traits_ui_editor import TraitsUIEditor @@ -14,28 +23,29 @@ @provides(IEditorManager) class EditorManager(HasTraits): """ The default editor manager. """ - #### 'IEditorManager' interface ########################################### + + # 'IEditorManager' interface ------------------------------------------- # The workbench window that the editor manager manages editors for ;^) - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, **traits): """ Constructor. """ - super(EditorManager, self).__init__(**traits) + super().__init__(**traits) # A mapping from editor to editor kind (the factory that created them). self._editor_to_kind_map = weakref.WeakKeyDictionary() return - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditorManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_editor(self, editor, kind): """ Registers an existing editor. """ @@ -83,13 +93,11 @@ return None - ########################################################################### + # ------------------------------------------------------------------------ # 'Protected' 'EditorManager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _is_editing(self, editor, obj, kind): """ Return True if the editor is editing the object. """ return editor.obj == obj - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/editor.py python-pyface-7.4.0/pyface/workbench/editor.py --- python-pyface-6.1.2/pyface/workbench/editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a workbench editor. """ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -Editor = toolkit_object('workbench.editor:Editor') -### EOF ####################################################################### +Editor = toolkit_object("workbench.editor:Editor") diff -Nru python-pyface-6.1.2/pyface/workbench/i_editor_manager.py python-pyface-7.4.0/pyface/workbench/i_editor_manager.py --- python-pyface-6.1.2/pyface/workbench/i_editor_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_editor_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The editor manager interface. """ -# Enthought library imports. from traits.api import Instance, Interface @@ -9,7 +17,7 @@ """ The editor manager interface. """ # The workbench window that the editor manager manages editors for ;^) - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") def add_editor(self, editor, kind): """ Registers an existing editor. """ @@ -41,5 +49,3 @@ def set_editor_memento(self, memento): """ Restore an editor from a memento and return it. """ - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/i_editor.py python-pyface-7.4.0/pyface/workbench/i_editor.py --- python-pyface-6.1.2/pyface/workbench/i_editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,26 +1,29 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface of a workbench editor. """ -# standard library imports + import uuid -# Enthought library imports. -from traits.api import Any, Bool, Event, VetoableEvent, Vetoable, \ - HasTraits, Instance, Interface + +from traits.api import ( + Any, + Bool, + Event, + VetoableEvent, + Vetoable, + Instance, +) from traits.api import provides -# Local imports. + from .i_workbench_part import IWorkbenchPart, MWorkbenchPart @@ -28,7 +31,7 @@ """ The interface of a workbench editor. """ # The optional command stack. - command_stack = Instance('apptools.undo.api.ICommandStack') + command_stack = Instance("pyface.undo.api.ICommandStack") # Is the object that the editor is editing 'dirty' i.e., has it been # modified but not saved? @@ -37,17 +40,17 @@ # The object that the editor is editing. # # The framework sets this when the editor is created. - obj = Any + obj = Any() - #### Editor Lifecycle Events ############################################## + # Editor Lifecycle Events ---------------------------------------------# # Fired when the editor is closing. - closing = VetoableEvent + closing = VetoableEvent() # Fired when the editor is closed. - closed = Event + closed = Event() - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def close(self): """ Close the editor. @@ -62,10 +65,11 @@ @provides(IEditor) class MEditor(MWorkbenchPart): """ Mixin containing common code for toolkit-specific implementations. """ - #### 'IEditor' interface ################################################## + + # 'IEditor' interface -------------------------------------------------# # The optional command stack. - command_stack = Instance('apptools.undo.api.ICommandStack') + command_stack = Instance("pyface.undo.api.ICommandStack") # Is the object that the editor is editing 'dirty' i.e., has it been # modified but not saved? @@ -74,34 +78,34 @@ # The object that the editor is editing. # # The framework sets this when the editor is created. - obj = Any + obj = Any() - #### Editor Lifecycle Events ############################################## + # Editor Lifecycle Events ---------------------------------------------# # Fired when the editor is opening. - opening = VetoableEvent + opening = VetoableEvent() # Fired when the editor has been opened. - open = Event + open = Event() # Fired when the editor is closing. closing = Event(VetoableEvent) # Fired when the editor is closed. - closed = Event + closed = Event() - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __str__(self): """ Return an informal string representation of the object. """ - return 'Editor(%s)' % self.id + return "Editor(%s)" % self.id - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _id_default(self): """ Trait initializer. """ @@ -111,9 +115,9 @@ # collisions between the ids of editors. return uuid.uuid4().hex - ########################################################################### + # ------------------------------------------------------------------------ # 'IEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def close(self): """ Close the editor. """ @@ -127,18 +131,15 @@ return - #### Initializers ######################################################### + # Initializers --------------------------------------------------------- def _command_stack_default(self): """ Trait initializer. """ # We make sure the undo package is entirely optional. try: - from apptools.undo.api import CommandStack + from pyface.undo.api import CommandStack except ImportError: return None return CommandStack(undo_manager=self.window.workbench.undo_manager) - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/__init__.py python-pyface-7.4.0/pyface/workbench/__init__.py --- python-pyface-6.1.2/pyface/workbench/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,8 +1,17 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import warnings warnings.warn( PendingDeprecationWarning( - "Workbench will be moved from pyface.workbench to apptools.workbench " - "in Pyface 7.0.0" - ), stacklevel=2 + "Workbench will be moved from pyface.workbench to apptools.workbench at a future point" + ), + stacklevel=2, ) diff -Nru python-pyface-6.1.2/pyface/workbench/i_perspective_item.py python-pyface-7.4.0/pyface/workbench/i_perspective_item.py --- python-pyface-6.1.2/pyface/workbench/i_perspective_item.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_perspective_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The interface for perspective items. """ -# Enthought library imports. from traits.api import Enum, Float, Interface, Str @@ -9,7 +17,7 @@ """ The interface for perspective items. """ # The Id of the view to display in the perspective. - id = Str + id = Str() # The position of the view relative to the item specified in the # 'relative_to' trait. @@ -23,12 +31,12 @@ # If the position is specified as 'with' you must specify a 'relative_to' # item other than the editor area (i.e., you cannot position a view 'with' # the editor area). - position = Enum('left', 'top', 'bottom', 'right', 'with') + position = Enum("left", "top", "bottom", "right", "with") # The Id of the view to position relative to. If this is not specified # (or if no view exists with this Id) then the view will be placed relative # to the editor area. - relative_to = Str + relative_to = Str() # The width of the item (as a fraction of the window width). # @@ -47,6 +55,4 @@ height = Float(-1) # The style of the dock window. - style_hint = Enum('tab', 'horizontal', 'vertical', 'fixed') - -#### EOF ###################################################################### + style_hint = Enum("tab", "horizontal", "vertical", "fixed") diff -Nru python-pyface-6.1.2/pyface/workbench/i_perspective.py python-pyface-7.4.0/pyface/workbench/i_perspective.py --- python-pyface-6.1.2/pyface/workbench/i_perspective.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_perspective.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,10 +1,18 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The perspective interface. """ -# Enthought library imports. from traits.api import Bool, Interface, List, Str, Tuple -# Local imports. + from .perspective_item import PerspectiveItem @@ -12,10 +20,10 @@ """ The perspective interface. """ # The perspective's unique identifier (unique within a workbench window). - id = Str + id = Str() # The perspective's name. - name = Str + name = Str() # The contents of the perspective. contents = List(PerspectiveItem) @@ -23,20 +31,18 @@ # The size of the editor area in this perspective. A value of (-1, -1) # indicates that the workbench window should choose an appropriate size # based on the sizes of the views in the perspective. - editor_area_size = Tuple + editor_area_size = Tuple() # Is the perspective enabled? - enabled = Bool + enabled = Bool() # Should the editor area be shown in this perspective? - show_editor_area = Bool + show_editor_area = Bool() - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create(self, window): """ Create the perspective in a workbench window. """ def show(self, window): """ Called when the perspective is shown in a workbench window. """ - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/i_view.py python-pyface-7.4.0/pyface/workbench/i_view.py --- python-pyface-6.1.2/pyface/workbench/i_view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for workbench views. """ -# Standard library imports. + import logging -# Enthought library imports. -from pyface.api import ImageResource -from traits.api import Bool, Enum, Float, Instance, List, provides, Str + +from pyface.api import Image +from traits.api import Bool, Str, provides from traits.util.camel_case import camel_case_to_words -# Local imports. from .i_perspective_item import IPerspectiveItem from .i_workbench_part import IWorkbenchPart, MWorkbenchPart from .perspective_item import PerspectiveItem @@ -39,18 +35,18 @@ # The category that the view belongs to (this can used to group views when # they are displayed to the user). - category = Str('General') + category = Str("General") # An image used to represent the view to the user (shown in the view tab # and in the view chooser etc). - image = Instance(ImageResource) + image = Image() # Whether the view is visible or not. visible = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IView' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate(self): """ Activate the view. @@ -71,7 +67,8 @@ @provides(IView) class MView(MWorkbenchPart, PerspectiveItem): """ Mixin containing common code for toolkit-specific implementations. """ - #### 'IView' interface #################################################### + + # 'IView' interface ---------------------------------------------------- # Is the view busy? (i.e., should the busy cursor (often an hourglass) be # displayed?). @@ -79,24 +76,24 @@ # The category that the view belongs to (this can be used to group views # when they are displayed to the user). - category = Str('General') + category = Str("General") # An image used to represent the view to the user (shown in the view tab # and in the view chooser etc). - image = Instance(ImageResource) + image = Image() # Whether the view is visible or not. visible = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _id_default(self): """ Trait initializer. """ - id = '%s.%s' % (type(self).__module__, type(self).__name__) - logger.warn('view %s has no Id - using <%s>' % (self, id)) + id = "%s.%s" % (type(self).__module__, type(self).__name__) + logger.warning("view %s has no Id - using <%s>" % (self, id)) # If no Id is specified then use the name. return id @@ -105,13 +102,13 @@ """ Trait initializer. """ name = camel_case_to_words(type(self).__name__) - logger.warn('view %s has no name - using <%s>' % (self, name)) + logger.warning("view %s has no name - using <%s>" % (self, name)) return name - ########################################################################### + # ------------------------------------------------------------------------ # 'IView' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate(self): """ Activate the view. @@ -120,20 +117,14 @@ self.window.activate_view(self) - return - def hide(self): """ Hide the view. """ self.window.hide_view(self) - return - def show(self): """ Show the view. """ self.window.show_view(self) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/i_workbench_part.py python-pyface-7.4.0/pyface/workbench/i_workbench_part.py --- python-pyface-6.1.2/pyface/workbench/i_workbench_part.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_workbench_part.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The interface for workbench parts. """ -# Enthought library imports. from traits.api import Any, Bool, HasTraits, Instance, Interface -from traits.api import List, provides, Str, Unicode +from traits.api import List, provides, Str class IWorkbenchPart(Interface): @@ -30,26 +26,26 @@ # The toolkit-specific control that represents the part. # # The framework sets this to the value returned by 'create_control'. - control = Any + control = Any() # Does the part currently have the focus? has_focus = Bool(False) # The part's globally unique identifier. - id = Str + id = Str() # The part's name (displayed to the user). - name = Unicode + name = Str() # The current selection within the part. - selection = List + selection = List() # The workbench window that the part is in. # # The framework sets this when the part is created. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_control(self, parent): """ Create the toolkit-specific control that represents the part. @@ -79,45 +75,44 @@ @provides(IWorkbenchPart) class MWorkbenchPart(HasTraits): """ Mixin containing common code for toolkit-specific implementations. """ - #### 'IWorkbenchPart' interface ########################################### + + # 'IWorkbenchPart' interface ------------------------------------------- # The toolkit-specific control that represents the part. # # The framework sets this to the value returned by 'create_control'. - control = Any + control = Any() # Does the part currently have the focus? has_focus = Bool(False) # The part's globally unique identifier. - id = Str + id = Str() # The part's name (displayed to the user). - name = Unicode + name = Str() # The current selection within the part. - selection = List + selection = List() # The workbench window that the part is in. # # The framework sets this when the part is created. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_control(self, parent): """ Create the toolkit-specific control that represents the part. """ - raise NotImplementedError + raise NotImplementedError() def destroy_control(self): """ Destroy the toolkit-specific control that represents the part. """ - raise NotImplementedError + raise NotImplementedError() def set_focus(self): """ Set the focus to the appropriate control in the part. """ - raise NotImplementedError - -#### EOF ###################################################################### + raise NotImplementedError() diff -Nru python-pyface-6.1.2/pyface/workbench/i_workbench.py python-pyface-7.4.0/pyface/workbench/i_workbench.py --- python-pyface-6.1.2/pyface/workbench/i_workbench.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_workbench.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,11 +1,19 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The workbench interface. """ -# Enthought library imports. from traits.api import Event, Instance, Interface, List, Str -from traits.api import provides, VetoableEvent +from traits.api import VetoableEvent + -# Local imports. from .user_perspective_manager import UserPerspectiveManager from .window_event import WindowEvent, VetoableWindowEvent from .workbench_window import WorkbenchWindow @@ -14,20 +22,20 @@ class IWorkbench(Interface): """ The workbench interface. """ - #### 'IWorkbench' interface ############################################### + # 'IWorkbench' interface ----------------------------------------------- # The active workbench window (the last one to get focus). active_window = Instance(WorkbenchWindow) # The optional application scripting manager. - script_manager = Instance('apptools.appscripting.api.IScriptManager') + script_manager = Instance("apptools.appscripting.api.IScriptManager") # A directory on the local file system that we can read and write to at # will. This is used to persist window layout information, etc. - state_location = Str + state_location = Str() # The optional undo manager. - undo_manager = Instance('apptools.undo.api.IUndoManager') + undo_manager = Instance("pyface.undo.api.IUndoManager") # The user defined perspectives manager. user_perspective_manager = Instance(UserPerspectiveManager) @@ -35,7 +43,7 @@ # All of the workbench windows created by the workbench. windows = List(WorkbenchWindow) - #### Workbench lifecycle events #### + # Workbench lifecycle events ---- # Fired when the workbench is about to exit. # @@ -43,14 +51,14 @@ # # a) The 'exit' method being called. # b) The last open window being closed. - exiting = VetoableEvent + exiting = VetoableEvent() # Fired when the workbench has exited. # # This is fired after the last open window has been closed. - exited = Event + exited = Event() - #### Window lifecycle events #### + # Window lifecycle events ---- # Fired when a workbench window has been created. window_created = Event(WindowEvent) @@ -67,9 +75,9 @@ # Fired when a workbench window has been closed. window_closed = Event(WindowEvent) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbench' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_window(self, **kw): """ Factory method that creates a new workbench window. """ @@ -101,5 +109,3 @@ Returns None if no such editor exists. """ - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/i_workbench_window_layout.py python-pyface-7.4.0/pyface/workbench/i_workbench_window_layout.py --- python-pyface-6.1.2/pyface/workbench/i_workbench_window_layout.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/i_workbench_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The workbench window layout interface. """ -# Enthought library imports. from traits.api import Event, HasTraits, Instance, Interface, Str from traits.api import provides -# Local imports. + from .i_editor import IEditor from .i_view import IView @@ -34,12 +30,12 @@ # The Id of the editor area. # FIXME v3: This is toolkit specific. - editor_area_id = Str + editor_area_id = Str() # The workbench window that this is the layout for. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - #### Events #### + # Events ---- # Fired when an editor is about to be opened (or restored). editor_opening = Event(IEditor) @@ -180,7 +176,7 @@ """ - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_view_memento(self): """ Returns the state of the views. @@ -214,16 +210,17 @@ @provides(IWorkbenchWindowLayout) class MWorkbenchWindowLayout(HasTraits): """ Mixin containing common code for toolkit-specific implementations. """ - #### 'IWorkbenchWindowLayout' interface ################################### + + # 'IWorkbenchWindowLayout' interface ----------------------------------- # The Id of the editor area. # FIXME v3: This is toolkit specific. - editor_area_id = Str + editor_area_id = Str() # The workbench window that this is the layout for. - window = Instance('pyface.workbench.api.WorkbenchWindow') + window = Instance("pyface.workbench.api.WorkbenchWindow") - #### Events #### + # Events ---- # Fired when an editor is about to be opened (or restored). editor_opening = Event(IEditor) @@ -249,111 +246,111 @@ # Fired when a view has been closed (*not* hidden!). view_closed = Event(IView) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def activate_editor(self, editor): """ Activate an editor. """ - raise NotImplementedError + raise NotImplementedError() def activate_view(self, view): """ Activate a view. """ - raise NotImplementedError + raise NotImplementedError() def add_editor(self, editor, title): """ Add an editor. """ - raise NotImplementedError + raise NotImplementedError() def add_view(self, view, position=None, relative_to=None, size=(-1, -1)): """ Add a view. """ - raise NotImplementedError + raise NotImplementedError() def close_editor(self, editor): """ Close an editor. """ - raise NotImplementedError + raise NotImplementedError() def close_view(self, view): """ Close a view. """ - raise NotImplementedError + raise NotImplementedError() def close(self): """ Close the entire window layout. """ - raise NotImplementedError + raise NotImplementedError() def create_initial_layout(self, parent): """ Create the initial window layout. """ - raise NotImplementedError + raise NotImplementedError() def contains_view(self, view): """ Return True if the view exists in the window layout. """ - raise NotImplementedError + raise NotImplementedError() def hide_editor_area(self): """ Hide the editor area. """ - raise NotImplementedError + raise NotImplementedError() def hide_view(self, view): """ Hide a view. """ - raise NotImplementedError + raise NotImplementedError() def refresh(self): """ Refresh the window layout to reflect any changes. """ - raise NotImplementedError + raise NotImplementedError() def reset_editors(self): """ Activate the first editor in every group. """ - raise NotImplementedError + raise NotImplementedError() def reset_views(self): """ Activate the first view in every region. """ - raise NotImplementedError + raise NotImplementedError() def show_editor_area(self): """ Show the editor area. """ - raise NotImplementedError + raise NotImplementedError() def show_view(self, view): """ Show a view. """ - raise NotImplementedError + raise NotImplementedError() - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_view_memento(self): """ Returns the state of the views. """ - raise NotImplementedError + raise NotImplementedError() def set_view_memento(self, memento): """ Restores the state of the views. """ - raise NotImplementedError + raise NotImplementedError() def get_editor_memento(self): """ Returns the state of the editors. """ - raise NotImplementedError + raise NotImplementedError() def set_editor_memento(self, memento): """ Restores the state of the editors. """ - raise NotImplementedError + raise NotImplementedError() def get_toolkit_memento(self): """ Return any toolkit-specific data that should be part of the memento. @@ -365,9 +362,9 @@ """ return - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'MWorkbenchWindowLayout' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _get_editor_references(self): """ Returns a reference to every editor. """ @@ -387,5 +384,3 @@ editor_references[editor.id] = editor_reference return editor_references - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/perspective_item.py python-pyface-7.4.0/pyface/workbench/perspective_item.py --- python-pyface-6.1.2/pyface/workbench/perspective_item.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/perspective_item.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,18 +1,27 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An item in a Perspective contents list. """ -# Enthought library imports. from traits.api import Enum, Float, HasTraits, provides, Str -# Local imports. + from .i_perspective_item import IPerspectiveItem @provides(IPerspectiveItem) class PerspectiveItem(HasTraits): """ An item in a Perspective contents list. """ + # The Id of the view to display in the perspective. - id = Str + id = Str() # The position of the view relative to the item specified in the # 'relative_to' trait. @@ -26,12 +35,12 @@ # If the position is specified as 'with' you must specify a 'relative_to' # item other than the editor area (i.e., you cannot position a view 'with' # the editor area). - position = Enum('left', 'top', 'bottom', 'right', 'with') + position = Enum("left", "top", "bottom", "right", "with") # The Id of the view to position relative to. If this is not specified # (or if no view exists with this Id) then the view will be placed relative # to the editor area. - relative_to = Str + relative_to = Str() # The width of the item (as a fraction of the window width). # @@ -50,6 +59,4 @@ height = Float(-1) # The style of the dock control created. - style_hint = Enum('tab', 'vertical', 'horizontal', 'fixed') - -#### EOF ###################################################################### + style_hint = Enum("tab", "vertical", "horizontal", "fixed") diff -Nru python-pyface-6.1.2/pyface/workbench/perspective.py python-pyface-7.4.0/pyface/workbench/perspective.py --- python-pyface-6.1.2/pyface/workbench/perspective.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/perspective.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,21 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ The default perspective. """ -# Standard library imports. import logging -# Enthought library imports. + from traits.api import Bool, HasTraits, List, provides, Str, Tuple -# Local imports. + from .i_perspective import IPerspective from .perspective_item import PerspectiveItem @@ -19,13 +27,14 @@ @provides(IPerspective) class Perspective(HasTraits): """ The default perspective. """ + # The ID of the default perspective. - DEFAULT_ID = 'pyface.workbench.default' + DEFAULT_ID = "pyface.workbench.default" # The name of the default perspective. - DEFAULT_NAME = 'Default' + DEFAULT_NAME = "Default" - #### 'IPerspective' interface ############################################# + # 'IPerspective' interface --------------------------------------------- # The perspective's unique identifier (unique within a workbench window). id = Str(DEFAULT_ID) @@ -47,20 +56,20 @@ # Should the editor area be shown in this perspective? show_editor_area = Bool(True) - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __str__(self): """ Return an informal string representation of the object. """ - return 'Perspective(%s)' % self.id + return "Perspective(%s)" % self.id - ########################################################################### + # ------------------------------------------------------------------------ # 'Perspective' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Initializers ######################################################### + # Initializers --------------------------------------------------------- def _id_default(self): """ Trait initializer. """ @@ -68,7 +77,7 @@ # If no Id is specified then use the name. return self.name - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create(self, window): """ Create the perspective in a workbench window. @@ -95,8 +104,6 @@ # Activate the first view in every region. window.reset_views() - return - def show(self, window): """ Called when the perspective is shown in a workbench window. @@ -108,9 +115,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _add_contents(self, window, contents): """ Adds the specified contents. """ @@ -126,8 +133,6 @@ for item in contents: self._add_perspective_item(window, item) - return - def _add_perspective_item(self, window, item): """ Adds a perspective item to a window. """ @@ -159,9 +164,7 @@ # fixme: This is worth keeping an eye on though. If we end up with # a strict mode that throws exceptions early and often for # developers, then this might be a good place to throw one ;^) - logger.error('missing view for perspective item <%s>' % item.id) - - return + logger.error("missing view for perspective item <%s>" % item.id) def _add_all(self, window): """ Adds *all* of the window's views defined in the window. """ @@ -170,8 +173,6 @@ if view.visible: self._add_view(window, view) - return - def _add_view(self, window, view): """ Adds a view to a window. """ @@ -189,5 +190,3 @@ ) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/tests/test_workbench_window.py python-pyface-7.4.0/pyface/workbench/tests/test_workbench_window.py --- python-pyface-6.1.2/pyface/workbench/tests/test_workbench_window.py 2019-06-14 12:22:56.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/tests/test_workbench_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,29 +1,40 @@ -import mock +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + import tempfile import shutil -import os import unittest +from unittest import mock -from traits.testing.unittest_tools import UnittestTools +from traits.testing.api import UnittestTools from pyface.workbench.perspective import Perspective from pyface.workbench.api import Workbench -from pyface.workbench.user_perspective_manager import UserPerspectiveManager -from pyface.workbench.workbench_window import (WorkbenchWindow, - WorkbenchWindowLayout, - WorkbenchWindowMemento) +from pyface.workbench.workbench_window import ( + WorkbenchWindow, + WorkbenchWindowLayout, + WorkbenchWindowMemento, +) class TestWorkbenchWindowUserPerspective(unittest.TestCase, UnittestTools): - def setUp(self): # A perspective with show_editor_area switched on - self.with_editor = Perspective(show_editor_area=True, - id="test_id", name="test_name") + self.with_editor = Perspective( + show_editor_area=True, id="test_id", name="test_name" + ) # A perspective with show_editor_area switched off - self.without_editor = Perspective(show_editor_area=False, - id="test_id2", name="test_name2") + self.without_editor = Perspective( + show_editor_area=False, id="test_id2", name="test_name2" + ) # Where the state file should be saved self.state_location = tempfile.mkdtemp(dir="./") @@ -51,8 +62,9 @@ def show_perspective(self, workbench_window, perspective): workbench_window.active_perspective = perspective workbench_window.layout.is_editor_area_visible = mock.MagicMock( - return_value=perspective.show_editor_area) - + return_value=perspective.show_editor_area + ) + def test_editor_area_with_perspectives(self): """ Test show_editor_area is respected while switching perspective""" @@ -63,7 +75,7 @@ # Add perspectives workbench.user_perspective_manager.add(self.with_editor) workbench.user_perspective_manager.add(self.without_editor) - + # There are the methods we want to test if they are called workbench_window.show_editor_area = mock.MagicMock() workbench_window.hide_editor_area = mock.MagicMock() @@ -74,12 +86,12 @@ # Show a perspective with an editor area self.show_perspective(workbench_window, self.with_editor) - + # show_editor_area should be called self.assertTrue(workbench_window.show_editor_area.called) # Show a perspective withOUT an editor area - workbench_window.hide_editor_area.reset_mock() + workbench_window.hide_editor_area.reset_mock() self.show_perspective(workbench_window, self.without_editor) # hide_editor_area should be called @@ -109,34 +121,38 @@ # Mock layout functions for pickling # We only care about show_editor_area and not the layout in this test - layout_functions = {"get_view_memento.return_value": (0, (None, None)), - "get_editor_memento.return_value": (0, (None, None)), - "get_toolkit_memento.return_value": (0, dict(geometry=""))} + layout_functions = { + "get_view_memento.return_value": (0, (None, None)), + "get_editor_memento.return_value": (0, (None, None)), + "get_toolkit_memento.return_value": (0, dict(geometry="")), + } workbench_window.layout.configure_mock(**layout_functions) - + # The following records perspective mementos to workbench_window._memento self.show_perspective(workbench_window, self.without_editor) self.show_perspective(workbench_window, self.with_editor) # Save the window layout to a state file workbench._save_window_layout(workbench_window) - + # We only needed the state file for this test del workbench_window del workbench - + # We create another workbench which uses the state location # and we test if we can retore the saved perspective correctly workbench, workbench_window = self.get_workbench_with_window() # Mock window factory since we already created a workbench window - workbench.window_factory = mock.MagicMock(return_value=workbench_window) + workbench.window_factory = mock.MagicMock( + return_value=workbench_window + ) # There are the methods we want to test if they are called workbench_window.show_editor_area = mock.MagicMock() workbench_window.hide_editor_area = mock.MagicMock() - + # This restores the perspectives and mementos workbench.create_window() @@ -144,9 +160,14 @@ workbench_window._create_contents(mock.Mock()) # Perspective mementos should be restored - self.assertIn(self.with_editor.id, workbench_window._memento.perspective_mementos) - self.assertIn(self.without_editor.id, workbench_window._memento.perspective_mementos) - + self.assertIn( + self.with_editor.id, workbench_window._memento.perspective_mementos + ) + self.assertIn( + self.without_editor.id, + workbench_window._memento.perspective_mementos, + ) + # Since the with_editor perspective is used last, # it should be used as initial perspective self.assertTrue(workbench_window.show_editor_area.called) @@ -155,12 +176,12 @@ # The restored perspectives are not the same instance as before # We need to get them using their id perspective_without_editor = workbench_window.get_perspective_by_id( - self.without_editor.id) - + self.without_editor.id + ) + # Show the perspective with editor area - workbench_window.hide_editor_area.reset_mock() + workbench_window.hide_editor_area.reset_mock() self.show_perspective(workbench_window, perspective_without_editor) # make sure hide_editor_area is called self.assertTrue(workbench_window.hide_editor_area.called) - diff -Nru python-pyface-6.1.2/pyface/workbench/traits_ui_editor.py python-pyface-7.4.0/pyface/workbench/traits_ui_editor.py --- python-pyface-6.1.2/pyface/workbench/traits_ui_editor.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/traits_ui_editor.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,21 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ An editor whose content is provided by a traits UI. """ -# Standard library imports. import logging -# Enthought library imports. + from traits.api import Instance, Str -# Local imports. + from .editor import Editor @@ -18,7 +26,7 @@ class TraitsUIEditor(Editor): """ An editor whose content is provided by a traits UI. """ - #### 'TraitsUIEditor' interface ########################################### + # 'TraitsUIEditor' interface ------------------------------------------- # The traits UI that represents the editor. # @@ -27,20 +35,20 @@ # The name of the traits UI view used to create the UI (if not specified, # the default traits UI view is used). - view = Str + view = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _name_default(self): """ Trait initializer. """ return str(self.obj) - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_control(self, parent): """ Creates the toolkit-specific control that represents the editor. @@ -64,15 +72,15 @@ # Give the traits UI a chance to clean itself up. if self.ui is not None: - logger.debug('disposing traits UI for editor [%s]', self) + logger.debug("disposing traits UI for editor [%s]", self) self.ui.dispose() self.ui = None return - ########################################################################### + # ------------------------------------------------------------------------ # 'TraitsUIEditor' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_ui(self, parent): """ Creates the traits UI that represents the editor. @@ -83,9 +91,7 @@ """ ui = self.obj.edit_traits( - parent=parent, view=self.view, kind='subpanel' + parent=parent, view=self.view, kind="subpanel" ) return ui - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/traits_ui_view.py python-pyface-7.4.0/pyface/workbench/traits_ui_view.py --- python-pyface-6.1.2/pyface/workbench/traits_ui_view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/traits_ui_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,21 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A view whose content is provided by a traits UI. """ -# Standard library imports. import logging -# Enthought library imports. + from traits.api import Any, Instance, Str -# Local imports. + from .view import View @@ -18,33 +26,33 @@ class TraitsUIView(View): """ A view whose content is provided by a traits UI. """ - #### 'TraitsUIView' interface ############################################# + # 'TraitsUIView' interface --------------------------------------------- # The object that we povide a traits UI of (this defaults to the view # iteself ie. 'self'). - obj = Any + obj = Any() # The traits UI that represents the view. # # The framework sets this to the value returned by 'create_ui'. - ui = Instance('traitsui.ui.UI') + ui = Instance("traitsui.ui.UI") # The name of the traits UI view used to create the UI (if not specified, # the default traits UI view is used). - view = Str + view = Str() - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbenchPart' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _name_default(self): """ Trait initializer. """ return str(self.obj) - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_control(self, parent): """ Creates the toolkit-specific control that represents the editor. @@ -68,7 +76,7 @@ # Give the traits UI a chance to clean itself up. if self.ui is not None: - logger.debug('disposing traits UI for view [%s]', self) + logger.debug("disposing traits UI for view [%s]", self) self.ui.dispose() self.ui = None # Break reference to the control, so the view is created afresh @@ -77,18 +85,18 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'TraitsUIView' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Trait initializers ################################################### + # Trait initializers --------------------------------------------------- def _obj_default(self): """ Trait initializer. """ return self - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_ui(self, parent): """ Creates the traits UI that represents the editor. @@ -99,9 +107,7 @@ """ ui = self.obj.edit_traits( - parent=parent, view=self.view, kind='subpanel' + parent=parent, view=self.view, kind="subpanel" ) return ui - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/user_perspective_manager.py python-pyface-7.4.0/pyface/workbench/user_perspective_manager.py --- python-pyface-6.1.2/pyface/workbench/user_perspective_manager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/user_perspective_manager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,22 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Manages a set of user perspectives. """ -# Standard library imports. import logging import os -# Enthought library imports. + from pyface.workbench.api import Perspective from traits.api import Any, Dict, HasTraits, Int, List, Property -from traits.api import Unicode +from traits.api import Str # Logging. @@ -18,11 +26,11 @@ class UserPerspectiveManager(HasTraits): """ Manages a set of user perspectives. """ - #### 'UserPerspective' interface ########################################## + # 'UserPerspective' interface -----------------------------------------# # A directory on the local file system that we can read and write to at # will. This is used to persist window layout information, etc. - state_location = Unicode + state_location = Str() # Next available user perspective id. next_id = Property(Int) @@ -34,50 +42,48 @@ perspectives = Property(List) # The name of the user defined perspectives definition file. - file_name = Property(Unicode) + file_name = Property(Str) - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # Shadow trait for the 'id_to_perspective' property. - _id_to_perspective = Any + _id_to_perspective = Any() - ########################################################################### + # ------------------------------------------------------------------------ # 'UserPerspective' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Properties ########################################################### + # Properties ----------------------------------------------------------- - def _get_next_id ( self ): + def _get_next_id(self): """ Property getter. """ # Get all of the current perspective ids: ids = list(self.id_to_perspective.keys()) # If there are none: - if len( ids ) == 0: + if len(ids) == 0: # Return the starting id: return 1 # Else return the current highest id + 1 as the next id: ids.sort() - return int( ids[-1][19:-2] ) + 1 + return int(ids[-1][19:-2]) + 1 - def _get_id_to_perspective ( self ): + def _get_id_to_perspective(self): """ Property getter. """ if self._id_to_perspective is None: self._id_to_perspective = dic = {} try: - fh = open( self.file_name, 'r' ) + fh = open(self.file_name, "r") for line in fh: - data = line.split( ':', 1 ) - if len( data ) == 2: + data = line.split(":", 1) + if len(data) == 2: id, name = data[0].strip(), data[1].strip() - dic[ id ] = Perspective( - id = id, - name = name, - show_editor_area = False + dic[id] = Perspective( + id=id, name=name, show_editor_area=False ) fh.close() except: @@ -85,25 +91,25 @@ return self._id_to_perspective - def _get_perspectives ( self ): + def _get_perspectives(self): """ Property getter. """ return list(self.id_to_perspective.values()) - def _get_file_name ( self ): + def _get_file_name(self): """ Property getter. """ - return os.path.join(self.state_location, '__user_perspective__') + return os.path.join(self.state_location, "__user_perspective__") - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def create_perspective(self, name, show_editor_area=True): """ Create a new (and empty) user-defined perspective. """ perspective = Perspective( - id = '__user_perspective_%09d__' % self.next_id, - name = name, - show_editor_area = show_editor_area + id="__user_perspective_%09d__" % self.next_id, + name=name, + show_editor_area=show_editor_area, ) # Add the perspective to the map. @@ -120,7 +126,7 @@ clone = perspective.clone_traits() # Give the clone a special user perspective Id! - clone.id = '__user_perspective_%09d__' % self.next_id + clone.id = "__user_perspective_%09d__" % self.next_id # Set any traits specified as keyword arguments. clone.trait_set(**traits) @@ -132,7 +138,7 @@ window._memento.perspective_mementos[clone.id] = ( window.layout.get_view_memento(), window.active_view and window.active_view.id or None, - window.layout.is_editor_area_visible() + window.layout.is_editor_area_visible(), ) # Update the persistent file information. @@ -145,13 +151,11 @@ self._update_persistent_data() - return - def add(self, perspective, name=None): """ Add a perspective with an optional name. """ # Define the id for the new perspective: - perspective.id = id = '__user_perspective_%09d__' % self.next_id + perspective.id = id = "__user_perspective_%09d__" % self.next_id # Save the new name (if specified): if name is not None: @@ -176,8 +180,6 @@ # Update the persistent file information: self._update_persistent_data() - return - def remove(self, id): """ Remove the user perspective with the specified id. @@ -199,23 +201,26 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _update_persistent_data(self): """ Update the persistent file information. """ try: - fh = open( self.file_name, 'w' ) - fh.write( '\n'.join( [ '%s: %s' % ( p.id, p.name ) - for p in self.perspectives ] ) ) + fh = open(self.file_name, "w") + fh.write( + "\n".join( + ["%s: %s" % (p.id, p.name) for p in self.perspectives] + ) + ) fh.close() except: - logger.error( "Could not write the user defined perspective " - "definition file: " + self.file_name ) + logger.error( + "Could not write the user defined perspective " + "definition file: " + self.file_name + ) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/view.py python-pyface-7.4.0/pyface/workbench/view.py --- python-pyface-6.1.2/pyface/workbench/view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,21 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a workbench view. """ # Import the toolkit specific version. from pyface.toolkit import toolkit_object -View = toolkit_object('workbench.view:View') -### EOF ####################################################################### +View = toolkit_object("workbench.view:View") diff -Nru python-pyface-6.1.2/pyface/workbench/window_event.py python-pyface-7.4.0/pyface/workbench/window_event.py --- python-pyface-6.1.2/pyface/workbench/window_event.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/window_event.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,25 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ Window events. """ -# Enthought library imports. from traits.api import HasTraits, Instance, Vetoable -# Local imports. + from .workbench_window import WorkbenchWindow class WindowEvent(HasTraits): """ A window lifecycle event. """ - #### 'WindowEvent' interface ############################################## + # 'WindowEvent' interface ---------------------------------------------# # The window that the event occurred on. window = Instance(WorkbenchWindow) @@ -21,5 +29,3 @@ """ A vetoable window lifecycle event. """ pass - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/workbench.py python-pyface-7.4.0/pyface/workbench/workbench.py --- python-pyface-6.1.2/pyface/workbench/workbench.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/workbench.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,19 +1,27 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A workbench. """ -# Standard library imports. -import six.moves.cPickle +import pickle import logging import os -# Enthought library imports. + from traits.etsconfig.api import ETSConfig from pyface.api import NO from traits.api import Bool, Callable, Event, HasTraits, provides -from traits.api import Instance, List, Unicode, Vetoable +from traits.api import Instance, List, Str, Vetoable from traits.api import VetoableEvent -# Local imports. + from .i_editor_manager import IEditorManager from .i_workbench import IWorkbench from .user_perspective_manager import UserPerspectiveManager @@ -33,7 +41,8 @@ any number of workbench windows. """ - #### 'IWorkbench' interface ############################################### + + # 'IWorkbench' interface ----------------------------------------------- # The active workbench window (the last one to get focus). active_window = Instance(WorkbenchWindow) @@ -42,14 +51,14 @@ editor_manager = Instance(IEditorManager) # The optional application scripting manager. - script_manager = Instance('apptools.appscripting.api.IScriptManager') + script_manager = Instance("apptools.appscripting.api.IScriptManager") # A directory on the local file system that we can read and write to at # will. This is used to persist window layout information, etc. - state_location = Unicode + state_location = Str() # The optional undo manager. - undo_manager = Instance('apptools.undo.api.IUndoManager') + undo_manager = Instance("pyface.undo.api.IUndoManager") # The user-defined perspectives manager. user_perspective_manager = Instance(UserPerspectiveManager) @@ -57,7 +66,7 @@ # All of the workbench windows created by the workbench. windows = List(WorkbenchWindow) - #### Workbench lifecycle events ########################################### + # Workbench lifecycle events ------------------------------------------- # Fired when the workbench is about to exit. # @@ -66,12 +75,12 @@ # a) The 'exit' method being called. # b) The last open window being closed. # - exiting = VetoableEvent + exiting = VetoableEvent() # Fired when the workbench has exited. - exited = Event + exited = Event() - #### Window lifecycle events ############################################## + # Window lifecycle events ---------------------------------------------# # Fired when a workbench window has been created. window_created = Event(WindowEvent) @@ -88,22 +97,22 @@ # Fired when a workbench window has been closed. window_closed = Event(WindowEvent) - #### 'Workbench' interface ################################################ + # 'Workbench' interface ------------------------------------------------ # The factory that is used to create workbench windows. This is used in # the default implementation of 'create_window'. If you override that # method then you obviously don't need to set this trait! window_factory = Callable - #### Private interface #################################################### + # Private interface ---------------------------------------------------- # An 'explicit' exit is when the the 'exit' method is called. # An 'implicit' exit is when the user closes the last open window. _explicit_exit = Bool(False) - ########################################################################### + # ------------------------------------------------------------------------ # 'IWorkbench' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_window(self, **kw): """ Factory method that creates a new workbench window. """ @@ -122,11 +131,11 @@ # NOTE: 'activated' is not fired on a window when the window first # opens and gets focus. It is only fired when the window comes from # lower in the stack to be the active window. - window.on_trait_change(self._on_window_activated, 'activated') - window.on_trait_change(self._on_window_opening, 'opening') - window.on_trait_change(self._on_window_opened, 'opened') - window.on_trait_change(self._on_window_closing, 'closing') - window.on_trait_change(self._on_window_closed, 'closed') + window.observe(self._on_window_activated, "activated") + window.observe(self._on_window_opening, "opening") + window.observe(self._on_window_opened, "opened") + window.observe(self._on_window_closing, "closing") + window.observe(self._on_window_closed, "closed") # Event notification. self.window_created = WindowEvent(window=window) @@ -146,7 +155,7 @@ """ - logger.debug('**** exiting the workbench ****') + logger.debug("**** exiting the workbench ****") # Event notification. self.exiting = event = Vetoable() @@ -174,11 +183,11 @@ exited = False if not exited: - logger.debug('**** exit of the workbench vetoed ****') + logger.debug("**** exit of the workbench vetoed ****") return exited - #### Convenience methods on the active window ############################# + # Convenience methods on the active window ----------------------------- def edit(self, obj, kind=None, use_existing=True): """ Edit an object in the active workbench window. """ @@ -206,33 +215,33 @@ return self.active_window.get_editor_by_id(id) - #### Message dialogs #### + # Message dialogs ---- def confirm(self, message, title=None, cancel=False, default=NO): """ Convenience method to show a confirmation dialog. """ return self.active_window.confirm(message, title, cancel, default) - def information(self, message, title='Information'): + def information(self, message, title="Information"): """ Convenience method to show an information message dialog. """ return self.active_window.information(message, title) - def warning(self, message, title='Warning'): + def warning(self, message, title="Warning"): """ Convenience method to show a warning message dialog. """ return self.active_window.warning(message, title) - def error(self, message, title='Error'): + def error(self, message, title="Error"): """ Convenience method to show an error message dialog. """ return self.active_window.error(message, title) - ########################################################################### + # ------------------------------------------------------------------------ # 'Workbench' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Initializers ######################################################### + # Initializers --------------------------------------------------------- def _state_location_default(self): """ Trait initializer. """ @@ -240,15 +249,15 @@ # It would be preferable to base this on GUI.state_location. state_location = os.path.join( ETSConfig.application_home, - 'pyface', - 'workbench', - ETSConfig.toolkit + "pyface", + "workbench", + ETSConfig.toolkit, ) if not os.path.exists(state_location): os.makedirs(state_location) - logger.debug('workbench state location is %s', state_location) + logger.debug("workbench state location is %s", state_location) return state_location @@ -257,7 +266,7 @@ # We make sure the undo package is entirely optional. try: - from apptools.undo.api import UndoManager + from pyface.undo.api import UndoManager except ImportError: return None @@ -268,18 +277,18 @@ return UserPerspectiveManager(state_location=self.state_location) - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Workbench' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_window(self, **kw): """ Factory method that creates a new workbench window. """ - raise NotImplementedError + raise NotImplementedError() - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _close_all_windows(self): """ Closes all open windows. @@ -309,14 +318,14 @@ def _restore_window_layout(self, window): """ Restore the window layout. """ - filename = os.path.join(self.state_location, 'window_memento') + filename = os.path.join(self.state_location, "window_memento") if os.path.exists(filename): try: # If the memento class itself has been modified then there # is a chance that the unpickle will fail. If so then we just # carry on as if there was no memento! - f = open(filename, 'rb') - memento = six.moves.cPickle.load(f) + f = open(filename, "rb") + memento = pickle.load(f) f.close() # The memento doesn't actually get used until the window is @@ -326,44 +335,38 @@ # If *anything* goes wrong then simply log the error and carry on # with no memento! except: - logger.exception('restoring window layout from %s', filename) - - return + logger.exception("restoring window layout from %s", filename) def _save_window_layout(self, window): """ Save the window layout. """ # Save the window layout. - f = open(os.path.join(self.state_location, 'window_memento'), 'wb') - six.moves.cPickle.dump(window.get_memento(), f) + f = open(os.path.join(self.state_location, "window_memento"), "wb") + pickle.dump(window.get_memento(), f) f.close() return - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - def _on_window_activated(self, window, trait_name, event): + def _on_window_activated(self, event): """ Dynamic trait change handler. """ - - logger.debug('window %s activated', window) + window = event.object + logger.debug("window %s activated", window) self.active_window = window - return - - def _on_window_opening(self, window, trait_name, event): + def _on_window_opening(self, event): """ Dynamic trait change handler. """ - + window = event.object # Event notification. self.window_opening = window_event = VetoableWindowEvent(window=window) if window_event.veto: - event.veto = True + event.new.veto = True - return - - def _on_window_opened(self, window, trait_name, event): + def _on_window_opened(self, event): """ Dynamic trait change handler. """ - + window = event.object # We maintain a list of all open windows so that (amongst other things) # we can detect when the user is attempting to close the last one. self.windows.append(window) @@ -376,16 +379,14 @@ # Event notification. self.window_opened = WindowEvent(window=window) - return - - def _on_window_closing(self, window, trait_name, event): + def _on_window_closing(self, event): """ Dynamic trait change handler. """ - + window = event.object # Event notification. self.window_closing = window_event = VetoableWindowEvent(window=window) if window_event.veto: - event.veto = True + event.new.veto = True else: # Is this the last open window? @@ -396,17 +397,15 @@ # Event notification. self.exiting = window_event = Vetoable() if window_event.veto: - event.veto = True + event.new.veto = True - if not event.veto: + if not event.new.veto: # Save the window size, position and layout. self._save_window_layout(window) - return - - def _on_window_closed(self, window, trait_name, event): + def _on_window_closed(self, event): """ Dynamic trait change handler. """ - + window = event.object self.windows.remove(window) # Event notification. @@ -418,5 +417,3 @@ self.exited = self return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/workbench_window_layout.py python-pyface-7.4.0/pyface/workbench/workbench_window_layout.py --- python-pyface-6.1.2/pyface/workbench/workbench_window_layout.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/workbench_window_layout.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ The implementation of a workbench window layout. """ @@ -18,7 +15,5 @@ from pyface.toolkit import toolkit_object WorkbenchWindowLayout = toolkit_object( - 'workbench.workbench_window_layout:WorkbenchWindowLayout' + "workbench.workbench_window_layout:WorkbenchWindowLayout" ) - -### EOF ####################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/workbench_window_memento.py python-pyface-7.4.0/pyface/workbench/workbench_window_memento.py --- python-pyface-6.1.2/pyface/workbench/workbench_window_memento.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/workbench_window_memento.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,7 +1,15 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A memento for a workbench window. """ -# Enthought library imports. from traits.api import Any, Dict, HasTraits, Str, Tuple @@ -9,10 +17,10 @@ """ A memento for a workbench window. """ # The Id of the active perspective. - active_perspective_id = Str + active_perspective_id = Str() # The memento for the editor area. - editor_area_memento = Any + editor_area_memento = Any() # Mementos for each perspective that has been seen. # @@ -21,13 +29,10 @@ perspective_mementos = Dict(Str, Any) # The position of the window. - position = Tuple + position = Tuple() # The size of the window. - size = Tuple + size = Tuple() # Any extra data the toolkit implementation may want to keep. toolkit_data = Any() - - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/workbench/workbench_window.py python-pyface-7.4.0/pyface/workbench/workbench_window.py --- python-pyface-6.1.2/pyface/workbench/workbench_window.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/workbench/workbench_window.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,31 @@ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! """ A workbench window. """ -# Standard library imports. import logging -# Enthought library imports. + from pyface.api import ApplicationWindow, GUI -from traits.api import Callable, Constant, Delegate, Event, Instance -from traits.api import List, Str, Tuple, Unicode, Vetoable, Undefined -from traits.api import on_trait_change, provides +from traits.api import ( + Constant, + Delegate, + Instance, + List, + Str, + Tuple, + Undefined, + Vetoable, + observe, +) -# Local imports. from .i_editor import IEditor from .i_editor_manager import IEditorManager from .i_perspective import IPerspective @@ -28,7 +43,7 @@ class WorkbenchWindow(ApplicationWindow): """ A workbench window. """ - #### 'IWorkbenchWindow' interface ######################################### + # 'IWorkbenchWindow' interface ----------------------------------------- # The view or editor that currently has the focus. active_part = Instance(IWorkbenchPart) @@ -37,12 +52,12 @@ editor_manager = Instance(IEditorManager) # The current selection within the window. - selection = List + selection = List() # The workbench that the window belongs to. - workbench = Instance('pyface.workbench.api.IWorkbench') + workbench = Instance("pyface.workbench.api.IWorkbench") - #### Editors ####################### + # Editors ----------------------- # The active editor. active_editor = Instance(IEditor) @@ -51,25 +66,25 @@ editors = List(IEditor) # The Id of the editor area. - editor_area_id = Constant('pyface.workbench.editors') + editor_area_id = Constant("pyface.workbench.editors") # The (initial) size of the editor area (the user is free to resize it of # course). editor_area_size = Tuple((100, 100)) # Fired when an editor is about to be opened (or restored). - editor_opening = Delegate('layout') # Event(IEditor) + editor_opening = Delegate("layout") # Event(IEditor) # Fired when an editor has been opened (or restored). - editor_opened = Delegate('layout') # Event(IEditor) + editor_opened = Delegate("layout") # Event(IEditor) # Fired when an editor is about to be closed. - editor_closing = Delegate('layout') # Event(IEditor) + editor_closing = Delegate("layout") # Event(IEditor) # Fired when an editor has been closed. - editor_closed = Delegate('layout') # Event(IEditor) + editor_closed = Delegate("layout") # Event(IEditor) - #### Views ######################### + # Views ------------------------- # The active view. active_view = Instance(IView) @@ -81,7 +96,7 @@ # its toolkit-specific control etc. views = List(IView) - #### Perspectives ################## + # Perspectives -----------------# # The active perspective. active_perspective = Instance(IPerspective) @@ -107,23 +122,23 @@ # visible when the window last closed is shown. If this is not the empty # string then the perspective with this Id is shown. # - default_perspective_id = Str + default_perspective_id = Str() - #### 'WorkbenchWindow' interface ########################################## + # 'WorkbenchWindow' interface -----------------------------------------# # The window layout is responsible for creating and managing the internal # structure of the window (i.e., it knows how to add and remove views and # editors etc). layout = Instance(WorkbenchWindowLayout) - #### 'Private' interface ################################################## + # 'Private' interface -------------------------------------------------# # The state of the window suitable for pickling etc. _memento = Instance(WorkbenchWindowMemento) - ########################################################################### + # ------------------------------------------------------------------------ # 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def open(self): """ Open the window. @@ -135,7 +150,7 @@ """ - logger.debug('window %s opening', self) + logger.debug("window %s opening", self) # Trait notification. self.opening = event = Vetoable() @@ -148,10 +163,10 @@ # Trait notification. self.opened = self - logger.debug('window %s opened', self) + logger.debug("window %s opened", self) else: - logger.debug('window %s open was vetoed', self) + logger.debug("window %s open was vetoed", self) # fixme: This is not actually part of the Pyface 'Window' API (but # maybe it should be). We return this to indicate whether the window @@ -168,7 +183,7 @@ """ - logger.debug('window %s closing', self) + logger.debug("window %s closing", self) if self.control is not None: # Trait notification. @@ -193,22 +208,22 @@ # Trait notification. self.closed = self - logger.debug('window %s closed', self) + logger.debug("window %s closed", self) else: - logger.debug('window %s close was vetoed', self) + logger.debug("window %s close was vetoed", self) else: - logger.debug('window %s is not open', self) + logger.debug("window %s is not open", self) # FIXME v3: This is not actually part of the Pyface 'Window' API (but # maybe it should be). We return this to indicate whether the window # actually closed. return self.control is None - ########################################################################### + # ------------------------------------------------------------------------ # Protected 'Window' interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_contents(self, parent): """ Create and return the window contents. """ @@ -233,16 +248,16 @@ return contents - ########################################################################### + # ------------------------------------------------------------------------ # 'WorkbenchWindow' interface. - ########################################################################### + # ------------------------------------------------------------------------ - #### Initializers ######################################################### + # Initializers --------------------------------------------------------- def _editor_manager_default(self): """ Trait initializer. """ - from editor_manager import EditorManager + from .editor_manager import EditorManager return EditorManager(window=self) @@ -251,22 +266,18 @@ return WorkbenchWindowLayout(window=self) - #### Methods ############################################################## + # Methods -------------------------------------------------------------# def activate_editor(self, editor): """ Activates an editor. """ self.layout.activate_editor(editor) - return - def activate_view(self, view): """ Activates a view. """ self.layout.activate_view(view) - return - def add_editor(self, editor, title=None): """ Adds an editor. @@ -280,8 +291,6 @@ self.layout.add_editor(editor, title) self.editors.append(editor) - return - def add_view(self, view, position=None, relative_to=None, size=(-1, -1)): """ Adds a view. """ @@ -289,18 +298,14 @@ # This case allows for views that are created and added dynamically # (i.e. they were not even known about when the window was created). - if not view in self.views: + if view not in self.views: self.views.append(view) - return - def close_editor(self, editor): """ Closes an editor. """ self.layout.close_editor(editor) - return - def close_view(self, view): """ Closes a view. @@ -311,8 +316,6 @@ self.hide_view(view) - return - def create_editor(self, obj, kind=None): """ Create an editor for an object. @@ -329,8 +332,6 @@ if editor.control is not None: editor.destroy_control() - return - def destroy_views(self, views): """ Destroy a list of views. """ @@ -338,8 +339,6 @@ if view.control is not None: view.destroy_control() - return - def edit(self, obj, kind=None, use_existing=True): """ Edit an object. @@ -370,7 +369,7 @@ editor = self.create_editor(obj, kind) if editor is None: - logger.warn('no editor for object %s', obj) + logger.warning("no editor for object %s", obj) self.add_editor(editor) self.activate_editor(editor) @@ -468,22 +467,16 @@ self.layout.hide_editor_area() - return - def hide_view(self, view): """ Hide a view. """ self.layout.hide_view(view) - return - def refresh(self): """ Refresh the window to reflect any changes. """ self.layout.refresh() - return - def reset_active_perspective(self): """ Reset the active perspective back to its original contents. """ @@ -498,42 +491,34 @@ # the perspective, its 'create_contents' method will be called again). self._show_perspective(perspective, perspective) - return - def reset_all_perspectives(self): """ Reset all perspectives back to their original contents. """ # Remove all perspective mementos (except user perspectives). for id in self._memento.perspective_mementos.keys(): - if not id.startswith('__user_perspective'): + if not id.startswith("__user_perspective"): del self._memento.perspective_mementos[id] # Re-display the active perspective. - self._show_perspective(self.active_perspective,self.active_perspective) - - return + self._show_perspective( + self.active_perspective, self.active_perspective + ) def reset_editors(self): """ Activate the first editor in every tab. """ self.layout.reset_editors() - return - def reset_views(self): """ Activate the first view in every tab. """ self.layout.reset_views() - return - def show_editor_area(self): """ Show the editor area. """ self.layout.show_editor_area() - return - def show_view(self, view): """ Show a view. """ @@ -553,7 +538,7 @@ return - #### Methods for saving and restoring the layout ########################## + # Methods for saving and restoring the layout -------------------------# def get_memento(self): """ Return the state of the window suitable for pickling etc. """ @@ -569,7 +554,7 @@ self._memento.perspective_mementos[self.active_perspective.id] = ( self.layout.get_view_memento(), self.active_view and self.active_view.id or None, - self.layout.is_editor_area_visible() + self.layout.is_editor_area_visible(), ) # The layout of the editor area. @@ -592,9 +577,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _add_view_in_default_position(self, view): """ Adds a view in its 'default' position. """ @@ -614,8 +599,6 @@ self.add_view(view, item.position, relative_to, size) - return - def _get_initial_perspective(self, *methods): """ Return the initial perspective. """ @@ -623,14 +606,12 @@ # If a default perspective was specified then we prefer that over # any other perspective. self._get_default_perspective, - # If there was no default perspective then try the perspective that # was active the last time the application was run. self._get_previous_perspective, - # If there was no previous perspective, then try the first one that # we know about. - self._get_first_perspective + self._get_first_perspective, ] for method in methods: @@ -640,7 +621,7 @@ # If we have no known perspectives, make a new blank one up. else: - logger.warn('no known perspectives - creating a new one') + logger.warning("no known perspectives - creating a new one") perspective = Perspective() return perspective @@ -658,7 +639,7 @@ if len(id) > 0: perspective = self.get_perspective_by_id(id) if perspective is None: - logger.warn('default perspective %s no longer available', id) + logger.warning("default perspective %s no longer available", id) else: perspective = None @@ -678,7 +659,7 @@ if len(id) > 0: perspective = self.get_perspective_by_id(id) if perspective is None: - logger.warn('previous perspective %s no longer available', id) + logger.warning("previous perspective %s no longer available", id) else: perspective = None @@ -729,11 +710,9 @@ self._memento.perspective_mementos[perspective.id] = ( self.layout.get_view_memento(), self.active_view and self.active_view.id or None, - self.layout.is_editor_area_visible() + self.layout.is_editor_area_visible(), ) - return - def _show_perspective(self, old, new): """ Show a perspective. """ @@ -794,8 +773,6 @@ if old is not None: self.refresh() - return - def _restore_contents(self): """ Restore the contents of the window. """ @@ -813,14 +790,14 @@ return - #### Trait change handlers ################################################ + # Trait change handlers ------------------------------------------------ - #### Static #### + # Static ---- def _active_perspective_changed(self, old, new): """ Static trait change handler. """ - logger.debug('active perspective changed from <%s> to <%s>', old, new) + logger.debug("active perspective changed from <%s> to <%s>", old, new) # Hide the old perspective... if old is not None: @@ -830,16 +807,12 @@ if new is not None: self._show_perspective(old, new) - return - def _active_editor_changed(self, old, new): """ Static trait change handler. """ - logger.debug('active editor changed from <%s> to <%s>', old, new) + logger.debug("active editor changed from <%s> to <%s>", old, new) self.active_part = new - return - def _active_part_changed(self, old, new): """ Static trait change handler. """ @@ -849,18 +822,14 @@ else: self.selection = new.selection - logger.debug('active part changed from <%s> to <%s>', old, new) - - return + logger.debug("active part changed from <%s> to <%s>", old, new) def _active_view_changed(self, old, new): """ Static trait change handler. """ - logger.debug('active view changed from <%s> to <%s>', old, new) + logger.debug("active view changed from <%s> to <%s>", old, new) self.active_part = new - return - def _views_changed(self, old, new): """ Static trait change handler. """ @@ -872,8 +841,6 @@ for view in new: view.window = self - return - def _views_items_changed(self, event): """ Static trait change handler. """ @@ -887,18 +854,17 @@ return - #### Dynamic #### + # Dynamic ---- - @on_trait_change('layout.editor_closed') - def _on_editor_closed(self, editor): + @observe("layout:editor_closed") + def _on_editor_closed(self, event): """ Dynamic trait change handler. """ - if editor is None or editor is Undefined: + if event.new is None or event.new is Undefined: return - - index = self.editors.index(editor) + index = self.editors.index(event.new) del self.editors[index] - if editor is self.active_editor: + if event.new is self.active_editor: if len(self.editors) > 0: index = min(index, len(self.editors) - 1) # If the user closed the editor manually then this method is @@ -913,33 +879,30 @@ return - @on_trait_change('editors.has_focus') - def _on_editor_has_focus_changed(self, obj, trait_name, old, new): + @observe("editors:items:has_focus") + def _on_editor_has_focus_changed(self, event): """ Dynamic trait change handler. """ - if trait_name == 'has_focus' and new: - self.active_editor = obj + if event.new: + self.active_editor = event.object return - @on_trait_change('views.has_focus') - def _has_focus_changed_for_view(self, obj, trait_name, old, new): + @observe("views:items:has_focus") + def _has_focus_changed_for_view(self, event): """ Dynamic trait change handler. """ - if trait_name == 'has_focus' and new: - self.active_view = obj + if event.new: + self.active_view = event.object return - @on_trait_change('views.visible') - def _visible_changed_for_view(self, obj, trait_name, old, new): + @observe("views:items:visible") + def _visible_changed_for_view(self, event): """ Dynamic trait change handler. """ - if trait_name == 'visible': - if not new: - if obj is self.active_view: - self.active_view = None + if not event.new: + if event.object is self.active_view: + self.active_view = None return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/aui.py python-pyface-7.4.0/pyface/wx/aui.py --- python-pyface-6.1.2/pyface/wx/aui.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/aui.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,25 +1,32 @@ -# Standard library imports. -import logging +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! -import os +import logging -# Major package imports. import wx -# Logger. -logger = logging.getLogger(__name__) - # Multiple AUI versions are no longer supported; the C version in wx.aui is not # capable of supporting the windowing flexibility needed by tasks. Therefore, # only AGW's pure-python AUI implementation is used. from wx.lib.agw import aui +# Logger. +logger = logging.getLogger(__name__) + # AGW's library does need some patching for some usability differences desired # for pyface but not for the standard wxPython version + class PyfaceAuiNotebook(aui.AuiNotebook): - if wx.version() >= '3.': + if wx.version() >= "3.": SetPageToolTip = aui.AuiNotebook.SetPageTooltip GetPageToolTip = aui.AuiNotebook.GetPageTooltip @@ -39,36 +46,52 @@ docks, panes = aui.CopyDocksAndPanes2(self._docks, self._panes) sash_size = self._art.GetMetric(aui.AUI_DOCKART_SASH_SIZE) - caption_size = self._art.GetMetric(aui.AUI_DOCKART_CAPTION_SIZE) - opposite_size = self.GetOppositeDockTotalSize(docks, dock.dock_direction) + opposite_size = self.GetOppositeDockTotalSize( + docks, dock.dock_direction + ) for tmpDock in docks: - if tmpDock.dock_direction == dock.dock_direction and \ - tmpDock.dock_layer == dock.dock_layer and \ - tmpDock.dock_row == dock.dock_row: + if ( + tmpDock.dock_direction == dock.dock_direction + and tmpDock.dock_layer == dock.dock_layer + and tmpDock.dock_row == dock.dock_row + ): tmpDock.size = 1 break neighbor_docks = [] - horizontal = dock.dock_direction == aui.AUI_DOCK_LEFT or dock.dock_direction == aui.AUI_DOCK_RIGHT - right_or_down = dock.dock_direction == aui.AUI_DOCK_RIGHT or dock.dock_direction == aui.AUI_DOCK_BOTTOM + horizontal = ( + dock.dock_direction == aui.AUI_DOCK_LEFT + or dock.dock_direction == aui.AUI_DOCK_RIGHT + ) + right_or_down = ( + dock.dock_direction == aui.AUI_DOCK_RIGHT + or dock.dock_direction == aui.AUI_DOCK_BOTTOM + ) for d in docks: - if d.dock_direction == dock.dock_direction and d.dock_layer == dock.dock_layer: + if ( + d.dock_direction == dock.dock_direction + and d.dock_layer == dock.dock_layer + ): if horizontal: neighbor_docks.append((d.rect.x, d.rect.width)) else: neighbor_docks.append((d.rect.y, d.rect.height)) neighbor_docks.sort() - sizer, panes, docks, uiparts = self.LayoutAll(panes, docks, [], True, False) + sizer, panes, docks, uiparts = self.LayoutAll( + panes, docks, [], True, False + ) client_size = self._frame.GetClientSize() sizer.SetDimension(0, 0, client_size.x, client_size.y) sizer.Layout() for part in uiparts: - part.rect = wx.RectPS(part.sizer_item.GetPosition(), part.sizer_item.GetSize()) + part.rect = wx.Rect( + part.sizer_item.GetPosition(), part.sizer_item.GetSize() + ) if part.type == aui.AuiDockUIPart.typeDock: part.dock.rect = part.rect @@ -76,9 +99,11 @@ new_dock = None for tmpDock in docks: - if tmpDock.dock_direction == dock.dock_direction and \ - tmpDock.dock_layer == dock.dock_layer and \ - tmpDock.dock_row == dock.dock_row: + if ( + tmpDock.dock_direction == dock.dock_direction + and tmpDock.dock_layer == dock.dock_layer + and tmpDock.dock_row == dock.dock_row + ): new_dock = tmpDock break @@ -137,15 +162,24 @@ find the other dock that is going to change size when resizing the specified dock. """ - horizontal = dock.dock_direction == aui.AUI_DOCK_LEFT or dock.dock_direction == aui.AUI_DOCK_RIGHT - right_or_down = dock.dock_direction == aui.AUI_DOCK_RIGHT or dock.dock_direction == aui.AUI_DOCK_BOTTOM + horizontal = ( + dock.dock_direction == aui.AUI_DOCK_LEFT + or dock.dock_direction == aui.AUI_DOCK_RIGHT + ) + right_or_down = ( + dock.dock_direction == aui.AUI_DOCK_RIGHT + or dock.dock_direction == aui.AUI_DOCK_BOTTOM + ) if horizontal: pos = point.x else: pos = point.y neighbor_docks = [] for d in self._docks: - if d.dock_direction == dock.dock_direction and d.dock_layer == dock.dock_layer: + if ( + d.dock_direction == dock.dock_direction + and d.dock_layer == dock.dock_layer + ): if horizontal: neighbor_docks.append((d.rect.x, d.rect.width, d)) else: @@ -212,10 +246,14 @@ new_dock_size = newPos.y - dock.rect.y elif direction == aui.AUI_DOCK_RIGHT: - new_dock_size = dock.rect.x + dock.rect.width - newPos.x - sash_size + new_dock_size = ( + dock.rect.x + dock.rect.width - newPos.x - sash_size + ) elif direction == aui.AUI_DOCK_BOTTOM: - new_dock_size = dock.rect.y + dock.rect.height - newPos.y - sash_size + new_dock_size = ( + dock.rect.y + dock.rect.height - newPos.y - sash_size + ) delta = new_dock_size - dock.size if delta < -dock.size + sash_size: @@ -242,7 +280,9 @@ oldPixsize = pane.rect.height newPixsize = oldPixsize + newPos.y - self._action_part.rect.y - totalPixsize, totalProportion = self.GetTotalPixSizeAndProportion(dock) + totalPixsize, totalProportion = self.GetTotalPixSizeAndProportion( + dock + ) partnerPane = self.GetPartnerPane(dock, pane) # prevent division by zero @@ -250,14 +290,18 @@ return # adjust for the surplus - while (oldPixsize > 0 and totalPixsize > 10 and \ - oldPixsize*totalProportion/totalPixsize < pane.dock_proportion): + while ( + oldPixsize > 0 + and totalPixsize > 10 + and oldPixsize * totalProportion / totalPixsize + < pane.dock_proportion + ): totalPixsize -= 1 # calculate the new proportion of the pane - newProportion = newPixsize*totalProportion/totalPixsize + newProportion = newPixsize * totalProportion / totalPixsize newProportion = aui.Clip(newProportion, 1, totalProportion) deltaProp = newProportion - pane.dock_proportion diff -Nru python-pyface-6.1.2/pyface/wx/clipboard.py python-pyface-7.4.0/pyface/wx/clipboard.py --- python-pyface-6.1.2/pyface/wx/clipboard.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/clipboard.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2009, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! -# -#------------------------------------------------------------------------------ - -import logging - -logger = logging.getLogger() -logger.warning('DEPRECATED: pyface.wx.clipboard, use pyface.api instead.') - -from pyface.ui.wx.clipboard import Clipboard -clipboard = Clipboard() diff -Nru python-pyface-6.1.2/pyface/wx/color.py python-pyface-7.4.0/pyface/wx/color.py --- python-pyface-6.1.2/pyface/wx/color.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/color.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Color utilities. """ @@ -21,9 +18,7 @@ def wx_to_enable_color(color): """ Convert a wx color spec. to an enable color spec. """ - enable_color = array((1.0,1.0,1.0,1.0)) - enable_color[:3] = asarray(color.Get())/255. + enable_color = array((1.0, 1.0, 1.0, 1.0)) + enable_color[:3] = asarray(color.Get()) / 255.0 return tuple(enable_color) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/dialog.py python-pyface-7.4.0/pyface/wx/dialog.py --- python-pyface-6.1.2/pyface/wx/dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Dialog utilities. """ -# Major package imports. import wx # A file dialog wildcard for Python files. @@ -41,7 +37,6 @@ # Base-class constructor. wx.FileDialog.__init__(self, parent, "Open", style=style, **kw) - return class OpenDirDialog(wx.DirDialog): """ An open-directory dialog. """ @@ -54,7 +49,6 @@ # Base-class constructor. wx.DirDialog.__init__(self, parent, "Open", style=style, **kw) - return class SaveFileAsDialog(wx.FileDialog): """ A save-file dialog. """ @@ -67,8 +61,6 @@ # Base-class constructor. wx.FileDialog.__init__(self, parent, "Save As", style=style, **kw) - return - def confirmation(parent, message, title=None, default=wx.NO_DEFAULT): """ Displays a confirmation dialog. """ @@ -76,8 +68,8 @@ dialog = wx.MessageDialog( parent, message, - _get_title(title, parent, 'Confirmation'), - wx.YES_NO | default | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP + _get_title(title, parent, "Confirmation"), + wx.YES_NO | default | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP, ) result = dialog.ShowModal() @@ -85,14 +77,15 @@ return result + def yes_no_cancel(parent, message, title=None, default=wx.NO_DEFAULT): """ Displays a Yes/No/Cancel dialog. """ dialog = wx.MessageDialog( parent, message, - _get_title(title, parent, 'Confirmation'), - wx.YES_NO | wx.CANCEL | default | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP + _get_title(title, parent, "Confirmation"), + wx.YES_NO | wx.CANCEL | default | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP, ) result = dialog.ShowModal() @@ -100,20 +93,20 @@ return result + def information(parent, message, title=None): """ Displays a modal information dialog. """ dialog = wx.MessageDialog( parent, message, - _get_title(title, parent, 'Information'), - wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP + _get_title(title, parent, "Information"), + wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP, ) dialog.ShowModal() dialog.Destroy() - return def warning(parent, message, title=None): """ Displays a modal warning dialog. """ @@ -121,14 +114,13 @@ dialog = wx.MessageDialog( parent, message, - _get_title(title, parent, 'Warning'), - wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP + _get_title(title, parent, "Warning"), + wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP, ) dialog.ShowModal() dialog.Destroy() - return def error(parent, message, title=None): """ Displays a modal error dialog. """ @@ -136,14 +128,13 @@ dialog = wx.MessageDialog( parent, message, - _get_title(title, parent, 'Error'), - wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP + _get_title(title, parent, "Error"), + wx.OK | wx.ICON_ERROR | wx.STAY_ON_TOP, ) dialog.ShowModal() dialog.Destroy() - return def _get_title(title, parent, default): """ Get a sensible title for a dialog! """ @@ -156,5 +147,3 @@ title = default return title - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/divider.py python-pyface-7.4.0/pyface/wx/divider.py --- python-pyface-6.1.2/pyface/wx/divider.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/divider.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A thin visual divider. """ -# Major package imports. import wx @@ -32,9 +28,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self): """ Creates the widget. """ @@ -42,5 +38,3 @@ self.SetSize((1, 1)) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/drag_and_drop.py python-pyface-7.4.0/pyface/wx/drag_and_drop.py --- python-pyface-6.1.2/pyface/wx/drag_and_drop.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/drag_and_drop.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,23 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Drag and drop utilities. """ -# Standard library imports. -import six -if six.PY2: - from inspect import getargspec -else: - # avoid deprecation warning - from inspect import getfullargspec as getargspec + +from inspect import getfullargspec -# Major package imports. import wx -import six -class Clipboard: +class Clipboard(object): """ The clipboard is used when dragging and dropping Python objects. """ # fixme: This obviously only works within a single process! @@ -36,8 +26,8 @@ clipboard = Clipboard() clipboard.drop_source = None -clipboard.source = None -clipboard.data = None +clipboard.source = None +clipboard.data = None class FileDropSource(wx.DropSource): @@ -56,20 +46,19 @@ clipboard.drop_source = self data_object = wx.FileDataObject() - if isinstance(files, six.text_type): + if isinstance(files, str): files = [files] for file in files: data_object.AddFile(file) # Create the drop source and begin the drag and drop operation: - super(FileDropSource, self).__init__(source) + super().__init__(source) self.SetData(data_object) self.result = self.DoDragDrop(True) def on_dropped(self, drag_result): """ Called when the data has been dropped. """ - return class FileDropTarget(wx.FileDropTarget): @@ -83,8 +72,6 @@ self.handler = handler - return - def OnDropFiles(self, x, y, filenames): """ Called when the files have been dropped. """ @@ -96,7 +83,7 @@ # The data format for Python objects! -PythonObject = wx.CustomDataFormat('PythonObject') +PythonObject = wx.DataFormat("PythonObject") class PythonDropSource(wx.DropSource): @@ -124,7 +111,7 @@ # Create our own data format and use it in a custom data object. data_object = wx.CustomDataObject(PythonObject) - data_object.SetData('dummy') + data_object.SetData(b"dummy") # And finally, create the drop source and begin the drag # and drop opperation. @@ -136,38 +123,34 @@ flags = wx.Drag_CopyOnly self.result = self.DoDragDrop(flags) - return - def on_dropped(self, drag_result): """ Called when the data has been dropped. """ if self.handler is not None: - if hasattr(self.handler, 'on_dropped'): + if hasattr(self.handler, "on_dropped"): # For backward compatibility we accept handler functions # with either 1 or 3 args, including self. If there are # 3 args then we pass the data and the drag_result. - args = getargspec(self.handler.on_dropped)[0] + args = getfullargspec(self.handler.on_dropped)[0] if len(args) == 3: self.handler.on_dropped(clipboard.data, drag_result) else: self.handler.on_dropped() else: - #print self.handler + # print self.handler # In this case we assume handler is a function. # For backward compatibility we accept handler functions # with either 0 or 2 args. If there are 2 args then # we pass the data and drag_result - args = getargspec(self.handler)[0] - if len(args)==2: + args = getfullargspec(self.handler)[0] + if len(args) == 2: self.handler(clipboard.data, drag_result) else: self.handler() - return - -class PythonDropTarget(wx.PyDropTarget): +class PythonDropTarget(wx.DropTarget): """ Drop target for Python objects. """ def __init__(self, handler): @@ -181,7 +164,7 @@ """ # Base-class constructor. - super(PythonDropTarget, self).__init__() + super().__init__() # The handler can either be a function that will be called when # any data is dropped onto the target, or an instance that supports @@ -192,20 +175,20 @@ # Specify the type of data we will accept. self.data_object = wx.DataObjectComposite() self.data = wx.CustomDataObject(PythonObject) - self.data_object.Add(self.data, preferred = True) + self.data_object.Add(self.data, preferred=True) self.file_data = wx.FileDataObject() self.data_object.Add(self.file_data) self.SetDataObject(self.data_object) - return - def OnData(self, x, y, default_drag_result): """ Called when OnDrop returns True. """ # First, if we have a source in the clipboard and the source # doesn't allow moves then change the default to copy - if clipboard.drop_source is not None and \ - not clipboard.drop_source.allow_move: + if ( + clipboard.drop_source is not None + and not clipboard.drop_source.allow_move + ): default_drag_result = wx.DragCopy elif clipboard.drop_source is None: # This means we might be receiving a file; try to import @@ -222,7 +205,7 @@ for name in names: f = File(name) files.append(f) - bindings.append(Binding(name = name, obj = f)) + bindings.append(Binding(name=name, obj=f)) clipboard.data = files clipboard.node = bindings except ImportError: @@ -232,12 +215,12 @@ # # fixme: We allow 'wx_dropped_on' and 'on_drop' because both Dave # and Martin want different things! Unify! - if hasattr(self.handler, 'wx_dropped_on'): + if hasattr(self.handler, "wx_dropped_on"): drag_result = self.handler.wx_dropped_on( x, y, clipboard.data, default_drag_result ) - elif hasattr(self.handler, 'on_drop'): + elif hasattr(self.handler, "on_drop"): drag_result = self.handler.on_drop( x, y, clipboard.data, default_drag_result ) @@ -268,7 +251,7 @@ data = clipboard.data if clipboard.drop_source is None: - if not hasattr(self.handler, 'wx_drag_any'): + if not hasattr(self.handler, "wx_drag_any"): # this is probably a file being dragged in, so just return return default_drag_result @@ -286,17 +269,17 @@ # # fixme: We allow 'wx_drag_over' and 'on_drag_over' because both Dave # and Martin want different things! Unify! - if hasattr(self.handler, 'wx_drag_any'): + if hasattr(self.handler, "wx_drag_any"): drag_result = self.handler.wx_drag_any( x, y, data, default_drag_result ) - elif hasattr(self.handler, 'wx_drag_over'): + elif hasattr(self.handler, "wx_drag_over"): drag_result = self.handler.wx_drag_over( x, y, data, default_drag_result ) - elif hasattr(self.handler, 'on_drag_over'): + elif hasattr(self.handler, "on_drag_over"): drag_result = self.handler.on_drag_over( x, y, data, default_drag_result ) @@ -309,11 +292,9 @@ def OnLeave(self): """ Called when the mouse leaves the drop target. """ - if hasattr(self.handler, 'wx_drag_leave'): + if hasattr(self.handler, "wx_drag_leave"): self.handler.wx_drag_leave(clipboard.data) - return - def OnDrop(self, x, y): """ Called when the user drops a data object on the target. @@ -322,5 +303,3 @@ """ return True - -#### EOF ##################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/font.py python-pyface-7.4.0/pyface/wx/font.py --- python-pyface-6.1.2/pyface/wx/font.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/font.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Font utilities. """ -# Major package imports. import wx @@ -28,12 +24,11 @@ underline = font.GetUnderlined() face_name = font.GetFaceName() - clone = wx.Font( - point_size, family, style, weight, underline, face_name, - ) + clone = wx.Font(point_size, family, style, weight, underline, face_name) return clone + def set_font_size(window, size): """ Recursively sets the font size starting from 'window'. """ @@ -53,7 +48,6 @@ for child in window.GetChildren(): set_font_size(child, size) - return def increase_font_size(window, delta=2): """ Recursively increases the font size starting from 'window'. """ @@ -74,14 +68,12 @@ for child in window.GetChildren(): increase_font_size(child, delta) - return def decrease_font_size(window, delta=2): """ Recursively decreases the font size starting from 'window'. """ increase_font_size(window, delta=-2) - return def set_bold_font(window): """ Set 'window's font to be bold. """ @@ -91,5 +83,3 @@ window.SetFont(font) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/grid/api.py python-pyface-7.4.0/pyface/wx/grid/api.py --- python-pyface-6.1.2/pyface/wx/grid/api.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/grid/api.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from __future__ import absolute_import +# Thanks for using Enthought open source! + from .grid import Grid from .grid_column import GridColumn diff -Nru python-pyface-6.1.2/pyface/wx/grid/grid_column.py python-pyface-7.4.0/pyface/wx/grid/grid_column.py --- python-pyface-6.1.2/pyface/wx/grid/grid_column.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/grid/grid_column.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A description of a column in a grid. """ -# Enthought library imports. from traits.api import Bool, HasTraits, Str @@ -22,12 +18,10 @@ """ A description of a column in a grid. """ # Column header. - label = Str + label = Str() # Type name of data allowed in the column. - type = Str + type = Str() # Is the column read-only? readonly = Bool(False) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/grid/grid_model.py python-pyface-7.4.0/pyface/wx/grid/grid_model.py --- python-pyface-6.1.2/pyface/wx/grid/grid_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/grid/grid_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,26 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A model that provides data for a grid. """ -# Major package imports. -from __future__ import print_function -import wx -from wx.grid import PyGridTableBase, GridTableMessage, GRIDTABLE_NOTIFY_ROWS_APPENDED -# Enthought library imports. -from traits.api import Any, Bool, HasTraits, Trait, Event, List +from wx.grid import ( + GridTableBase, + GridTableMessage, + GRIDTABLE_NOTIFY_ROWS_APPENDED, +) + + +from traits.api import Any, Bool, HasTraits, Event, List + -# Local imports. from .grid_column import GridColumn from .grid_row import GridRow @@ -31,7 +30,7 @@ # fixme : factor this default model into "SimpleGridModel" or similar # An optional 2-dimensional list/array containing the grid data. - data = Any + data = Any() # The rows in the model. rows = List(GridRow) @@ -46,8 +45,7 @@ show_column_headers = Bool(True) # Fired when the data in the model has changed. - model_changed = Event - + model_changed = Event() def __init__(self, **traits): """ Create a new grid model. """ @@ -68,9 +66,9 @@ return - ########################################################################### - # 'wxPyGridTableBase' interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'wxGridTableBase' interface. + # ------------------------------------------------------------------------ def GetNumberRows(self): """ Return the number of rows in the model. """ @@ -103,13 +101,11 @@ except IndexError: pass - return '' + return "" def SetValue(self, row, col, value): """ Set the value at the specified row and column. """ - label = self.GetColLabelValue(col) - try: self.data[row][col] = value @@ -121,15 +117,11 @@ # Tell the grid that we've added a row. # # N.B wxGridTableMessage(table, whatWeDid, howMany) - message = GridTableMessage( - self, GRIDTABLE_NOTIFY_ROWS_APPENDED, 1 - ) + message = GridTableMessage(self, GRIDTABLE_NOTIFY_ROWS_APPENDED, 1) # Trait event notification. self.model_changed = message - return - def GetRowLabelValue(self, row): """ Called when the grid needs to display a row label. """ @@ -190,27 +182,27 @@ return True -class _GridTableBase(PyGridTableBase): +class _GridTableBase(GridTableBase): """ A model that provides data for a grid. """ - ########################################################################### + # ------------------------------------------------------------------------ # 'object' interface. - ########################################################################### + # ------------------------------------------------------------------------ def __init__(self, model): """ Creates a new table base. """ # Base class constructor. - PyGridTableBase.__init__(self) + GridTableBase.__init__(self) # The Pyface model that provides the data. self.model = model return - ########################################################################### - # 'wxPyGridTableBase' interface. - ########################################################################### + # ------------------------------------------------------------------------ + # 'wxGridTableBase' interface. + # ------------------------------------------------------------------------ def GetNumberRows(self): """ Return the number of rows in the model. """ @@ -279,5 +271,3 @@ """ Called when the view is deleting rows. """ return self.model.DeleteRows(pos, num_rows) - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/grid/grid.py python-pyface-7.4.0/pyface/wx/grid/grid.py --- python-pyface-6.1.2/pyface/wx/grid/grid.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/grid/grid.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,25 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A grid (spreadsheet) widget. """ -# Major package imports. -from __future__ import print_function import wx -from wx.grid import Grid as wxGrid - - -# Local imports. -from .grid_model import GridModel +from wx.grid import ( + Grid as wxGrid, + GridTableMessage, + GRIDTABLE_NOTIFY_ROWS_APPENDED, + GRIDTABLE_NOTIFY_ROWS_DELETED, + GRIDTABLE_NOTIFY_COLS_APPENDED, + GRIDTABLE_NOTIFY_COLS_DELETED, +) class Grid(wxGrid): @@ -36,14 +34,6 @@ # The model that provides the data and row/column information. self.model = None - # Automatically size columns and rows to fit their content. - # - # fixme: wx seems sensitive to the location of these two lines. Put - # them *before* the call to 'SetTable', otherwise the grid takes - # forever to initialize! - ##self.AutoSizeColumns() - ##self.AutoSizeRows() - # Don't display any extra space around the rows and columns. self.SetMargins(0, 0) @@ -60,29 +50,27 @@ # # fixme: We should create a default model if one is not supplied. self.SetTable(model._grid_table_base, True) - model.on_trait_change(self._on_model_changed, 'model_changed') + model.observe(self._on_model_changed, "model_changed") - wx.grid.EVT_GRID_CELL_CHANGE(self, self._on_cell_change) - wx.grid.EVT_GRID_SELECT_CELL(self, self._on_select_cell) + self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self._on_cell_change) + self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_select_cell) # This starts the cell editor on a double-click as well as on a second # click. - wx.grid.EVT_GRID_CELL_LEFT_DCLICK(self, self._on_cell_left_dclick) + self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self._on_cell_left_dclick) # This pops up a context menu. - #wx.grid.EVT_GRID_CELL_RIGHT_CLICK(self, self._on_cell_right_click) + # wx.grid.EVT_GRID_CELL_RIGHT_CLICK(self, self._on_cell_right_click) # We handle key presses to change the behavior of the and # keys to make manual data entry smoother. - wx.EVT_KEY_DOWN(self, self._on_key_down) + self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) # Initialize the row and column models. self._initialize_rows(model) self._initialize_columns(model) self._initialize_fonts() - return - def _initialize_fonts(self): """ Initialize the label fonts. """ @@ -91,8 +79,6 @@ self.SetColLabelAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) self.SetRowLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE) - return - def _initialize_rows(self, model): """ Initialize the row headers. """ @@ -105,11 +91,9 @@ attr = wx.grid.GridCellAttr() attr.SetReadOnly() attr.SetRenderer(None) - attr.SetBackgroundColour('linen') + attr.SetBackgroundColour("linen") self.SetRowAttr(index, attr) - return - def _initialize_columns(self, model): """ Initialize the column headers. """ @@ -122,43 +106,25 @@ attr = wx.grid.GridCellAttr() attr.SetReadOnly() attr.SetRenderer(None) - attr.SetBackgroundColour('linen') + attr.SetBackgroundColour("linen") self.SetColAttr(index, attr) return - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_cell_change(self, evt): """ Called when the contents of a cell have been changed. """ - row = evt.GetRow() - col = evt.GetCol() - - ##print 'Cell changed at', row, col - value = self.GetTable().GetValue(row, col) - - ##print 'New value', value - ##print 'Type', type(value) - evt.Skip() - return - def _on_select_cell(self, evt): """ Called when the user has moved to another cell. """ - ##row = evt.GetRow() - ##col = evt.GetCol() - - ##print 'Cell selected at', row, col - evt.Skip() - return - def _on_cell_left_dclick(self, evt): """ Called when the left mouse button was double-clicked. @@ -174,8 +140,6 @@ if self.CanEnableCellControl(): self.EnableCellEditControl() - return - def _on_cell_right_click(self, evt): """ Called when a right click occurred in a cell. """ @@ -194,12 +158,10 @@ # Popup a context menu allowing the user to delete the row. menu = wx.Menu() menu.Append(101, "Delete Row") - wx.EVT_MENU(self, 101, self._on_delete_row) + self.Bind(wx.EVT_MENU, self._on_delete_row, id=101) self.PopupMenu(menu, evt.GetPosition()) - return - def _on_key_down(self, evt): """ Called when a key is pressed. """ @@ -222,8 +184,6 @@ else: evt.Skip() - return - def _on_delete_row(self, evt): """ Called when the 'Delete Row' context menu item is selected. """ @@ -234,56 +194,63 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_model_changed(self, message): + def _on_model_changed(self, event): """ Called when the model has changed. """ - + message = event.new self.BeginBatch() self.ProcessTableMessage(message) self.EndBatch() return - ########################################################################### + # ------------------------------------------------------------------------ # 'Grid' interface. - ########################################################################### + # ------------------------------------------------------------------------ def Reset(self): - print('Reset') - #attr = grid.GridCellAttr() - #renderer = MyRenderer() - #attr.SetRenderer(renderer) + print("Reset") + # attr = grid.GridCellAttr() + # renderer = MyRenderer() + # attr.SetRenderer(renderer) - #self.SetColSize(0, 50) - #self.SetColAttr(0, attr) + # self.SetColSize(0, 50) + # self.SetColAttr(0, attr) self.ForceRefresh() - return - - def ResetView(self, grid): """ (wxGrid) -> Reset the grid view. Call this to update the grid if rows and columns have been added or deleted """ - print('*************************VirtualModel.reset_view') + print("*************************VirtualModel.reset_view") grid = self grid.BeginBatch() for current, new, delmsg, addmsg in [ - (self._rows, self.GetNumberRows(), GRIDTABLE_NOTIFY_ROWS_DELETED, GRIDTABLE_NOTIFY_ROWS_APPENDED), - (self._cols, self.GetNumberCols(), GRIDTABLE_NOTIFY_COLS_DELETED, GRIDTABLE_NOTIFY_COLS_APPENDED), + ( + self._rows, + self.GetNumberRows(), + GRIDTABLE_NOTIFY_ROWS_DELETED, + GRIDTABLE_NOTIFY_ROWS_APPENDED, + ), + ( + self._cols, + self.GetNumberCols(), + GRIDTABLE_NOTIFY_COLS_DELETED, + GRIDTABLE_NOTIFY_COLS_APPENDED, + ), ]: if new < current: - msg = GridTableMessage(self,delmsg,new,current-new) + msg = GridTableMessage(self, delmsg, new, current - new) grid.ProcessTableMessage(msg) elif new > current: - msg = GridTableMessage(self,addmsg,new-current) + msg = GridTableMessage(self, addmsg, new - current) grid.ProcessTableMessage(msg) self.UpdateValues(grid) grid.EndBatch() @@ -301,11 +268,9 @@ return - - - ########################################################################### + # ------------------------------------------------------------------------ # Protected interface. - ########################################################################### + # ------------------------------------------------------------------------ def _move_to_next_cell(self, expandSelection=False): """ Move to the 'next' cell. """ @@ -350,5 +315,3 @@ self.MakeCellVisible(newRow, self.GetNumberCols() - 1) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/grid/grid_row.py python-pyface-7.4.0/pyface/wx/grid/grid_row.py --- python-pyface-6.1.2/pyface/wx/grid/grid_row.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/grid/grid_row.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A description of a row in a grid. """ -# Enthought library imports. from traits.api import HasTraits @@ -27,5 +23,3 @@ self.__dict__.update(row_data) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/grid/__init__.py python-pyface-7.4.0/pyface/wx/grid/__init__.py --- python-pyface-6.1.2/pyface/wx/grid/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/grid/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/wx/image_cache.py python-pyface-7.4.0/pyface/wx/image_cache.py --- python-pyface-6.1.2/pyface/wx/image_cache.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/image_cache.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ An image cache. """ -# Major package imports. import wx -class ImageCache: +class ImageCache(object): """ An image cache. """ def __init__(self, width, height): @@ -28,13 +24,13 @@ self._height = height # The images in the cache! - self._images = {} # {filename : bitmap} + self._images = {} # {filename : bitmap} return - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageCache' interface. - ########################################################################### + # ------------------------------------------------------------------------ def get_image(self, filename): """ Returns the specified image (currently as a bitmap). """ @@ -59,16 +55,17 @@ return bmp - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _scale(self, image): """ Scales the specified image (if necessary). """ - if image.GetWidth() != self._width or image.GetHeight()!= self._height: + if ( + image.GetWidth() != self._width + or image.GetHeight() != self._height + ): image.Rescale(self._width, self._height) return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/image_list.py python-pyface-7.4.0/pyface/wx/image_list.py --- python-pyface-6.1.2/pyface/wx/image_list.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/image_list.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A cached image list. """ -# Major package imports. import wx -import six # fixme: rename to 'CachedImageList'?!? @@ -33,13 +28,13 @@ self._height = height # Cache of the indexes of the images in the list! - self._cache = {} # {filename : index} + self._cache = {} # {filename : index} return - ########################################################################### + # ------------------------------------------------------------------------ # 'ImageList' interface. - ########################################################################### + # ------------------------------------------------------------------------ def GetIndex(self, filename): """ Returns the index of the specified image. @@ -51,14 +46,14 @@ # If the icon is a string then it is the filename of some kind of # image (e.g 'foo.gif', 'image/foo.png' etc). - if isinstance(filename, six.string_types): + if isinstance(filename, str): # Try the cache first. index = self._cache.get(filename) if index is None: # Load the image from the file and add it to the list. # # N.B 'wx.BITMAP_TYPE_ANY' tells wxPython to attempt to - # ---- autodetect the image format. + # autodetect the image format. image = wx.Image(filename, wx.BITMAP_TYPE_ANY) # We force all images in the cache to be the same size. @@ -76,17 +71,17 @@ # Otherwise the icon is *actually* an icon (in our case, probably # related to a MIME type). else: - #image = filename - #self._scale(image) - #bmp = image.ConvertToBitmap() - #index = self.Add(bmp) + # image = filename + # self._scale(image) + # bmp = image.ConvertToBitmap() + # index = self.Add(bmp) - #return index + # return index icon = filename # We also force them to be bitmaps! - bmp = wx.EmptyBitmap(self._width, self._height) + bmp = wx.Bitmap(self._width, self._height) bmp.CopyFromIcon(icon) # We force all images in the cache to be the same size. image = wx.ImageFromBitmap(bmp) @@ -98,16 +93,17 @@ return index - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _scale(self, image): """ Scales the specified image (if necessary). """ - if image.GetWidth() != self._width or image.GetHeight()!= self._height: + if ( + image.GetWidth() != self._width + or image.GetHeight() != self._height + ): image.Rescale(self._width, self._height) return image - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/image.py python-pyface-7.4.0/pyface/wx/image.py --- python-pyface-6.1.2/pyface/wx/image.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/image.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,17 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -import os, wx +# Thanks for using Enthought open source! + +import os + +import wx + from traits.util.resource import get_path diff -Nru python-pyface-6.1.2/pyface/wx/__init__.py python-pyface-7.4.0/pyface/wx/__init__.py --- python-pyface-6.1.2/pyface/wx/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,14 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ - +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/wx/lazy_switcher.py python-pyface-7.4.0/pyface/wx/lazy_switcher.py --- python-pyface-6.1.2/pyface/wx/lazy_switcher.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/lazy_switcher.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,24 +1,20 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Classes to provide a switcher. """ -# Major package imports. import wx from wx.lib.scrolledpanel import ScrolledPanel as wxScrolledPanel -# Enthought library imports. + from traits.api import HasTraits, Int @@ -32,18 +28,18 @@ """ Creates a new switcher model. """ # The items to display in the switcher control. - self.items = [] # (str label, object value) + self.items = [] # (str label, object value) return - ########################################################################### + # ------------------------------------------------------------------------ # 'SwitcherModel' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_page(self, parent, index): """ Creates a page for the switcher panel. """ - raise NotImplementedError + raise NotImplementedError() class SwitcherControl(wx.Panel): @@ -65,24 +61,24 @@ self._create_widget(model, label) # Listen for when the selected item in the model is changed. - model.on_trait_change(self._on_selected_changed, 'selected') + model.observe(self._on_selected_changed, "selected") return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_selected_changed(self, selected): + def _on_selected_changed(self, event): """ Called when the selected item in the model is changed. """ - + selected = event.new self.combo.SetSelection(selected) return - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_combobox(self, event): """ Called when the combo box selection is changed. """ @@ -94,9 +90,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self, model, label): """ Creates the widget.""" @@ -104,7 +100,6 @@ self.sizer = sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self.SetAutoLayout(True) - ##self.SetBackgroundColour("light grey") # Switcher combo. sizer.Add(self._combo(self, model, label), 1, wx.EXPAND) @@ -112,8 +107,6 @@ # Resize the panel to match the sizer's minimal size. sizer.Fit(self) - return - def _combo(self, parent, model, label): """ Creates the switcher combo. """ @@ -126,9 +119,7 @@ # Combo. self.combo = combo = wx.ComboBox( - parent, - -1, - style=wx.CB_DROPDOWN | wx.CB_READONLY + parent, -1, style=wx.CB_DROPDOWN | wx.CB_READONLY ) sizer.Add(combo, 1, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 5) @@ -139,7 +130,7 @@ combo.Append(name, data) # Listen for changes to the selected item. - wx.EVT_COMBOBOX(self, combo.GetId(), self._on_combobox) + self.Bind(wx.EVT_COMBOBOX, self._on_combobox, id=combo.GetId()) # If the model's selected variable has been set ... if model.selected != -1: @@ -172,14 +163,11 @@ # Create the widget! self._create_widget(model, label) - # Listen for when the selected item in the model is changed. - #model.on_trait_change(self._on_selected_changed, 'selected') - return - ########################################################################### + # ------------------------------------------------------------------------ # 'SwitcherPanel' interface. - ########################################################################### + # ------------------------------------------------------------------------ def show_page(self, index): """ Shows the page at the specified index. """ @@ -188,20 +176,9 @@ return - ########################################################################### - # Trait event handlers. - ########################################################################### - - def _on_selected_changed(self, selected): - """ Called when the selected item in the model is changed. """ - - self._show_page(selected) - - return - - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self, model, label): """ Creates the widget. """ @@ -209,10 +186,6 @@ self.sizer = sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self.SetAutoLayout(True) - ##self.SetBackgroundColour('red') - - #if model.selected != -1: - # self._show_page(model.selected) # Nothing to add here as we add the panel contents lazily! pass @@ -220,14 +193,11 @@ # Resize the panel to match the sizer's minimal size. sizer.Fit(self) - return - def _show_page(self, index): """ Shows the page at the specified index. """ # If a page is already displayed then hide it. if self.current is not None: - current_size = self.current.GetSize() self.current.Show(False) self.sizer.Remove(self.current) @@ -240,9 +210,6 @@ # Add it to the cache! self._page_cache[index] = page - #if self.current is not None: - # page.SetSize(current_size) - # Display the page. self.sizer.Add(page, 15, wx.EXPAND, 5) page.Show(True) @@ -252,10 +219,6 @@ # Force a new layout of the sizer's children but KEEPING the current # dimension. self.sizer.Layout() - #self.sizer.Fit(self) - #self.SetupScrolling() - - return class Switcher(wx.Panel): @@ -275,9 +238,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self, model, label): """ Creates the widget. """ @@ -298,5 +261,3 @@ sizer.Fit(self) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/pager.py python-pyface-7.4.0/pyface/wx/pager.py --- python-pyface-6.1.2/pyface/wx/pager.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/pager.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A pager contains a set of pages, but only shows one at a time. """ -# Major package imports. import wx from wx.lib.scrolledpanel import ScrolledPanel as wxScrolledPanel @@ -22,7 +18,6 @@ class Pager(wxScrolledPanel): """ A pager contains a set of pages, but only shows one at a time. """ - def __init__(self, parent, wxid, **kw): """ Creates a new pager. """ @@ -31,7 +26,7 @@ self.SetupScrolling() # The pages in the pager! - self._pages = {} # { str name : wx.Window page } + self._pages = {} # { str name : wx.Window page } # The page that is currently displayed. self._current_page = None @@ -41,9 +36,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'Pager' interface. - ########################################################################### + # ------------------------------------------------------------------------ def add_page(self, name, page): """ Adds a page with the specified name. """ @@ -78,9 +73,9 @@ return page - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self): """ Creates the widget. """ @@ -89,16 +84,12 @@ self.SetSizer(sizer) self.SetAutoLayout(True) - return - def _hide_page(self, page): """ Hides the specified page. """ page.Show(False) self._sizer.Remove(page) - return - def _show_page(self, page): """ Shows the specified page. """ @@ -108,5 +99,3 @@ self._current_page = page return page - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/progress_meter.py python-pyface-7.4.0/pyface/wx/progress_meter.py --- python-pyface-6.1.2/pyface/wx/progress_meter.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/progress_meter.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import wx -class ProgressDialog(wx.ProgressDialog): +class ProgressDialog(wx.ProgressDialog): def __init__(self, *args, **kwds): wx.ProgressDialog.__init__(self, *args, **kwds) def SetButtonLabel(self, title): """ Change the Cancel button label to something else eg Stop.""" - button = self.FindWindowById(wx.ID_CANCEL) + button = self.Window.FindWindowById(wx.ID_CANCEL) button.SetLabel(title) return diff -Nru python-pyface-6.1.2/pyface/wx/python_stc.py python-pyface-7.4.0/pyface/wx/python_stc.py --- python-pyface-6.1.2/pyface/wx/python_stc.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/python_stc.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,22 +1,19 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import wx from wx import stc import keyword -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- demoText = """\ ## This version of the editor has been set up to edit Python source @@ -25,98 +22,142 @@ """ -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- -if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 10, - 'size2': 8, - } +if wx.Platform == "__WXMSW__": + faces = { + "times": "Times New Roman", + "mono": "Courier New", + "helv": "Arial", + "other": "Comic Sans MS", + "size": 10, + "size2": 8, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 12, - 'size2': 10, - } + faces = { + "times": "Times", + "mono": "Courier", + "helv": "Helvetica", + "other": "new century schoolbook", + "size": 12, + "size2": 10, + } -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- + class PythonSTC(stc.StyledTextCtrl): def __init__(self, parent, ID): - stc.StyledTextCtrl.__init__(self, parent, ID, - style = wx.NO_FULL_REPAINT_ON_RESIZE) + stc.StyledTextCtrl.__init__( + self, parent, ID, style=wx.NO_FULL_REPAINT_ON_RESIZE + ) - self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) - self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) + self.CmdKeyAssign(ord("B"), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) + self.CmdKeyAssign(ord("N"), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) self.SetLexer(stc.STC_LEX_PYTHON) self.SetKeyWords(0, " ".join(keyword.kwlist)) self.SetProperty("fold", "1") self.SetProperty("tab.timmy.whinge.level", "1") - self.SetMargins(0,0) + self.SetMargins(0, 0) self.SetViewWhiteSpace(False) - #self.SetBufferedDraw(False) + # self.SetBufferedDraw(False) self.SetEdgeMode(stc.STC_EDGE_BACKGROUND) self.SetEdgeColumn(78) # Setup a margin to hold fold markers - ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER? + # WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER? self.SetFoldFlags(16) - #mic - #self.SetMarginType(2, stc.STC_MARGIN_SYMBOL) - #self.SetMarginMask(2, stc.STC_MASK_FOLDERS) - #self.SetMarginSensitive(2, True) - #self.SetMarginWidth(2, 12) + # mic + # self.SetMarginType(2, stc.STC_MARGIN_SYMBOL) + # self.SetMarginMask(2, stc.STC_MASK_FOLDERS) + # self.SetMarginSensitive(2, True) + # self.SetMarginWidth(2, 12) # line numbers in the margin self.SetMarginType(1, stc.STC_MARGIN_NUMBER) self.SetMarginWidth(1, 25) - if 0: # simple folder marks, like the old version - self.MarkerDefine(stc.STC_MARKNUM_FOLDER, - stc.STC_MARK_ARROW, "navy", "navy") - self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, - stc.STC_MARK_ARROWDOWN, "navy", "navy") + if 0: # simple folder marks, like the old version + self.MarkerDefine( + stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "navy", "navy" + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDEROPEN, + stc.STC_MARK_ARROWDOWN, + "navy", + "navy", + ) # Set these to an invisible mark - self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, - stc.STC_MARK_BACKGROUND, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, - stc.STC_MARK_BACKGROUND, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, - stc.STC_MARK_BACKGROUND, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, - stc.STC_MARK_BACKGROUND, "white", "black") - - elif 0: # more involved "outlining" folder marks - self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, - stc.STC_MARK_BOXPLUSCONNECTED, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, - stc.STC_MARK_BOXMINUSCONNECTED, - "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, - stc.STC_MARK_TCORNER, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, - stc.STC_MARK_LCORNER, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, - stc.STC_MARK_VLINE, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDER, - stc.STC_MARK_BOXPLUS, "white", "black") - self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, - stc.STC_MARK_BOXMINUS, "white", "black") - - - stc.EVT_STC_UPDATEUI(self, ID, self.OnUpdateUI) - stc.EVT_STC_MARGINCLICK(self, ID, self.OnMarginClick) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDEROPENMID, + stc.STC_MARK_BACKGROUND, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDERMIDTAIL, + stc.STC_MARK_BACKGROUND, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDERSUB, + stc.STC_MARK_BACKGROUND, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDERTAIL, + stc.STC_MARK_BACKGROUND, + "white", + "black", + ) + + elif 0: # more involved "outlining" folder marks + self.MarkerDefine( + stc.STC_MARKNUM_FOLDEREND, + stc.STC_MARK_BOXPLUSCONNECTED, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDEROPENMID, + stc.STC_MARK_BOXMINUSCONNECTED, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDERMIDTAIL, + stc.STC_MARK_TCORNER, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDERTAIL, + stc.STC_MARK_LCORNER, + "white", + "black", + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black" + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black" + ) + self.MarkerDefine( + stc.STC_MARKNUM_FOLDEROPEN, + stc.STC_MARK_BOXMINUS, + "white", + "black", + ) + self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) + self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick) # Make some styles, The lexer defines what each style is used for, we # just have to define what each style looks like. This set is adapted @@ -125,96 +166,111 @@ self.StyleClearAll() # Global default styles for all languages - self.StyleSetSpec(stc.STC_STYLE_DEFAULT, - "face:%(helv)s,size:%(size)d" % faces) - self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, - "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces) - self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, - "face:%(other)s" % faces) - self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, - "fore:#FFFFFF,back:#0000FF,bold") - self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, - "fore:#000000,back:#FF0000,bold") + self.StyleSetSpec( + stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces + ) + self.StyleSetSpec( + stc.STC_STYLE_LINENUMBER, + "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces, + ) + self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces) + self.StyleSetSpec( + stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold" + ) + self.StyleSetSpec( + stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold" + ) # Python styles # White space - self.StyleSetSpec(stc.STC_P_DEFAULT, - "fore:#808080,face:%(helv)s,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_DEFAULT, + "fore:#808080,face:%(helv)s,size:%(size)d" % faces, + ) # Comment - self.StyleSetSpec(stc.STC_P_COMMENTLINE, - "fore:#007F00,face:%(other)s,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_COMMENTLINE, + "fore:#007F00,face:%(other)s,size:%(size)d" % faces, + ) # Number - self.StyleSetSpec(stc.STC_P_NUMBER, - "fore:#007F7F,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces + ) # String - self.StyleSetSpec(stc.STC_P_STRING, - "fore:#7F007F,italic,face:%(times)s,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_STRING, + "fore:#7F007F,italic,face:%(times)s,size:%(size)d" % faces, + ) # Single quoted string - self.StyleSetSpec(stc.STC_P_CHARACTER, - "fore:#7F007F,italic,face:%(times)s,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_CHARACTER, + "fore:#7F007F,italic,face:%(times)s,size:%(size)d" % faces, + ) # Keyword - self.StyleSetSpec(stc.STC_P_WORD, - "fore:#00007F,bold,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces + ) # Triple quotes - self.StyleSetSpec(stc.STC_P_TRIPLE, - "fore:#7F0000,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces + ) # Triple double quotes - self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, - "fore:#7F0000,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces + ) # Class name definition - self.StyleSetSpec(stc.STC_P_CLASSNAME, - "fore:#0000FF,bold,underline,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_CLASSNAME, + "fore:#0000FF,bold,underline,size:%(size)d" % faces, + ) # Function or method name definition - self.StyleSetSpec(stc.STC_P_DEFNAME, - "fore:#007F7F,bold,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces + ) # Operators - self.StyleSetSpec(stc.STC_P_OPERATOR, - "bold,size:%(size)d" % faces) + self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces) # Identifiers - self.StyleSetSpec(stc.STC_P_IDENTIFIER, - "fore:#808080,face:%(helv)s,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_IDENTIFIER, + "fore:#808080,face:%(helv)s,size:%(size)d" % faces, + ) # Comment-blocks - self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, - "fore:#7F7F7F,size:%(size)d" % faces) + self.StyleSetSpec( + stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces + ) # End of line where string is not closed - self.StyleSetSpec(stc.STC_P_STRINGEOL, - "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) - + self.StyleSetSpec( + stc.STC_P_STRINGEOL, + "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" + % faces, + ) self.SetCaretForeground("BLUE") - wx.EVT_KEY_DOWN(self, self.OnKeyPressed) - + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) def OnKeyPressed(self, event): if self.CallTipActive(): self.CallTipCancel() - # KeyCode used to be a method. Now it is an integer. - # Handle either case. - if type(event.KeyCode) is int: - # wx2.8+ - key = event.KeyCode - else: - # wx2.6 - key = event.KeyCode() + key = event.KeyCode if key == 32 and event.ControlDown(): pos = self.GetCurrentPos() # Tips if event.ShiftDown(): self.CallTipSetBackground("yellow") - self.CallTipShow(pos, 'param1, param2') + self.CallTipShow(pos, "param1, param2") # Code completion else: # fixme: What is this mess!!! - #lst = [] - #for x in range(50000): + # lst = [] + # for x in range(50000): # lst.append('%05d' % x) - #st = " ".join(lst) - #print len(st) - #self.AutoCompShow(0, st) + # st = " ".join(lst) + # print len(st) + # self.AutoCompShow(0, st) # fixme: What is this mess!!! kw = keyword.kwlist[:] @@ -233,7 +289,6 @@ else: event.Skip() - def OnUpdateUI(self, evt): # check for matching braces braceAtCaret = -1 @@ -245,32 +300,35 @@ styleBefore = self.GetStyleAt(caretPos - 1) # check before - if (charBefore and - chr(charBefore) in "[]{}()" and - styleBefore==stc.STC_P_OPERATOR): + if ( + charBefore + and chr(charBefore) in "[]{}()" + and styleBefore == stc.STC_P_OPERATOR + ): braceAtCaret = caretPos - 1 # check after if braceAtCaret < 0: charAfter = self.GetCharAt(caretPos) styleAfter = self.GetStyleAt(caretPos) - if (charAfter and - chr(charAfter) in "[]{}()" and - styleAfter == stc.STC_P_OPERATOR): + if ( + charAfter + and chr(charAfter) in "[]{}()" + and styleAfter == stc.STC_P_OPERATOR + ): braceAtCaret = caretPos if braceAtCaret >= 0: braceOpposite = self.BraceMatch(braceAtCaret) - if braceAtCaret != -1 and braceOpposite == -1: + if braceAtCaret != -1 and braceOpposite == -1: self.BraceBadLight(braceAtCaret) else: self.BraceHighlight(braceAtCaret, braceOpposite) - #pt = self.PointFromPosition(braceOpposite) - #self.Refresh(True, Rect(pt.x, pt.y, 5,5)) - #print pt - #self.Refresh(False) - + # pt = self.PointFromPosition(braceOpposite) + # self.Refresh(True, Rect(pt.x, pt.y, 5,5)) + # print pt + # self.Refresh(False) def OnMarginClick(self, evt): # fold and unfold as needed @@ -279,7 +337,10 @@ self.FoldAll() else: lineClicked = self.LineFromPosition(evt.GetPosition()) - if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG: + if ( + self.GetFoldLevel(lineClicked) + & stc.STC_FOLDLEVELHEADERFLAG + ): if evt.GetShift(): self.SetFoldExpanded(lineClicked, True) self.Expand(lineClicked, True, True, 1) @@ -293,7 +354,6 @@ else: self.ToggleFold(lineClicked) - def FoldAll(self): lineCount = self.GetLineCount() expanding = True @@ -302,13 +362,16 @@ for lineNum in range(lineCount): if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG: expanding = not self.GetFoldExpanded(lineNum) - break; + break lineNum = 0 while lineNum < lineCount: level = self.GetFoldLevel(lineNum) - if level & stc.STC_FOLDLEVELHEADERFLAG and \ - (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE: + if ( + level & stc.STC_FOLDLEVELHEADERFLAG + and (level & stc.STC_FOLDLEVELNUMBERMASK) + == stc.STC_FOLDLEVELBASE + ): if expanding: self.SetFoldExpanded(lineNum, True) @@ -318,12 +381,10 @@ lastChild = self.GetLastChild(lineNum, -1) self.SetFoldExpanded(lineNum, False) if lastChild > lineNum: - self.HideLines(lineNum+1, lastChild) + self.HideLines(lineNum + 1, lastChild) lineNum = lineNum + 1 - - def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): lastChild = self.GetLastChild(line, level) line = line + 1 @@ -346,36 +407,36 @@ self.SetFoldExpanded(line, True) else: self.SetFoldExpanded(line, False) - line = self.Expand(line, doExpand, force, visLevels-1) + line = self.Expand(line, doExpand, force, visLevels - 1) else: if doExpand and self.GetFoldExpanded(line): - line = self.Expand(line, True, force, visLevels-1) + line = self.Expand(line, True, force, visLevels - 1) else: - line = self.Expand(line, False, force, visLevels-1) + line = self.Expand(line, False, force, visLevels - 1) else: - line = line + 1; + line = line + 1 return line -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- _USE_PANEL = 1 + def runTest(frame, nb, log): if not _USE_PANEL: ed = p = stc.PythonSTC(nb, -1) else: - p = wx.Panel(nb, -1, style = wx.NO_FULL_REPAINT_ON_RESIZE) + p = wx.Panel(nb, -1, style=wx.NO_FULL_REPAINT_ON_RESIZE) ed = PythonSTC(p, -1) s = wx.BoxSizer(wx.HORIZONTAL) s.Add(ed, 1, wx.EXPAND) p.SetSizer(s) p.SetAutoLayout(True) - - ed.SetText(demoText + open('Main.py').read()) + ed.SetText(demoText + open("Main.py").read()) ed.EmptyUndoBuffer() ed.Colourise(0, -1) @@ -386,8 +447,7 @@ return p - -#---------------------------------------------------------------------- +# ---------------------------------------------------------------------- overview = """\ @@ -397,13 +457,3 @@ be helpful. """ - - -if __name__ == '__main__': - # fixme: This has been re-factored into not working. No run module. - import sys,os - import run - run.main(['', os.path.basename(sys.argv[0])]) - -#---------------------------------------------------------------------- - diff -Nru python-pyface-6.1.2/pyface/wx/scrolled_message_dialog.py python-pyface-7.4.0/pyface/wx/scrolled_message_dialog.py --- python-pyface-6.1.2/pyface/wx/scrolled_message_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/scrolled_message_dialog.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,42 +1,42 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import wx from wx.lib.layoutf import Layoutf + class ScrolledMessageDialog(wx.Dialog): - def __init__(self, parent, msg, caption, pos = wx.DefaultPosition, size = (500,300)): + def __init__( + self, parent, msg, caption, pos=wx.DefaultPosition, size=(500, 300) + ): wx.Dialog.__init__(self, parent, -1, caption, pos, size) x, y = pos if x == -1 and y == -1: self.CenterOnScreen(wx.BOTH) - text = wx.TextCtrl(self, -1, msg, wx.DefaultPosition, wx.DefaultSize, - wx.TE_READONLY | - wx.TE_MULTILINE | - wx.HSCROLL | - wx.TE_RICH2 - ) + text = wx.TextCtrl( + self, + -1, + msg, + wx.DefaultPosition, + wx.DefaultSize, + wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL | wx.TE_RICH2, + ) font = wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL) text.SetStyle(0, len(msg), wx.TextAttr(font=font)) ok = wx.Button(self, wx.ID_OK, "OK") - text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok))) - ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,))) + text.SetConstraints(Layoutf("t=t5#1;b=t5#2;l=l5#1;r=r5#1", (self, ok))) + ok.SetConstraints(Layoutf("b=b5#1;x%w50#1;w!80;h!25", (self,))) self.SetAutoLayout(1) self.Layout() - - diff -Nru python-pyface-6.1.2/pyface/wx/shell.py python-pyface-7.4.0/pyface/wx/shell.py --- python-pyface-6.1.2/pyface/wx/shell.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/shell.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,99 +1,112 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """The PyCrust Shell is an interactive text control in which a user types in commands to be sent to the interpreter. This particular shell is based on wxPython's wxStyledTextCtrl. The latest files are always available at the SourceForge project page at http://sourceforge.net/projects/pycrust/. Sponsored by Orbtech - Your source for Python programming expertise.""" -from __future__ import print_function + __author__ = "Patrick K. O'Brien " __cvsid__ = "$Id: shell.py,v 1.2 2003/06/13 17:59:34 dmorrill Exp $" __revision__ = "$Revision: 1.2 $"[11:-2] -from wx.wx import * +import wx +import wx.stc +from wx import * from wx.stc import * + +wx.StyledTextCtrl = wx.stc.StyledTextCtrl import keyword import os import sys from wx.py.pseudo import PseudoFileIn, PseudoFileOut, PseudoFileErr from wx.py.version import VERSION -# local imports + from .drag_and_drop import PythonObject from .drag_and_drop import clipboard as enClipboard -sys.ps3 = '<-- ' # Input prompt. +sys.ps3 = "<-- " # Input prompt. -NAVKEYS = (WXK_END, WXK_LEFT, WXK_RIGHT, WXK_UP, WXK_DOWN, WXK_PRIOR, WXK_NEXT) - -if wxPlatform == '__WXMSW__': - faces = { 'times' : 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Lucida Console', - 'lucida' : 'Lucida Console', - 'other' : 'Comic Sans MS', - 'size' : 10, - 'lnsize' : 9, - 'backcol': '#FFFFFF', - } +NAVKEYS = ( + wx.WXK_END, + wx.WXK_LEFT, + wx.WXK_RIGHT, + wx.WXK_UP, + wx.WXK_DOWN, + wx.WXK_PAGEUP, + wx.WXK_PAGEDOWN +) + +if wxPlatform == "__WXMSW__": + faces = { + "times": "Times New Roman", + "mono": "Courier New", + "helv": "Lucida Console", + "lucida": "Lucida Console", + "other": "Comic Sans MS", + "size": 10, + "lnsize": 9, + "backcol": "#FFFFFF", + } # Versions of wxPython prior to 2.3.2 had a sizing bug on Win platform. # The font was 2 points too large. So we need to reduce the font size. if (wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER) < (2, 3, 2): - faces['size'] -= 2 - faces['lnsize'] -= 2 + faces["size"] -= 2 + faces["lnsize"] -= 2 else: # GTK - faces = { 'times' : 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other' : 'new century schoolbook', - 'size' : 12, - 'lnsize' : 10, - 'backcol': '#FFFFFF', - } + faces = { + "times": "Times", + "mono": "Courier", + "helv": "Helvetica", + "other": "new century schoolbook", + "size": 12, + "lnsize": 10, + "backcol": "#FFFFFF", + } -class ShellFacade: +class ShellFacade(object): """Simplified interface to all shell-related functionality. This is a semi-transparent facade, in that all attributes of other are still accessible, even though only some are visible to the user.""" - name = 'PyCrust Shell Interface' + name = "PyCrust Shell Interface" revision = __revision__ def __init__(self, other): """Create a ShellFacade instance.""" - methods = ['ask', - 'clear', - 'pause', - 'prompt', - 'quit', - 'redirectStderr', - 'redirectStdin', - 'redirectStdout', - 'run', - 'runfile', - 'wrap', - 'zoom', - ] + methods = [ + "ask", + "clear", + "pause", + "prompt", + "quit", + "redirectStderr", + "redirectStdin", + "redirectStdout", + "run", + "runfile", + "wrap", + "zoom", + ] for method in methods: self.__dict__[method] = getattr(other, method) d = self.__dict__ - d['other'] = other - d['helpText'] = \ -""" + d["other"] = other + d[ + "helpText" + ] = """ * Key bindings: Home Go to the beginning of the command or line. Shift+Home Select to the beginning of the command or line. @@ -136,28 +149,39 @@ def _getAttributeNames(self): """Return list of magic attributes to extend introspection.""" - list = ['autoCallTip', - 'autoComplete', - 'autoCompleteCaseInsensitive', - 'autoCompleteIncludeDouble', - 'autoCompleteIncludeMagic', - 'autoCompleteIncludeSingle', - ] + list = [ + "autoCallTip", + "autoComplete", + "autoCompleteCaseInsensitive", + "autoCompleteIncludeDouble", + "autoCompleteIncludeMagic", + "autoCompleteIncludeSingle", + ] list.sort() return list -class Shell(wxStyledTextCtrl): +class Shell(wx.StyledTextCtrl): """PyCrust Shell based on wxStyledTextCtrl.""" - name = 'PyCrust Shell' + name = "PyCrust Shell" revision = __revision__ - def __init__(self, parent, id=-1, pos=wxDefaultPosition, \ - size=wxDefaultSize, style=wxCLIP_CHILDREN, introText='', \ - locals=None, InterpClass=None, *args, **kwds): + def __init__( + self, + parent, + id=-1, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.CLIP_CHILDREN, + introText="", + locals=None, + InterpClass=None, + *args, + **kwds + ): """Create a PyCrust Shell instance.""" - wxStyledTextCtrl.__init__(self, parent, id, pos, size, style) + wx.StyledTextCtrl.__init__(self, parent, id, pos, size, style) # Grab these so they can be restored by self.redirect* methods. self.stdin = sys.stdin self.stdout = sys.stdout @@ -167,29 +191,33 @@ # Add the current working directory "." to the search path. sys.path.insert(0, os.curdir) # Import a default interpreter class if one isn't provided. - if InterpClass == None: - from PyCrust.interpreter import Interpreter + if InterpClass is None: + from wx.py.interpreter import Interpreter else: Interpreter = InterpClass # Create default locals so we have something interesting. - shellLocals = {'__name__': 'PyCrust-Shell', - '__doc__': 'PyCrust-Shell, The PyCrust Python Shell.', - '__version__': VERSION, - } + shellLocals = { + "__name__": "PyCrust-Shell", + "__doc__": "PyCrust-Shell, The PyCrust Python Shell.", + "__version__": VERSION, + } # Add the dictionary that was passed in. if locals: shellLocals.update(locals) # Create a replacement for stdin. self.reader = PseudoFileIn(self.readline) - self.reader.input = '' + self.reader.input = "" self.reader.isreading = 0 # Set up the interpreter. - self.interp = Interpreter(locals=shellLocals, \ - rawin=self.raw_input, \ - stdin=self.reader, \ - stdout=PseudoFileOut(self.writeOut), \ - stderr=PseudoFileErr(self.writeErr), \ - *args, **kwds) + self.interp = Interpreter( + locals=shellLocals, + rawin=self.raw_input, + stdin=self.reader, + stdout=PseudoFileOut(self.writeOut), + stderr=PseudoFileErr(self.writeErr), + *args, + **kwds + ) # Find out for which keycodes the interpreter will autocomplete. self.autoCompleteKeys = self.interp.getAutoCompleteKeys() # Keep track of the last non-continuation prompt positions. @@ -207,26 +235,34 @@ self.historyIndex = -1 self.historyPrefix = 0 # Assign handlers for keyboard events. - EVT_KEY_DOWN(self, self.OnKeyDown) - EVT_CHAR(self, self.OnChar) + self.Bind(EVT_KEY_DOWN, self.OnKeyDown) + self.Bind(EVT_CHAR, self.OnChar) # Assign handlers for wxSTC events. - EVT_STC_UPDATEUI(self, id, self.OnUpdateUI) - EVT_STC_USERLISTSELECTION(self, id, self.OnHistorySelected) + self.Bind(EVT_STC_UPDATEUI, self.OnUpdateUI) + self.Bind(EVT_STC_USERLISTSELECTION, self.OnHistorySelected) # Configure various defaults and user preferences. self.config() # Display the introductory banner information. - try: self.showIntro(introText) - except: pass + try: + self.showIntro(introText) + except: + pass # Assign some pseudo keywords to the interpreter's namespace. - try: self.setBuiltinKeywords() - except: pass + try: + self.setBuiltinKeywords() + except: + pass # Add 'shell' to the interpreter's local namespace. - try: self.setLocalShell() - except: pass + try: + self.setLocalShell() + except: + pass # Do this last so the user has complete control over their # environment. They can override anything they want. - try: self.execStartupScript(self.interp.startupScript) - except: pass + try: + self.execStartupScript(self.interp.startupScript) + except: + pass def destroy(self): # del self.interp @@ -234,11 +270,11 @@ def config(self): """Configure shell based on user preferences.""" - self.SetMarginType(1, wxSTC_MARGIN_NUMBER) + self.SetMarginType(1, wx.STC_MARGIN_NUMBER) self.SetMarginWidth(1, 40) - self.SetLexer(wxSTC_LEX_PYTHON) - self.SetKeyWords(0, ' '.join(keyword.kwlist)) + self.SetLexer(wx.STC_LEX_PYTHON) + self.SetKeyWords(0, " ".join(keyword.kwlist)) self.setStyles(faces) self.SetViewWhiteSpace(0) @@ -251,7 +287,7 @@ self.autoCompleteIncludeDouble = 1 self.autoCompleteCaseInsensitive = 1 self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) - self.AutoCompSetSeparator(ord('\n')) + self.AutoCompSetSeparator(ord("\n")) # Do we want to automatically pop up command argument help? self.autoCallTip = 1 self.CallTipSetBackground(wxColour(255, 255, 232)) @@ -261,10 +297,11 @@ except AttributeError: pass - def showIntro(self, text=''): + def showIntro(self, text=""): """Display introductory text in the shell.""" if text: - if not text.endswith(os.linesep): text += os.linesep + if not text.endswith(os.linesep): + text += os.linesep self.write(text) try: self.write(self.interp.introText) @@ -277,9 +314,13 @@ This simply sets "close", "exit" and "quit" to a helpful string. """ - import six.moves.builtins - six.moves.builtins.close = six.moves.builtins.exit = six.moves.builtins.quit = \ - 'Click on the close button to leave the application.' + import builtins + + builtins.close = ( + builtins.exit + ) = ( + builtins.quit + ) = "Click on the close button to leave the application." def quit(self): """Quit the application.""" @@ -290,41 +331,53 @@ # Other applications, like PythonCard, may choose to hide rather than # quit so we should just post the event and let the surrounding app # decide what it wants to do. - self.write('Click on the close button to leave the application.') + self.write("Click on the close button to leave the application.") def setLocalShell(self): """Add 'shell' to locals as reference to ShellFacade instance.""" - self.interp.locals['shell'] = ShellFacade(other=self) + self.interp.locals["shell"] = ShellFacade(other=self) def execStartupScript(self, startupScript): """Execute the user's PYTHONSTARTUP script if they have one.""" if startupScript and os.path.isfile(startupScript): - startupText = 'Startup script executed: ' + startupScript - self.push('print %s;execfile(%s)' % \ - ('startupText', 'startupScript')) + startupText = "Startup script executed: " + startupScript + self.push( + "print(%s);exec(open(%s).read())" + % (repr(startupText), repr(startupScript)) + ) else: - self.push('') + self.push("") def setStyles(self, faces): """Configure font size, typeface and color for lexer.""" # Default style - self.StyleSetSpec(wxSTC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d,back:%(backcol)s" % faces) + self.StyleSetSpec( + wxSTC_STYLE_DEFAULT, + "face:%(mono)s,size:%(size)d,back:%(backcol)s" % faces, + ) self.StyleClearAll() # Built in styles - self.StyleSetSpec(wxSTC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces) + self.StyleSetSpec( + wxSTC_STYLE_LINENUMBER, + "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces, + ) self.StyleSetSpec(wxSTC_STYLE_CONTROLCHAR, "face:%(mono)s" % faces) self.StyleSetSpec(wxSTC_STYLE_BRACELIGHT, "fore:#0000FF,back:#FFFF88") self.StyleSetSpec(wxSTC_STYLE_BRACEBAD, "fore:#FF0000,back:#FFFF88") # Python styles self.StyleSetSpec(wxSTC_P_DEFAULT, "face:%(mono)s" % faces) - self.StyleSetSpec(wxSTC_P_COMMENTLINE, "fore:#007F00,face:%(mono)s" % faces) + self.StyleSetSpec( + wxSTC_P_COMMENTLINE, "fore:#007F00,face:%(mono)s" % faces + ) self.StyleSetSpec(wxSTC_P_NUMBER, "") self.StyleSetSpec(wxSTC_P_STRING, "fore:#7F007F,face:%(mono)s" % faces) - self.StyleSetSpec(wxSTC_P_CHARACTER, "fore:#7F007F,face:%(mono)s" % faces) + self.StyleSetSpec( + wxSTC_P_CHARACTER, "fore:#7F007F,face:%(mono)s" % faces + ) self.StyleSetSpec(wxSTC_P_WORD, "fore:#00007F,bold") self.StyleSetSpec(wxSTC_P_TRIPLE, "fore:#7F0000") self.StyleSetSpec(wxSTC_P_TRIPLEDOUBLE, "fore:#000033,back:#FFFFE8") @@ -333,7 +386,10 @@ self.StyleSetSpec(wxSTC_P_OPERATOR, "") self.StyleSetSpec(wxSTC_P_IDENTIFIER, "") self.StyleSetSpec(wxSTC_P_COMMENTBLOCK, "fore:#7F7F7F") - self.StyleSetSpec(wxSTC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces) + self.StyleSetSpec( + wxSTC_P_STRINGEOL, + "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces, + ) def OnUpdateUI(self, evt): """Check for matching braces.""" @@ -343,33 +399,39 @@ caretPos = self.GetCurrentPos() if caretPos > 0: charBefore = self.GetCharAt(caretPos - 1) - #*** Patch to fix bug in wxSTC for wxPython < 2.3.3. + # *** Patch to fix bug in wxSTC for wxPython < 2.3.3. if charBefore < 0: charBefore = 32 # Mimic a space. - #*** + # *** styleBefore = self.GetStyleAt(caretPos - 1) # Check before. - if charBefore and chr(charBefore) in '[]{}()' \ - and styleBefore == wxSTC_P_OPERATOR: + if ( + charBefore + and chr(charBefore) in "[]{}()" + and styleBefore == wx.STC_P_OPERATOR + ): braceAtCaret = caretPos - 1 # Check after. if braceAtCaret < 0: charAfter = self.GetCharAt(caretPos) - #*** Patch to fix bug in wxSTC for wxPython < 2.3.3. + # *** Patch to fix bug in wxSTC for wxPython < 2.3.3. if charAfter < 0: charAfter = 32 # Mimic a space. - #*** + # *** styleAfter = self.GetStyleAt(caretPos) - if charAfter and chr(charAfter) in '[]{}()' \ - and styleAfter == wxSTC_P_OPERATOR: + if ( + charAfter + and chr(charAfter) in "[]{}()" + and styleAfter == wxSTC_P_OPERATOR + ): braceAtCaret = caretPos if braceAtCaret >= 0: braceOpposite = self.BraceMatch(braceAtCaret) - if braceAtCaret != -1 and braceOpposite == -1: + if braceAtCaret != -1 and braceOpposite == -1: self.BraceBadLight(braceAtCaret) else: self.BraceHighlight(braceAtCaret, braceOpposite) @@ -394,22 +456,25 @@ # Get the command between the prompt and the cursor. # Add the autocomplete character to the end of the command. command = self.GetTextRange(stoppos, currpos) - if command == '': + if command == "": self.historyShow() else: command += chr(key) self.write(chr(key)) - if self.autoComplete: self.autoCompleteShow(command) - elif key == ord('('): + if self.autoComplete: + self.autoCompleteShow(command) + elif key == ord("("): # The left paren activates a call tip and cancels # an active auto completion. - if self.AutoCompActive(): self.AutoCompCancel() + if self.AutoCompActive(): + self.AutoCompCancel() # Get the command between the prompt and the cursor. # Add the '(' to the end of the command. - self.ReplaceSelection('') - command = self.GetTextRange(stoppos, currpos) + '(' - self.write('(') - if self.autoCallTip: self.autoCallTipShow(command) + self.ReplaceSelection("") + command = self.GetTextRange(stoppos, currpos) + "(" + self.write("(") + if self.autoCallTip: + self.autoCallTipShow(command) else: # Allow the normal event handling to take place. event.Skip() @@ -423,22 +488,25 @@ altDown = event.AltDown() shiftDown = event.ShiftDown() currpos = self.GetCurrentPos() - endpos = self.GetTextLength() selecting = self.GetSelectionStart() != self.GetSelectionEnd() # Return (Enter) is used to submit a command to the interpreter. if not controlDown and key == WXK_RETURN: if self.AutoCompActive(): event.Skip() return - if self.CallTipActive(): self.CallTipCancel() + if self.CallTipActive(): + self.CallTipCancel() self.processLine() # Ctrl+Return (Cntrl+Enter) is used to insert a line break. elif controlDown and key == WXK_RETURN: - if self.AutoCompActive(): self.AutoCompCancel() - if self.CallTipActive(): self.CallTipCancel() - if (not self.more and - (self.GetTextRange(self.promptPosEnd, - self.GetCurrentPos()) == '')): + if self.AutoCompActive(): + self.AutoCompCancel() + if self.CallTipActive(): + self.CallTipCancel() + if not self.more and ( + self.GetTextRange(self.promptPosEnd, self.GetCurrentPos()) + == "" + ): self.historyShow() else: self.insertLineBreak() @@ -455,16 +523,23 @@ else: self.clearCommand() # Cut to the clipboard. - elif (controlDown and key in (ord('X'), ord('x'))) \ - or (shiftDown and key == WXK_DELETE): + elif (controlDown and key in (ord("X"), ord("x"))) or ( + shiftDown and key == WXK_DELETE + ): self.Cut() # Copy to the clipboard. - elif controlDown and not shiftDown \ - and key in (ord('C'), ord('c'), WXK_INSERT): + elif ( + controlDown + and not shiftDown + and key in (ord("C"), ord("c"), WXK_INSERT) + ): self.Copy() # Copy to the clipboard, including prompts. - elif controlDown and shiftDown \ - and key in (ord('C'), ord('c'), WXK_INSERT): + elif ( + controlDown + and shiftDown + and key in (ord("C"), ord("c"), WXK_INSERT) + ): self.CopyWithPrompts() # Home needs to be aware of the prompt. elif key == WXK_HOME: @@ -484,21 +559,22 @@ elif selecting and key not in NAVKEYS and not self.CanEdit(): pass # Paste from the clipboard. - elif (controlDown and not shiftDown \ - and key in (ord('V'), ord('v'))) \ - or (shiftDown and not controlDown and key == WXK_INSERT): + elif ( + controlDown and not shiftDown and key in (ord("V"), ord("v")) + ) or (shiftDown and not controlDown and key == WXK_INSERT): self.Paste() # Paste from the clipboard, run commands. - elif controlDown and shiftDown \ - and key in (ord('V'), ord('v')): + elif controlDown and shiftDown and key in (ord("V"), ord("v")): self.PasteAndRun() # Replace with the previous command from the history buffer. - elif (controlDown and key == WXK_UP) \ - or (altDown and key in (ord('P'), ord('p'))): + elif (controlDown and key == WXK_UP) or ( + altDown and key in (ord("P"), ord("p")) + ): self.OnHistoryReplace(step=+1) # Replace with the next command from the history buffer. - elif (controlDown and key == WXK_DOWN) \ - or (altDown and key in (ord('N'), ord('n'))): + elif (controlDown and key == WXK_DOWN) or ( + altDown and key in (ord("N"), ord("n")) + ): self.OnHistoryReplace(step=-1) # Insert the previous command from the history buffer. elif (shiftDown and key == WXK_UP) and self.CanEdit(): @@ -529,10 +605,10 @@ elif key == WXK_INSERT: pass # Don't allow line deletion. - elif controlDown and key in (ord('L'), ord('l')): + elif controlDown and key in (ord("L"), ord("l")): pass # Don't allow line transposition. - elif controlDown and key in (ord('T'), ord('t')): + elif controlDown and key in (ord("T"), ord("t")): pass # Basic navigation keys should work anywhere. elif key in NAVKEYS: @@ -548,13 +624,13 @@ startpos = self.promptPosEnd endpos = self.GetTextLength() self.SetSelection(startpos, endpos) - self.ReplaceSelection('') + self.ReplaceSelection("") self.more = 0 def OnHistoryReplace(self, step): """Replace with the previous/next command from the history buffer.""" if not self.historyPrefix: - self.historyPrefix = 1 + self.historyPrefix = 1 self.historyMatches = None prefix = self.getCommand(rstrip=0) n = len(prefix) @@ -568,15 +644,15 @@ def replaceFromHistory(self, step, history=None): """Replace selection with command from the history buffer.""" - self.ReplaceSelection('') + self.ReplaceSelection("") if history is None: history = self.history newindex = self.historyIndex + step if -1 <= newindex <= len(history): self.historyIndex = newindex - if 0 <= newindex <= len(history)-1: + if 0 <= newindex <= len(history) - 1: command = history[self.historyIndex] - command = command.replace('\n', os.linesep + sys.ps2) + command = command.replace("\n", os.linesep + sys.ps2) self.ReplaceSelection(command) def OnHistoryInsert(self, step): @@ -602,15 +678,17 @@ return # Search upwards from the current history position and loop back # to the beginning if we don't find anything. - if (self.historyIndex <= -1) \ - or (self.historyIndex >= len(self.history)-2): + if (self.historyIndex <= -1) or ( + self.historyIndex >= len(self.history) - 2 + ): searchOrder = list(range(len(self.history))) else: - searchOrder = list(range(self.historyIndex+1, len(self.history))) + \ - list(range(self.historyIndex)) + searchOrder = list( + range(self.historyIndex + 1, len(self.history)) + ) + list(range(self.historyIndex)) for i in searchOrder: command = self.history[i] - if command[:len(searchText)] == searchText: + if command[: len(searchText)] == searchText: # Replace the current selection with the one we've found. self.ReplaceSelection(command[len(searchText):]) endpos = self.GetCurrentPos() @@ -649,12 +727,12 @@ command = self.GetTextRange(startpos, endpos) lines = command.split(os.linesep + sys.ps2) lines = [line.rstrip() for line in lines] - command = '\n'.join(lines) + command = "\n".join(lines) if self.reader.isreading: if not command: # Match the behavior of the standard Python shell when # the user hits return without entering a value. - command = '\n' + command = "\n" self.reader.input = command self.write(os.linesep) else: @@ -699,11 +777,11 @@ self.GotoLine(line) stoppos = self.GetCurrentPos() command = self.GetTextRange(startpos, stoppos) - command = command.replace(os.linesep + sys.ps2, '\n') + command = command.replace(os.linesep + sys.ps2, "\n") command = command.rstrip() - command = command.replace('\n', os.linesep + sys.ps2) + command = command.replace("\n", os.linesep + sys.ps2) else: - command = '' + command = "" if rstrip: command = command.rstrip() return command @@ -717,7 +795,7 @@ # Strip the prompt off the front of text leaving just the command. command = self.lstripPrompt(text) if command == text: - command = '' # Real commands have prompts. + command = "" # Real commands have prompts. if rstrip: command = command.rstrip() return command @@ -754,8 +832,9 @@ self.historyPrefix = 0 # Insert this command into the history, unless it's a blank # line or the same as the last command. - if command != '' \ - and (len(self.history) == 0 or command != self.history[0]): + if command != "" and ( + len(self.history) == 0 or command != self.history[0] + ): self.history.insert(0, command) def write(self, text): @@ -768,11 +847,11 @@ def fixLineEndings(self, text): """Return text with line endings replaced by OS-specific endings.""" - lines = text.split('\r\n') + lines = text.split("\r\n") for l in range(len(lines)): - chunks = lines[l].split('\r') + chunks = lines[l].split("\r") for c in range(len(chunks)): - chunks[c] = os.linesep.join(chunks[c].split('\n')) + chunks[c] = os.linesep.join(chunks[c].split("\n")) lines[l] = os.linesep.join(chunks) text = os.linesep.join(lines) return text @@ -805,46 +884,45 @@ self.EmptyUndoBuffer() # XXX Add some autoindent magic here if more. if self.more: - self.write(' '*4) # Temporary hack indentation. + self.write(" " * 4) # Temporary hack indentation. self.EnsureCaretVisible() self.ScrollToColumn(0) def readline(self): """Replacement for stdin.readline().""" - input = '' + input = "" reader = self.reader reader.isreading = 1 self.prompt() try: while not reader.input: - wxYield() + wx.Yield() input = reader.input finally: - reader.input = '' + reader.input = "" reader.isreading = 0 return input - def raw_input(self, prompt=''): + def raw_input(self, prompt=""): """Return string based on user input.""" if prompt: self.write(prompt) return self.readline() - def ask(self, prompt='Please enter your response:'): + def ask(self, prompt="Please enter your response:"): """Get response from the user using a dialog box.""" - dialog = wxTextEntryDialog(None, prompt, \ - 'Input Dialog (Raw)', '') + dialog = wx.TextEntryDialog(None, prompt, "Input Dialog (Raw)", "") try: if dialog.ShowModal() == wxID_OK: text = dialog.GetValue() return text finally: dialog.Destroy() - return '' + return "" def pause(self): """Halt execution pending a response from the user.""" - self.ask('Press enter to continue:') + self.ask("Press enter to continue:") def clear(self): """Delete all text from the shell.""" @@ -861,8 +939,10 @@ endpos = self.GetTextLength() self.SetCurrentPos(endpos) command = command.rstrip() - if prompt: self.prompt() - if verbose: self.write(command) + if prompt: + self.prompt() + if verbose: + self.write(command) self.push(command) def runfile(self, filename): @@ -871,7 +951,7 @@ try: self.prompt() for command in file.readlines(): - if command[:6] == 'shell.': # Run shell methods silently. + if command[:6] == "shell.": # Run shell methods silently. self.run(command, prompt=0, verbose=0) else: self.run(command, prompt=0, verbose=1) @@ -880,22 +960,25 @@ def autoCompleteShow(self, command): """Display auto-completion popup list.""" - list = self.interp.getAutoCompleteList(command, - includeMagic=self.autoCompleteIncludeMagic, - includeSingle=self.autoCompleteIncludeSingle, - includeDouble=self.autoCompleteIncludeDouble) + list = self.interp.getAutoCompleteList( + command, + includeMagic=self.autoCompleteIncludeMagic, + includeSingle=self.autoCompleteIncludeSingle, + includeDouble=self.autoCompleteIncludeDouble, + ) if list: - options = '\n'.join(list) + options = "\n".join(list) offset = 0 self.AutoCompShow(offset, options) def autoCallTipShow(self, command): """Display argument spec and docstring in a popup bubble thingie.""" - if self.CallTipActive: self.CallTipCancel() + if self.CallTipActive: + self.CallTipCancel() (name, argspec, tip) = self.interp.getCallTip(command) if argspec: startpos = self.GetCurrentPos() - self.write(argspec + ')') + self.write(argspec + ")") endpos = self.GetCurrentPos() self.SetSelection(endpos, startpos) if tip: @@ -906,25 +989,25 @@ tippos = max(tippos, fallback) self.CallTipShow(tippos, tip) - def historyShow(self, prefix=''): + def historyShow(self, prefix=""): items = [] for item in self.history: - item = item.replace( '\n', '\\n' ) - if (prefix == item[:len(prefix)]) and item not in items: + item = item.replace("\n", "\\n") + if (prefix == item[: len(prefix)]) and item not in items: items.append(item) - self.UserListShow(1, '\n'.join(items)) + self.UserListShow(1, "\n".join(items)) def OnHistorySelected(self, event): command = event.GetText() - if command.find('\\n') >= 0: - command += '\\n' - command = command.replace( '\\n', os.linesep + sys.ps2) + if command.find("\\n") >= 0: + command += "\\n" + command = command.replace("\\n", os.linesep + sys.ps2) self.clearCommand() self.write(command) # Process the command if the 'Enter' key was pressed: key = event.GetKey() if key == 28 or key == 1241712: # Is there a 'name' for the Enter key? - self.processLine() + self.processLine() def topLevelComplete(self): command = self.getCommand(rstrip=0) @@ -934,7 +1017,7 @@ if len(completions) == 1: self.write(completions[0][len(command):]) else: - self.AutoCompShow(len(command), '\n'.join(completions)) + self.AutoCompShow(len(command), "\n".join(completions)) return 1 def writeOut(self, text): @@ -968,9 +1051,11 @@ def CanCut(self): """Return true if text is selected and can be cut.""" - if self.GetSelectionStart() != self.GetSelectionEnd() \ - and self.GetSelectionStart() >= self.promptPosEnd \ - and self.GetSelectionEnd() >= self.promptPosEnd: + if ( + self.GetSelectionStart() != self.GetSelectionEnd() + and self.GetSelectionStart() >= self.promptPosEnd + and self.GetSelectionEnd() >= self.promptPosEnd + ): return 1 else: return 0 @@ -981,9 +1066,10 @@ def CanPaste(self): """Return true if a paste should succeed.""" - if self.CanEdit() and \ - (wxStyledTextCtrl.CanPaste(self) or \ - wxTheClipboard.IsSupported(PythonObject)): + if self.CanEdit() and ( + wx.StyledTextCtrl.CanPaste(self) + or wx.TheClipboard.IsSupported(PythonObject) + ): return 1 else: return 0 @@ -991,8 +1077,10 @@ def CanEdit(self): """Return true if editing should succeed.""" if self.GetSelectionStart() != self.GetSelectionEnd(): - if self.GetSelectionStart() >= self.promptPosEnd \ - and self.GetSelectionEnd() >= self.promptPosEnd: + if ( + self.GetSelectionStart() >= self.promptPosEnd + and self.GetSelectionEnd() >= self.promptPosEnd + ): return 1 else: return 0 @@ -1002,10 +1090,12 @@ def Cut(self): """Remove selection and place it on the clipboard.""" if self.CanCut() and self.CanCopy(): - if self.AutoCompActive(): self.AutoCompCancel() - if self.CallTipActive: self.CallTipCancel() + if self.AutoCompActive(): + self.AutoCompCancel() + if self.CallTipActive: + self.CallTipCancel() self.Copy() - self.ReplaceSelection('') + self.ReplaceSelection("") def Copy(self): """Copy selection and place it on the clipboard.""" @@ -1015,19 +1105,19 @@ command = command.replace(os.linesep + sys.ps1, os.linesep) command = self.lstripPrompt(text=command) data = wxTextDataObject(command) - if wxTheClipboard.Open(): - wxTheClipboard.SetData(data) - wxTheClipboard.Close() + if wx.TheClipboard.Open(): + wx.TheClipboard.SetData(data) + wx.TheClipboard.Close() def CopyWithPrompts(self): """Copy selection, including prompts, and place it on the clipboard.""" if self.CanCopy(): command = self.GetSelectedText() - data = wxTextDataObject(command) - if wxTheClipboard.Open(): - wxTheClipboard.SetData(data) - wxTheClipboard.Close() + data = wx.TextDataObject(command) + if wx.TheClipboard.Open(): + wx.TheClipboard.SetData(data) + wx.TheClipboard.Close() def Paste(self): """Replace selection with clipboard contents.""" @@ -1035,50 +1125,51 @@ try: if wxTheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)): data = wxTextDataObject() - if wxTheClipboard.GetData(data): - self.ReplaceSelection('') + if wx.TheClipboard.GetData(data): + self.ReplaceSelection("") command = data.GetText() command = command.rstrip() command = self.fixLineEndings(command) command = self.lstripPrompt(text=command) - command = command.replace(os.linesep + sys.ps2, '\n') - command = command.replace(os.linesep, '\n') - command = command.replace('\n', os.linesep + sys.ps2) + command = command.replace(os.linesep + sys.ps2, "\n") + command = command.replace(os.linesep, "\n") + command = command.replace("\n", os.linesep + sys.ps2) self.write(command) - if wxTheClipboard.IsSupported(PythonObject) and \ - self.python_obj_paste_handler is not None: + if ( + wx.TheClipboard.IsSupported(PythonObject) + and self.python_obj_paste_handler is not None + ): # note that the presence of a PythonObject on the # clipboard is really just a signal to grab the data # from our singleton clipboard instance data = enClipboard.data self.python_obj_paste_handler(data) finally: - wxTheClipboard.Close() + wx.TheClipboard.Close() - return def PasteAndRun(self): """Replace selection with clipboard contents, run commands.""" - if wxTheClipboard.Open(): - if wxTheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)): - data = wxTextDataObject() - if wxTheClipboard.GetData(data): + if wx.TheClipboard.Open(): + if wx.TheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)): + data = wx.TextDataObject() + if wx.TheClipboard.GetData(data): endpos = self.GetTextLength() self.SetCurrentPos(endpos) startpos = self.promptPosEnd self.SetSelection(startpos, endpos) - self.ReplaceSelection('') + self.ReplaceSelection("") text = data.GetText() text = text.strip() text = self.fixLineEndings(text) text = self.lstripPrompt(text=text) - text = text.replace(os.linesep + sys.ps1, '\n') - text = text.replace(os.linesep + sys.ps2, '\n') - text = text.replace(os.linesep, '\n') - lines = text.split('\n') + text = text.replace(os.linesep + sys.ps1, "\n") + text = text.replace(os.linesep + sys.ps2, "\n") + text = text.replace(os.linesep, "\n") + lines = text.split("\n") commands = [] - command = '' + command = "" for line in lines: - if line.strip() != '' and line.lstrip() == line: + if line.strip() != "" and line.lstrip() == line: # New command. if command: # Add the previous command to the list. @@ -1087,21 +1178,21 @@ command = line else: # Multiline command. Add to the command. - command += '\n' + command += "\n" command += line commands.append(command) for command in commands: - command = command.replace('\n', os.linesep + sys.ps2) + command = command.replace("\n", os.linesep + sys.ps2) self.write(command) self.processLine() - wxTheClipboard.Close() + wx.TheClipboard.Close() def wrap(self, wrap=1): """Sets whether text is word wrapped.""" try: self.SetWrapMode(wrap) except AttributeError: - return 'Wrapping is not available in this version of PyCrust.' + return "Wrapping is not available in this version of PyCrust." def zoom(self, points=0): """Set the zoom level. @@ -1111,135 +1202,217 @@ self.SetZoom(points) -wxID_SELECTALL = wxNewId() -ID_AUTOCOMP = wxNewId() -ID_AUTOCOMP_SHOW = wxNewId() -ID_AUTOCOMP_INCLUDE_MAGIC = wxNewId() -ID_AUTOCOMP_INCLUDE_SINGLE = wxNewId() -ID_AUTOCOMP_INCLUDE_DOUBLE = wxNewId() -ID_CALLTIPS = wxNewId() -ID_CALLTIPS_SHOW = wxNewId() - -ID_FILLING = wxNewId() -ID_FILLING_AUTO_UPDATE = wxNewId() -ID_FILLING_SHOW_METHODS = wxNewId() -ID_FILLING_SHOW_CLASS = wxNewId() -ID_FILLING_SHOW_DICT = wxNewId() -ID_FILLING_SHOW_DOC = wxNewId() -ID_FILLING_SHOW_MODULE = wxNewId() +wxID_SELECTALL = wx.NewId() +ID_AUTOCOMP = wx.NewId() +ID_AUTOCOMP_SHOW = wx.NewId() +ID_AUTOCOMP_INCLUDE_MAGIC = wx.NewId() +ID_AUTOCOMP_INCLUDE_SINGLE = wx.NewId() +ID_AUTOCOMP_INCLUDE_DOUBLE = wx.NewId() +ID_CALLTIPS = wx.NewId() +ID_CALLTIPS_SHOW = wx.NewId() + +ID_FILLING = wx.NewId() +ID_FILLING_AUTO_UPDATE = wx.NewId() +ID_FILLING_SHOW_METHODS = wx.NewId() +ID_FILLING_SHOW_CLASS = wx.NewId() +ID_FILLING_SHOW_DICT = wx.NewId() +ID_FILLING_SHOW_DOC = wx.NewId() +ID_FILLING_SHOW_MODULE = wx.NewId() -class ShellMenu: +class ShellMenu(object): """Mixin class to add standard menu items.""" def createMenus(self): m = self.fileMenu = wxMenu() m.AppendSeparator() - m.Append(wxID_EXIT, 'E&xit', 'Exit PyCrust') + m.Append(wxID_EXIT, "E&xit", "Exit PyCrust") m = self.editMenu = wxMenu() - m.Append(wxID_UNDO, '&Undo \tCtrl+Z', 'Undo the last action') - m.Append(wxID_REDO, '&Redo \tCtrl+Y', 'Redo the last undone action') + m.Append(wxID_UNDO, "&Undo \tCtrl+Z", "Undo the last action") + m.Append(wxID_REDO, "&Redo \tCtrl+Y", "Redo the last undone action") m.AppendSeparator() - m.Append(wxID_CUT, 'Cu&t \tCtrl+X', 'Cut the selection') - m.Append(wxID_COPY, '&Copy \tCtrl+C', 'Copy the selection') - m.Append(wxID_PASTE, '&Paste \tCtrl+V', 'Paste') + m.Append(wxID_CUT, "Cu&t \tCtrl+X", "Cut the selection") + m.Append(wxID_COPY, "&Copy \tCtrl+C", "Copy the selection") + m.Append(wxID_PASTE, "&Paste \tCtrl+V", "Paste") m.AppendSeparator() - m.Append(wxID_CLEAR, 'Cle&ar', 'Delete the selection') - m.Append(wxID_SELECTALL, 'Select A&ll', 'Select all text') + m.Append(wxID_CLEAR, "Cle&ar", "Delete the selection") + m.Append(wxID_SELECTALL, "Select A&ll", "Select all text") m = self.autocompMenu = wxMenu() - m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion', \ - 'Show auto completion during dot syntax', 1) - m.Append(ID_AUTOCOMP_INCLUDE_MAGIC, 'Include Magic Attributes', \ - 'Include attributes visible to __getattr__ and __setattr__', 1) - m.Append(ID_AUTOCOMP_INCLUDE_SINGLE, 'Include Single Underscores', \ - 'Include attibutes prefixed by a single underscore', 1) - m.Append(ID_AUTOCOMP_INCLUDE_DOUBLE, 'Include Double Underscores', \ - 'Include attibutes prefixed by a double underscore', 1) + m.Append( + ID_AUTOCOMP_SHOW, + "Show Auto Completion", + "Show auto completion during dot syntax", + 1, + ) + m.Append( + ID_AUTOCOMP_INCLUDE_MAGIC, + "Include Magic Attributes", + "Include attributes visible to __getattr__ and __setattr__", + 1, + ) + m.Append( + ID_AUTOCOMP_INCLUDE_SINGLE, + "Include Single Underscores", + "Include attibutes prefixed by a single underscore", + 1, + ) + m.Append( + ID_AUTOCOMP_INCLUDE_DOUBLE, + "Include Double Underscores", + "Include attibutes prefixed by a double underscore", + 1, + ) m = self.calltipsMenu = wxMenu() - m.Append(ID_CALLTIPS_SHOW, 'Show Call Tips', \ - 'Show call tips with argument specifications', 1) + m.Append( + ID_CALLTIPS_SHOW, + "Show Call Tips", + "Show call tips with argument specifications", + 1, + ) m = self.optionsMenu = wxMenu() - m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu, \ - 'Auto Completion Options') - m.AppendMenu(ID_CALLTIPS, '&Call Tips', self.calltipsMenu, \ - 'Call Tip Options') + m.AppendMenu( + ID_AUTOCOMP, + "&Auto Completion", + self.autocompMenu, + "Auto Completion Options", + ) + m.AppendMenu( + ID_CALLTIPS, "&Call Tips", self.calltipsMenu, "Call Tip Options" + ) - if hasattr( self, 'crust' ): + if hasattr(self, "crust"): fm = self.fillingMenu = wxMenu() - fm.Append(ID_FILLING_AUTO_UPDATE, 'Automatic Update', - 'Automatically update tree view after each command', 1) - fm.Append(ID_FILLING_SHOW_METHODS, 'Show Methods', - 'Show methods and functions in the tree view', 1) - fm.Append(ID_FILLING_SHOW_CLASS, 'Show __class__', - 'Show __class__ entries in the tree view', 1) - fm.Append(ID_FILLING_SHOW_DICT, 'Show __dict__', - 'Show __dict__ entries in the tree view', 1) - fm.Append(ID_FILLING_SHOW_DOC, 'Show __doc__', - 'Show __doc__ entries in the tree view', 1) - fm.Append(ID_FILLING_SHOW_MODULE, 'Show __module__', - 'Show __module__ entries in the tree view', 1) - m.AppendMenu(ID_FILLING, '&Filling', fm, 'Filling Options') + fm.Append( + ID_FILLING_AUTO_UPDATE, + "Automatic Update", + "Automatically update tree view after each command", + 1, + ) + fm.Append( + ID_FILLING_SHOW_METHODS, + "Show Methods", + "Show methods and functions in the tree view", + 1, + ) + fm.Append( + ID_FILLING_SHOW_CLASS, + "Show __class__", + "Show __class__ entries in the tree view", + 1, + ) + fm.Append( + ID_FILLING_SHOW_DICT, + "Show __dict__", + "Show __dict__ entries in the tree view", + 1, + ) + fm.Append( + ID_FILLING_SHOW_DOC, + "Show __doc__", + "Show __doc__ entries in the tree view", + 1, + ) + fm.Append( + ID_FILLING_SHOW_MODULE, + "Show __module__", + "Show __module__ entries in the tree view", + 1, + ) + m.AppendMenu(ID_FILLING, "&Filling", fm, "Filling Options") m = self.helpMenu = wxMenu() m.AppendSeparator() - m.Append(wxID_ABOUT, '&About...', 'About PyCrust') + m.Append(wxID_ABOUT, "&About...", "About PyCrust") b = self.menuBar = wxMenuBar() - b.Append(self.fileMenu, '&File') - b.Append(self.editMenu, '&Edit') - b.Append(self.optionsMenu, '&Options') - b.Append(self.helpMenu, '&Help') + b.Append(self.fileMenu, "&File") + b.Append(self.editMenu, "&Edit") + b.Append(self.optionsMenu, "&Options") + b.Append(self.helpMenu, "&Help") self.SetMenuBar(b) - EVT_MENU(self, wxID_EXIT, self.OnExit) - EVT_MENU(self, wxID_UNDO, self.OnUndo) - EVT_MENU(self, wxID_REDO, self.OnRedo) - EVT_MENU(self, wxID_CUT, self.OnCut) - EVT_MENU(self, wxID_COPY, self.OnCopy) - EVT_MENU(self, wxID_PASTE, self.OnPaste) - EVT_MENU(self, wxID_CLEAR, self.OnClear) - EVT_MENU(self, wxID_SELECTALL, self.OnSelectAll) - EVT_MENU(self, wxID_ABOUT, self.OnAbout) - EVT_MENU(self, ID_AUTOCOMP_SHOW, \ - self.OnAutoCompleteShow) - EVT_MENU(self, ID_AUTOCOMP_INCLUDE_MAGIC, \ - self.OnAutoCompleteIncludeMagic) - EVT_MENU(self, ID_AUTOCOMP_INCLUDE_SINGLE, \ - self.OnAutoCompleteIncludeSingle) - EVT_MENU(self, ID_AUTOCOMP_INCLUDE_DOUBLE, \ - self.OnAutoCompleteIncludeDouble) - EVT_MENU(self, ID_CALLTIPS_SHOW, \ - self.OnCallTipsShow) - - EVT_UPDATE_UI(self, wxID_UNDO, self.OnUpdateMenu) - EVT_UPDATE_UI(self, wxID_REDO, self.OnUpdateMenu) - EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateMenu) - EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateMenu) - EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateMenu) - EVT_UPDATE_UI(self, wxID_CLEAR, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_AUTOCOMP_SHOW, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_MAGIC, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_SINGLE, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_DOUBLE, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_CALLTIPS_SHOW, self.OnUpdateMenu) - - if hasattr( self, 'crust' ): - EVT_MENU(self, ID_FILLING_AUTO_UPDATE, self.OnFillingAutoUpdate) - EVT_MENU(self, ID_FILLING_SHOW_METHODS, self.OnFillingShowMethods) - EVT_MENU(self, ID_FILLING_SHOW_CLASS, self.OnFillingShowClass) - EVT_MENU(self, ID_FILLING_SHOW_DICT, self.OnFillingShowDict) - EVT_MENU(self, ID_FILLING_SHOW_DOC, self.OnFillingShowDoc) - EVT_MENU(self, ID_FILLING_SHOW_MODULE, self.OnFillingShowModule) - EVT_UPDATE_UI(self, ID_FILLING_AUTO_UPDATE, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_FILLING_SHOW_METHODS, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_FILLING_SHOW_CLASS, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_FILLING_SHOW_DICT, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_FILLING_SHOW_DOC, self.OnUpdateMenu) - EVT_UPDATE_UI(self, ID_FILLING_SHOW_MODULE, self.OnUpdateMenu) + self.Bind(EVT_MENU, self.OnExit, id=wx.ID_EXIT) + self.Bind(EVT_MENU, self.OnUndo, id=wx.ID_UNDO) + self.Bind(EVT_MENU, self.OnRedo, id=wx.ID_REDO) + self.Bind(EVT_MENU, self.OnCut, id=wx.ID_CUT) + self.Bind(EVT_MENU, self.OnCopy, id=wx.ID_COPY) + self.Bind(EVT_MENU, self.OnPaste, id=wx.ID_PASTE) + self.Bind(EVT_MENU, self.OnClear, id=wx.ID_CLEAR) + self.Bind(EVT_MENU, self.OnSelectAll, id=wx.ID_SELECTALL) + self.Bind(EVT_MENU, self.OnAbout, id=wx.ID_ABOUT) + self.Bind(EVT_MENU, self.OnAutoCompleteShow, ID_AUTOCOMP_SHOW) + self.Bind( + EVT_MENU, + self.OnAutoCompleteIncludeMagic, + id=ID_AUTOCOMP_INCLUDE_MAGIC, + ) + self.Bind( + EVT_MENU, + self.OnAutoCompleteIncludeSingle, + id=ID_AUTOCOMP_INCLUDE_SINGLE, + ) + self.Bind( + EVT_MENU, + self.OnAutoCompleteIncludeDouble, + id=ID_AUTOCOMP_INCLUDE_DOUBLE, + ) + self.Bind(EVT_MENU, self.OnCallTipsShow, id=ID_CALLTIPS_SHOW) + + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=wx.ID_UNDO) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=wx.ID_REDO) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=wx.ID_CUT) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=wx.ID_COPY) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=wx.ID_PASTE) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=wx.ID_CLEAR) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_SHOW) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_INCLUDE_MAGIC + ) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_INCLUDE_SINGLE + ) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_AUTOCOMP_INCLUDE_DOUBLE + ) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_CALLTIPS_SHOW) + + if hasattr(self, "crust"): + self.Bind( + EVT_MENU, self.OnFillingAutoUpdate, id=ID_FILLING_AUTO_UPDATE + ) + self.Bind( + EVT_MENU, self.OnFillingShowMethods, id=ID_FILLING_SHOW_METHODS + ) + self.Bind( + EVT_MENU, self.OnFillingShowClass, id=ID_FILLING_SHOW_CLASS + ) + self.Bind( + EVT_MENU, self.OnFillingShowDict, id=ID_FILLING_SHOW_DICT + ) + self.Bind(EVT_MENU, self.OnFillingShowDoc, id=ID_FILLING_SHOW_DOC) + self.Bind( + EVT_MENU, self.OnFillingShowModule, id=ID_FILLING_SHOW_MODULE + ) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FILLING_AUTO_UPDATE + ) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FILLING_SHOW_METHODS + ) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FILLING_SHOW_CLASS + ) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FILLING_SHOW_DICT + ) + self.Bind(EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FILLING_SHOW_DOC) + self.Bind( + EVT_UPDATE_UI, self.OnUpdateMenu, id=ID_FILLING_SHOW_MODULE + ) def OnExit(self, event): self.Close(True) @@ -1268,16 +1441,19 @@ def OnAbout(self, event): """Display an About PyCrust window.""" import sys - title = 'About PyCrust' - text = 'PyCrust %s\n\n' % VERSION + \ - 'Yet another Python shell, only flakier.\n\n' + \ - 'Half-baked by Patrick K. O\'Brien,\n' + \ - 'the other half is still in the oven.\n\n' + \ - 'Shell Revision: %s\n' % self.shell.revision + \ - 'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \ - 'Python Version: %s\n' % sys.version.split()[0] + \ - 'wxPython Version: %s\n' % wx.__version__ + \ - 'Platform: %s\n' % sys.platform + + title = "About PyCrust" + text = ( + "PyCrust %s\n\n" % VERSION + + "Yet another Python shell, only flakier.\n\n" + + "Half-baked by Patrick K. O'Brien,\n" + + "the other half is still in the oven.\n\n" + + "Shell Revision: %s\n" % self.shell.revision + + "Interpreter Revision: %s\n\n" % self.shell.interp.revision + + "Python Version: %s\n" % sys.version.split()[0] + + "wxPython Version: %s\n" % wx.__version__ + + "Platform: %s\n" % sys.platform + ) dialog = wxMessageDialog(self, text, title, wxOK | wxICON_INFORMATION) dialog.ShowModal() dialog.Destroy() diff -Nru python-pyface-6.1.2/pyface/wx/sized_panel.py python-pyface-7.4.0/pyface/wx/sized_panel.py --- python-pyface-6.1.2/pyface/wx/sized_panel.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/sized_panel.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,16 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A panel sized by a sizer. """ -# Major package imports. import wx @@ -37,22 +33,18 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # 'SizedPanel' interface. - ########################################################################### + # ------------------------------------------------------------------------ def Fit(self): """ Resizes the panel to match the sizer's minimal size. """ self.sizer.Fit(self) - return - def Layout(self): """ Lays out the sizer without changing the panel geometry. """ self.sizer.Layout() return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/spacer.py python-pyface-7.4.0/pyface/wx/spacer.py --- python-pyface-6.1.2/pyface/wx/spacer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spacer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ A panel used as a spacer. It is in a separate class so that you can set the background color in a @@ -20,7 +17,6 @@ """ -# Major package imports. import wx @@ -38,15 +34,13 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self): """ Create the widget! """ - #self.SetBackgroundColour("brown") + # self.SetBackgroundColour("brown") return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/spreadsheet/abstract_grid_view.py python-pyface-7.4.0/pyface/wx/spreadsheet/abstract_grid_view.py --- python-pyface-6.1.2/pyface/wx/spreadsheet/abstract_grid_view.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spreadsheet/abstract_grid_view.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,54 +1,17 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from numpy import arange +# Thanks for using Enthought open source! import wx -from wx.grid import Grid, PyGridTableBase -from wx.grid import PyGridCellRenderer -from wx.grid import GridCellTextEditor, GridCellStringRenderer +from wx.grid import Grid from wx.grid import GridCellFloatRenderer, GridCellFloatEditor -from wx.lib.mixins.grid import GridAutoEditMixin - - -class ComboboxFocusHandler(wx.EvtHandler): - """ Workaround for combobox focus problems in wx 2.6.""" - - # This is copied from enthought/pyface/grid.combobox_focus_handler.py. - # Since this was the only thing that pyface.wx needed from pyface, - # and it's a temporary workaround for an outdated version of wx, we're just - # copying it here instead of introducing a dependency on a large package. - - def __init__(self): - wx.EvtHandler.__init__(self) - wx.EVT_KILL_FOCUS(self, self._on_kill_focus) - return - - def _on_kill_focus(self, evt): - - # this is pretty egregious. somehow the combobox gives up focus - # as soon as it starts up, causing the grid to remove the editor. - # so we just don't let it give up focus. i suspect this will cause - # some other problem down the road, but it seems to work for now. - # fixme: remove this h*ck once the bug is fixed in wx. - editor = evt.GetEventObject() - if isinstance(editor, wx._controls.ComboBox) and \ - evt.GetWindow() is None: - return - evt.Skip() - return - class AbstractGridView(Grid): """ Enthought's default spreadsheet view. @@ -65,72 +28,61 @@ # We have things set up to edit on a single click - so we have to select # an initial cursor location that is off of the screen otherwise a cell # will be in edit mode as soon as the grid fires up. - self.moveTo = [1000,1] + self.moveTo = [1000, 1] self.edit = False # this seems like a busy idle ... - wx.EVT_IDLE(self, self.OnIdle) + self.Bind(wx.EVT_IDLE, self.OnIdle) # Enthought specific display controls ... self.init_labels() self.init_data_types() self.init_handlers() - wx.grid.EVT_GRID_EDITOR_CREATED(self, self._on_editor_created) - - return - - - # needed to handle problem in wx 2.6 with combobox cell editors - def _on_editor_created(self, evt): - - editor = evt.GetControl() - editor.PushEventHandler(ComboboxFocusHandler()) - - evt.Skip() - return + self.Bind(wx.grid.EVT_GRID_EDITOR_CREATED, self._on_editor_created) def init_labels(self): - self.SetLabelFont(wx.Font(self.GetFont().GetPointSize(), - wx.SWISS, wx.NORMAL, wx.BOLD)) + self.SetLabelFont( + wx.Font( + self.GetFont().GetPointSize(), wx.SWISS, wx.NORMAL, wx.BOLD + ) + ) self.SetGridLineColour("blue") self.SetColLabelAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) self.SetRowLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE) - return - def init_data_types(self): """ If the model says a cell is of a specified type, the grid uses the specific renderer and editor set in this method. """ - self.RegisterDataType("LogData", GridCellFloatRenderer(precision=3), GridCellFloatEditor()) - - return + self.RegisterDataType( + "LogData", + GridCellFloatRenderer(precision=3), + GridCellFloatEditor(), + ) def init_handlers(self): - wx.grid.EVT_GRID_CELL_LEFT_CLICK(self, self.OnCellLeftClick) - wx.grid.EVT_GRID_CELL_RIGHT_CLICK(self, self.OnCellRightClick) - wx.grid.EVT_GRID_CELL_LEFT_DCLICK(self, self.OnCellLeftDClick) - wx.grid.EVT_GRID_CELL_RIGHT_DCLICK(self, self.OnCellRightDClick) - - wx.grid.EVT_GRID_LABEL_LEFT_CLICK(self, self.OnLabelLeftClick) - wx.grid.EVT_GRID_LABEL_RIGHT_CLICK(self, self.OnLabelRightClick) - wx.grid.EVT_GRID_LABEL_LEFT_DCLICK(self, self.OnLabelLeftDClick) - wx.grid.EVT_GRID_LABEL_RIGHT_DCLICK(self, self.OnLabelRightDClick) - - wx.grid.EVT_GRID_ROW_SIZE(self, self.OnRowSize) - wx.grid.EVT_GRID_COL_SIZE(self, self.OnColSize) - - wx.grid.EVT_GRID_RANGE_SELECT(self, self.OnRangeSelect) - wx.grid.EVT_GRID_CELL_CHANGE(self, self.OnCellChange) - wx.grid.EVT_GRID_SELECT_CELL(self, self.OnSelectCell) - - wx.grid.EVT_GRID_EDITOR_SHOWN(self, self.OnEditorShown) - wx.grid.EVT_GRID_EDITOR_HIDDEN(self, self.OnEditorHidden) - wx.grid.EVT_GRID_EDITOR_CREATED(self, self.OnEditorCreated) - - return + self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnCellLeftClick) + self.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.OnCellRightClick) + self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.OnCellLeftDClick) + self.Bind(wx.grid.EVT_GRID_CELL_RIGHT_DCLICK, self.OnCellRightDClick) + + self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.OnLabelLeftClick) + self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.OnLabelRightClick) + self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.OnLabelLeftDClick) + self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_DCLICK, self.OnLabelRightDClick) + + self.Bind(wx.grid.EVT_GRID_ROW_SIZE, self.OnRowSize) + self.Bind(wx.grid.EVT_GRID_COL_SIZE, self.OnColSize) + + self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self.OnRangeSelect) + self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnCellChange) + self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnSelectCell) + + self.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnEditorShown) + self.Bind(wx.grid.EVT_GRID_EDITOR_HIDDEN, self.OnEditorHidden) + self.Bind(wx.grid.EVT_GRID_EDITOR_CREATED, self.OnEditorCreated) def SetColLabelsVisible(self, show=True): """ This only works if you 'hide' then 'show' the labels. @@ -140,7 +92,6 @@ self.SetColLabelSize(0) else: self.SetColLabelSize(self._default_col_label_size) - return def SetRowLabelsVisible(self, show=True): """ This only works if you 'hide' then 'show' the labels. @@ -150,7 +101,6 @@ self.SetRowLabelSize(0) else: self.SetRowLabelSize(self._default_row_label_size) - return def SetTable(self, table, *args): """ Some versions of wxPython do not return the correct @@ -176,7 +126,7 @@ evt.Skip() def OnCellRightClick(self, evt): - #print self.GetDefaultRendererForCell(evt.GetRow(), evt.GetCol()) + # print self.GetDefaultRendererForCell(evt.GetRow(), evt.GetCol()) evt.Skip() def OnCellLeftDClick(self, evt): @@ -206,7 +156,7 @@ evt.Skip() def OnRangeSelect(self, evt): - #if evt.Selecting(): + # if evt.Selecting(): # print "OnRangeSelect: top-left %s, bottom-right %s\n" % (evt.GetTopLeftCoords(), evt.GetBottomRightCoords()) evt.Skip() @@ -218,12 +168,12 @@ of a spreadsheet. See also self.OnSelectCell(). """ - if self.edit == True: + if self.edit: if self.CanEnableCellControl(): self.EnableCellEditControl() self.edit = False - if self.moveTo != None: + if self.moveTo is not None: self.SetGridCursor(self.moveTo[0], self.moveTo[1]) self.moveTo = None @@ -245,4 +195,3 @@ def OnEditorCreated(self, evt): evt.Skip() -#------------------------------------------------------------------------------- diff -Nru python-pyface-6.1.2/pyface/wx/spreadsheet/default_renderer.py python-pyface-7.4.0/pyface/wx/spreadsheet/default_renderer.py --- python-pyface-6.1.2/pyface/wx/spreadsheet/default_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spreadsheet/default_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,63 +1,53 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! -# -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -#------------------------------------------------------------------------------- # -#------------------------------------------------------------------------------- +# Thanks for using Enthought open source! -from __future__ import print_function -import types from string import atof import wx from wx.grid import PyGridCellRenderer -import six -#------------------------------------------------------------------------------- class DefaultRenderer(PyGridCellRenderer): """ This renderer provides the default representation of an Enthought spreadsheet cell. """ - selected_cells = wx.Brush(wx.Colour(255,255,200), wx.SOLID) + selected_cells = wx.Brush(wx.Colour(255, 255, 200), wx.SOLID) normal_cells = wx.Brush("white", wx.SOLID) - odd_cells = wx.Brush(wx.Colour(240,240,240), wx.SOLID) - error_cells = wx.Brush(wx.Colour(255,122,122), wx.SOLID) - warn_cells = wx.Brush(wx.Colour(255,242,0), wx.SOLID) + odd_cells = wx.Brush(wx.Colour(240, 240, 240), wx.SOLID) + error_cells = wx.Brush(wx.Colour(255, 122, 122), wx.SOLID) + warn_cells = wx.Brush(wx.Colour(255, 242, 0), wx.SOLID) def __init__(self, color="black", font="ARIAL", fontsize=8): PyGridCellRenderer.__init__(self) self.color = color self.foundary = font self.fontsize = fontsize - self.font = wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL,0, font) + self.font = wx.Font( + fontsize, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, font + ) def Clone(self): return DefaultRenderer(self.color, self.foundary, self.fontsize) def Draw(self, grid, attr, dc, rect, row, col, isSelected): - self.DrawBackground(grid, attr, dc, rect, row, col, isSelected); - self.DrawForeground(grid, attr, dc, rect, row, col, isSelected); + self.DrawBackground(grid, attr, dc, rect, row, col, isSelected) + self.DrawForeground(grid, attr, dc, rect, row, col, isSelected) dc.DestroyClippingRegion() - return def DrawBackground(self, grid, attr, dc, rect, row, col, isSelected): """ Erases whatever is already in the cell by drawing over it. """ # We have to set the clipping region on the grid's DC, # otherwise the text will spill over to the next cell - dc.SetClippingRect(rect) + dc.SetClippingRegion(rect) # overwrite anything currently in the cell ... dc.SetBackgroundMode(wx.SOLID) @@ -66,13 +56,12 @@ if isSelected: dc.SetBrush(DefaultRenderer.selected_cells) - elif row%2: + elif row % 2: dc.SetBrush(DefaultRenderer.normal_cells) else: dc.SetBrush(DefaultRenderer.odd_cells) dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) - return def DrawForeground(self, grid, attr, dc, rect, row, col, isSelected): """ Draws the cell (text) on top of the existing background color. @@ -81,16 +70,15 @@ text = grid.model.GetValue(row, col) dc.SetTextForeground(self.color) dc.SetFont(self.font) - dc.DrawText(self.FormatText(text), rect.x+1, rect.y+1) + dc.DrawText(self.FormatText(text), rect.x + 1, rect.y + 1) - self.DrawEllipses(grid, attr, dc, rect, row, col, isSelected); - return + self.DrawEllipses(grid, attr, dc, rect, row, col, isSelected) def FormatText(self, text): """ Formats numbers to 3 decimal places. """ try: - text = '%0.3f' % atof(text) + text = "%0.3f" % atof(text) except: pass @@ -100,21 +88,22 @@ """ Adds three dots "..." to indicate the cell is truncated. """ text = grid.model.GetValue(row, col) - if not isinstance(text, six.string_types): + if not isinstance(text, str): msg = 'Problem appending "..." to cell: %d %d' % (row, col) raise TypeError(msg) width, height = dc.GetTextExtent(text) - if width > rect.width-2: + if width > rect.width - 2: width, height = dc.GetTextExtent("...") - x = rect.x+1 + rect.width-2 - width - dc.DrawRectangle(x, rect.y+1, width+1, height) - dc.DrawText("...", x, rect.y+1) - return + x = rect.x + 1 + rect.width - 2 - width + dc.DrawRectangle(x, rect.y + 1, width + 1, height) + dc.DrawText("...", x, rect.y + 1) def GetBestSize88(self, grid, attr, dc, row, col): """ This crashes the app - hmmmm. """ size = PyGridCellRenderer.GetBestSize(self, grid, attr, dc, row, col) - print('-----------------------------', size) + print("-----------------------------", size) return size -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- diff -Nru python-pyface-6.1.2/pyface/wx/spreadsheet/font_renderer.py python-pyface-7.4.0/pyface/wx/spreadsheet/font_renderer.py --- python-pyface-6.1.2/pyface/wx/spreadsheet/font_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spreadsheet/font_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,20 +1,18 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import wx from .default_renderer import DefaultRenderer + class FontRenderer(DefaultRenderer): """Render data in the specified color and font and fontsize. """ @@ -23,16 +21,14 @@ text = grid.model.GetValue(row, col) dc.SetTextForeground(self.color) dc.SetFont(self.font) - dc.DrawText(text, rect.x+1, rect.y+1) - - return + dc.DrawText(text, rect.x + 1, rect.y + 1) def DrawOld(self, grid, attr, dc, rect, row, col, isSelected): # Here we draw text in a grid cell using various fonts # and colors. We have to set the clipping region on # the grid's DC, otherwise the text will spill over # to the next cell - dc.SetClippingRect(rect) + dc.SetClippingRegion(rect) # clear the background dc.SetBackgroundMode(wx.SOLID) @@ -58,7 +54,7 @@ dc.SetTextForeground(self.color) dc.SetFont(self.font) - dc.DrawText(text, rect.x+1, rect.y+1) + dc.DrawText(text, rect.x + 1, rect.y + 1) # Okay, now for the advanced class :) # Let's add three dots "..." @@ -66,18 +62,19 @@ # when the text is larger than the grid cell width, height = dc.GetTextExtent(text) - if width > rect.width-2: + if width > rect.width - 2: width, height = dc.GetTextExtent("...") - x = rect.x+1 + rect.width-2 - width - dc.DrawRectangle(x, rect.y+1, width+1, height) - dc.DrawText("...", x, rect.y+1) + x = rect.x + 1 + rect.width - 2 - width + dc.DrawRectangle(x, rect.y + 1, width + 1, height) + dc.DrawText("...", x, rect.y + 1) dc.DestroyClippingRegion() - return -class FontRendererFactory88: + +class FontRendererFactory88(object): """ I don't grok why this Factory (which I copied from the wx demo) was ever necessary? """ + def __init__(self, color, font, fontsize): """ (color, font, fontsize) -> set of a factory to generate diff -Nru python-pyface-6.1.2/pyface/wx/spreadsheet/__init__.py python-pyface-7.4.0/pyface/wx/spreadsheet/__init__.py --- python-pyface-6.1.2/pyface/wx/spreadsheet/__init__.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spreadsheet/__init__.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,13 +1,9 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! diff -Nru python-pyface-6.1.2/pyface/wx/spreadsheet/unit_renderer.py python-pyface-7.4.0/pyface/wx/spreadsheet/unit_renderer.py --- python-pyface-6.1.2/pyface/wx/spreadsheet/unit_renderer.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spreadsheet/unit_renderer.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,16 +1,13 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + import wx try: @@ -20,23 +17,21 @@ from .default_renderer import DefaultRenderer -class UnitRenderer(DefaultRenderer): +class UnitRenderer(DefaultRenderer): def DrawForeground(self, grid, attr, dc, rect, row, col, isSelected): dc.SetBackgroundMode(wx.TRANSPARENT) text = grid.model.GetValue(row, col) - #print 'Rendering ', row, col, text, text.__class__ + # print 'Rendering ', row, col, text, text.__class__ dc.SetFont(self.font) - dc.DrawText(text, rect.x+1, rect.y+1) - - return + dc.DrawText(text, rect.x + 1, rect.y + 1) def DrawBackground(self, grid, attr, dc, rect, row, col, isSelected): """ Erases whatever is already in the cell by drawing over it. """ # We have to set the clipping region on the grid's DC, # otherwise the text will spill over to the next cell - dc.SetClippingRect(rect) + dc.SetClippingRegion(rect) # overwrite anything currently in the cell ... dc.SetBackgroundMode(wx.SOLID) @@ -54,30 +49,31 @@ dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) return -#------------------------------------------------------------------------------- -class MultiUnitRenderer(DefaultRenderer): - def __init__( self, color="black", font="ARIAL", fontsize=8, - suppress_warnings=False): +# ------------------------------------------------------------------------------- + + +class MultiUnitRenderer(DefaultRenderer): + def __init__( + self, color="black", font="ARIAL", fontsize=8, suppress_warnings=False + ): self.suppress_warnings = suppress_warnings - DefaultRenderer.__init__( self, color, font, fontsize ) + DefaultRenderer.__init__(self, color, font, fontsize) def DrawForeground(self, grid, attr, dc, rect, row, col, isSelected): dc.SetBackgroundMode(wx.TRANSPARENT) text = grid.model.GetValue(row, col) dc.SetFont(self.font) - dc.DrawText(text, rect.x+1, rect.y+1) - - return + dc.DrawText(text, rect.x + 1, rect.y + 1) def DrawBackground(self, grid, attr, dc, rect, row, col, isSelected): """ Erases whatever is already in the cell by drawing over it. """ # We have to set the clipping region on the grid's DC, # otherwise the text will spill over to the next cell - dc.SetClippingRect(rect) + dc.SetClippingRegion(rect) # overwrite anything currently in the cell ... dc.SetBackgroundMode(wx.SOLID) @@ -96,12 +92,14 @@ # AI units of 'impedance ((kg/s)*(g/cc))' creates list of 3, not 2! try: - family, other_text = family[:-1].split('(') + family, other_text = family[:-1].split("(") except: - family, other_text = family[:-1].split(' ') + family, other_text = family[:-1].split(" ") if unit_parser: - other_unit = unit_parser.parse_unit(other_text, self.suppress_warnings) + other_unit = unit_parser.parse_unit( + other_text, self.suppress_warnings + ) dimensionally_equivalent = this_unit.can_convert(other_unit) else: other_unit = None diff -Nru python-pyface-6.1.2/pyface/wx/spreadsheet/virtual_model.py python-pyface-7.4.0/pyface/wx/spreadsheet/virtual_model.py --- python-pyface-6.1.2/pyface/wx/spreadsheet/virtual_model.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/spreadsheet/virtual_model.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,50 +1,54 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ -from __future__ import print_function -from wx.grid import Grid, PyGridTableBase, GridCellAttr, GridTableMessage, GridCellFloatRenderer -from wx.grid import GRIDTABLE_NOTIFY_ROWS_DELETED, GRIDTABLE_NOTIFY_ROWS_APPENDED -from wx.grid import GRIDTABLE_NOTIFY_COLS_DELETED, GRIDTABLE_NOTIFY_COLS_APPENDED +# Thanks for using Enthought open source! + + +from wx.grid import ( + PyGridTableBase, + GridCellAttr, + GridTableMessage, +) +from wx.grid import ( + GRIDTABLE_NOTIFY_ROWS_DELETED, + GRIDTABLE_NOTIFY_ROWS_APPENDED, +) +from wx.grid import ( + GRIDTABLE_NOTIFY_COLS_DELETED, + GRIDTABLE_NOTIFY_COLS_APPENDED, +) from wx.grid import GRIDTABLE_REQUEST_VIEW_GET_VALUES -from wx.grid import GRID_VALUE_BOOL -from wx import ALIGN_LEFT, ALIGN_CENTRE, Colour -from .default_renderer import DefaultRenderer class VirtualModel(PyGridTableBase): """ A custom wxGrid Table that expects a user supplied data source. THIS CLASS IS NOT LIMITED TO ONLY DISPLAYING LOG DATA! """ + def __init__(self, data, column_names): """data is currently a list of the form [(rowname, dictionary), dictionary.get(colname, None) returns the data for a cell """ - ##print 'Initializing virtual model' PyGridTableBase.__init__(self) self.set_data_source(data) self.colnames = column_names - #self.renderers = {"DEFAULT_RENDERER":DefaultRenderer()} - #self.editors = {} + # self.renderers = {"DEFAULT_RENDERER":DefaultRenderer()} + # self.editors = {} # we need to store the row length and col length to see if the table has changed size self._rows = self.GetNumberRows() self._cols = self.GetNumberCols() -#------------------------------------------------------------------------------- -# Implement/override the methods from PyGridTableBase -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Implement/override the methods from PyGridTableBase + # ------------------------------------------------------------------------------- def GetNumberCols(self): return len(self.colnames) @@ -65,12 +69,12 @@ return self._data[row][1].get(self.GetColLabelValue(col), "") def SetValue(self, row, col, value): - print('Setting value %d %d %s' % (row, col, value)) - print('Before ', self.GetValue(row, col)) + print("Setting value %d %d %s" % (row, col, value)) + print("Before ", self.GetValue(row, col)) self._data[row][1][self.GetColLabelValue(col)] = value - print('After ', self.GetValue(row, col)) + print("After ", self.GetValue(row, col)) - ''' def GetTypeName(self, row, col): + """ def GetTypeName(self, row, col): if col == 2 or col == 6: res = "MeasurementUnits" elif col == 7: @@ -78,11 +82,11 @@ else: res = self.base_GetTypeName(row, col) # print 'asked for type of col ', col, ' ' ,res - return res''' + return res""" -#------------------------------------------------------------------------------- -# Accessors for the Enthought data model (a dict of dicts) -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Accessors for the Enthought data model (a dict of dicts) + # ------------------------------------------------------------------------------- def get_data_source(self): """ The data structure we provide the data in. """ @@ -92,26 +96,35 @@ self._data = source return -#------------------------------------------------------------------------------- -# Methods controlling updating and editing of cells in grid -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Methods controlling updating and editing of cells in grid + # ------------------------------------------------------------------------------- def ResetView(self, grid): """ (wxGrid) -> Reset the grid view. Call this to update the grid if rows and columns have been added or deleted """ - ##print 'VirtualModel.reset_view' grid.BeginBatch() for current, new, delmsg, addmsg in [ - (self._rows, self.GetNumberRows(), GRIDTABLE_NOTIFY_ROWS_DELETED, GRIDTABLE_NOTIFY_ROWS_APPENDED), - (self._cols, self.GetNumberCols(), GRIDTABLE_NOTIFY_COLS_DELETED, GRIDTABLE_NOTIFY_COLS_APPENDED), + ( + self._rows, + self.GetNumberRows(), + GRIDTABLE_NOTIFY_ROWS_DELETED, + GRIDTABLE_NOTIFY_ROWS_APPENDED, + ), + ( + self._cols, + self.GetNumberCols(), + GRIDTABLE_NOTIFY_COLS_DELETED, + GRIDTABLE_NOTIFY_COLS_APPENDED, + ), ]: if new < current: - msg = GridTableMessage(self,delmsg,new,current-new) + msg = GridTableMessage(self, delmsg, new, current - new) grid.ProcessTableMessage(msg) elif new > current: - msg = GridTableMessage(self,addmsg,new-current) + msg = GridTableMessage(self, addmsg, new - current) grid.ProcessTableMessage(msg) self.UpdateValues(grid) grid.EndBatch() @@ -127,24 +140,22 @@ grid.AdjustScrollbars() grid.ForceRefresh() - def UpdateValues(self, grid): """Update all displayed values""" # This sends an event to the grid table to update all of the values msg = GridTableMessage(self, GRIDTABLE_REQUEST_VIEW_GET_VALUES) grid.ProcessTableMessage(msg) - def GetAttr88(self, row, col, someExtraParameter ): - print('Overridden GetAttr ', row, col) + def GetAttr88(self, row, col, someExtraParameter): + print("Overridden GetAttr ", row, col) """Part of a workaround to avoid use of attributes, queried by _PropertyGrid's IsCurrentCellReadOnly""" - #property = self.GetPropertyForCoordinate( row, col ) - #object = self.GetObjectForCoordinate( row, col ) - #if property.ReadOnly( object ): + # property = self.GetPropertyForCoordinate( row, col ) + # object = self.GetObjectForCoordinate( row, col ) + # if property.ReadOnly( object ): attr = GridCellAttr() - attr.SetReadOnly( 1 ) + attr.SetReadOnly(1) return attr - #return None - + # return None def _updateColAttrs88(self, grid): """ @@ -153,18 +164,18 @@ """ for col, colname in enumerate(self.colnames): attr = GridCellAttr() - #attr.SetAlignment(ALIGN_LEFT, ALIGN_CENTRE) + # attr.SetAlignment(ALIGN_LEFT, ALIGN_CENTRE) if colname in self.renderers: # renderer = self.plugins[colname](self) renderer = self.renderers[colname] - #if renderer.colSize: + # if renderer.colSize: # grid.SetColSize(col, renderer.colSize) - #if renderer.rowSize: + # if renderer.rowSize: # grid.SetDefaultRowSize(renderer.rowSize) # attr.SetReadOnly(False) # attr.SetRenderer(renderer) else: - renderer = self.renderers["DEFAULT_RENDERER"] # .Clone() + renderer = self.renderers["DEFAULT_RENDERER"] # .Clone() attr.SetRenderer(renderer) @@ -180,20 +191,20 @@ grid.SetColAttr(col, attr) return -#------------------------------------------------------------------------------ -# code to manipulate the table (non wx related) -#------------------------------------------------------------------------------ + # ------------------------------------------------------------------------------ + # code to manipulate the table (non wx related) + # ------------------------------------------------------------------------------ def AppendRow(self, row): """ Append a tupe containing (name, data) """ name, data = row - print('Appending ', name) + print("Appending ", name) self._data.append(row) - '''entry = {} + """entry = {} for name in self.colnames: entry[name] = "Appended_%i"%row - return''' + return""" def DeleteCols88(self, cols): """ @@ -207,7 +218,7 @@ cols = cols[:] cols.sort() for i in cols: - self.colnames.pop(i-deleteCount) + self.colnames.pop(i - deleteCount) # we need to advance the delete count # to make sure we delete the right columns deleteCount += 1 @@ -216,7 +227,7 @@ def DeleteRow(self, row): name, data = row - print('Deleting ', name) + print("Deleting ", name) self._data.remove(row) def DeleteRows88(self, rows): @@ -228,7 +239,7 @@ rows = rows[:] rows.sort() for i in rows: - self._data.pop(i-deleteCount) + self._data.pop(i - deleteCount) # we need to advance the delete count # to make sure we delete the right rows deleteCount += 1 diff -Nru python-pyface-6.1.2/pyface/wx/switcher.py python-pyface-7.4.0/pyface/wx/switcher.py --- python-pyface-6.1.2/pyface/wx/switcher.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/switcher.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,27 +1,24 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Classes to provide a switcher. """ # paranoid checkin in case Mr Chilver's changes break the distribution code # todo - it wasn't paranoia - reconcile this with lazy_switcher.py at some point -# Major package imports. + import wx from wx.lib.scrolledpanel import ScrolledPanel as wxScrolledPanel -# Enthought library imports. + from traits.api import HasTraits @@ -30,25 +27,25 @@ __traits__ = { # The index of the selected 'page'. - 'selected' : -1, + "selected": -1 } def __init__(self): """ Creates a new switcher model. """ # The items to display in the switcher control. - self.items = [] # (str label, object value) + self.items = [] # (str label, object value) return - ########################################################################### + # ------------------------------------------------------------------------ # 'SwitcherModel' interface. - ########################################################################### + # ------------------------------------------------------------------------ def create_page(self, parent, index): """ Creates a page for the switcher panel. """ - raise NotImplementedError + raise NotImplementedError() class SwitcherControl(wx.Panel): @@ -70,24 +67,24 @@ self._create_widget(model, label) # Listen for when the selected item in the model is changed. - model.on_trait_change(self._on_selected_changed, 'selected') + model.observe(self._on_selected_changed, "selected") return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_selected_changed(self, selected): + def _on_selected_changed(self, event): """ Called when the selected item in the model is changed. """ - + selected = event.new self.combo.SetSelection(selected) return - ########################################################################### + # ------------------------------------------------------------------------ # wx event handlers. - ########################################################################### + # ------------------------------------------------------------------------ def _on_combobox(self, event): """ Called when the combo box selection is changed. """ @@ -99,9 +96,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self, model, label): """ Creates the widget. """ @@ -109,7 +106,6 @@ self.sizer = sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self.SetAutoLayout(True) - ##self.SetBackgroundColour("light grey") # Switcher combo. sizer.Add(self._combo(self, model, label), 1, wx.EXPAND) @@ -117,8 +113,6 @@ # Resize the panel to match the sizer's minimal size. sizer.Fit(self) - return - def _combo(self, parent, model, label): """ Creates the switcher combo box. """ @@ -131,9 +125,7 @@ # Combo. self.combo = combo = wx.ComboBox( - parent, - -1, - style=wx.CB_DROPDOWN | wx.CB_READONLY + parent, -1, style=wx.CB_DROPDOWN | wx.CB_READONLY ) sizer.Add(combo, 1, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 5) @@ -144,7 +136,7 @@ combo.Append(name, data) # Listen for changes to the selected item. - wx.EVT_COMBOBOX(self, combo.GetId(), self._on_combobox) + combo.Bind(wx.EVT_COMBOBOX, self._on_combobox) # If the model's selected variable has been set ... if model.selected != -1: @@ -178,24 +170,24 @@ self._create_widget(model, label) # Listen for when the selected item in the model is changed. - model.on_trait_change(self._on_selected_changed, 'selected') + model.observe(self._on_selected_changed, "selected") return - ########################################################################### + # ------------------------------------------------------------------------ # Trait event handlers. - ########################################################################### + # ------------------------------------------------------------------------ - def _on_selected_changed(self, selected): + def _on_selected_changed(self, event): """ Called when the selected item in the model is changed. """ - + selected = event.new self._show_page(selected) return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self, model, label): """ Creates the widget. """ @@ -213,8 +205,6 @@ # Resize the panel to match the sizer's minimal size. sizer.Fit(self) - return - def _show_page(self, index): """ Shows the page at the specified index. """ @@ -242,8 +232,6 @@ # dimension. self.sizer.Layout() - return - class Switcher(wx.Panel): """ A switcher. """ @@ -261,9 +249,9 @@ return - ########################################################################### + # ------------------------------------------------------------------------ # Private interface. - ########################################################################### + # ------------------------------------------------------------------------ def _create_widget(self, model, label): """ Creates the widget. """ @@ -284,5 +272,3 @@ sizer.Fit(self) return - -#### EOF ###################################################################### diff -Nru python-pyface-6.1.2/pyface/wx/util/font_helper.py python-pyface-7.4.0/pyface/wx/util/font_helper.py --- python-pyface-6.1.2/pyface/wx/util/font_helper.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/wx/util/font_helper.py 2022-02-01 12:21:15.000000000 +0000 @@ -1,33 +1,27 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2005, Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. # # This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! # -# Author: Enthought, Inc. -# Description: -#------------------------------------------------------------------------------ +# Thanks for using Enthought open source! + """ Utility functions for working with wx Fonts. """ -# Major package imports. import wx def new_font_like(font, **kw): """ Creates a new font, like another one, only different. Maybe. """ - point_size = kw.get('point_size', font.GetPointSize()) - family = kw.get('family', font.GetFamily()) - style = kw.get('style', font.GetStyle()) - weight = kw.get('weight', font.GetWeight()) - underline = kw.get('underline', font.GetUnderlined()) - face_name = kw.get('face_name', font.GetFaceName()) + point_size = kw.get("point_size", font.GetPointSize()) + family = kw.get("family", font.GetFamily()) + style = kw.get("style", font.GetStyle()) + weight = kw.get("weight", font.GetWeight()) + underline = kw.get("underline", font.GetUnderlined()) + face_name = kw.get("face_name", font.GetFaceName()) return wx.Font(point_size, family, style, weight, underline, face_name) - -### EOF ####################################################################### diff -Nru python-pyface-6.1.2/pyface/xrc_dialog.py python-pyface-7.4.0/pyface/xrc_dialog.py --- python-pyface-6.1.2/pyface/xrc_dialog.py 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/pyface/xrc_dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# Copyright (c) 2017, Enthought, Inc. -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in enthought/LICENSE.txt and may be redistributed only -# under the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# Thanks for using Enthought open source! - -import logging - -logger = logging.getLogger(__name__) -logger.warning( - 'DEPRECATED: pyface.xrc_dialog, use pyface.ui.wx.xrc_dialog instead. ' - 'Will be removed in Pyface 7.') - -from pyface.ui.wx.xrc_dialog import * diff -Nru python-pyface-6.1.2/pyface.egg-info/PKG-INFO python-pyface-7.4.0/pyface.egg-info/PKG-INFO --- python-pyface-6.1.2/pyface.egg-info/PKG-INFO 2019-07-22 09:15:04.000000000 +0000 +++ python-pyface-7.4.0/pyface.egg-info/PKG-INFO 2022-02-02 10:48:07.000000000 +0000 @@ -1,41 +1,59 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: pyface -Version: 6.1.2 +Version: 7.4.0 Summary: traits-capable windowing framework Home-page: http://docs.enthought.com/pyface -Author: ETS Developers -Author-email: enthought-dev@enthought.com +Author: David C. Morrill, et al. +Author-email: dmorrill@enthought.com +Maintainer: ETS Developers +Maintainer-email: enthought-dev@enthought.com License: BSD Download-URL: https://github.com/enthought/pyface -Description-Content-Type: UNKNOWN Description: ========================================== - pyface: traits-capable windowing framework + Pyface: Traits-capable Windowing Framework ========================================== - .. image:: https://travis-ci.org/enthought/pyface.svg?branch=master - :target: https://travis-ci.org/enthought/pyface - - .. image:: https://ci.appveyor.com/api/projects/status/68nfb049cdq9wqd1/branch/master?svg=true - :target: https://ci.appveyor.com/project/EnthoughtOSS/pyface/branch/master - - .. image:: https://codecov.io/github/enthought/pyface/coverage.svg?branch=master - :target: https://codecov.io/github/enthought/pyface?branch=master - - - The pyface project contains a toolkit-independent GUI abstraction layer, + The Pyface project contains a toolkit-independent GUI abstraction layer, which is used to support the "visualization" features of the Traits package. Thus, you can write code in terms of the Traits API (views, items, editors, - etc.), and let pyface and your selected toolkit and back-end take care of + etc.), and let Pyface and your selected toolkit and back-end take care of the details of displaying them. The following GUI backends are supported: - - wxPython - - PyQt - - PySide + - PySide2 (stable) and PySide6 (experimental) + - PyQt5 (stable) and PyQt6 (in development) + - wxPython 4 (experimental) + + Installation + ------------ + + GUI backends are marked as optional dependencies of Pyface. Some features + or infrastructures may also require additional dependencies. + + To install with PySide2 dependencies:: + + $ pip install pyface[pyside2] + + To install with PySide6 dependencies (experimental):: + + $ pip install pyface[pyside6] - **Warning:** The default toolkit if none is supplied is ``qt4``. - This changed from ``wx`` in Pyface 5.0.. + To install with PyQt5 dependencies:: + + $ pip install pyface[pyqt5] + + To install with wxPython4 dependencies (experimental):: + + $ pip install pyface[wx] + + ``pillow`` is an optional dependency for the PILImage class:: + + $ pip install pyface[pillow] + + To install with additional test dependencies:: + + $ pip install pyface[test] Documentation ------------- @@ -49,13 +67,15 @@ Pyface depends on: - * a GUI toolkit: one of PySide, PyQt or WxPython - * `Traits `_ + * a GUI toolkit as described above + * Pygments for syntax highlighting in the Qt code editor widget. - * some widgets may have additional optional dependencies. + * some widgets may have additional optional dependencies such as NumPy or + Pillow. + Platform: Windows Platform: Linux @@ -72,10 +92,20 @@ Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Provides-Extra: wx +Provides-Extra: pyqt +Provides-Extra: pyqt5 +Provides-Extra: pyqt6 +Provides-Extra: pyside2 +Provides-Extra: pyside6 +Provides-Extra: pillow +Provides-Extra: test diff -Nru python-pyface-6.1.2/pyface.egg-info/requires.txt python-pyface-7.4.0/pyface.egg-info/requires.txt --- python-pyface-6.1.2/pyface.egg-info/requires.txt 2019-07-22 09:15:04.000000000 +0000 +++ python-pyface-7.4.0/pyface.egg-info/requires.txt 2022-02-02 10:48:07.000000000 +0000 @@ -1,17 +1,38 @@ -traits +traits>=6.2 + +[:python_version < "3.8"] +importlib-metadata>=3.6.0 + +[:python_version < "3.9"] +importlib-resources>=1.1.0 + +[pillow] +pillow [pyqt] pyqt>=4.10 pygments [pyqt5] -pyqt>=5 +pyqt5 +pygments + +[pyqt6] +pyqt6 +pygments + +[pyside2] +pyside2 +shiboken2 pygments -[pyside] -pyside>=1.2 +[pyside6] +pyside6 pygments +[test] +packaging + [wx] -wxpython>=2.8.10 +wxpython>=4 numpy diff -Nru python-pyface-6.1.2/pyface.egg-info/SOURCES.txt python-pyface-7.4.0/pyface.egg-info/SOURCES.txt --- python-pyface-6.1.2/pyface.egg-info/SOURCES.txt 2019-07-22 09:15:04.000000000 +0000 +++ python-pyface-7.4.0/pyface.egg-info/SOURCES.txt 2022-02-02 10:48:07.000000000 +0000 @@ -1,19 +1,13 @@ -CHANGES.txt +LICENSE-CC-BY-3.0.txt LICENSE.txt MANIFEST.in README.rst -TODO.txt -appveyor-clean-cache.txt -ci-src-requirements.txt -dev_requirements.txt -doc-src-requirements.txt image_LICENSE.txt image_LICENSE_Eclipse.txt image_LICENSE_Nuvola.txt image_LICENSE_OOo.txt +setup.cfg setup.py -tox-requirements.txt -docs/CHANGES.txt docs/DockWindow.ppt docs/DockWindowFeature.doc docs/DockWindowFeature.pdf @@ -21,12 +15,19 @@ docs/source/applications.rst docs/source/changelog.rst docs/source/conf.py +docs/source/data_view.rst docs/source/fields.rst +docs/source/image_types.rst docs/source/index.rst docs/source/overview.rst +docs/source/standard_dialogs.rst docs/source/submodules.rst +docs/source/testing.rst docs/source/timer.rst docs/source/toolkits.rst +docs/source/traits.rst +docs/source/undo.rst +docs/source/widgets.rst docs/source/_static/default.css docs/source/_static/e-logo-rev.jpg docs/source/_static/et.png @@ -46,18 +47,12 @@ docs/source/api/pyface.action.menu_bar_manager.rst docs/source/api/pyface.action.menu_manager.rst docs/source/api/pyface.action.rst +docs/source/api/pyface.action.schema.action_manager_builder.rst +docs/source/api/pyface.action.schema.api.rst +docs/source/api/pyface.action.schema.rst +docs/source/api/pyface.action.schema.schema.rst +docs/source/api/pyface.action.schema.schema_addition.rst docs/source/api/pyface.action.status_bar_manager.rst -docs/source/api/pyface.action.tests.rst -docs/source/api/pyface.action.tests.test_action.rst -docs/source/api/pyface.action.tests.test_action_controller.rst -docs/source/api/pyface.action.tests.test_action_event.rst -docs/source/api/pyface.action.tests.test_action_item.rst -docs/source/api/pyface.action.tests.test_action_manager.rst -docs/source/api/pyface.action.tests.test_field_action.rst -docs/source/api/pyface.action.tests.test_group.rst -docs/source/api/pyface.action.tests.test_gui_application_action.rst -docs/source/api/pyface.action.tests.test_listening_action.rst -docs/source/api/pyface.action.tests.test_traitsui_widget_action.rst docs/source/api/pyface.action.tool_bar_manager.rst docs/source/api/pyface.action.tool_palette_manager.rst docs/source/api/pyface.action.traitsui_widget_action.rst @@ -65,15 +60,48 @@ docs/source/api/pyface.api.rst docs/source/api/pyface.application.rst docs/source/api/pyface.application_window.rst +docs/source/api/pyface.array_image.rst docs/source/api/pyface.base_toolkit.rst docs/source/api/pyface.beep.rst docs/source/api/pyface.clipboard.rst +docs/source/api/pyface.color.rst +docs/source/api/pyface.color_dialog.rst docs/source/api/pyface.confirmation_dialog.rst docs/source/api/pyface.constant.rst +docs/source/api/pyface.data_view.abstract_data_exporter.rst +docs/source/api/pyface.data_view.abstract_data_model.rst +docs/source/api/pyface.data_view.abstract_value_type.rst +docs/source/api/pyface.data_view.api.rst +docs/source/api/pyface.data_view.data_formats.rst +docs/source/api/pyface.data_view.data_models.api.rst +docs/source/api/pyface.data_view.data_models.array_data_model.rst +docs/source/api/pyface.data_view.data_models.data_accessors.rst +docs/source/api/pyface.data_view.data_models.row_table_data_model.rst +docs/source/api/pyface.data_view.data_models.rst +docs/source/api/pyface.data_view.data_view_errors.rst +docs/source/api/pyface.data_view.data_view_widget.rst +docs/source/api/pyface.data_view.data_wrapper.rst +docs/source/api/pyface.data_view.exporters.api.rst +docs/source/api/pyface.data_view.exporters.item_exporter.rst +docs/source/api/pyface.data_view.exporters.row_exporter.rst +docs/source/api/pyface.data_view.exporters.rst +docs/source/api/pyface.data_view.i_data_view_widget.rst +docs/source/api/pyface.data_view.i_data_wrapper.rst +docs/source/api/pyface.data_view.index_manager.rst +docs/source/api/pyface.data_view.rst +docs/source/api/pyface.data_view.value_types.api.rst +docs/source/api/pyface.data_view.value_types.bool_value.rst +docs/source/api/pyface.data_view.value_types.color_value.rst +docs/source/api/pyface.data_view.value_types.constant_value.rst +docs/source/api/pyface.data_view.value_types.editable_value.rst +docs/source/api/pyface.data_view.value_types.enum_value.rst +docs/source/api/pyface.data_view.value_types.no_value.rst +docs/source/api/pyface.data_view.value_types.numeric_value.rst +docs/source/api/pyface.data_view.value_types.rst +docs/source/api/pyface.data_view.value_types.text_value.rst docs/source/api/pyface.dialog.rst docs/source/api/pyface.directory_dialog.rst docs/source/api/pyface.drop_handler.rst -docs/source/api/pyface.expandable_header.rst docs/source/api/pyface.expandable_panel.rst docs/source/api/pyface.fields.api.rst docs/source/api/pyface.fields.combo_field.rst @@ -81,32 +109,40 @@ docs/source/api/pyface.fields.i_field.rst docs/source/api/pyface.fields.i_spin_field.rst docs/source/api/pyface.fields.i_text_field.rst +docs/source/api/pyface.fields.i_time_field.rst +docs/source/api/pyface.fields.i_toggle_field.rst docs/source/api/pyface.fields.rst docs/source/api/pyface.fields.spin_field.rst -docs/source/api/pyface.fields.tests.field_mixin.rst -docs/source/api/pyface.fields.tests.rst -docs/source/api/pyface.fields.tests.test_combo_field.rst -docs/source/api/pyface.fields.tests.test_spin_field.rst -docs/source/api/pyface.fields.tests.test_text_field.rst docs/source/api/pyface.fields.text_field.rst +docs/source/api/pyface.fields.time_field.rst +docs/source/api/pyface.fields.toggle_field.rst docs/source/api/pyface.file_dialog.rst docs/source/api/pyface.filter.rst +docs/source/api/pyface.font.rst +docs/source/api/pyface.font_dialog.rst docs/source/api/pyface.gui.rst docs/source/api/pyface.gui_application.rst docs/source/api/pyface.heading_text.rst docs/source/api/pyface.i_about_dialog.rst docs/source/api/pyface.i_application_window.rst docs/source/api/pyface.i_clipboard.rst +docs/source/api/pyface.i_color_dialog.rst docs/source/api/pyface.i_confirmation_dialog.rst docs/source/api/pyface.i_dialog.rst docs/source/api/pyface.i_directory_dialog.rst docs/source/api/pyface.i_drop_handler.rst docs/source/api/pyface.i_file_dialog.rst +docs/source/api/pyface.i_font_dialog.rst docs/source/api/pyface.i_gui.rst docs/source/api/pyface.i_heading_text.rst +docs/source/api/pyface.i_image.rst docs/source/api/pyface.i_image_cache.rst docs/source/api/pyface.i_image_resource.rst +docs/source/api/pyface.i_layered_panel.rst +docs/source/api/pyface.i_layout_item.rst +docs/source/api/pyface.i_layout_widget.rst docs/source/api/pyface.i_message_dialog.rst +docs/source/api/pyface.i_pil_image.rst docs/source/api/pyface.i_progress_dialog.rst docs/source/api/pyface.i_python_editor.rst docs/source/api/pyface.i_python_shell.rst @@ -118,14 +154,13 @@ docs/source/api/pyface.i_window.rst docs/source/api/pyface.image.image.rst docs/source/api/pyface.image.rst -docs/source/api/pyface.image_button.rst docs/source/api/pyface.image_cache.rst -docs/source/api/pyface.image_list.rst docs/source/api/pyface.image_resource.rst docs/source/api/pyface.image_widget.rst docs/source/api/pyface.ipython_widget.rst docs/source/api/pyface.key_pressed_event.rst docs/source/api/pyface.layered_panel.rst +docs/source/api/pyface.layout_widget.rst docs/source/api/pyface.list_box.rst docs/source/api/pyface.list_box_model.rst docs/source/api/pyface.mdi_application_window.rst @@ -133,6 +168,7 @@ docs/source/api/pyface.message_dialog.rst docs/source/api/pyface.mimedata.rst docs/source/api/pyface.multi_toolbar_window.rst +docs/source/api/pyface.pil_image.rst docs/source/api/pyface.preference.api.rst docs/source/api/pyface.preference.preference_dialog.rst docs/source/api/pyface.preference.preference_node.rst @@ -198,62 +234,17 @@ docs/source/api/pyface.tasks.task_window_backend.rst docs/source/api/pyface.tasks.task_window_layout.rst docs/source/api/pyface.tasks.tasks_application.rst -docs/source/api/pyface.tasks.tests.rst -docs/source/api/pyface.tasks.tests.test_action_manager_builder.rst -docs/source/api/pyface.tasks.tests.test_dock_pane_toggle_group.rst -docs/source/api/pyface.tasks.tests.test_editor_area_pane.rst -docs/source/api/pyface.tasks.tests.test_enaml_dock_pane.rst -docs/source/api/pyface.tasks.tests.test_enaml_editor.rst -docs/source/api/pyface.tasks.tests.test_enaml_task_pane.rst -docs/source/api/pyface.tasks.tests.test_task_layout.rst -docs/source/api/pyface.tasks.tests.test_task_window.rst -docs/source/api/pyface.tasks.tests.test_tasks_application.rst -docs/source/api/pyface.tasks.tests.test_topological_sort.rst docs/source/api/pyface.tasks.topological_sort.rst docs/source/api/pyface.tasks.traits_dock_pane.rst docs/source/api/pyface.tasks.traits_editor.rst docs/source/api/pyface.tasks.traits_task_pane.rst -docs/source/api/pyface.tests.python_shell_script.rst -docs/source/api/pyface.tests.rst -docs/source/api/pyface.tests.test_about_dialog.rst -docs/source/api/pyface.tests.test_application.rst -docs/source/api/pyface.tests.test_application_window.rst -docs/source/api/pyface.tests.test_base_toolkit.rst -docs/source/api/pyface.tests.test_beep.rst -docs/source/api/pyface.tests.test_clipboard.rst -docs/source/api/pyface.tests.test_confirmation_dialog.rst -docs/source/api/pyface.tests.test_dialog.rst -docs/source/api/pyface.tests.test_directory_dialog.rst -docs/source/api/pyface.tests.test_file_dialog.rst -docs/source/api/pyface.tests.test_gui_application.rst -docs/source/api/pyface.tests.test_heading_text.rst -docs/source/api/pyface.tests.test_image_cache.rst -docs/source/api/pyface.tests.test_image_resource.rst -docs/source/api/pyface.tests.test_message_dialog.rst -docs/source/api/pyface.tests.test_new_toolkit.init.rst -docs/source/api/pyface.tests.test_new_toolkit.rst -docs/source/api/pyface.tests.test_new_toolkit.widget.rst -docs/source/api/pyface.tests.test_progress_dialog.rst -docs/source/api/pyface.tests.test_python_editor.rst -docs/source/api/pyface.tests.test_python_shell.rst -docs/source/api/pyface.tests.test_resource_manager.rst -docs/source/api/pyface.tests.test_single_choice_dialog.rst -docs/source/api/pyface.tests.test_splash_screen.rst -docs/source/api/pyface.tests.test_splash_screen_log_handler.rst -docs/source/api/pyface.tests.test_split_application_window.rst -docs/source/api/pyface.tests.test_split_dialog.rst -docs/source/api/pyface.tests.test_split_panel.rst -docs/source/api/pyface.tests.test_system_metrics.rst -docs/source/api/pyface.tests.test_toolkit.rst -docs/source/api/pyface.tests.test_ui_traits.rst -docs/source/api/pyface.tests.test_widget.rst -docs/source/api/pyface.tests.test_window.rst +docs/source/api/pyface.testing.layout_widget_mixin.rst +docs/source/api/pyface.testing.rst +docs/source/api/pyface.testing.widget_mixin.rst docs/source/api/pyface.timer.api.rst docs/source/api/pyface.timer.do_later.rst docs/source/api/pyface.timer.i_timer.rst docs/source/api/pyface.timer.rst -docs/source/api/pyface.timer.tests.rst -docs/source/api/pyface.timer.tests.test_timer.rst docs/source/api/pyface.timer.timer.rst docs/source/api/pyface.toolkit.rst docs/source/api/pyface.tree.api.rst @@ -269,20 +260,31 @@ docs/source/api/pyface.tree.tree.rst docs/source/api/pyface.tree.tree_model.rst docs/source/api/pyface.ui_traits.rst -docs/source/api/pyface.util.font_helper.rst -docs/source/api/pyface.util.grid.api.rst -docs/source/api/pyface.util.grid.grid.rst -docs/source/api/pyface.util.grid.grid_column.rst -docs/source/api/pyface.util.grid.grid_model.rst -docs/source/api/pyface.util.grid.grid_row.rst -docs/source/api/pyface.util.grid.rst +docs/source/api/pyface.undo.abstract_command.rst +docs/source/api/pyface.undo.action.abstract_command_stack_action.rst +docs/source/api/pyface.undo.action.api.rst +docs/source/api/pyface.undo.action.command_action.rst +docs/source/api/pyface.undo.action.redo_action.rst +docs/source/api/pyface.undo.action.rst +docs/source/api/pyface.undo.action.undo_action.rst +docs/source/api/pyface.undo.api.rst +docs/source/api/pyface.undo.command_stack.rst +docs/source/api/pyface.undo.i_command.rst +docs/source/api/pyface.undo.i_command_stack.rst +docs/source/api/pyface.undo.i_undo_manager.rst +docs/source/api/pyface.undo.rst +docs/source/api/pyface.undo.undo_manager.rst +docs/source/api/pyface.util.color_helpers.rst +docs/source/api/pyface.util.color_parser.rst +docs/source/api/pyface.util.event_loop_helper.rst +docs/source/api/pyface.util.font_parser.rst +docs/source/api/pyface.util.gui_test_assistant.rst docs/source/api/pyface.util.guisupport.rst docs/source/api/pyface.util.id_helper.rst -docs/source/api/pyface.util.python_stc.rst +docs/source/api/pyface.util.image_helpers.rst +docs/source/api/pyface.util.modal_dialog_tester.rst docs/source/api/pyface.util.rst docs/source/api/pyface.util.testing.rst -docs/source/api/pyface.util.tests.rst -docs/source/api/pyface.util.tests.test_id_helper.rst docs/source/api/pyface.viewer.api.rst docs/source/api/pyface.viewer.column_provider.rst docs/source/api/pyface.viewer.content_provider.rst @@ -353,8 +355,6 @@ docs/source/api/pyface.workbench.perspective.rst docs/source/api/pyface.workbench.perspective_item.rst docs/source/api/pyface.workbench.rst -docs/source/api/pyface.workbench.tests.rst -docs/source/api/pyface.workbench.tests.test_workbench_window.rst docs/source/api/pyface.workbench.traits_ui_editor.rst docs/source/api/pyface.workbench.traits_ui_view.rst docs/source/api/pyface.workbench.user_perspective_manager.rst @@ -364,23 +364,22 @@ docs/source/api/pyface.workbench.workbench_window.rst docs/source/api/pyface.workbench.workbench_window_layout.rst docs/source/api/pyface.workbench.workbench_window_memento.rst -docs/source/api/pyface.xrc_dialog.rst -docs/source/sphinxext/__init__.py -docs/source/sphinxext/refactordoc/AUTHORS.txt -docs/source/sphinxext/refactordoc/CHANGELOG.txt -docs/source/sphinxext/refactordoc/LICENSE.txt -docs/source/sphinxext/refactordoc/README.txt -docs/source/sphinxext/refactordoc/TODO.txt -docs/source/sphinxext/refactordoc/__init__.py -docs/source/sphinxext/refactordoc/base_doc.py -docs/source/sphinxext/refactordoc/class_doc.py -docs/source/sphinxext/refactordoc/enaml_decl_doc.py -docs/source/sphinxext/refactordoc/fields.py -docs/source/sphinxext/refactordoc/function_doc.py -docs/source/sphinxext/refactordoc/line_functions.py +docs/source/examples/data_model_indices.py +docs/source/examples/dict_data_model.py +docs/source/images/about_dialog.png +docs/source/images/color_dialog.png +docs/source/images/column_selection_type.png +docs/source/images/confirmation_dialog.png +docs/source/images/data_view_indices.png +docs/source/images/dict_data_model.png +docs/source/images/error_dialog.png +docs/source/images/information_dialog.png +docs/source/images/progress_dialog.png +docs/source/images/row_selection_type.png +docs/source/images/single_choice_dialog.png +docs/source/images/warning_dialog.png examples/application_window.py examples/chained_wizard.py -examples/dialog.py examples/expandable.py examples/explorer.py examples/file_filters.py @@ -397,7 +396,6 @@ examples/menu_manager.py examples/multi_toolbar_window.py examples/node_tree.py -examples/progress_dialog.py examples/python_editor.py examples/python_shell.py examples/splash_screen.py @@ -415,14 +413,26 @@ examples/application/python_editor/images/document_new.png examples/application/python_editor/images/document_open.png examples/application/python_editor/images/document_save.png -examples/application/python_editor/images/image_LICENSE.txt examples/application/python_editor/images/python_icon.png examples/application/python_editor/images/python_logo.png examples/application/python_shell/python_shell_application.py examples/application/python_shell/python_shell_window.py -examples/application/python_shell/images/image_LICENSE.txt examples/application/python_shell/images/python_icon.png examples/application/python_shell/images/python_logo.png +examples/data_view/array_example.py +examples/data_view/column_data_model.py +examples/data_view/column_example.py +examples/data_view/example_data.py +examples/data_view/row_table_example.py +examples/data_view/images/ca.png +examples/data_view/images/gb.png +examples/data_view/images/us.png +examples/dialogs/color_dialog.py +examples/dialogs/dialog.py +examples/dialogs/directory_dialog.py +examples/dialogs/file_dialog.py +examples/dialogs/font_dialog.py +examples/dialogs/progress_dialog.py examples/dock/dock_test.py examples/dock/dock_test2.py examples/dock/dock_test3.py @@ -430,11 +440,9 @@ examples/dock/envisage_test.py examples/dock/images/folder.png examples/dock/images/gear.png -examples/dock/images/image_LICENSE.txt examples/images/closed_folder.png examples/images/closed_folder_24x24.png examples/images/document.png -examples/images/image_LICENSE.txt examples/images/open_folder.png examples/images/splash.png examples/tasks/advanced/example_panes.py @@ -447,19 +455,11 @@ examples/tasks/advanced/images/document_new.png examples/tasks/advanced/images/document_open.png examples/tasks/advanced/images/document_save.png -examples/tasks/advanced/images/image_LICENSE.txt examples/tasks/basic/example_panes.py examples/tasks/basic/example_task.py examples/tasks/basic/run.py examples/tasks/basic/images/document_open.png examples/tasks/basic/images/document_save.png -examples/tasks/basic/images/image_LICENSE.txt -examples/tasks/enaml/employee.py -examples/tasks/enaml/employee_view.enaml -examples/tasks/enaml/empty_form.enaml -examples/tasks/enaml/enaml_panes.py -examples/tasks/enaml/enaml_task.py -examples/tasks/enaml/run.py examples/tasks/steps/README.txt examples/tasks/steps/mac-menubar-switching.py examples/tasks/steps/step1.py @@ -471,6 +471,11 @@ examples/tasks/steps/step6a.py examples/tasks/steps/step7.py examples/tasks/steps/step8.py +examples/undo/commands.py +examples/undo/example.py +examples/undo/example_editor_manager.py +examples/undo/example_undo_window.py +examples/undo/model.py examples/workbench/black_view.py examples/workbench/blue_view.py examples/workbench/color_view.py @@ -482,41 +487,51 @@ examples/workbench/run.py examples/workbench/yellow_view.py examples/workbench/images/example.ico -examples/workbench/images/image_LICENSE.txt pyface/__init__.py pyface/_version.py pyface/about_dialog.py pyface/api.py pyface/application.py pyface/application_window.py +pyface/array_image.py pyface/base_toolkit.py pyface/beep.py pyface/clipboard.py +pyface/color.py +pyface/color_dialog.py pyface/confirmation_dialog.py pyface/constant.py pyface/dialog.py pyface/directory_dialog.py pyface/drop_handler.py -pyface/expandable_header.py pyface/expandable_panel.py pyface/file_dialog.py pyface/filter.py +pyface/font.py +pyface/font_dialog.py pyface/gui.py pyface/gui_application.py pyface/heading_text.py pyface/i_about_dialog.py pyface/i_application_window.py pyface/i_clipboard.py +pyface/i_color_dialog.py pyface/i_confirmation_dialog.py pyface/i_dialog.py pyface/i_directory_dialog.py pyface/i_drop_handler.py pyface/i_file_dialog.py +pyface/i_font_dialog.py pyface/i_gui.py pyface/i_heading_text.py +pyface/i_image.py pyface/i_image_cache.py pyface/i_image_resource.py +pyface/i_layered_panel.py +pyface/i_layout_item.py +pyface/i_layout_widget.py pyface/i_message_dialog.py +pyface/i_pil_image.py pyface/i_progress_dialog.py pyface/i_python_editor.py pyface/i_python_shell.py @@ -526,14 +541,13 @@ pyface/i_system_metrics.py pyface/i_widget.py pyface/i_window.py -pyface/image_button.py pyface/image_cache.py -pyface/image_list.py pyface/image_resource.py pyface/image_widget.py pyface/ipython_widget.py pyface/key_pressed_event.py pyface/layered_panel.py +pyface/layout_widget.py pyface/list_box.py pyface/list_box_model.py pyface/mdi_application_window.py @@ -541,6 +555,7 @@ pyface/message_dialog.py pyface/mimedata.py pyface/multi_toolbar_window.py +pyface/pil_image.py pyface/progress_dialog.py pyface/python_editor.py pyface/python_shell.py @@ -558,7 +573,6 @@ pyface/ui_traits.py pyface/widget.py pyface/window.py -pyface/xrc_dialog.py pyface.egg-info/PKG-INFO pyface.egg-info/SOURCES.txt pyface.egg-info/dependency_links.txt @@ -586,7 +600,15 @@ pyface/action/traitsui_widget_action.py pyface/action/window_action.py pyface/action/images/action.png -pyface/action/images/image_LICENSE.txt +pyface/action/schema/__init__.py +pyface/action/schema/_topological_sort.py +pyface/action/schema/action_manager_builder.py +pyface/action/schema/api.py +pyface/action/schema/schema.py +pyface/action/schema/schema_addition.py +pyface/action/schema/tests/__init__.py +pyface/action/schema/tests/test_action_manager_builder.py +pyface/action/schema/tests/test_topological_sort.py pyface/action/tests/__init__.py pyface/action/tests/test_action.py pyface/action/tests/test_action_controller.py @@ -598,6 +620,63 @@ pyface/action/tests/test_gui_application_action.py pyface/action/tests/test_listening_action.py pyface/action/tests/test_traitsui_widget_action.py +pyface/data_view/__init__.py +pyface/data_view/abstract_data_exporter.py +pyface/data_view/abstract_data_model.py +pyface/data_view/abstract_value_type.py +pyface/data_view/api.py +pyface/data_view/data_formats.py +pyface/data_view/data_view_errors.py +pyface/data_view/data_view_widget.py +pyface/data_view/data_wrapper.py +pyface/data_view/i_data_view_widget.py +pyface/data_view/i_data_wrapper.py +pyface/data_view/index_manager.py +pyface/data_view/data_models/__init__.py +pyface/data_view/data_models/api.py +pyface/data_view/data_models/array_data_model.py +pyface/data_view/data_models/data_accessors.py +pyface/data_view/data_models/row_table_data_model.py +pyface/data_view/data_models/tests/__init__.py +pyface/data_view/data_models/tests/test_api.py +pyface/data_view/data_models/tests/test_array_data_model.py +pyface/data_view/data_models/tests/test_data_accessors.py +pyface/data_view/data_models/tests/test_row_table_data_model.py +pyface/data_view/exporters/__init__.py +pyface/data_view/exporters/api.py +pyface/data_view/exporters/item_exporter.py +pyface/data_view/exporters/row_exporter.py +pyface/data_view/exporters/tests/__init__.py +pyface/data_view/exporters/tests/test_api.py +pyface/data_view/exporters/tests/test_item_exporter.py +pyface/data_view/exporters/tests/test_row_exporter.py +pyface/data_view/tests/__init__.py +pyface/data_view/tests/test_abstract_data_exporter.py +pyface/data_view/tests/test_abstract_value_type.py +pyface/data_view/tests/test_api.py +pyface/data_view/tests/test_data_formats.py +pyface/data_view/tests/test_data_view_widget.py +pyface/data_view/tests/test_data_wrapper.py +pyface/data_view/tests/test_index_manager.py +pyface/data_view/value_types/__init__.py +pyface/data_view/value_types/api.py +pyface/data_view/value_types/bool_value.py +pyface/data_view/value_types/color_value.py +pyface/data_view/value_types/constant_value.py +pyface/data_view/value_types/editable_value.py +pyface/data_view/value_types/enum_value.py +pyface/data_view/value_types/no_value.py +pyface/data_view/value_types/numeric_value.py +pyface/data_view/value_types/text_value.py +pyface/data_view/value_types/tests/__init__.py +pyface/data_view/value_types/tests/test_bool_value.py +pyface/data_view/value_types/tests/test_color_value.py +pyface/data_view/value_types/tests/test_constant_value.py +pyface/data_view/value_types/tests/test_editable_value.py +pyface/data_view/value_types/tests/test_enum_value.py +pyface/data_view/value_types/tests/test_no_value.py +pyface/data_view/value_types/tests/test_numeric_value.py +pyface/data_view/value_types/tests/test_text_value.py pyface/dock/__init__.py pyface/dock/api.py pyface/dock/dock_sizer.py @@ -617,7 +696,6 @@ pyface/dock/images/close_drag.png pyface/dock/images/close_tab.png pyface/dock/images/feature_tool.png -pyface/dock/images/image_LICENSE.txt pyface/dock/images/sh_bottom.png pyface/dock/images/sh_middle.png pyface/dock/images/sh_top.png @@ -634,6 +712,8 @@ pyface/dock/images/tab_scroll_lr.png pyface/dock/images/tab_scroll_r.png pyface/dock/images/window.png +pyface/dock/tests/__init__.py +pyface/dock/tests/test_dock_sizer.py pyface/fields/__init__.py pyface/fields/api.py pyface/fields/combo_field.py @@ -641,13 +721,19 @@ pyface/fields/i_field.py pyface/fields/i_spin_field.py pyface/fields/i_text_field.py +pyface/fields/i_time_field.py +pyface/fields/i_toggle_field.py pyface/fields/spin_field.py pyface/fields/text_field.py +pyface/fields/time_field.py +pyface/fields/toggle_field.py pyface/fields/tests/__init__.py pyface/fields/tests/field_mixin.py pyface/fields/tests/test_combo_field.py pyface/fields/tests/test_spin_field.py pyface/fields/tests/test_text_field.py +pyface/fields/tests/test_time_field.py +pyface/fields/tests/test_toggle_field.py pyface/grid/__init__.py pyface/grid/api.py pyface/grid/checkbox_image_renderer.py @@ -670,17 +756,14 @@ pyface/image/library/icons.zip pyface/image/library/image_LICENSE.txt pyface/image/library/std.zip -pyface/images/about.jpg +pyface/image/tests/__init__.py +pyface/image/tests/test_image.py +pyface/images/about.png pyface/images/background.jpg -pyface/images/carat_closed.png -pyface/images/carat_open.png pyface/images/close.png -pyface/images/image_LICENSE.txt pyface/images/image_not_found.png -pyface/images/panel_gradient.png -pyface/images/panel_gradient_over.png pyface/images/question.png -pyface/images/splash.jpg +pyface/images/splash.png pyface/preference/__init__.py pyface/preference/api.py pyface/preference/preference_dialog.py @@ -688,6 +771,8 @@ pyface/preference/preference_page.py pyface/qt/QtCore.py pyface/qt/QtGui.py +pyface/qt/QtMultimedia.py +pyface/qt/QtMultimediaWidgets.py pyface/qt/QtNetwork.py pyface/qt/QtOpenGL.py pyface/qt/QtScript.py @@ -747,33 +832,45 @@ pyface/tasks/contrib/__init__.py pyface/tasks/contrib/python_shell.py pyface/tasks/tests/__init__.py -pyface/tasks/tests/test_action_manager_builder.py +pyface/tasks/tests/test_advanced_editor_area_pane.py pyface/tasks/tests/test_dock_pane_toggle_group.py pyface/tasks/tests/test_editor_area_pane.py pyface/tasks/tests/test_enaml_dock_pane.py pyface/tasks/tests/test_enaml_editor.py pyface/tasks/tests/test_enaml_task_pane.py +pyface/tasks/tests/test_split_editor_area_pane.py +pyface/tasks/tests/test_task_action_manager_builder.py pyface/tasks/tests/test_task_layout.py pyface/tasks/tests/test_task_window.py pyface/tasks/tests/test_tasks_application.py -pyface/tasks/tests/test_topological_sort.py +pyface/testing/__init__.py +pyface/testing/layout_widget_mixin.py +pyface/testing/widget_mixin.py pyface/tests/__init__.py pyface/tests/python_shell_script.py pyface/tests/test_about_dialog.py +pyface/tests/test_api.py pyface/tests/test_application.py pyface/tests/test_application_window.py +pyface/tests/test_array_image.py pyface/tests/test_base_toolkit.py pyface/tests/test_beep.py pyface/tests/test_clipboard.py +pyface/tests/test_color.py +pyface/tests/test_color_dialog.py pyface/tests/test_confirmation_dialog.py pyface/tests/test_dialog.py pyface/tests/test_directory_dialog.py pyface/tests/test_file_dialog.py +pyface/tests/test_font.py +pyface/tests/test_font_dialog.py pyface/tests/test_gui_application.py pyface/tests/test_heading_text.py pyface/tests/test_image_cache.py pyface/tests/test_image_resource.py +pyface/tests/test_layered_panel.py pyface/tests/test_message_dialog.py +pyface/tests/test_pil_image.py pyface/tests/test_progress_dialog.py pyface/tests/test_python_editor.py pyface/tests/test_python_shell.py @@ -790,7 +887,6 @@ pyface/tests/test_widget.py pyface/tests/test_window.py pyface/tests/images/core.png -pyface/tests/images/image_LICENSE.txt pyface/tests/test_new_toolkit/__init__.py pyface/tests/test_new_toolkit/init.py pyface/tests/test_new_toolkit/widget.py @@ -816,11 +912,11 @@ pyface/tree/tree_model.py pyface/tree/images/closed_folder.png pyface/tree/images/document.png -pyface/tree/images/image_LICENSE.txt pyface/tree/images/open_folder.png pyface/ui/__init__.py pyface/ui/null/__init__.py pyface/ui/null/clipboard.py +pyface/ui/null/color.py pyface/ui/null/image_resource.py pyface/ui/null/init.py pyface/ui/null/resource_manager.py @@ -841,17 +937,24 @@ pyface/ui/qt4/application_window.py pyface/ui/qt4/beep.py pyface/ui/qt4/clipboard.py +pyface/ui/qt4/color.py +pyface/ui/qt4/color_dialog.py pyface/ui/qt4/confirmation_dialog.py pyface/ui/qt4/dialog.py pyface/ui/qt4/directory_dialog.py pyface/ui/qt4/file_dialog.py +pyface/ui/qt4/font.py +pyface/ui/qt4/font_dialog.py pyface/ui/qt4/gui.py pyface/ui/qt4/heading_text.py pyface/ui/qt4/image_cache.py pyface/ui/qt4/image_resource.py pyface/ui/qt4/init.py +pyface/ui/qt4/layered_panel.py +pyface/ui/qt4/layout_widget.py pyface/ui/qt4/message_dialog.py pyface/ui/qt4/mimedata.py +pyface/ui/qt4/pil_image.py pyface/ui/qt4/progress_dialog.py pyface/ui/qt4/python_editor.py pyface/ui/qt4/python_shell.py @@ -883,14 +986,22 @@ pyface/ui/qt4/console/completion_lexer.py pyface/ui/qt4/console/console_widget.py pyface/ui/qt4/console/history_console_widget.py +pyface/ui/qt4/data_view/__init__.py +pyface/ui/qt4/data_view/data_view_item_model.py +pyface/ui/qt4/data_view/data_view_widget.py +pyface/ui/qt4/data_view/data_wrapper.py +pyface/ui/qt4/data_view/tests/__init__.py +pyface/ui/qt4/data_view/tests/test_data_view_item_model.py +pyface/ui/qt4/data_view/tests/test_data_wrapper.py pyface/ui/qt4/fields/__init__.py pyface/ui/qt4/fields/combo_field.py pyface/ui/qt4/fields/field.py pyface/ui/qt4/fields/spin_field.py pyface/ui/qt4/fields/text_field.py +pyface/ui/qt4/fields/time_field.py +pyface/ui/qt4/fields/toggle_field.py pyface/ui/qt4/images/application.png pyface/ui/qt4/images/heading_level_1.png -pyface/ui/qt4/images/image_LICENSE.txt pyface/ui/qt4/tasks/__init__.py pyface/ui/qt4/tasks/advanced_editor_area_pane.py pyface/ui/qt4/tasks/dock_pane.py @@ -902,10 +1013,13 @@ pyface/ui/qt4/tasks/task_window_backend.py pyface/ui/qt4/tasks/util.py pyface/ui/qt4/tasks/tests/__init__.py +pyface/ui/qt4/tasks/tests/test_dock_pane.py +pyface/ui/qt4/tasks/tests/test_main_window_layout.py pyface/ui/qt4/tasks/tests/test_split_editor_area_pane.py pyface/ui/qt4/tests/__init__.py pyface/ui/qt4/tests/bad_import.py pyface/ui/qt4/tests/test_gui.py +pyface/ui/qt4/tests/test_message_dialog.py pyface/ui/qt4/tests/test_mimedata.py pyface/ui/qt4/tests/test_progress_dialog.py pyface/ui/qt4/tests/test_qt_imports.py @@ -913,12 +1027,17 @@ pyface/ui/qt4/timer/do_later.py pyface/ui/qt4/timer/timer.py pyface/ui/qt4/util/__init__.py +pyface/ui/qt4/util/datetime.py pyface/ui/qt4/util/event_loop_helper.py pyface/ui/qt4/util/gui_test_assistant.py +pyface/ui/qt4/util/image_helpers.py pyface/ui/qt4/util/modal_dialog_tester.py pyface/ui/qt4/util/testing.py pyface/ui/qt4/util/tests/__init__.py +pyface/ui/qt4/util/tests/test_datetime.py +pyface/ui/qt4/util/tests/test_event_loop_helper.py pyface/ui/qt4/util/tests/test_gui_test_assistant.py +pyface/ui/qt4/util/tests/test_image_helpers.py pyface/ui/qt4/util/tests/test_modal_dialog_tester.py pyface/ui/qt4/wizard/__init__.py pyface/ui/qt4/wizard/wizard.py @@ -936,12 +1055,16 @@ pyface/ui/wx/application_window.py pyface/ui/wx/beep.py pyface/ui/wx/clipboard.py +pyface/ui/wx/color.py +pyface/ui/wx/color_dialog.py pyface/ui/wx/confirmation_dialog.py pyface/ui/wx/dialog.py pyface/ui/wx/directory_dialog.py pyface/ui/wx/expandable_header.py pyface/ui/wx/expandable_panel.py pyface/ui/wx/file_dialog.py +pyface/ui/wx/font.py +pyface/ui/wx/font_dialog.py pyface/ui/wx/gui.py pyface/ui/wx/heading_text.py pyface/ui/wx/image_button.py @@ -952,10 +1075,12 @@ pyface/ui/wx/init.py pyface/ui/wx/ipython_widget.py pyface/ui/wx/layered_panel.py +pyface/ui/wx/layout_widget.py pyface/ui/wx/list_box.py pyface/ui/wx/mdi_application_window.py pyface/ui/wx/message_dialog.py pyface/ui/wx/multi_toolbar_window.py +pyface/ui/wx/pil_image.py pyface/ui/wx/progress_dialog.py pyface/ui/wx/python_editor.py pyface/ui/wx/python_shell.py @@ -975,16 +1100,23 @@ pyface/ui/wx/action/tool_bar_manager.py pyface/ui/wx/action/tool_palette.py pyface/ui/wx/action/tool_palette_manager.py +pyface/ui/wx/data_view/__init__.py +pyface/ui/wx/data_view/data_view_model.py +pyface/ui/wx/data_view/data_view_widget.py +pyface/ui/wx/data_view/data_wrapper.py +pyface/ui/wx/data_view/tests/__init__.py +pyface/ui/wx/data_view/tests/test_data_wrapper.py pyface/ui/wx/fields/__init__.py pyface/ui/wx/fields/combo_field.py pyface/ui/wx/fields/field.py pyface/ui/wx/fields/spin_field.py pyface/ui/wx/fields/text_field.py +pyface/ui/wx/fields/time_field.py +pyface/ui/wx/fields/toggle_field.py pyface/ui/wx/grid/__init__.py pyface/ui/wx/grid/api.py pyface/ui/wx/grid/checkbox_image_renderer.py pyface/ui/wx/grid/checkbox_renderer.py -pyface/ui/wx/grid/combobox_focus_handler.py pyface/ui/wx/grid/composite_grid_model.py pyface/ui/wx/grid/edit_image_renderer.py pyface/ui/wx/grid/edit_renderer.py @@ -998,16 +1130,18 @@ pyface/ui/wx/grid/trait_grid_cell_adapter.py pyface/ui/wx/grid/trait_grid_model.py pyface/ui/wx/grid/images/checked.png -pyface/ui/wx/grid/images/image_LICENSE.txt pyface/ui/wx/grid/images/image_not_found.png pyface/ui/wx/grid/images/table_edit.png pyface/ui/wx/grid/images/unchecked.png pyface/ui/wx/grid/tests/__init__.py -pyface/ui/wx/grid/tests/composite_grid_model_test_case.py -pyface/ui/wx/grid/tests/simple_grid_model_test_case.py +pyface/ui/wx/grid/tests/test_composite_grid_model.py +pyface/ui/wx/grid/tests/test_simple_grid_model.py pyface/ui/wx/images/application.ico +pyface/ui/wx/images/carat_closed.png +pyface/ui/wx/images/carat_open.png pyface/ui/wx/images/heading_level_1.png -pyface/ui/wx/images/image_LICENSE.txt +pyface/ui/wx/images/panel_gradient.png +pyface/ui/wx/images/panel_gradient_over.png pyface/ui/wx/images/warning.png pyface/ui/wx/preference/__init__.py pyface/ui/wx/preference/preference_dialog.py @@ -1027,6 +1161,10 @@ pyface/ui/wx/timer/timer.py pyface/ui/wx/tree/__init__.py pyface/ui/wx/tree/tree.py +pyface/ui/wx/util/__init__.py +pyface/ui/wx/util/image_helpers.py +pyface/ui/wx/util/tests/__init__.py +pyface/ui/wx/util/tests/test_image_helpers.py pyface/ui/wx/viewer/__init__.py pyface/ui/wx/viewer/table_viewer.py pyface/ui/wx/viewer/tree_viewer.py @@ -1040,21 +1178,49 @@ pyface/ui/wx/workbench/view_set_structure_handler.py pyface/ui/wx/workbench/workbench_dock_window.py pyface/ui/wx/workbench/workbench_window_layout.py +pyface/undo/__init__.py +pyface/undo/abstract_command.py +pyface/undo/api.py +pyface/undo/command_stack.py +pyface/undo/i_command.py +pyface/undo/i_command_stack.py +pyface/undo/i_undo_manager.py +pyface/undo/undo_manager.py +pyface/undo/action/__init__.py +pyface/undo/action/abstract_command_stack_action.py +pyface/undo/action/api.py +pyface/undo/action/command_action.py +pyface/undo/action/redo_action.py +pyface/undo/action/undo_action.py +pyface/undo/action/tests/__init__.py +pyface/undo/action/tests/test_actions.py +pyface/undo/tests/__init__.py +pyface/undo/tests/test_command_stack.py +pyface/undo/tests/test_undo_manager.py +pyface/undo/tests/testing_commands.py pyface/util/__init__.py +pyface/util/_optional_dependencies.py +pyface/util/color_helpers.py +pyface/util/color_parser.py +pyface/util/event_loop_helper.py pyface/util/fix_introspect_bug.py -pyface/util/font_helper.py +pyface/util/font_parser.py +pyface/util/gui_test_assistant.py pyface/util/guisupport.py pyface/util/id_helper.py -pyface/util/python_stc.py +pyface/util/image_helpers.py +pyface/util/modal_dialog_tester.py pyface/util/testing.py -pyface/util/grid/__init__.py -pyface/util/grid/api.py -pyface/util/grid/grid.py -pyface/util/grid/grid_column.py -pyface/util/grid/grid_model.py -pyface/util/grid/grid_row.py pyface/util/tests/__init__.py +pyface/util/tests/test__optional_dependencies.py +pyface/util/tests/test_color_helpers.py +pyface/util/tests/test_color_parser.py +pyface/util/tests/test_event_loop_helper.py +pyface/util/tests/test_font_parser.py +pyface/util/tests/test_gui_test_assistant.py pyface/util/tests/test_id_helper.py +pyface/util/tests/test_image_helpers.py +pyface/util/tests/test_modal_dialog_tester.py pyface/viewer/__init__.py pyface/viewer/api.py pyface/viewer/column_provider.py @@ -1136,7 +1302,6 @@ pyface/workbench/tests/test_workbench_window.py pyface/wx/__init__.py pyface/wx/aui.py -pyface/wx/clipboard.py pyface/wx/color.py pyface/wx/dialog.py pyface/wx/divider.py diff -Nru python-pyface-6.1.2/README.rst python-pyface-7.4.0/README.rst --- python-pyface-6.1.2/README.rst 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/README.rst 2022-02-01 15:52:57.000000000 +0000 @@ -1,31 +1,48 @@ ========================================== -pyface: traits-capable windowing framework +Pyface: Traits-capable Windowing Framework ========================================== -.. image:: https://travis-ci.org/enthought/pyface.svg?branch=master - :target: https://travis-ci.org/enthought/pyface - -.. image:: https://ci.appveyor.com/api/projects/status/68nfb049cdq9wqd1/branch/master?svg=true - :target: https://ci.appveyor.com/project/EnthoughtOSS/pyface/branch/master - -.. image:: https://codecov.io/github/enthought/pyface/coverage.svg?branch=master - :target: https://codecov.io/github/enthought/pyface?branch=master - - -The pyface project contains a toolkit-independent GUI abstraction layer, +The Pyface project contains a toolkit-independent GUI abstraction layer, which is used to support the "visualization" features of the Traits package. Thus, you can write code in terms of the Traits API (views, items, editors, -etc.), and let pyface and your selected toolkit and back-end take care of +etc.), and let Pyface and your selected toolkit and back-end take care of the details of displaying them. The following GUI backends are supported: -- wxPython -- PyQt -- PySide +- PySide2 (stable) and PySide6 (experimental) +- PyQt5 (stable) and PyQt6 (in development) +- wxPython 4 (experimental) + +Installation +------------ + +GUI backends are marked as optional dependencies of Pyface. Some features +or infrastructures may also require additional dependencies. + +To install with PySide2 dependencies:: + + $ pip install pyface[pyside2] + +To install with PySide6 dependencies (experimental):: + + $ pip install pyface[pyside6] + +To install with PyQt5 dependencies:: -**Warning:** The default toolkit if none is supplied is ``qt4``. - This changed from ``wx`` in Pyface 5.0.. + $ pip install pyface[pyqt5] + +To install with wxPython4 dependencies (experimental):: + + $ pip install pyface[wx] + +``pillow`` is an optional dependency for the PILImage class:: + + $ pip install pyface[pillow] + +To install with additional test dependencies:: + + $ pip install pyface[test] Documentation ------------- @@ -39,10 +56,39 @@ Pyface depends on: -* a GUI toolkit: one of PySide, PyQt or WxPython - * `Traits `_ +* a GUI toolkit as described above + * Pygments for syntax highlighting in the Qt code editor widget. -* some widgets may have additional optional dependencies. +* some widgets may have additional optional dependencies such as NumPy or + Pillow. + +.. end_of_long_description + +Developing Pyface +----------------- + +The `etstool.py` script provides utilities to assist developers wanting to work +on Pyface. To use it, you will need to have the source checked out via Git, +Enthought's `EDM `__ distribution manager, and +a minimal environment containing at least the +`Click `__ library. + +You can then follow the instructions in ``etstool.py``. In particular: + +- use `etstool.py install` to create environments for particular toolkits and + runtimes +- use `etstool.py shell` to activate those environments +- use `etstool.py test` to run the tests in those environments +- use `etstool.py flake8` to perform style checks +- use `etstool.py docs` to build the documentation +- use `etstool.py test-all` to run the tests across all supported runtimes and toolkits + +License +------- + +Pyface source code is licensed with a BSD-style license. Some default images +are licensed with other licenses. See the license files for further +information. diff -Nru python-pyface-6.1.2/setup.cfg python-pyface-7.4.0/setup.cfg --- python-pyface-6.1.2/setup.cfg 2019-07-22 09:15:12.000000000 +0000 +++ python-pyface-7.4.0/setup.cfg 2022-02-02 10:48:09.000000000 +0000 @@ -1,3 +1,18 @@ +[flake8] +exclude = docs +per-file-ignores = + */api.py:F401 + pyface/qt/Qt*.py:F401,F403,F405 + pyface/grid/*.py:F403,E402 + pyface/grid/api.py:F401,F403,E402 + pyface/tasks/tests/test_enaml*.py:E402 + pyface/wx/shell.py:F403,F405,E402 + examples/*:H101,F401,F403,F405,E402 +ignore = + E266,W503,E722,E731,E741 + H101 + E226,E501,W291,W293,W391,W504 + [egg_info] tag_build = tag_date = 0 diff -Nru python-pyface-6.1.2/setup.py python-pyface-7.4.0/setup.py --- python-pyface-6.1.2/setup.py 2019-07-21 09:29:00.000000000 +0000 +++ python-pyface-7.4.0/setup.py 2022-02-02 09:52:38.000000000 +0000 @@ -1,23 +1,80 @@ -# Copyright (c) 2008-2015 by Enthought, Inc. +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX # All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! import os import re +import runpy import subprocess from setuptools import setup, find_packages -MAJOR = 6 -MINOR = 1 -MICRO = 2 +# Version information; update this by hand when making a new bugfix or feature +# release. The actual package version is autogenerated from this information +# together with information from the version control system, and then injected +# into the package source. +MAJOR = 7 +MINOR = 4 +MICRO = 0 +PRERELEASE = "" IS_RELEASED = True -VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) +# If this file is part of a Git export (for example created with "git archive", +# or downloaded from GitHub), ARCHIVE_COMMIT_HASH gives the full hash of the +# commit that was exported. +ARCHIVE_COMMIT_HASH = "$Format:%H$" + +# Templates for version strings. +RELEASED_VERSION = "{major}.{minor}.{micro}{prerelease}" +UNRELEASED_VERSION = "{major}.{minor}.{micro}{prerelease}.dev{dev}" + +# Paths to the autogenerated version file and the Git directory. +HERE = os.path.abspath(os.path.dirname(__file__)) +VERSION_FILE = os.path.join(HERE, "pyface", "_version.py") +GIT_DIRECTORY = os.path.join(HERE, ".git") + +# Template for the autogenerated version file. +VERSION_FILE_TEMPLATE = """\ +# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX +# All rights reserved. +# +# This software is provided without warranty under the terms of the BSD +# license included in LICENSE.txt and may be redistributed only under +# the conditions described in the aforementioned license. The license +# is also available online at http://www.enthought.com/licenses/BSD.txt +# +# Thanks for using Enthought open source! + +# THIS FILE IS GENERATED FROM SETUP.PY + +#: The full version of the package, including a development suffix +#: for unreleased versions of the package. +version = '{version}' + +#: The full version of the package, same as 'version' +#: Kept for backward compatibility +full_version = version + +#: The Git revision from which this release was made. +git_revision = '{git_revision}' + +#: Flag whether this is a final release +is_released = {is_released} +""" + +# Git executable to use to get revision information. +GIT = "git" -def read_module(module, package='pyface'): - """ Read a simple .py file from pyface in a safe way. +def read_module(module, package="pyface"): + """ Read a simple .py file from package in a safe way. It would be simpler to import the file, but that can be problematic in an unknown system, so we exec the file instead and extract the variables. @@ -26,110 +83,230 @@ sufficient to get version and requirements information. """ base_dir = os.path.dirname(__file__) - module_name = package + '.' + module - path = os.path.join(base_dir, package, module+'.py') - with open(path, 'r') as fp: - code = compile(fp.read(), module_name, 'exec') + module_name = package + "." + module + path = os.path.join(base_dir, package, module + ".py") + with open(path, "r") as fp: + code = compile(fp.read(), module_name, "exec") context = {} exec(code, context) return context # Return the git revision as a string -def git_version(): +def _git_info(): + """ + Get information about the given commit from Git. + + Returns + ------- + git_count : int + Number of revisions from this commit to the initial commit. + git_revision : str + Commit hash for HEAD. + + Raises + ------ + EnvironmentError + If Git is not available. + subprocess.CalledProcessError + If Git is available, but the version command fails (most likely + because there's no Git repository here). + """ def _minimal_ext_cmd(cmd): # construct minimal environment env = {} - for k in ['SYSTEMROOT', 'PATH']: + for k in ["SYSTEMROOT", "PATH"]: v = os.environ.get(k) if v is not None: env[k] = v # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' + env["LANGUAGE"] = "C" + env["LANG"] = "C" + env["LC_ALL"] = "C" out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env, + cmd, stdout=subprocess.PIPE, env=env ).communicate()[0] return out try: - out = _minimal_ext_cmd(['git', 'describe', '--tags']) + out = _minimal_ext_cmd(["git", "describe", "--tags"]) except OSError: - out = '' + out = "" - git_description = out.strip().decode('ascii') - expr = r'.*?\-(?P\d+)-g(?P[a-fA-F0-9]+)' + git_description = out.strip().decode("ascii") + expr = r".*?\-(?P\d+)-g(?P[a-fA-F0-9]+)" match = re.match(expr, git_description) if match is None: - git_revision, git_count = 'Unknown', '0' + git_revision, git_count = "Unknown", "0" else: - git_revision, git_count = match.group('hash'), match.group('count') + git_revision, git_count = match.group("hash"), match.group("count") - return git_revision, git_count + return git_count, git_revision -def write_version_py(filename='pyface/_version.py'): - template = """\ -# THIS FILE IS GENERATED FROM PYFACE SETUP.PY -version = '{version}' -full_version = '{full_version}' -git_revision = '{git_revision}' -is_released = {is_released} +def git_version(): + """ + Construct version information from local variables and Git. -if not is_released: - version = full_version -""" - # Adding the git rev number needs to be done inside - # write_version_py(), otherwise the import of pyface._version messes - # up the build under Python 3. - fullversion = VERSION - if os.path.exists('.git'): - git_rev, dev_num = git_version() - elif os.path.exists('pyface/_version.py'): - # must be a source distribution, use existing version file - try: - data = read_module('_version') - git_rev = data['git_revision'] - fullversion_source = data['full_version'] - except ImportError: - raise ImportError("Unable to read git_revision. Try removing " - "pyface/_version.py and the build directory " - "before building.") - - match = re.match(r'.*?\.dev(?P\d+)', fullversion_source) - if match is None: - dev_num = '0' - else: - dev_num = match.group('dev_num') - else: - git_rev = 'Unknown' - dev_num = '0' + Returns + ------- + version : str + Package version. + git_revision : str + The full commit hash for the current Git revision. + + Raises + ------ + EnvironmentError + If Git is not available. + subprocess.CalledProcessError + If Git is available, but the version command fails (most likely + because there's no Git repository here). + """ + git_count, git_revision = _git_info() + version_template = RELEASED_VERSION if IS_RELEASED else UNRELEASED_VERSION + version = version_template.format( + major=MAJOR, + minor=MINOR, + micro=MICRO, + prerelease=PRERELEASE, + dev=git_count, + ) + return version, git_revision - if not IS_RELEASED: - fullversion += '.dev{0}'.format(dev_num) - with open(filename, "wt") as fp: - fp.write(template.format(version=VERSION, - full_version=fullversion, - git_revision=git_rev, - is_released=IS_RELEASED)) +def archive_version(): + """ + Construct version information for an archive. + + Returns + ------- + version : str + Package version. + git_revision : str + The full commit hash for the current Git revision. + + Raises + ------ + ValueError + If this does not appear to be an archive. + """ + if "$" in ARCHIVE_COMMIT_HASH: + raise ValueError("This does not appear to be an archive.") + + version_template = RELEASED_VERSION if IS_RELEASED else UNRELEASED_VERSION + version = version_template.format( + major=MAJOR, + minor=MINOR, + micro=MICRO, + prerelease=PRERELEASE, + dev="-unknown", + ) + return version, ARCHIVE_COMMIT_HASH + + +def write_version_file(version, git_revision, filename=VERSION_FILE): + """ + Write version information to the version file. + + Overwrites any existing version file. + + Parameters + ---------- + version : str + Package version. + git_revision : str + The full commit hash for the current Git revision. + filename : str + Path to the version file. + """ + with open(filename, "w", encoding="utf-8") as version_file: + version_file.write( + VERSION_FILE_TEMPLATE.format( + version=version, + git_revision=git_revision, + is_released=IS_RELEASED, + ) + ) + + +def read_version_file(): + """ + Read version information from the version file, if it exists. + + Returns + ------- + version : str + The full version, including any development suffix. + git_revision : str + The full commit hash for the current Git revision. + + Raises + ------ + EnvironmentError + If the version file does not exist. + """ + version_info = runpy.run_path(VERSION_FILE) + return (version_info["version"], version_info["git_revision"]) + + +def resolve_version(): + """ + Process version information and write a version file if necessary. + + Returns the current version information. + + Returns + ------- + version : str + Package version. + git_revision : str + The full commit hash for the current Git revision. + """ + if os.path.isdir(GIT_DIRECTORY): + # This is a local clone; compute version information and write + # it to the version file, overwriting any existing information. + version = git_version() + print("Computed package version: {}".format(version)) + print("Writing version to version file {}.".format(VERSION_FILE)) + write_version_file(*version) + elif "$" not in ARCHIVE_COMMIT_HASH: + # This is a source archive. + version = archive_version() + print("Archive package version: {}".format(version)) + print("Writing version to version file {}.".format(VERSION_FILE)) + write_version_file(*version) + elif os.path.isfile(VERSION_FILE): + # This is a source distribution. Read the version information. + print("Reading version file {}".format(VERSION_FILE)) + version = read_version_file() + print("Package version from version file: {}".format(version)) + else: + raise RuntimeError( + "Unable to determine package version. No local Git clone " + "detected, and no version file found at {}.".format(VERSION_FILE) + ) - return fullversion + return version if __name__ == "__main__": - __version__ = write_version_py() - data = read_module('__init__') - __requires__ = data['__requires__'] - __extras_require__ = data['__extras_require__'] - - setup(name='pyface', - version=__version__, - url='http://docs.enthought.com/pyface', - author='David C. Morrill, et al.', - author_email='dmorrill@enthought.com', - classifiers=[c.strip() for c in """\ + __version__, _ = resolve_version() + data = read_module("__init__") + __requires__ = data["__requires__"] + __extras_require__ = data["__extras_require__"] + with open("README.rst", "r", encoding="utf-8") as readme: + LONG_DESCRIPTION = readme.read().split(".. end_of_long_description")[0] + + setup( + name="pyface", + version=__version__, + url="http://docs.enthought.com/pyface", + author="David C. Morrill, et al.", + author_email="dmorrill@enthought.com", + classifiers=[ + c.strip() + for c in """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers Intended Audience :: Science/Research @@ -140,42 +317,48 @@ Operating System :: POSIX Operating System :: Unix Programming Language :: Python - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 Topic :: Scientific/Engineering Topic :: Software Development Topic :: Software Development :: Libraries - """.splitlines() if len(c.split()) > 0], - description='traits-capable windowing framework', - long_description=open('README.rst').read(), - download_url='https://github.com/enthought/pyface', - install_requires=__requires__, - extras_require=__extras_require__, - license='BSD', - maintainer='ETS Developers', - maintainer_email='enthought-dev@enthought.com', - package_data={'': [ - 'image/library/*.zip', - 'images/*', - 'action/images/*', - 'dock/images/*', - 'tree/images/*', - 'tests/images/*', - 'ui/qt4/images/*', - 'ui/wx/images/*', - 'ui/wx/grid/images/*', - ]}, - packages=find_packages(), - entry_points = { - 'pyface.toolkits': [ - 'qt4 = pyface.ui.qt4.init:toolkit_object', - 'qt = pyface.ui.qt4.init:toolkit_object', - 'wx = pyface.ui.wx.init:toolkit_object', - 'null = pyface.ui.null.init:toolkit_object', - ], - }, - platforms=["Windows", "Linux", "Mac OS-X", "Unix", "Solaris"], - zip_safe=False, + """.splitlines() + if len(c.split()) > 0 + ], + description="traits-capable windowing framework", + long_description=LONG_DESCRIPTION, + long_description_content_type="text/x-rst", + download_url="https://github.com/enthought/pyface", + install_requires=__requires__, + extras_require=__extras_require__, + license="BSD", + maintainer="ETS Developers", + maintainer_email="enthought-dev@enthought.com", + package_data={ + "": [ + "image/library/*.zip", + "images/*", + "action/images/*", + "dock/images/*", + "tree/images/*", + "tests/images/*", + "ui/qt4/images/*", + "ui/wx/images/*", + "ui/wx/grid/images/*", + ] + }, + packages=find_packages(), + entry_points={ + "pyface.toolkits": [ + "qt4 = pyface.ui.qt4.init:toolkit_object", + "qt = pyface.ui.qt4.init:toolkit_object", + "wx = pyface.ui.wx.init:toolkit_object", + "null = pyface.ui.null.init:toolkit_object", + ] + }, + platforms=["Windows", "Linux", "Mac OS-X", "Unix", "Solaris"], + python_requires=">=3.6", + zip_safe=False, ) diff -Nru python-pyface-6.1.2/TODO.txt python-pyface-7.4.0/TODO.txt --- python-pyface-6.1.2/TODO.txt 2019-05-03 08:18:49.000000000 +0000 +++ python-pyface-7.4.0/TODO.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -* Remove the import of pyface.resource for core functionality in - resource_manager.py and i_image_resource.py. - -* Remove the dependency on etsdevtools.developer AND apptools.io by refactoring - dock features modules. - -* Remove the dock dependence on TraitsBackendWX by removing the direct imports - of traitsui.wx. - -* Need to make use of drag and drop features optional in this project's code - (see dock, grid, sheet, tree, and viewer/tree_viewer) or help make - traits.util's drag_and_drop module backend agnostic. Right now, it has - a runtime dependency that wx be installed. diff -Nru python-pyface-6.1.2/tox-requirements.txt python-pyface-7.4.0/tox-requirements.txt --- python-pyface-6.1.2/tox-requirements.txt 2019-05-03 08:18:50.000000000 +0000 +++ python-pyface-7.4.0/tox-requirements.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -mock -nose -numpy -pygments -traits -git+http://github.com/enthought/traitsui.git#egg=traitsui -traits_enaml ; python_version == '2.7' -enaml ; python_version == '2.7'