toga-qt 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. toga_qt-0.5.3/CONTRIBUTING.md +11 -0
  2. toga_qt-0.5.3/LICENSE +27 -0
  3. toga_qt-0.5.3/PKG-INFO +60 -0
  4. toga_qt-0.5.3/README.md +29 -0
  5. toga_qt-0.5.3/pyproject.toml +80 -0
  6. toga_qt-0.5.3/setup.cfg +4 -0
  7. toga_qt-0.5.3/src/toga_qt/__init__.py +3 -0
  8. toga_qt-0.5.3/src/toga_qt/app.py +216 -0
  9. toga_qt-0.5.3/src/toga_qt/colors.py +14 -0
  10. toga_qt-0.5.3/src/toga_qt/command.py +142 -0
  11. toga_qt-0.5.3/src/toga_qt/container.py +39 -0
  12. toga_qt-0.5.3/src/toga_qt/dialogs.py +82 -0
  13. toga_qt-0.5.3/src/toga_qt/factory.py +64 -0
  14. toga_qt-0.5.3/src/toga_qt/fonts.py +11 -0
  15. toga_qt-0.5.3/src/toga_qt/icons.py +47 -0
  16. toga_qt-0.5.3/src/toga_qt/images.py +56 -0
  17. toga_qt-0.5.3/src/toga_qt/keys.py +146 -0
  18. toga_qt-0.5.3/src/toga_qt/libs/__init__.py +3 -0
  19. toga_qt-0.5.3/src/toga_qt/libs/env.py +12 -0
  20. toga_qt-0.5.3/src/toga_qt/libs/testing.py +5 -0
  21. toga_qt-0.5.3/src/toga_qt/libs/utils.py +20 -0
  22. toga_qt-0.5.3/src/toga_qt/paths.py +20 -0
  23. toga_qt-0.5.3/src/toga_qt/resources/activityindicator.qml +8 -0
  24. toga_qt-0.5.3/src/toga_qt/resources/toga.png +0 -0
  25. toga_qt-0.5.3/src/toga_qt/screens.py +52 -0
  26. toga_qt-0.5.3/src/toga_qt/statusicons.py +35 -0
  27. toga_qt-0.5.3/src/toga_qt/widgets/__init__.py +0 -0
  28. toga_qt-0.5.3/src/toga_qt/widgets/activityindicator.py +48 -0
  29. toga_qt-0.5.3/src/toga_qt/widgets/base.py +128 -0
  30. toga_qt-0.5.3/src/toga_qt/widgets/box.py +19 -0
  31. toga_qt-0.5.3/src/toga_qt/widgets/button.py +52 -0
  32. toga_qt-0.5.3/src/toga_qt/widgets/imageview.py +46 -0
  33. toga_qt-0.5.3/src/toga_qt/widgets/label.py +31 -0
  34. toga_qt-0.5.3/src/toga_qt/widgets/switch.py +34 -0
  35. toga_qt-0.5.3/src/toga_qt/widgets/textinput.py +77 -0
  36. toga_qt-0.5.3/src/toga_qt/window.py +386 -0
  37. toga_qt-0.5.3/src/toga_qt.egg-info/PKG-INFO +60 -0
  38. toga_qt-0.5.3/src/toga_qt.egg-info/SOURCES.txt +59 -0
  39. toga_qt-0.5.3/src/toga_qt.egg-info/dependency_links.txt +1 -0
  40. toga_qt-0.5.3/src/toga_qt.egg-info/entry_points.txt +2 -0
  41. toga_qt-0.5.3/src/toga_qt.egg-info/requires.txt +5 -0
  42. toga_qt-0.5.3/src/toga_qt.egg-info/top_level.txt +1 -0
  43. toga_qt-0.5.3/tests_backend/app.py +166 -0
  44. toga_qt-0.5.3/tests_backend/dialogs.py +83 -0
  45. toga_qt-0.5.3/tests_backend/fonts.py +20 -0
  46. toga_qt-0.5.3/tests_backend/hardware/__init__.py +3 -0
  47. toga_qt-0.5.3/tests_backend/icons.py +54 -0
  48. toga_qt-0.5.3/tests_backend/images.py +14 -0
  49. toga_qt-0.5.3/tests_backend/probe.py +89 -0
  50. toga_qt-0.5.3/tests_backend/screens.py +20 -0
  51. toga_qt-0.5.3/tests_backend/widgets/__init__.py +0 -0
  52. toga_qt-0.5.3/tests_backend/widgets/activityindicator.py +10 -0
  53. toga_qt-0.5.3/tests_backend/widgets/base.py +98 -0
  54. toga_qt-0.5.3/tests_backend/widgets/box.py +12 -0
  55. toga_qt-0.5.3/tests_backend/widgets/button.py +21 -0
  56. toga_qt-0.5.3/tests_backend/widgets/imageview.py +19 -0
  57. toga_qt-0.5.3/tests_backend/widgets/label.py +27 -0
  58. toga_qt-0.5.3/tests_backend/widgets/properties.py +28 -0
  59. toga_qt-0.5.3/tests_backend/widgets/switch.py +11 -0
  60. toga_qt-0.5.3/tests_backend/widgets/textinput.py +51 -0
  61. toga_qt-0.5.3/tests_backend/window.py +120 -0
