Skip to content

Conversation

@johnzhou721
Copy link
Contributor

@johnzhou721 johnzhou721 commented Sep 18, 2025

WIP: Fixes #1142

PR Checklist:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

@johnzhou721 johnzhou721 changed the title Testbed and core changes to support Qt backend operations New Qt Backend Sep 18, 2025
@johnzhou721 johnzhou721 changed the title New Qt Backend New Backend - Qt Sep 18, 2025
@johnzhou721
Copy link
Contributor Author

@freakboy3742 By now I have not added CI testing yet because the test skips have not yet convereged at 100%

This is rough, I need to go back and clean up comments (especially some random jokes I've made -- if you have spare time I give you permission to try to spot them) but a few things I need confirmation on:

  • Is using linux_qt as the platform identification name okay with you? It detects this platform when TOGA_QT is set to 1 or if the application is running under KDE now.
  • I've mentioned the PySide6 import hack before -- specifically doing this:
import site
import sys


def import_pyside6():
    """Temporarily break isolation to import system PySide6."""
    system_site = site.getsitepackages()
    print(system_site)
    old_path = sys.path.copy()
    sys.path.extend(system_site)
    import PySide6  # noqa

    sys.path = old_path


import_pyside6()

So if beeware/briefcase#2480 (which I've filed for a feature to replace installed PySide6 with system symlink when packaging an app) doesn't get resolved in the duration of this PR, should I still remove this hack such that the new backend will not be able to look native and access system themes?

  • I've added a NativeIcon class to icons as seen at
    class NativeIcon:
    """
    For internal use for Qt backend only
    """
    def __init__(self, native): # pragma: no cover
    self.factory = get_platform_factory()
    self._impl = self.factory.NativeIcon(native)
    since native icons are needed for undo and redo actions in Qt -- see
    icon=NativeIcon(QIcon.fromTheme("edit-undo")),
    (it demands an interfaced icon there). Is this acceptable, and if not, how should I deal with this situation?

Thank you!

johnzhou721

This comment was marked as duplicate.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 Could you please respond to those questions I've asked about above? Thank you!

@freakboy3742
Copy link
Member

@freakboy3742 By now I have not added CI testing yet because the test skips have not yet convereged at 100%

This is rough, I need to go back and clean up comments (especially some random jokes I've made -- if you have spare time I give you permission to try to spot them) but a few things I need confirmation on:

  • Is using linux_qt as the platform identification name okay with you? It detects this platform when TOGA_QT is set to 1 or if the application is running under KDE now.

No. Platform identification is only used because sys.platform isn't a reliable identifier on Android (and, a long time ago, wasn't a reliable identifier on web). Once Python 3.13 is the oldest supported release, toga.platform.current_platform should be deprecated.

You'll also note that the current GTK backend doesn't describe its platform as GTK - it's linux.

The approach used by Textual is the model to follow here. If you're on Linux, toga_qt is a candidate backend; if that's the only backend, it's used; if there's more than one candidate installed in the current environment, TOGA_BACKEND=toga_qt disambiguates.

  • I've mentioned the PySide6 import hack before -- specifically doing this:
    ...
    So if beeware/briefcase#2480 (which I've filed for a feature to replace installed PySide6 with system symlink when packaging an app) doesn't get resolved in the duration of this PR, should I still remove this hack such that the new backend will not be able to look native and access system themes?

For now, I'd say yes. We can only cook with the ingredients we're given. If Qt doesn't work in virtual environments, that's a problem for Qt to resolve. It's not up to us to work around it - if only because there may be use cases for not adopting the global environment.

If there's a hack that could be installed, I'd argue the system_pyside6 approach I've described previously is the way to install that hack.

  • I've added a NativeIcon class to icons as seen at
    class NativeIcon:
    """
    For internal use for Qt backend only
    """
    def __init__(self, native): # pragma: no cover
    self.factory = get_platform_factory()
    self._impl = self.factory.NativeIcon(native)

    since native icons are needed for undo and redo actions in Qt -- see
    icon=NativeIcon(QIcon.fromTheme("edit-undo")),

    (it demands an interfaced icon there). Is this acceptable, and if not, how should I deal with this situation?

In core - No - or, at least, there's a much bigger concept that needs to be wrapped here. If the underlying question is about "system icons" - GTK has an analogous concept, which we don't currently handle. See #2441 for some initial thoughts about this (although it doesn't mention the GTK analog that exists).

@johnzhou721
Copy link
Contributor Author

johnzhou721 commented Sep 26, 2025

@freakboy3742 Thanks for the responses, there's much going on and I'm in the middle of a major comments cleanup (it's done through GitHub's review interface so expect a huge email next week with all the ntoes I made for myself, sorry about that)

Re to response 1: Then how the heck am I gonna get all the tests skipped by platform? Do I need a bunch of stub probes to just say "skip on Qt", sort of like GTK4?

Anyways if there's 1 platform only also for Briefcase how do we handle the testbed then? Right now I used tests_backend_qt for qt probes and hacks it at conftest.py: https://github.com/beeware/toga/pull/3769/files#diff-b845936988736dd9f884ab2aeb4175fa8ee7914217ed87d3c8aad18f803e74f0R24-R33 -- I can check the backend there [b]ut (EDITED TYPO) it just feels messy to have a hack like that at all.

Re to response 2: IMO system-pyside6 for symlinking system packages is not feasible. With all this wheel madness going on you simply don't know where the final system-pyside6 package even gets installed and you can't put symlinks in wheels either (it extracts as plain text when extracted by pip, which uses its own extraction logic). There's some options for "build destination" in setup.py but that's to like an intermediate location. Should I extract this sys.modules hackery into a system-pyside6 package that installs a shim PySide6.py to replace itself, then?

Re to response 3: Yeah, but it'd look really weird on Qt without like a native icon attached to the actions. KDE inserts extra space before an action with no icon to fill up for the icon space.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 Also do you think that beeware/briefcase#2480 is a candicate issue to be handled on Briefcase? Or are we like if PySide6 doesn't work it doesn't work and we're not going to have our packaging tool symlink a system copy?

johnzhou721

This comment was marked as resolved.

johnzhou721

This comment was marked as duplicate.

Increase delay to ensure it completes on slow systems
NFI what's going on with Qt tests... seems that canberra-gtk-play is not working sometimes.
Removed duplicate FIXME comment regarding unique value combinations.
@johnzhou721
Copy link
Contributor Author

@freakboy3742 Can macOS-arm64 be reran? Thanks.

@freakboy3742
Copy link
Member

Comments on this?

... I don't think this has clarified things as much as you think it has.

What I'm trying to get at here is that in the third diagram, we see that the cause of the recursion is that Qt under Wayland can't round-trip the correct window state a while later. MINIMIZED is sort of special here that it has this bug. But all the other window states would round-trip correctly under both X11 and Wayland. But then I'd argue that if reading the window state isn't even reliable, we'd just dissupport window states entirely. But reading the window state is reliable in non-minimized case.

It seems like you've missed a middle ground here: allow state transitions, and document (for now, at least) that retrieving the MIMIMIZED window state on Wayland may not work, skipping any tests that exercise that code path.

Again - we don't need to solve all problems at once. We need an initial proof of life that this backend "works" for some non-trivial definition of "work", with an architecture that passes the sniff tests, with a test suite that runs reliably without generating test failures. The feature set you've got here definitely meets the first criterion. We can iterate on new features once the core Qt support lands.

Right now, I'm trying to nail down the last of the "sniff test" items. An architecture for state changes that is prone to recursive behavior definitely doesn't pass that test.

@johnzhou721
Copy link
Contributor Author

johnzhou721 commented Oct 22, 2025

@freakboy3742 I've reworked the architechture to be much simpler to make it block the next 100ms whenever a window state change occurs (or is requested, in case there is another request between the request and the signal back from Qt). If in the 100ms block someone requests another state change from the programatic side, we buffer it and at the end of the 100ms, check for if the program requested a state change (NOT if the requested state change matches the current state), then we reapply. Multiple change events from the user in rapid succession will keep blocking until 100ms from the last one.

I'll wait for test pass, and rerequest a review. All items should be resolved.


EDIT -- To clarify: this will no longer handle rapid changes correctly if the user mashes keys or presses MINIMIZE in the middle of the changes, but you said in the Qt issue that this is not a major concern (as opposed to an infinite-loop architechutre).

@johnzhou721 johnzhou721 requested a review from kattni October 22, 2025 01:03
@freakboy3742
Copy link
Member

@johnzhou721 Ack - I'll take a look as soon as I can; early next week seems most likely.

@johnzhou721
Copy link
Contributor Author

@freakboy3742 FYI -- While updating the support table, I also corrected some errors in it -- they should be fairly obvious.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New backend - Qt

3 participants