Skip to content

spike(qt): visual ceiling, polished Widgets vs QML (8c follow-up) - #30

Draft
DesvoSoft wants to merge 5 commits into
devfrom
spike/qt-visual
Draft

DesvoSoft wants to merge 5 commits into
devfrom
spike/qt-visual

Conversation

@DesvoSoft

Copy link
Copy Markdown
Owner

This is a follow-up to #29 and builds on its branch. The question here is not speed (#29 covered that) but how far Qt can take the look of the app. The full write-up is the "Visual ceiling" section of docs/QT-SPIKE.md. This is a draft and exploratory; please don't merge.

The same main screen is built twice: server list, status header with Start/Stop, console and command bar. Both versions sit on one QObject backend over app/api and use the design tokens, the website's line icons, Mica and Spanish.

  • A: Qt Widgets (--ui=qt --variant=widgets): painted animated buttons, a pulsing status pill, a sliding selection indicator, real shadows, skeletons and Mica.
  • B: Qt Quick/QML (--ui=qt --variant=qml): MultiEffect shadows, glow and frosted glass, page transitions, and an animated ListView.

Results

A: Widgets B: QML
Frames during transitions p95 18.5–20.3 ms, max ~32 ms p95 18.2 ms, max ≤ 20.6 ms (locked to 60 Hz)
CPU when idle / animating 0.2 / 3.1–3.7 % 0.6–0.8 / 0.8–0.9 % (GPU 3–5 %)
Screen open to first frame 264–392 ms 446–471 ms
Exe size (one-file, trimmed) 31.4 MiB 46.1 MiB
Packaged cold start 1,725 ms 2,053 ms (CustomTkinter: 2,410 ms)
Packaged memory (USS) 49 MB 66 MB
Lines of code 547 412 QML + 76 Python

The two versions share 391 lines.

Recommendation: Widgets-first hybrid. Use Widgets for every screen, with a small design kit built from variant A. QML stays an option for a dashboard island later, at a cost of +15 MiB and a second language. For 8d this means +2 days in step 1, about 26 days in total.

Limits found

  • Widgets:
    • One graphics effect per widget, and they can't be nested.
    • No blur of the app's own content behind a panel.
  • QML:
    • Accessibility: must be added by hand.
    • Translations: there is no gettext in QML, so strings come from Python.
    • Packaging and dev setup:
      • Plugin paths hit the Windows MAX_PATH limit from a deep venv.
      • When trimming the plugin set, a wrong cut only shows up at runtime. Importing QtQuick needs QtNetwork and QtOpenGL.

Reproduce with tools/qt_visual_bench.py and packaging/qt_visual.spec.

Roadmap 8c. app/ui_qt sits only on app/api: server list and live status
header, batched console with command input, close-to-tray choice and
QSystemTrayIcon, the Engine & Version dialog, a dark/light theme built
from the AppConfig tokens, and Spanish from the same gettext catalog
(28 new strings). QtDispatcher posts API callbacks to the GUI thread
through a queued signal.

packaging/qt_spike.spec builds a 30.9 MiB one-file exe; PySide6 lives
only in requirements-qt.txt and the release specs exclude it.
tools/qt_spike_bench.py reproduces the numbers in docs/QT-SPIKE.md
(GO with conditions); ROADMAP 8c updated.
# Conflicts:
#	assets/locales/es/LC_MESSAGES/zbb.mo
#	assets/locales/es/LC_MESSAGES/zbb.po
#	assets/locales/zbb.pot
The same main screen two ways over one QObject backend on app/api:
Qt Widgets pushed with painted animated controls, real shadows, a sliding
selection indicator, skeletons and a Mica backdrop (--variant=widgets),
and Qt Quick/QML with MultiEffect shadows, glow and frosted glass,
transitions and an animated ListView (--variant=qml). Shared line icons
in the website's style, Spanish from the gettext catalog.

tools/qt_visual_bench.py measures open time, frame intervals, CPU/GPU,
console bursts and captures the app window only; packaging/qt_visual.spec
builds either variant. Results and the Widgets-first hybrid
recommendation are in docs/QT-SPIKE.md.
Keeps the native window styles (shadow, rounded corners, animations,
Aero Snap, edge resizing) and removes only the caption: WM_NCCALCSIZE
drops the non-client area (and keeps a maximized window inside the work
area), WM_NCHITTEST marks the caption strip, the resize edges and the
maximize button (HTMAXBUTTON, so Windows 11 shows Snap Layouts).
Windows 11-style caption buttons with Segoe Fluent Icons glyphs.

With a removed caption DWM leaves the old caption band without Mica, so
the custom bar uses opaque theme backgrounds; both options and the
verification are documented in docs/QT-SPIKE.md.
@DesvoSoft

Copy link
Copy Markdown
Owner Author

Added a custom Windows 11 title bar to variant A (app/ui_qt/visual/titlebar.py, commit e648e4e).

  • What still works: the native styles are kept, so shadow, rounded corners, Aero Snap and edge resizing all still work.
  • What is custom: only the caption is removed, through WM_NCCALCSIZE and WM_NCHITTEST. The maximize button returns HTMAXBUTTON, which is what makes Windows 11 show Snap Layouts.
  • Verified here:
    • All 10 hit-test points return the expected value.
    • A non-client click on maximize fills the work area exactly, and a second click restores the window.
    • Minimize works.
  • Limit: with the caption removed, DWM does not draw Mica in the old caption band, which leaves a seam. The custom bar therefore uses opaque theme backgrounds. Native bar with Mica remains the alternative.

Screenshots of both options are in the "Custom title bar (Widgets)" section of docs/QT-SPIKE.md.

This branch has not been deployed

No deployments
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.

1 participant