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.
- toga_qt-0.5.3/CONTRIBUTING.md +11 -0
- toga_qt-0.5.3/LICENSE +27 -0
- toga_qt-0.5.3/PKG-INFO +60 -0
- toga_qt-0.5.3/README.md +29 -0
- toga_qt-0.5.3/pyproject.toml +80 -0
- toga_qt-0.5.3/setup.cfg +4 -0
- toga_qt-0.5.3/src/toga_qt/__init__.py +3 -0
- toga_qt-0.5.3/src/toga_qt/app.py +216 -0
- toga_qt-0.5.3/src/toga_qt/colors.py +14 -0
- toga_qt-0.5.3/src/toga_qt/command.py +142 -0
- toga_qt-0.5.3/src/toga_qt/container.py +39 -0
- toga_qt-0.5.3/src/toga_qt/dialogs.py +82 -0
- toga_qt-0.5.3/src/toga_qt/factory.py +64 -0
- toga_qt-0.5.3/src/toga_qt/fonts.py +11 -0
- toga_qt-0.5.3/src/toga_qt/icons.py +47 -0
- toga_qt-0.5.3/src/toga_qt/images.py +56 -0
- toga_qt-0.5.3/src/toga_qt/keys.py +146 -0
- toga_qt-0.5.3/src/toga_qt/libs/__init__.py +3 -0
- toga_qt-0.5.3/src/toga_qt/libs/env.py +12 -0
- toga_qt-0.5.3/src/toga_qt/libs/testing.py +5 -0
- toga_qt-0.5.3/src/toga_qt/libs/utils.py +20 -0
- toga_qt-0.5.3/src/toga_qt/paths.py +20 -0
- toga_qt-0.5.3/src/toga_qt/resources/activityindicator.qml +8 -0
- toga_qt-0.5.3/src/toga_qt/resources/toga.png +0 -0
- toga_qt-0.5.3/src/toga_qt/screens.py +52 -0
- toga_qt-0.5.3/src/toga_qt/statusicons.py +35 -0
- toga_qt-0.5.3/src/toga_qt/widgets/__init__.py +0 -0
- toga_qt-0.5.3/src/toga_qt/widgets/activityindicator.py +48 -0
- toga_qt-0.5.3/src/toga_qt/widgets/base.py +128 -0
- toga_qt-0.5.3/src/toga_qt/widgets/box.py +19 -0
- toga_qt-0.5.3/src/toga_qt/widgets/button.py +52 -0
- toga_qt-0.5.3/src/toga_qt/widgets/imageview.py +46 -0
- toga_qt-0.5.3/src/toga_qt/widgets/label.py +31 -0
- toga_qt-0.5.3/src/toga_qt/widgets/switch.py +34 -0
- toga_qt-0.5.3/src/toga_qt/widgets/textinput.py +77 -0
- toga_qt-0.5.3/src/toga_qt/window.py +386 -0
- toga_qt-0.5.3/src/toga_qt.egg-info/PKG-INFO +60 -0
- toga_qt-0.5.3/src/toga_qt.egg-info/SOURCES.txt +59 -0
- toga_qt-0.5.3/src/toga_qt.egg-info/dependency_links.txt +1 -0
- toga_qt-0.5.3/src/toga_qt.egg-info/entry_points.txt +2 -0
- toga_qt-0.5.3/src/toga_qt.egg-info/requires.txt +5 -0
- toga_qt-0.5.3/src/toga_qt.egg-info/top_level.txt +1 -0
- toga_qt-0.5.3/tests_backend/app.py +166 -0
- toga_qt-0.5.3/tests_backend/dialogs.py +83 -0
- toga_qt-0.5.3/tests_backend/fonts.py +20 -0
- toga_qt-0.5.3/tests_backend/hardware/__init__.py +3 -0
- toga_qt-0.5.3/tests_backend/icons.py +54 -0
- toga_qt-0.5.3/tests_backend/images.py +14 -0
- toga_qt-0.5.3/tests_backend/probe.py +89 -0
- toga_qt-0.5.3/tests_backend/screens.py +20 -0
- toga_qt-0.5.3/tests_backend/widgets/__init__.py +0 -0
- toga_qt-0.5.3/tests_backend/widgets/activityindicator.py +10 -0
- toga_qt-0.5.3/tests_backend/widgets/base.py +98 -0
- toga_qt-0.5.3/tests_backend/widgets/box.py +12 -0
- toga_qt-0.5.3/tests_backend/widgets/button.py +21 -0
- toga_qt-0.5.3/tests_backend/widgets/imageview.py +19 -0
- toga_qt-0.5.3/tests_backend/widgets/label.py +27 -0
- toga_qt-0.5.3/tests_backend/widgets/properties.py +28 -0
- toga_qt-0.5.3/tests_backend/widgets/switch.py +11 -0
- toga_qt-0.5.3/tests_backend/widgets/textinput.py +51 -0
- 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
|
+
[](https://pypi.python.org/pypi/toga-qt)
|
|
35
|
+
[](https://github.com/beeware/toga-qt/blob/main/LICENSE)
|
|
36
|
+
[](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.
|
toga_qt-0.5.3/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# toga-qt
|
|
2
|
+
|
|
3
|
+
[](https://pypi.python.org/pypi/toga-qt)
|
|
4
|
+
[](https://github.com/beeware/toga-qt/blob/main/LICENSE)
|
|
5
|
+
[](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
|
+
]
|
toga_qt-0.5.3/setup.cfg
ADDED
|
@@ -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)
|