@@ -0,0 +1,11 @@
1
+ # Contributing
2
+
3
+ BeeWare <3's contributions!
4
+
5
+ Please be aware that BeeWare operates under a [Code of
6
+ Conduct](https://beeware.org/community/behavior/code-of-conduct/).
7
+
8
+ If you'd like to contribute to Toga development, our [contribution
9
+ guide](https://toga.beeware.org/en/latest/how-to/contribute/index.html) details how
10
+ to set up a development environment, and other requirements we have as part of our
11
+ contribution process.
toga_qt-0.5.3/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2025 Russell Keith-Magee.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of Toga nor the names of its contributors may
15
+ be used to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
toga_qt-0.5.3/PKG-INFO ADDED
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: toga-qt
3
+ Version: 0.5.3
4
+ Summary: An Qt (KDE) backend for the Toga widget toolkit.
5
+ Author-email: Russell Keith-Magee <russell@keith-magee.com>
6
+ Maintainer-email: BeeWare Team <team@beeware.org>
7
+ License-Expression: BSD-3-Clause
8
+ Keywords: gui,widget,cross-platform,toga,desktop,qt
9
+ Classifier: Development Status :: 1 - Planning
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Topic :: Software Development :: User Interfaces
21
+ Classifier: Topic :: Software Development :: Widget Sets
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: qasync==0.28.0
26
+ Requires-Dist: toga-core==0.5.3
27
+ Provides-Extra: pyside6
28
+ Requires-Dist: PySide6-Essentials==6.10.1; extra == "pyside6"
29
+ Dynamic: license-file
30
+ Dynamic: requires-dist
31
+
32
+ # toga-qt
33
+
34
+ [![Python Versions](https://img.shields.io/pypi/pyversions/toga-qt.svg)](https://pypi.python.org/pypi/toga-qt)
35
+ [![BSD-3-Clause License](https://img.shields.io/pypi/l/toga-qt.svg)](https://github.com/beeware/toga-qt/blob/main/LICENSE)
36
+ [![Project status](https://img.shields.io/pypi/status/toga-qt.svg)](https://pypi.python.org/pypi/toga-qt)
37
+
38
+ A Qt backend for the [Toga widget toolkit](https://beeware.org/toga).
39
+
40
+ This package isn't much use by itself; it needs to be combined with [the core Toga library](https://pypi.python.org/pypi/toga-core).
41
+
42
+ For platform requirements, see the [Qt platform documentation](https://toga.beeware.org/en/latest/reference/platforms/linux/qt#prerequisites).
43
+
44
+ For more details, see the [Toga project on GitHub](https://github.com/beeware/toga).
45
+
46
+ ## Community
47
+
48
+ Toga is part of the [BeeWare suite](https://beeware.org). You can talk to the community through:
49
+
50
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
51
+ - [Discord](https://beeware.org/bee/chat/)
52
+ - The Toga [GitHub Discussions forum](https://github.com/beeware/toga/discussions)
53
+
54
+ We foster a welcoming and respectful community as described in our [BeeWare Community Code of Conduct](https://beeware.org/community/behavior/).
55
+
56
+ ## Contributing
57
+
58
+ If you experience problems with Toga, [log them on GitHub](https://github.com/beeware/toga/issues).
59
+
60
+ If you'd like to contribute to Toga development, our [contribution guide](https://toga.beeware.org/en/latest/how-to/contribute) details how to set up a development environment, and other requirements we have as part of our contribution process.
@@ -0,0 +1,29 @@
1
+ # toga-qt
2
+
3
+ [![Python Versions](https://img.shields.io/pypi/pyversions/toga-qt.svg)](https://pypi.python.org/pypi/toga-qt)
4
+ [![BSD-3-Clause License](https://img.shields.io/pypi/l/toga-qt.svg)](https://github.com/beeware/toga-qt/blob/main/LICENSE)
5
+ [![Project status](https://img.shields.io/pypi/status/toga-qt.svg)](https://pypi.python.org/pypi/toga-qt)
6
+
7
+ A Qt backend for the [Toga widget toolkit](https://beeware.org/toga).
8
+
9
+ This package isn't much use by itself; it needs to be combined with [the core Toga library](https://pypi.python.org/pypi/toga-core).
10
+
11
+ For platform requirements, see the [Qt platform documentation](https://toga.beeware.org/en/latest/reference/platforms/linux/qt#prerequisites).
12
+
13
+ For more details, see the [Toga project on GitHub](https://github.com/beeware/toga).
14
+
15
+ ## Community
16
+
17
+ Toga is part of the [BeeWare suite](https://beeware.org). You can talk to the community through:
18
+
19
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
20
+ - [Discord](https://beeware.org/bee/chat/)
21
+ - The Toga [GitHub Discussions forum](https://github.com/beeware/toga/discussions)
22
+
23
+ We foster a welcoming and respectful community as described in our [BeeWare Community Code of Conduct](https://beeware.org/community/behavior/).
24
+
25
+ ## Contributing
26
+
27
+ If you experience problems with Toga, [log them on GitHub](https://github.com/beeware/toga/issues).
28
+
29
+ If you'd like to contribute to Toga development, our [contribution guide](https://toga.beeware.org/en/latest/how-to/contribute) details how to set up a development environment, and other requirements we have as part of our contribution process.
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools==80.9.0",
4
+ "setuptools_scm==9.2.2",
5
+ "setuptools_dynamic_dependencies==1.0.0",
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ dynamic = ["version", "dependencies"]
11
+ name = "toga-qt"
12
+ description = "An Qt (KDE) backend for the Toga widget toolkit."
13
+ readme = "README.md"
14
+ requires-python = ">= 3.10"
15
+ license = "BSD-3-Clause"
16
+ license-files = [
17
+ "LICENSE"
18
+ ]
19
+ authors = [
20
+ {name="Russell Keith-Magee", email="russell@keith-magee.com"},
21
+ ]
22
+ maintainers = [
23
+ {name="BeeWare Team", email="team@beeware.org"},
24
+ ]
25
+ keywords = [
26
+ "gui",
27
+ "widget",
28
+ "cross-platform",
29
+ "toga",
30
+ "desktop",
31
+ "qt",
32
+ ]
33
+ classifiers = [
34
+ "Development Status :: 1 - Planning",
35
+ "Intended Audience :: Developers",
36
+ "Operating System :: OS Independent",
37
+ "Programming Language :: Python :: 3",
38
+ "Programming Language :: Python :: 3.10",
39
+ "Programming Language :: Python :: 3.11",
40
+ "Programming Language :: Python :: 3.12",
41
+ "Programming Language :: Python :: 3.13",
42
+ "Programming Language :: Python :: 3.14",
43
+ "Programming Language :: Python :: 3 :: Only",
44
+ "Topic :: Software Development",
45
+ "Topic :: Software Development :: User Interfaces",
46
+ "Topic :: Software Development :: Widget Sets",
47
+ ]
48
+
49
+
50
+ [project.entry-points."toga.backends"]
51
+ linux = "toga_qt"
52
+
53
+ [tool.setuptools_scm]
54
+ root = ".."
55
+
56
+ [tool.setuptools_dynamic_dependencies]
57
+ dependencies = [
58
+ "qasync == 0.28.0",
59
+ "toga-core == {version}",
60
+ ]
61
+
62
+ [project.optional-dependencies]
63
+ pyside6 = [
64
+ "PySide6-Essentials == 6.10.1",
65
+ ]
66
+
67
+ [tool.coverage.run]
68
+ parallel = true
69
+ branch = true
70
+ relative_files = true
71
+
72
+ # See notes in the root pyproject.toml file.
73
+ source = ["src"]
74
+ source_pkgs = ["toga_qt"]
75
+
76
+ [tool.coverage.paths]
77
+ source = [
78
+ "src/toga_qt",
79
+ "**/toga_qt",
80
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ import travertino
2
+
3
+ __version__ = travertino._package_version(__file__, __name__)
@@ -0,0 +1,216 @@
1
+ import asyncio
2
+
3
+ from PySide6.QtCore import QSize, Qt
4
+ from PySide6.QtGui import QCursor, QGuiApplication
5
+ from PySide6.QtWidgets import QApplication, QMessageBox
6
+ from qasync import QEventLoop
7
+
8
+ import toga
9
+ from toga.command import Command, Group
10
+
11
+ from .command import EditOperation
12
+ from .libs import create_qapplication
13
+ from .screens import Screen as ScreenImpl
14
+
15
+
16
+ def _create_about_dialog(app):
17
+ """
18
+ Qt has an API, namely QMessageBox.about etc, to produce these
19
+ dialogs. However, these static APIs are blocking and modal, which
20
+ is unlike native apps on KDE where the About dialogs are non-modal.
21
+ """
22
+
23
+ message = (
24
+ f'<h2 style="font-weight: normal; margin-bottom: 0px">'
25
+ f"{app.interface.formal_name}</h2>"
26
+ )
27
+ versionauthor = []
28
+ if app.interface.version:
29
+ versionauthor.append(f"Version {app.interface.version}")
30
+ if app.interface.author:
31
+ versionauthor.append(f"Copyright \u00a9 {app.interface.author}")
32
+ if versionauthor != []:
33
+ message += f"<p>{'<br>'.join(versionauthor)}</p>"
34
+ if app.interface.home_page:
35
+ message += (
36
+ f"<p><a href={app.interface.home_page}>{app.interface.home_page}</a></p>"
37
+ )
38
+ dialog = QMessageBox(
39
+ QMessageBox.Information,
40
+ app.interface.formal_name,
41
+ message,
42
+ QMessageBox.NoButton,
43
+ app.get_current_window(),
44
+ )
45
+ icon = dialog.windowIcon()
46
+ dialog.setIconPixmap(icon.pixmap(icon.actualSize(QSize(64, 64))))
47
+ dialog.setModal(False)
48
+ return dialog
49
+
50
+
51
+ class App:
52
+ # Qt apps exit when the last window is closed
53
+ CLOSE_ON_LAST_WINDOW = True
54
+ # Qt apps use default command line handling
55
+ HANDLES_COMMAND_LINE = False
56
+
57
+ def __init__(self, interface):
58
+ self.interface = interface
59
+ self.interface._impl = self
60
+
61
+ self.native = create_qapplication()
62
+ self.loop = QEventLoop(self.native)
63
+ asyncio.set_event_loop(self.loop)
64
+ self.app_close_event = asyncio.Event()
65
+ # Connect the native signal to an asyncio Event in order
66
+ # for the main event loop to finish running upon app exit
67
+ self.native.aboutToQuit.connect(self.app_close_event.set)
68
+ # Qt does not have a native "applicaction started" signal;
69
+ # however, tasks scheduled on the event loop will only start
70
+ # as soon as the application is running.
71
+ self.loop.call_soon_threadsafe(self.interface._startup)
72
+
73
+ self.cursorhidden = False
74
+
75
+ ######################################################################
76
+ # Commands and menus
77
+ ######################################################################
78
+
79
+ def create_standard_commands(self):
80
+ # On KDE, default bundled apps have the following extra commands,
81
+ # and they automatically enable / disable based on if the associated
82
+ # functionality is available for the current focused widget.
83
+ # There's not a satisfying way to implement that in Qt though...
84
+ # I've referenced https://stackoverflow.com/questions/2047456, so
85
+ # we omit the enabled detection for now.
86
+ # Those KDE bundled apps only have one textfield in the application,
87
+ # so it's trivial for them to implement it.
88
+ self.interface.commands.add(
89
+ Command(
90
+ EditOperation("undo"),
91
+ "Undo",
92
+ shortcut=toga.Key.MOD_1 + "z",
93
+ group=Group.EDIT,
94
+ order=10,
95
+ ),
96
+ Command(
97
+ EditOperation("redo"),
98
+ "Redo",
99
+ shortcut=toga.Key.SHIFT + toga.Key.MOD_1 + "z",
100
+ group=Group.EDIT,
101
+ order=20,
102
+ ),
103
+ Command(
104
+ EditOperation("cut", True),
105
+ "Cut",
106
+ shortcut=toga.Key.MOD_1 + "x",
107
+ group=Group.EDIT,
108
+ section=10,
109
+ order=10,
110
+ ),
111
+ Command(
112
+ EditOperation("copy"),
113
+ "Copy",
114
+ shortcut=toga.Key.MOD_1 + "c",
115
+ group=Group.EDIT,
116
+ section=10,
117
+ order=20,
118
+ ),
119
+ Command(
120
+ EditOperation("paste", True),
121
+ "Paste",
122
+ shortcut=toga.Key.MOD_1 + "v",
123
+ group=Group.EDIT,
124
+ section=10,
125
+ order=30,
126
+ ),
127
+ )
128
+
129
+ def create_menus(self):
130
+ for window in self.interface.windows:
131
+ if hasattr(window._impl, "create_menus"): # pragma: no branch
132
+ window._impl.create_menus()
133
+
134
+ ######################################################################
135
+ # App lifecycle
136
+ ######################################################################
137
+
138
+ # We can't call this under test conditions, because it would kill the test harness
139
+ def exit(self): # pragma: no cover
140
+ self.native.quit()
141
+
142
+ def main_loop(self):
143
+ self.loop.run_until_complete(self.app_close_event.wait())
144
+
145
+ def set_icon(self, icon):
146
+ for window in QApplication.topLevelWidgets():
147
+ window.setWindowIcon(icon._impl.native)
148
+ self.interface.commands[Command.ABOUT].icon = icon
149
+ self.interface.commands[Command.PREFERENCES].icon = icon
150
+
151
+ def set_main_window(self, window):
152
+ pass
153
+
154
+ ######################################################################
155
+ # App resources
156
+ ######################################################################
157
+
158
+ def get_screens(self):
159
+ screens = QGuiApplication.screens()
160
+ primary = QGuiApplication.primaryScreen()
161
+ screens = [primary] + [
162
+ s for s in screens if s != primary
163
+ ] # Ensure first is primary
164
+
165
+ return [ScreenImpl(native=monitor) for monitor in screens]
166
+
167
+ ######################################################################
168
+ # App state
169
+ ######################################################################
170
+
171
+ def get_dark_mode_state(self):
172
+ return QGuiApplication.styleHints().colorScheme() == Qt.ColorScheme.Dark
173
+
174
+ ######################################################################
175
+ # App capabilities
176
+ ######################################################################
177
+
178
+ async def _beep(self):
179
+ process = await asyncio.create_subprocess_exec(
180
+ "canberra-gtk-play", "-i", "bell"
181
+ )
182
+ await process.wait()
183
+
184
+ def beep(self):
185
+ asyncio.create_task(self._beep())
186
+
187
+ def show_about_dialog(self):
188
+ # A reference to the about dialog is stored for facilitate testing.
189
+ # A new instance is created each time to ensure correct window
190
+ # membership.
191
+ self._about_dialog = _create_about_dialog(self)
192
+ self._about_dialog.show()
193
+
194
+ ######################################################################
195
+ # Cursor control
196
+ ######################################################################
197
+
198
+ def hide_cursor(self):
199
+ if not self.cursorhidden:
200
+ self.cursorhidden = True
201
+ self.native.setOverrideCursor(QCursor(Qt.BlankCursor))
202
+
203
+ def show_cursor(self):
204
+ if self.cursorhidden:
205
+ self.cursorhidden = False
206
+ self.native.restoreOverrideCursor()
207
+
208
+ ######################################################################
209
+ # Window control
210
+ ######################################################################
211
+
212
+ def get_current_window(self):
213
+ return self.native.activeWindow()
214
+
215
+ def set_current_window(self, window):
216
+ window._impl.native.activateWindow()
@@ -0,0 +1,14 @@
1
+ from PySide6.QtGui import QColor
2
+ from travertino.colors import rgb
3
+
4
+
5
+ def native_color(c):
6
+ if c == "transparent":
7
+ return QColor(0, 0, 0, 0)
8
+ return QColor(c.rgba.r, c.rgba.g, c.rgba.b, c.rgba.a * 255)
9
+
10
+
11
+ def toga_color(c):
12
+ if c.alpha() == 0 and c.red() == 0 and c.green() == 0 and c.blue() == 0:
13
+ return "transparent"
14
+ return rgb(c.red(), c.green(), c.blue(), c.alpha() / 255)
@@ -0,0 +1,142 @@
1
+ import sys
2
+
3
+ from PySide6.QtGui import QAction
4
+ from PySide6.QtWidgets import QApplication
5
+
6
+ from toga import Command as StandardCommand, Group, Key
7
+
8
+ from .keys import toga_to_qt_key
9
+
10
+
11
+ class EditOperation:
12
+ """
13
+ Perform a menu item property onto the focused widget, similar to
14
+ SEL in Objective-C. This is used to implement the Edit, Copy, etc.
15
+ actions.
16
+
17
+ :param: needwrite: Whether write access is required for the focus
18
+ widget.
19
+ """
20
+
21
+ def __init__(self, method_name, needwrite=False):
22
+ self.method_name = method_name
23
+ self.needwrite = needwrite
24
+
25
+ def __call__(self, interface):
26
+ fw = QApplication.focusWidget()
27
+ if not fw:
28
+ return
29
+ if self.needwrite:
30
+ fnwrite = getattr(fw, "isReadOnly", None)
31
+ if fnwrite():
32
+ return
33
+ fn = getattr(fw, self.method_name, None)
34
+ fn()
35
+
36
+
37
+ class Command:
38
+ """
39
+ Command `native` property is a list of native widgets associated with the command.
40
+
41
+ Native widgets is of type QAction
42
+ """
43
+
44
+ def __init__(self, interface):
45
+ self.interface = interface
46
+ self.native = []
47
+
48
+ @classmethod
49
+ def standard(self, app, id):
50
+ # ---- File menu ----------
51
+ if id == StandardCommand.PREFERENCES:
52
+ return {
53
+ "text": "Configure " + app.formal_name,
54
+ "shortcut": Key.MOD_1 + Key.SHIFT + ",",
55
+ "group": Group.SETTINGS,
56
+ "section": sys.maxsize - 1,
57
+ "icon": app.icon,
58
+ }
59
+ elif id == StandardCommand.EXIT:
60
+ return {
61
+ "text": "Quit",
62
+ "shortcut": Key.MOD_1 + "q",
63
+ "group": Group.FILE,
64
+ "section": sys.maxsize,
65
+ }
66
+
67
+ # ---- File menu -----------------------------------
68
+ elif id == StandardCommand.NEW:
69
+ return {
70
+ "text": "New",
71
+ "shortcut": Key.MOD_1 + "n",
72
+ "group": Group.FILE,
73
+ "section": 0,
74
+ "order": 0,
75
+ }
76
+ elif id == StandardCommand.OPEN:
77
+ return {
78
+ "text": "Open...",
79
+ "shortcut": Key.MOD_1 + "o",
80
+ "group": Group.FILE,
81
+ "section": 10,
82
+ "order": 0,
83
+ }
84
+
85
+ elif id == StandardCommand.SAVE:
86
+ return {
87
+ "text": "Save",
88
+ "shortcut": Key.MOD_1 + "s",
89
+ "group": Group.FILE,
90
+ "section": 20,
91
+ "order": 0,
92
+ }
93
+ elif id == StandardCommand.SAVE_AS:
94
+ return {
95
+ "text": "Save As...",
96
+ "shortcut": Key.MOD_1 + "S",
97
+ "group": Group.FILE,
98
+ "section": 20,
99
+ "order": 10,
100
+ }
101
+ elif id == StandardCommand.SAVE_ALL:
102
+ return {
103
+ "text": "Save All",
104
+ "shortcut": Key.MOD_1 + "l",
105
+ "group": Group.FILE,
106
+ "section": 20,
107
+ "order": 20,
108
+ }
109
+ # ---- Help menu -----------------------------------
110
+ elif id == StandardCommand.VISIT_HOMEPAGE:
111
+ return None # KDE apps have homepage link in about dialog
112
+ elif id == StandardCommand.ABOUT:
113
+ return {
114
+ "text": f"About {app.formal_name}",
115
+ "group": Group.HELP,
116
+ "section": sys.maxsize,
117
+ "icon": app.icon,
118
+ }
119
+
120
+ raise ValueError(f"Unknown standard command {id!r}")
121
+
122
+ def set_enabled(self, value):
123
+ enabled = self.interface.enabled
124
+ for widget in self.native:
125
+ widget.setEnabled(enabled)
126
+
127
+ def create_menu_item(self):
128
+ item = QAction(self.interface.text)
129
+
130
+ if self.interface.icon:
131
+ item.setIcon(self.interface.icon._impl.native)
132
+
133
+ item.triggered.connect(self.interface.action)
134
+
135
+ if self.interface.shortcut is not None:
136
+ item.setShortcut(toga_to_qt_key(self.interface.shortcut))
137
+
138
+ item.setEnabled(self.interface.enabled)
139
+
140
+ self.native.append(item)
141
+
142
+ return item
@@ -0,0 +1,39 @@
1
+ from PySide6.QtWidgets import QWidget
2
+
3
+
4
+ class Container:
5
+ def __init__(self, content=None, layout_native=None, on_refresh=None):
6
+ self.native = QWidget()
7
+ self.native.hide()
8
+ self.layout_native = self.native if layout_native is None else layout_native
9
+ self._content = None
10
+ self.on_refresh = on_refresh
11
+
12
+ self.content = content # Set initial content
13
+
14
+ @property
15
+ def width(self):
16
+ return self.layout_native.width()
17
+
18
+ @property
19
+ def height(self):
20
+ return self.layout_native.height()
21
+
22
+ @property
23
+ def content(self):
24
+ return self._content
25
+
26
+ @content.setter
27
+ def content(self, widget):
28
+ if self.content:
29
+ self._content.container = None
30
+ self._content.native.setParent(None)
31
+
32
+ self._content = widget
33
+
34
+ if widget:
35
+ widget.container = self
36
+ widget.native.setParent(self.native)
37
+
38
+ def refreshed(self):
39
+ self.on_refresh(self)