Qt für WebAssembly

Mit Qt für Webassembly können Sie Qt-Anwendungen im Web ausführen.

WebAssembly (abgekürzt Wasm) ist ein binäres Befehlsformat, das für die Ausführung in einer virtuellen Maschine, zum Beispiel in einem Webbrowser, vorgesehen ist.

Mit Qt für WebAssembly können Sie Ihre Anwendung als Webanwendung verteilen, die in einer Browser-Sandbox ausgeführt wird. Dieser Ansatz eignet sich für verteilte Webanwendungen, die keinen vollen Zugriff auf die Funktionen des Host-Geräts erfordern.

Hinweis: Qt for WebAssembly ist eine unterstützte Plattform, aber einige Module werden noch nicht unterstützt oder befinden sich in der Tech Preview. Siehe Unterstützte Qt-Module.

Erste Schritte mit Qt für WebAssembly

Das Erstellen von Qt-Anwendungen für WebAssembly ist ähnlich wie das Erstellen von Qt für andere Plattformen. Sie müssen ein SDK (Emscripten) installieren, Qt installieren (oder Qt aus dem Quellcode erstellen) und schließlich die Anwendung erstellen. Es gibt einige Unterschiede, zum Beispiel unterstützt Qt für WebAssembly weniger Module und weniger Funktionen als andere Qt-Builds.

Installieren von Emscripten

Emscripten ist eine Toolchain für die Kompilierung nach WebAssembly. Mit ihr können Sie Qt im Web mit nahezu nativer Geschwindigkeit ohne Browser-Plugins ausführen.

Weitere Informationen zur Installation des Emscripten SDK finden Sie in der Emscripten-Dokumentation.

Nach der Installation sollten Sie den Emscripten-Compiler in Ihrem Pfad haben. Überprüfen Sie dies mit dem folgenden Befehl:

em++ --version

Jede Nebenversion von Qt zielt auf eine bestimmte Emscripten-Version ab, die in Patch-Versionen unverändert bleibt. Die Binärpakete von Qt werden unter Verwendung der Emscripten-Zielversion erstellt. Anwendungen sollten die gleiche Version verwenden, da Emscripten keine ABI-Kompatibilität zwischen den Versionen garantiert.

Die Emscripten-Versionen sind:

  • Qt 6.2: 2.0.14
  • Qt 6.3: 3.0.0
  • Qt 6.4: 3.1.14
  • Qt 6.5: 3.1.25
  • Qt 6.6: 3.1.37
  • Qt 6.7: 3.1.50
  • Qt 6.8: 3.1.56

Verwenden Sie emsdk, um bestimmte Versionen von Emscripten zu installieren. Zur Installation von Qt 6.8 geben Sie zum Beispiel ein:

  • ./emsdk install 3.1.56
  • ./emsdk aktivieren 3.1.56

Unter Windows befindet sich Emscripten nach der Installation in Ihrem Pfad. Unter macOS oder Linux müssen Sie es zu Ihrem Pfad hinzufügen, etwa so:

source /path/to/emsdk/emsdk_env.sh

Überprüfen Sie dies mit dem folgenden Befehl:

em++ --version

Sie können Qt aus dem Quellcode bauen, wenn Sie mehr Flexibilität bei der Auswahl der Emscripten-Version benötigen. In diesem Fall sind die oben genannten Versionen Mindestversionen. Spätere Versionen werden voraussichtlich funktionieren, können aber Verhaltensänderungen einführen, die Änderungen an Qt erfordern.

Installieren von Qt

Laden Sie Qt aus dem Download-Bereich Ihres Qt-Kontos herunter. Wir bieten Builds für Linux, macOS und Windows als Entwicklungsplattformen an.

Die Binär-Builds sind so konzipiert, dass sie auf so vielen Browsern wie möglich ausgeführt werden können, und es gibt sie als Single-Thread- und Multi-Thread-Versionen. Nicht standardisierte Funktionen wie Wasm SIMD und Wasm exceptions werden von den Binär-Builds nicht unterstützt.

Qt aus dem Quellcode erstellen

Wenn Sie Qt aus dem Quellcode erstellen, können Sie Qt-Konfigurationsoptionen wie Thread-Unterstützung, OpenGL ES-Level oder SIMD-Unterstützung einstellen. Laden Sie die Qt-Quellen aus dem Download-Bereich Ihres Qt-Kontos herunter.

Konfigurieren Sie Qt als Cross-Compile-Build für die Plattform wasm-emscripten. Dadurch werden die Konfigurationsoptionen -static, -no-feature-thread und -no-make examples gesetzt. Sie können die Thread-Unterstützung mit der Option -feature-thread, configure aktivieren. Shared Library Builds werden nicht unterstützt.

Sie benötigen einen Host-Build der gleichen Qt-Version und geben diesen Pfad in der CMake-Variablen QT_HOST_PATH oder mit dem Argument -qt-host-path configure an.

./configure -qt-host-path /path/to/Qt -platform wasm-emscripten -prefix $PWD/qtbase

Hinweis: configure verwendet immer das Ninja Generator- und Build-Tool, wenn eine ninja ausführbare Datei verfügbar ist. Ninja ist plattformübergreifend, funktionsreich, leistungsfähig und wird für alle Plattformen empfohlen. Die Verwendung anderer Generatoren kann funktionieren, wird aber nicht offiziell unterstützt.

Stellen Sie unter Windows sicher, dass Sie Mingw-w64 in Ihrem PATH haben und konfigurieren Sie mit dem Folgenden:

configure -qt-host-path C:\Path\to\Qt -no-warnings-are-errors -platform wasm-emscripten -prefix %CD%\qtbase

Bauen Sie dann die erforderlichen Module:

cmake --build . -t qtbase -t qtdeclarative [-t another_module]

Erstellen von Anwendungen auf der Kommandozeile

Qt für WebAssembly unterstützt die Erstellung von Anwendungen mit qmake und make, oder CMake mit ninja oder make.

$ /path/to/qt-wasm/qtbase/bin/qt-cmake .
$ cmake --build .

Hinweis: Wenn Sie vanilla CMake (im Gegensatz zu qt-cmake unter Linux oder qt-cmake.bat unter Windows) verwenden, denken Sie daran, eine Toolchain-Datei mit "-DCMAKE_TOOLCHAIN_FILE" anzugeben, wie bei jedem anderen plattformübergreifenden Build. Für Details siehe hier: Erste Schritte mit CMake.

Die Erstellung der Anwendung erzeugt mehrere Ausgabedateien, darunter eine .wasm-Datei, die die Anwendung und den Qt-Code (statisch verlinkt) enthält, sowie eine .html-Datei, die im Browser geöffnet werden kann, um die Anwendung auszuführen.

Hinweis: Emscripten erzeugt auf der Debug-Ebene "-g" relativ große .wasm-Dateien. Ziehen Sie das Linken mit "-g2" für Debug-Builds in Betracht.

Ausführen von Anwendungen

Zum Ausführen der Anwendung ist ein Webserver erforderlich. Die Build-Ausgabedateien sind allesamt statische Inhalte, so dass jeder Webserver ausreicht. Einige Anwendungsfälle können eine spezielle Serverkonfiguration erfordern, z. B. die Bereitstellung von https-Zertifikaten oder das Setzen von http-Headern, die für die Unterstützung von Multithreading erforderlich sind.

Emrun

Emscripten bietet das Dienstprogramm emrun für den Test von Anwendungen. Emrun startet einen Webserver, startet einen Browser und erfasst und leitet stdout/stderr (die normalerweise an die JavaScript-Konsole gehen) weiter.

/path/to/emscripten/emrun --browser=firefox appname.html

Python http.server

Eine weitere Möglichkeit besteht darin, einen Entwicklungs-Webserver zu starten und dann den Webbrowser separat zu starten. Eine der einfachsten Optionen ist http.server von Python:

python -m http.server

Beachten Sie, dass es sich hierbei nur um einen einfachen Webserver handelt, der das für das Threading erforderliche SharedArrayBuffer nicht unterstützt, da die unten genannten erforderlichen COOP- und COED-Header nicht gesendet werden.

qtwasmserver

Qt stellt einen Entwickler-Webserver zur Verfügung, der mkcert verwendet, um https-Zertifikate zu erzeugen. Dies ermöglicht das Testen von Webfunktionen, die einen sicheren Kontext erfordern. Beachten Sie, dass die Übertragung über http://localhost auch als sicher gilt, ohne dass ein Zertifikat erforderlich ist.

Der Webserver setzt auch die COOP- und COEP-Header auf Werte, die die Unterstützung für SharedArrayBuffer und Multithreading ermöglichen.

Das qtwasmserver-Skript startet einen Server, der sich standardmäßig an localhost bindet. Sie können mit dem Kommandozeilenargument -a weitere Adressen hinzufügen oder --all verwenden, um sich an alle verfügbaren Adressen zu binden.

python /path/to/qtbase/util/wasm/qtwasmserver/qtwasmserver.py --all

Erstellen von Anwendungen mit Qt Creator

Einrichten von Qt Creator für WebAssembly.

Bereitstellen von Anwendungen im Web

Beim Erstellen einer Anwendung werden mehrere Dateien erzeugt (ersetzen Sie in der folgenden Tabelle "app" durch den Namen der Anwendung).

Erstellte DateiKurzbeschreibung
app.htmlHTML-Container
qtloader.jsJavaScript-API zum Laden von Qt-Anwendungen
app.jsVon Emscripten generierte JavaScript-Laufzeitumgebung
app.wasmapp.binary

Sie können app.html so einsetzen, wie sie ist, oder sie zugunsten einer eigenen HTML-Datei verwerfen. Kleinere Anpassungen, wie z.B. das Ändern des Splash-Screen-Bildes vom Qt-Logo zum app-Logo, sind ebenfalls möglich. In beiden Fällen bietet qtloader.js eine JavaScript-API zum Laden der Anwendung.

Komprimieren Sie die Wasm-Datei vor dem Deployment mit gzip oder brotli, da diese eine bessere Komprimierungsrate als die anderen Tools bieten. Weitere Informationen finden Sie unter Minimierung der Größe von Binärdateien.

Die Aktivierung bestimmter Funktionen, wie z. B. Multi-Threading und SIMD, erzeugt .wasm-Binärdateien, die mit Browsern, die die aktivierte Funktion nicht unterstützen, nicht kompatibel sind. Es ist möglich, diese Einschränkung zu umgehen, indem man mehrere .wasm-Dateien erstellt und dann die JavaScript-Feature-Erkennung verwendet, um die richtige auszuwählen, aber beachten Sie, dass Qt keine Funktionalität für diese Vorgehensweise bietet.

Verwendung von qtloader

Qt bietet eine JavaScript-API für das Herunterladen, Kompilieren und Instanziieren von Qt for WebAssembly-Anwendungen. Diese Lade-API umhüllt die von Emscripten bereitgestellte Ladefunktionalität und bietet zusätzliche nützliche Funktionen für Qt-basierte Anwendungen. Sie ist in der Datei qtloader.js implementiert. Eine Kopie dieser Datei wird zur Erstellungszeit in das Build-Verzeichnis geschrieben.

Eine typische Verwendung sieht wie folgt aus:

const app_container_element = ...;
const instance = await qtLoad({
    qt: {
        containerElements: [ app_container_element ],
        onLoaded: () => { /* handle application load completed */  },
        onExit: () => {  /* handle application exit */ },
    }
});

Der Code ruft die qtLoad()-Loader-Funktion mit einem Konfigurationsobjekt auf. Dieses Konfigurationsobjekt kann alle emscripten Konfigurationsoptionen sowie ein spezielles "qt" Konfigurationsobjekt enthalten. Das qt-Konfigurationsobjekt unterstützt die folgenden Eigenschaften:

EigenschaftKurzbeschreibung
containerElementsArray von HTML-Container-Elementen. Die Anwendung sieht diese als QScreens.
onLoadedCallback, wenn die Anwendung das Laden abgeschlossen hat.
onExitCallback für das Beenden der Anwendung.

Das containerElements-Array ist die Hauptschnittstelle zwischen Qt und der Webseite, wobei die HTML-Elemente in diesem Array (typischerweise <div>-Elemente) die Position des Anwendungsinhalts auf der Webseite angeben.

Die Anwendung sieht jedes Containerelement als eine QScreen Instanz und kann wie gewohnt Anwendungsfenster auf den Bildschirminstanzen platzieren. Fenster mit dem Status Qt::WindowFullScreen nutzen den gesamten Bildschirmbereich, während Nicht-"Fullscreen"-Fenster Fensterdekorationen erhalten.

Die Funktion qtLoad() gibt ein Versprechen zurück, das eine Emscripten-Instanz erzeugt, wenn es erwartet wird. Die Instanz ermöglicht den Zugriff auf die von Embind exportierten Funktionen. Qt exportiert mehrere solcher Funktionen, und diese Funktionen bilden die Instanz-API.

Verwendung der Qt-Instanz-API

Qt bietet mehrere Instanzfunktionen. Derzeit unterstützen diese das Hinzufügen und Entfernen von Containerelementen zur Laufzeit.

EigenschaftKurzbeschreibung
qtAddContainerElementHinzufügen eines Container-Elements. Beim Hinzufügen eines Elements wird ein neues QScreen hinzugefügt.
qtRemoveContainerElementEntfernt ein Containerelement und den dazugehörigen Bildschirm.
qtSetContainerElementsSetzt alle Containerelemente
qtResizeContainerElementBringt Qt dazu, Änderungen an der Größe von Containerelementen zu übernehmen.

Portierung auf den Qt 6.6 qtloader

Qt 6.6 enthält einen neuen qtloader mit einer vereinfachten Implementierung und einem kleineren Umfang. Dies beinhaltet API-Änderungen, die eine Portierung von Anwendungs-JavaScript-Code erfordern können. Qt bietet eine Kompatibilitäts-API, um den Übergang zu erleichtern. Je nach Anwendungsfall gibt es mehrere Möglichkeiten:

  • Wenn Sie die generierte Datei app.html direkt verwenden, wird diese Datei ebenfalls zur Build-Zeit aktualisiert. Es ist keine Aktion erforderlich.
  • Wenn Sie die grundlegenden qtloader-Funktionen verwenden, können Sie die in Qt 6.6 enthaltene Kompatibilitäts-API als vorübergehende Maßnahme verwenden. Diese API wird in einer zukünftigen Version entfernt werden; Sie sollten ein Update einplanen, um den neuen qtloader zu verwenden. Die Portierung von Schritt 1 unten ist erforderlich.
  • Wenn Sie erweiterte Funktionen verwenden (z. B. das Hinzufügen von Containerelementen zur Laufzeit), dann ist eine Portierung auf den neuen Loader oder die Instanz-API erforderlich. Die Portierungsschritte 1 und 2 unten sind erforderlich.

Portierungsschritte

  1. Binden Sie die app.js (von Emscripten generierte JavaScript-Laufzeit) in die ladende HTML-Datei ein.
    <script src="app.js"></script>

    Vor Qt 6.6 hat qtloader diese JavaScript-Datei geladen und ausgewertet. Dies geschieht nun nicht mehr, und die Datei muss mit einem <script>-Tag eingebunden werden.

  2. Portierung zur Verwendung der neuen JavaScript- und Instanz-API.

Siehe Dokumentationsabschnitte oben.

Unterstützte Browser

Desktop

Qt für WebAssembly wurde für die folgenden Browser entwickelt und getestet:

  • Chrome
  • Firefox
  • Safari
  • Edge

Qt sollte laufen, wenn der Browser WebAssembly unterstützt. Qt hat eine feste WebGL-Anforderung, auch wenn die Anwendung selbst keine hardwarebeschleunigte Grafik verwendet. Browser, die WebAssembly unterstützen, unterstützen oft WebGL, obwohl einige Browser ältere oder nicht unterstützte GPUs auf eine schwarze Liste setzen. s/qtloader.js bietet APIs, um zu prüfen, ob WebGL verfügbar ist.

Qt macht keinen direkten Gebrauch von Betriebssystemfunktionen und es macht keinen Unterschied, ob z.B. FireFox auf Windows oder macOS läuft. Qt verwendet einige Betriebssystemanpassungen, zum Beispiel für die Handhabung der Strg- und Cmd-Tasten unter macOS.

Mobil

Qt for WebAssembly-Anwendungen laufen auf mobilen Browsern wie Safari und Android Chrome.

Unterstützte Qt-Module

Qt für WebAssembly unterstützt eine Teilmenge der Qt-Module und -Funktionen. Getestete Module sind unten aufgelistet, andere Module können funktionieren oder auch nicht.

In allen Fällen ist die Modulunterstützung möglicherweise nicht vollständig und es können zusätzliche Einschränkungen bestehen, entweder aufgrund der Browser-Sandbox oder aufgrund der Unvollständigkeit der Qt-Plattformportierung. Weitere Informationen finden Sie unter Entwickeln mit Qt für WebAssembly.

Entwickeln mit Qt für WebAssembly

Bauen mit CMake

Wenn eine Emscripten-spezifische Konfiguration in CMake erforderlich ist, kann der folgende Code verwendet werden:

if(EMSCRIPTEN)
    # WebAssembly specific code
else()
    # other platforms
endif()

Dieser Code ermöglicht die Anpassung von Emscripten-spezifischen Konfigurationen und gewährleistet gleichzeitig die Kompatibilität mit anderen Plattformen.

OpenGL und WebGL

Qt for WebAssembly unterstützt hardwarebeschleunigtes Rendering mit https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API WebGL.

WebGL ist eng an OpenGL ES angelehnt, mit der folgenden Versionszuordnung:

OpenGLWebGL
OpengL ES 2WebGL 1
OpengL ES 3WebGL 2

Qt verwendet die höchste verfügbare WebGL-Version. Dies ist bei den heutigen Browsern in der Regel WebGL 2, kann aber auch WebGL 1 sein, wenn die Hardware dies nicht zulässt. Wir empfehlen, mit Qt for WebAssembly Geräte anzusteuern, die WebGL 2 unterstützen.

Die Unterschiede zwischen Web- und Desktop-OpenGL sind in WebGL- und OpenGL-Unterschiede dokumentiert. Es gibt weitere Unterschiede zwischen WebGL 1.0 und WebGL 2.0, die in der WebGL 2.0-Spezifikation dokumentiert sind.

Eine WebGL-freundliche Untermenge von ES2 (und ES3) wird standardmäßig verwendet. Wenn Sie glDrawArrays und glDrawElements ohne gebundene Puffer verwenden müssen, können Sie die volle ES2-Unterstützung aktivieren, indem Sie

target_link_options(<your target> PRIVATE -s FULL_ES2=1)

und/oder die vollständige ES3-Emulation durch Hinzufügen von

target_link_options(<your target> PRIVATE -s FULL_ES3=1)

zu Ihrem Projekt CMakeLists.txt hinzufügen.

Gemeinsame Nutzung von OpenGL-Kontexten

WebGL erlaubt nur einen Kontext pro nativer Oberfläche und unterstützt keine gemeinsame Nutzung von OpenGL-Kontexten. Qt umgeht dies, indem es den nativen Kontext wiederverwendet, wenn mehrere OpenGLContexts verwendet werden.

Wir empfehlen, die Verwendung von OpenGL-Kontexten auf einen Kontext pro nativer Oberfläche zu beschränken, z.B. durch die Verwendung von QOpenGLWindow anstelle von QOpenGLWidget. QOpenGLWidget kann funktionieren, vorausgesetzt, dass der Anwendungscode alle relevanten OpenGL-Zustände wiederherstellt, wenn er QOpenGLWidget::paintGL()-Aufrufe tätigt, und sich nicht darauf verlässt, dass der Zustand zwischen QOpenGLWidget::initializeGL()- und QOpenGLWidget::paintGL()-Aufrufen erhalten bleibt.

Multithreading

Qt for WebAssembly unterstützt Multithreading unter Verwendung der Pthreads-Unterstützung von Emscripten, wobei jeder Thread durch einen Webworker unterstützt wird. Aktivieren Sie Multithreading, indem Sie die Komponente "WebAssembly (multi-threaded)" von Qt Maintenance Tool installieren oder Qt aus dem Quellcode erstellen und das Flag "-feature-thread" an configure übergeben.

Vorhandener Threading-Code kann im Allgemeinen wiederverwendet werden, muss aber möglicherweise modifiziert werden, um die Besonderheiten der pthread-Implementierung zu umgehen. Einige Emscripten- und Qt-Features werden nicht unterstützt, dazu gehören das Thread-Proxying-Feature und die Qt Quick threaded render loop.

Beachten Sie, dass es besonders wichtig ist, den Haupt-Thread in Qt für WebAssembly nicht zu blockieren, da der Haupt-Thread möglicherweise benötigt wird, um Anfragen von sekundären Threads zu bedienen. Zum Beispiel werden alle Timer in Qt auf dem Hauptthread geplant und werden nicht ausgelöst, wenn der Hauptthread blockiert ist. Ein weiteres Beispiel ist, dass das Erstellen eines neuen Webworkers (für einen Thread) nur vom Hauptthread aus erfolgen kann.

Emscripten bietet hierfür einige Abhilfemaßnahmen. Kurzfristige Wartezeiten, wie z. B. das Einholen einer Mutex-Sperre, werden durch Busy-Waiting und die Verarbeitung von Ereignissen während des Wartens auf die Sperre unterstützt. Längere Wartezeiten auf dem Hauptthread sollten vermieden werden. Insbesondere die gängige Praxis, QThread::wait() oder pthread_join() aufzurufen, um auf einen sekundären Thread zu warten, wird nicht funktionieren, es sei denn, die Anwendung kann garantieren, dass der Thread (und der Webworker) bereits gestartet wurde und in der Lage ist, zum Zeitpunkt des wait()- oder join()-Aufrufs ohne Hilfe des Hauptthreads fertig zu werden.

Die Multithreading-Funktion erfordert die Unterstützung des Browsers für die SharedArrayBuffer-API. (Normalerweise speichert Emscripten den Heap in einem ArrayBuffer-Objekt. Für das Multithreading muss der Heap mit Web Workern geteilt werden und ein SharedArrayBuffer wird benötigt.) Diese API ist im Allgemeinen in allen modernen Browsern verfügbar, kann aber deaktiviert werden, wenn bestimmte Sicherheitsanforderungen nicht erfüllt sind. WebAssembly-Binärdateien mit aktivierter Thread-Unterstützung können dann nicht ausgeführt werden, auch wenn die Binärdatei nicht tatsächlich einen Thread startet.

Die Aktivierung von SharedArrayBuffer erfordert einen sicheren Browsing-Kontext (bei dem die Seite über https:// oder http://localhost bereitgestellt wird) und dass sich die Seite im herkunftsübergreifenden isolierten Modus befindet. Letzteres kann durch Setzen der so genannten COOP- und COEP-Header auf dem Webserver erreicht werden:

  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Embedder-Policy: require-corp

SIMD

Emscripten unterstützt WebAssembly SIMD, das 128-Bit-SIMD-Typen und -Operationen für WebAssembly bereitstellt.

Erzeugen Sie Qt aus dem Quellcode und konfigurieren Sie es mit dem Flag -feature-wasm-simd128, um es zu aktivieren; dies wird das Flag -msimd128 zur Kompilier- und Linkzeit übergeben. Beachten Sie, dass Qt zu diesem Zeitpunkt keine für wasm-simd optimierten Codepfade enthält. Wenn Sie jedoch wasm-simd aktivieren, wird die automatische Vektorisierung des Compilers aktiviert, bei der der Compiler die SIMD-Anweisungen verwenden kann.

Sie können WebAssembly SIMD direkt ansprechen, indem Sie entweder die GCC/Clang SIMD Vector Extensions oder die WASM SIMD128-Intrinsics verwenden. Weitere Informationen finden Sie in der Emscripten SIMD-Dokumentation .

Darüber hinaus unterstützt Emscripten die Emulation/Übersetzung von x86 SSE-Befehlen in Wasm SIMD-Befehle. Qt verwendet diese Emulation nicht, da die Verwendung von SSE-SIMD-Befehlen, die kein natives Wasm-SIMD-Äquivalent haben, zu Leistungseinbußen führen kann.

Beachten Sie, dass SIMD-aktivierte Binärdateien nicht mit Browsern kompatibel sind, die WebAssembly SIMD nicht unterstützen, auch wenn die SIMD-Codepfade nicht zur Laufzeit aufgerufen werden. Die SIMD-Unterstützung muss möglicherweise in den erweiterten Konfigurationen des Browsers aktiviert werden, z. B. in "about:config" oder "chrome:flags".

Vernetzung

Qt bietet begrenzte Unterstützung für Netzwerke. Im Allgemeinen können Netzwerkprotokolle, die bereits im Web verwendet werden, auch von Qt genutzt werden, während andere aufgrund der Web-Sandbox nicht direkt verfügbar sind.

Die folgenden Protokolle werden unterstützt:

  • QNetworkAccessManager http-Anfragen an den Ursprungsserver der Webseite oder an einen Server, der CORS unterstützt. Dies schließt XMLHttpRequest aus QML ein.
  • QWebSocket Verbindungen zu einem beliebigen Host. Beachten Sie, dass Webseiten, die über das sichere https-Protokoll bereitgestellt werden, nur Websockets-Verbindungen über das sichere wss-Protokoll zulassen.
  • Emulierte POSIX-TCP-Sockets über WebSockets, unter Verwendung der von Emscripten bereitgestellten Funktionalität. Beachten Sie, dass dazu ein Weiterleitungsserver erforderlich ist, der die Socket-Übersetzung übernimmt.

Alle anderen Netzwerkprotokolle werden nicht unterstützt.

Lokaler Dateizugriff

Der Zugriff auf das Dateisystem ist im Web in einer Sandbox untergebracht, was Auswirkungen darauf hat, wie die Anwendung mit Dateien arbeitet. Die Web-Plattform bietet APIs für den Zugriff auf das lokale Dateisystem in einer Weise, die unter der Kontrolle des Benutzers steht, sowie APIs für den Zugriff auf persistenten Speicher. Emscripten und Qt umhüllen diese Funktionen und bieten APIs, die von C++- und Qt-basierten Anwendungen aus leichter zu verwenden sind.

Die Webplattform bietet Funktionen für den Zugriff auf lokale Dateien und dauerhaften Speicher:

  • <input type="file"> zur Anzeige eines nativen Dialogs zum Öffnen von Dateien, in dem der Benutzer eine Datei auswählen kann.
  • IndexedDB bietet persistenten lokalen Speicher (nicht außerhalb des Browsers zugänglich)

Emscripten bietet mehrere Dateisysteme mit einer POSIX-ähnlichen API. Dazu gehören:

  • das ephemere Dateisystem MEMFS, das Dateien im Arbeitsspeicher speichert
  • das persistente Dateisystem IDBFS, das Dateien unter Verwendung von IndexedDB speichert

Emscripten hängt beim Start der Anwendung ein temporäres MEMFS-Dateisystem in "/" ein. Das bedeutet, dass QFile verwendet werden kann und standardmäßig Dateien im Speicher liest und schreibt. Qt bietet auch andere APIs:

Zugriff auf die Zwischenablage

Qt unterstützt das Kopieren und Einfügen von Text in die Systemzwischenablage, mit einigen Unterschieden aufgrund der Web-Sandbox. Im Allgemeinen erfordert der Zugriff auf die Zwischenablage die Erlaubnis des Benutzers, die durch die Behandlung eines Eingabeereignisses (z.B. STRG+c) oder durch die Verwendung der Zwischenablage-API eingeholt werden kann.

Browser, die die Zwischenablage-API unterstützen, werden bevorzugt. Beachten Sie, dass eine Voraussetzung für diese API darin besteht, dass die Webseite über eine sichere Verbindung (z. B. https) bereitgestellt wird, und dass bei einigen Browsern möglicherweise Konfigurationsflags geändert werden müssen.

  • Chrome Version 66 und Safari Version 13.1 unterstützen die Zwischenablage-API
  • Firefox Version 90 unterstützt die Zwischenablage-API, wenn Sie die folgenden Flags in 'about:config' aktivieren:
    dom.events.asyncClipboard.read
    dom.events.asyncClipboard.clipboardItem

Schriftarten

Das Qt WASM-Modul enthält 3 eingebettete Schriftarten: "Bitstream Vera Sans" (Fallback-Schriftart), "DejaVu Sans", "DejaVu Sans Mono".

Diese Schriftarten bieten einen begrenzten Zeichensatz. Qt bietet mehrere Optionen zum Hinzufügen zusätzlicher Schriftarten:

Eine davon ist die Verwendung von FontLoader in QML, das eine Schriftart entweder per URL oder über das Qt Resource System (wie es auch bei den üblichen Desktop-Anwendungen funktioniert) abrufen kann.

Die andere Möglichkeit, Schriftarten zu verwenden, ist das Hinzufügen über QFontDatabase::addApplicationFontFromData.

Barrierefreiheit und Screenreader

Qt für WebAssembly bietet grundlegende Unterstützung für Bildschirmlesegeräte. Einfache UI-Elemente wie Schaltflächen und Kontrollkästchen funktionieren, während komplexere UI-Elemente wie Tabellen- oder Baumansichten möglicherweise nicht unterstützt werden. Sowohl Qt Widgets als auch Qt Quick werden unterstützt.

Die folgenden Screenreader-/Browser-Konfigurationen wurden getestet und sind als funktionsfähig bekannt. Andere Browser und Screenreader können ebenfalls funktionieren.

  • VoiceOver mit Safari unter macOS
  • VoiceOver mit Chrome unter macOS

Die Implementierung der Barrierefreiheit funktioniert durch die Erstellung von "Schatten"-HTML-Elementen, die die Barrierefreiheitsinformationen für die Qt UI-Elemente bereitstellen. Diese Funktionalität ist standardmäßig deaktiviert. Der Endbenutzer kann sie aktivieren, indem er eine Schaltfläche "Bildschirmleser aktivieren" mit dem Bildschirmleser auswählt. Wenn sie aktiviert ist, wird die Webseite mit barrierefreien Elementen aufgefüllt.

Start der Anwendung und die Ereignisschleife

Qt for WebAssembly unterstützt den Standard-Qt-Startup-Ansatz, bei dem die Anwendung ein QApplication -Objekt erstellt und die exec-Funktion aufruft:

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QWindow appWindow;

    return app.exec();
}

Der obige exec()-Aufruf blockiert und verarbeitet normalerweise Ereignisse bis zum Herunterfahren der Anwendung. Leider ist dies auf der Web-Plattform nicht möglich, da das Blockieren des Haupt-Threads nicht erlaubt ist. Stattdessen muss die Kontrolle nach der Verarbeitung jedes Ereignisses an die Ereignisschleife des Browsers zurückgegeben werden.

Qt umgeht dies, indem exec() die Kontrolle über den Haupt-Thread an den Browser zurückgibt, während der Stack erhalten bleibt. Aus der Sicht des Anwendungscodes wird die exec()-Funktion aufgerufen und die Ereignisverarbeitung erfolgt wie gewohnt. Allerdings kehrt der exec()-Aufruf niemals zurück, auch nicht beim Beenden der Anwendung.

Dieses Verhalten ist normalerweise akzeptabel, da der Browser den Anwendungsspeicher beim Herunterfahren der Anwendung freigibt. Es bedeutet jedoch, dass der Shutdown-Code nicht ausgeführt wird, da das Anwendungsobjekt geleakt wird und sein Destruktor nicht ausgeführt wird.

Sie können dies vermeiden, indem Sie main() so umschreiben, dass es asynchron ist, was möglich ist, da Emscripten die Laufzeit nicht beendet, wenn main() zurückkehrt. Der Anwendungscode verzichtet dann auf den exec()-Aufruf und kann Qt sauber beenden, indem er die Fenster- und Anwendungsobjekte der obersten Ebene löscht.

QApplication *g_app = nullptr;
AppWindow *g_appWindow = nullptr;

int main(int argc, char **argv)
{
    g_app = new QApplication(argc, argv);
    g_appWindow = new AppWindow();
    return 0;
}

Asynchronisieren

Der Standard-Build von Qt für WebAssembly unterstützt aufgrund von Beschränkungen der Web-Plattform nicht den Wiedereintritt in die Ereignisschleife, zum Beispiel durch den Aufruf von QEventLoop::exec() oder QDialog::exec().

Das asyncify-Feature von Emscripten hebt diese Einschränkungen auf, indem es synchrone Aufrufe (wie QEventLoop::exec() und QDialog::exec()) in die Ereignisschleife zurückkehren lässt. Verschachtelte Aufrufe werden nicht unterstützt, und aus diesem Grund wird asyncify nicht für den Top-Level-Aufruf QApplication::exec() verwendet.

Funktionen, die asyncify erfordern, sind:

  • QDialogs, QMessageBoxen mit Rückgabewerten.
  • Drag and Drop (insbesondere Ziehen).
  • Verschachtelte/sekundäre Ereignisschleifen exec().

Aktivieren Sie asyncify, indem Sie die "-sASYNCIFY -Os"-Flags zu den Linker-Optionen hinzufügen:

CMake:

target_link_options(<your target> PUBLIC -sASYNCIFY -Os)

qmake:

QMAKE_LFLAGS += -sASYNCIFY -Os

Das Aktivieren von asyncify fügt Overhead in Form von erhöhten Binärgrößen und erhöhter CPU-Auslastung hinzu. Bauen Sie mit aktivierten Optimierungen, um den Overhead zu minimieren.

Asyncify JSPI (JavaScript Promise Integration)

JSPI ist ein neues Asyncify-Backend, das mit nativer Browserunterstützung anstelle von Codeumwandlungen implementiert wurde. Im Vergleich zu Emscripten asyncify verbessert dies die Verbindungszeiten von Anwendungen und erhöht nicht die Codegröße.

Die JSPI-WebAssembly-Funktion befindet sich derzeit in der "Implementierungsphase" und ist noch nicht standardisiert.

Aktivieren Sie JSPI, indem Sie das Flag "-sJSPI" zu den Linker-Optionen hinzufügen:

CMake:

target_link_options(<your target> PUBLIC -sJSPI)

qmake:

QMAKE_LFLAGS += -sJSPI

Fehlersuche und Profiling

Das Debugging von Wasm erfolgt über die JavaScript-Konsole des Browsers. Das Debuggen von Anwendungen auf Wasm direkt in Qt Creator ist nicht möglich.

Mit Emscripten-Linkerargumenten können Sie das Debugging noch weiter verfeinern:

  • -s LIBRARY_DEBUG=1 (Bibliotheksaufrufe ausgeben)
  • -s SYSCALL_DEBUG=1 (druckt Sys-Aufrufe aus)
  • -s FS_LOG=1 (druckt Dateisystem-Operationen aus)
  • -s SOCKET_DEBUG (Socket, Netzwerkdatenübertragung ausgeben)

CMake:

target_link_options(<your target> PRIVATE -s LIBRARY_DEBUG=1)

qmake:

QMAKE_LFLAGS_DEBUG += -s LIBRARY_DEBUG=1

Optimieren

Qt for WebAssembly verwendet die Emscripten-Toolchain, um Binärdateien zu erzeugen, und es gibt viele Flags, die die Leistung und die Größe der Binärdateien beeinflussen können. Siehe Emscripten: Code-Optimierung für weitere Informationen.

Sie können Linker- und Compiler-Flags genau wie bei normalen C++-Anwendungen übergeben:

target_compile_options(<your target> PRIVATE -oz -flto)
target_link_options(<your target> PRIVATE -flto)
QMAKE_CXXFLAGS += -oz -flto
QMAKE_LFLAGS += -flto

Minimierung der Größe von Binärdateien

Um ein nahtloses Benutzererlebnis zu bieten, ist es wichtig, die Zeit zum Herunterladen und Laden von WebAssembly-Anwendungen zu reduzieren. Kleinere Anwendungsbinärdateien sind einer der wichtigsten Aspekte, die einen schnelleren Download ermöglichen. Verwenden Sie die folgenden Alternativen, um die Größe der Binärdateien zu reduzieren:

  • Stellen Sie sicher, dass Sie Release-Builds verteilen. Debug-Builds enthalten Debug-Symbole und sind viel größer.
  • Aktivieren Sie die Komprimierung auf einem Server. Die gebräuchlichsten Algorithmen wie gzip und Brotli funktionieren gut bei Wasm-Binärdateien und können deren Größe drastisch reduzieren.
  • Probieren Sie Compiler- und Linker-Flags aus, die zu kleineren Binärdateien führen können (z. B. '-os', '-oz'). Die Ergebnisse hängen von der jeweiligen Anwendung ab.
  • Deaktivieren Sie nicht benötigte Funktionen beim Kompilieren von Qt für WebAssembly aus dem Quellcode (siehe unten).
Ausschalten von Funktionen

Eine WebAssembly-Anwendung verlinkt standardmäßig statisch mit den Qt-Bibliotheken, wodurch der Compiler toten Code eliminieren kann. Aufgrund der dynamischen Natur von Qt ist es jedoch nicht immer möglich, dass der Compiler solche Optimierungen durchführt.

Wenn Sie Qt für WebAssembly aus dem Quellcode erstellen, können Sie Funktionen deaktivieren, um die Größe der Qt-Binärdateien und - als Folge davon - die Größe der .wasm-Binärdateien zu reduzieren. Qt deaktiviert einige Funktionen standardmäßig für die WebAssembly-Plattform, aber Sie können auch Funktionen deaktivieren, die Ihre Anwendung nicht verwendet. Weitere Informationen finden Sie unter Deaktivierte Funktionen.

Sie können die folgenden Funktionen deaktivieren, um die Größe der Binärdateien zu reduzieren (in der Regel um 10-15%):

Argument konfigurierenKurzbeschreibung
-no-feature-cssparserParser für Cascading Style Sheets.
-no-feature-datetimeeditBearbeiten von Daten und Zeiten (hängt von datetimeparser ab).
-no-feature-datetimeparserParsen von Datum-Zeit-Texten.
-no-feature-dockwidgetAndocken von Widgets innerhalb von QMainWindow oder sie als Top-Level-Fenster auf dem Desktop schweben lassen.
-no-feature-gesturesFramework für Gesten.
-no-feature-mimetypeBehandlung von Mimetypes.
-no-feature-qml-networkNetzwerk-Transparenz.
-no-feature-qml-list-modelListModel QML-Typ.
-no-feature-qml-table-modelTableModel QML-Typ.
-no-feature-quick-canvasCanvas-Element.
-kein-merkmal-schnell-pfadPfad-Elemente.
-no-feature-quick-pathviewPathView Element.
-no-feature-quick-treeviewTreeView Element.
-no-feature-style-stylesheetWidget-Stil, der über CSS konfigurierbar ist.
-no-feature-tableviewStandardmäßige Model/View-Implementierung einer Tabellenansicht.
-no-feature-texthtmlparserParser für HTML.
-no-feature-textmarkdownreaderMarkdown (CommonMark und GitHub) Leser.
-no-feature-textodfwriterODF-Schreiber.

Wasm Ausnahmen

Qt wird standardmäßig ohne Ausnahmeunterstützung gebaut, wobei das Auslösen einer Ausnahme zum Abbruch des Programms führt. WebAssembly-Ausnahmen können aktiviert werden, indem man aus dem Quellcode kompiliert und das Flag -feature-wasm-exceptions an Qt configure übergibt. Dadurch wird das Flag -fwasm-exceptions zur Kompilier- und Linkzeit an den Compiler weitergegeben. Qt unterstützt nicht die Aktivierung der Emscripten-Unterstützung für die frühere JavaScript-basierte Ausnahme-Implementierung.

Beachten Sie, dass der Aufruf von QApplication::exec() aufgrund von internen Implementierungsdetails nicht unterstützt wird, wenn Ausnahmen aktiviert sind. Schreiben Sie stattdessen main() in der Form, dass es früh zurückkehrt und nicht exec() aufruft, wie in Anwendungsstart und Ereignisschleife beschrieben.

Freigegebene Bibliotheken und dynamisches Linking Entwickler-Vorschau

Qt für WebAssembly verwendet standardmäßig statisches Linking, bei dem die Anwendung als eine einzige WebAssembly-Datei bereitgestellt wird, die die Qt-Bibliotheken und den Anwendungscode enthält. Dynamisches Linking ist ein alternativer Build-Modus, bei dem jede Bibliothek und jedes Plugin einzeln verteilt wird.

Eine Anwendung, die Qt Quick verwendet, kann zum Beispiel die folgenden Bibliotheken und Plugins nutzen:

  • <qtpath>/lib/libQt6Core.so
  • <qtpfad>/lib/libQt6Gui.so
  • <qtpfad>/lib/libQt6Qml.so
  • <qtpfad>/lib/libQt6Quick.so
  • <qtpfad>/plugins/bildformate/libqjpeg.so
  • <qtpfad>/plugins/bildformate/libqjgif.so
  • <qtpath>/qml/QtQuick/Window/libquickwindowplugin.so

Die Unterstützung für dynamisches Linking befindet sich derzeit in der Entwicklervorschau. Die Implementierung eignet sich für das Prototyping und die Evaluierung, ist aber nicht für den Produktionseinsatz geeignet. Zu den aktuellen Einschränkungen und Beschränkungen gehören:

  • Das Emscripten SDK muss gepatcht werden. Verwenden Sie emsdk 3.1.52, und wenden Sie diese Patches an: patch#1 und patch#2.
  • Multithreading wird nicht unterstützt.
  • Asyncify wird nicht unterstützt.

Schnellstart

Das Build- und Deployment-Verfahren unterscheidet sich geringfügig von dem der statischen Wasm- und Shared-Desktop-Builds. Es empfiehlt sich, mit einem kleinen Beispiel zu beginnen, bevor Sie mit dem Build einer vollständigen Anwendung fortfahren.

  1. Erstellen Sie Qt aus dem Quellcode und übergeben Sie die Option "-shared" an das Qt-Konfigurationsskript.
  2. Erstellen Sie Ihre Anwendung mit Qt aus Schritt 1.
  3. Stellen Sie die Qt-Installation bereit, indem Sie ein Verzeichnis mit dem Namen "qt" in das Anwendungsverzeichnis kopieren oder mit diesem verknüpfen
    • ln -s <qtpfad> qt
    • cp -r <qtpfad> qt
  4. Erstellen Sie Plugin-Vorladelisten, indem Sie Deployment-Skripte ausführen.
    • <qtpath>/qtbase/util/wasm/preload/deploy_qt_plugins.py <qtpath>
    • <qtpath>/qtbase/util/wasm/preload/deploy_qml_imports.py <qthostpath> <qtpath>

Einsatz von gemeinsam genutzten Bibliotheken in der Tiefe

Der Build der gemeinsamen Bibliotheken von Qt wird in zwei Stufen bereitgestellt, wobei in der ersten Stufe der Qt- und Anwendungs-Build zum Herunterladen vom Webserver bereitgestellt wird und in der zweiten Stufe die erforderlichen Qt-Plugins und Qt Quick -Importe beim Start der Anwendung heruntergeladen werden.

Im ersten Schritt stellen Sie die Qt-Installation zum Herunterladen vom Webserver bereit. Je nach den Besonderheiten der Webserver-Einrichtung kann es verschiedene Möglichkeiten geben, dies zu erreichen. In der Regel erwartet der Qt-Lader, dass er die Qt-Bibliotheken und Plugins in einem Verzeichnis mit dem Namen "qt" findet, relativ zu der html-Datei, die die Anwendung lädt.

Wenn Sie die Anwendung bereits als Teil des Deployments auf den Webserver kopieren, ist das Kopieren von Qt ebenfalls eine mögliche Option. Wenn Sie die Anwendung direkt von der Build-Directory aus bereitstellen - was in der Entwicklungsphase oft der Fall ist -, dann kann ein Symlink zu Qt gut funktionieren.

Bereiten Sie sich auf den zweiten Schritt vor, indem Sie Vorladelisten für Qt-Komponenten wie Plugins und Qt Quick -Importe erstellen. Das Vorladen stellt sicher, dass alle benötigten Qt-Komponenten beim Start der Anwendung zur Verfügung stehen. Das verzögerte Laden, bei dem die Komponenten bei Bedarf heruntergeladen werden, ist ebenfalls möglich, wird hier aber nicht behandelt.

Das Vorladen wird durch den Qt JavaScript Loader implementiert, der Dateien vom Webserver in das von Emscripten bereitgestellte In-Memory-Dateisystem herunterlädt. Welche Dateien heruntergeladen werden sollen, wird über json-formatierte Download-Listen festgelegt. Qt bietet zwei Skripte zum Erzeugen von Vorladelisten, siehe Abschnitt Schnellstart oben.

Bekannte Probleme

  • Verschachtelte Ereignisschleifen werden nicht unterstützt. Anwendungen sollten keine API wie QDialog::exec() und QEventLoop::exec() aufrufen. Die experimentelle Funktion Asyncify kann verwendet werden.
  • Drucken wird nicht unterstützt
  • QDnsLookup Lookups, QTcpSocket, QSsl funktionieren nicht und werden aufgrund der Web-Sandbox nicht unterstützt
  • Schriftarten: Die Wasm-Sandbox erlaubt keinen Zugriff auf Systemschriftarten. Schriftartendateien müssen mit der Anwendung verteilt werden, zum Beispiel in Qt-Ressourcen oder durch Herunterladen. Qt für WebAssembly selbst bettet eine solche Schriftart ein.
  • Es kann Artefakte von nicht initialisiertem Grafikspeicher auf einigen Qt Quick Controls 2 Komponenten geben, wie z.B. Checkboxen. Dies ist manchmal auf HighDPi-Displays zu sehen.
  • Native Stile für Windows und macOS werden nicht unterstützt, da Wasm als Plattform diese Möglichkeit nicht bietet.
  • Link-Zeit-Fehler wie "wasm-ld: error: initial memory too small", erfordert die Anpassung der anfänglichen Speichergröße. Verwenden Sie QT_WASM_INITIAL_MEMORY, um die anfängliche Größe in KB zu setzen, die ein Vielfaches von 64KB (65536) sein muss. Die Voreinstellung ist 50 MB. In CMakeLists.txt: set_target_properties(<target> PROPERTIES QT_WASM_INITIAL_MEMORY "150MB")
  • add_executable in CMakeLists.txt erzeugt nicht <target>.html oder kopiert qtloader.js. Verwenden Sie stattdessen qt_add_executable.
  • QWebSocket Verbindungen werden von Emscripten nur auf dem Hauptthread unterstützt.
  • QWebSockets for WebAssembly unterstützt nicht das Senden von Ping- oder Pong-Frames, da die API, die Webseiten und Browsern zur Verfügung steht, diese Funktionalität nicht bereitstellt.
  • Laufzeitfehler wie z.B. "RangeError: Out of memory" kann umgangen werden, indem MAXIMUM_MEMORY auf einen Wert gesetzt wird, den das Gerät unterstützt, z.B.
    target_link_options(<your target> PRIVATE -s MAXIMUM_MEMORY=1GB)
  • Um QtWebsockets zu verwenden, muss das Unterprotokoll für die Verwendung von QtMqtt möglicherweise auf "mqtt" gesetzt werden. Verwenden Sie QWebSocketHandshakeOptions, wenn Sie die QWebSocket öffnen.

Andere Themen

Qt-Konfigurationsoptionen-Referenz

Die folgenden Konfigurationsoptionen sind relevant, wenn Qt für WebAssembly aus dem Quellcode erstellt wird.

Configure ArgumentKurzbeschreibung
-feature-threadMulti-threaded Wasm.
-Eigenschaft-wasm-simd128Aktiviert WebAssembly SIMD-Unterstützung.
-Merkmal-wasm-exceptionsAktiviert die Unterstützung von WebAssembly-Ausnahmen.
-Merkmal-opengles3Verwendung von opengles3 zusätzlich zum Standard opengles2.
-Geräte-Option QT_EMSCRIPTEN_ASYNCIFY=1Aktiviert die Unterstützung von asyncify.
-Geräte-Option QT_EMSCRIPTEN_ASYNCIFY=2Aktiviert asyncify (JSPI) Unterstützung.

Qt deaktiviert einige Funktionen standardmäßig für die WebAssembly-Plattform, um die Binärgröße zu reduzieren. Sie können eine Funktion explizit aktivieren, wenn Sie Qt für WebAssembly konfigurieren:

Argument konfigurierenKurzbeschreibung
-feature-topleveldomainBietet Unterstützung für die Überprüfung, ob eine Domain eine Top-Level-Domain ist.

Typische Download-Größen

Erwarteter Fußabdruck (Downloadgröße): Wasm-Module, wie sie vom Compiler erzeugt werden, können groß sein, lassen sich aber gut komprimieren:

Beispielgzipbrotli
helloglwindow (QtCore + QtGui)2.8M2.1M
Wiggly-Widget (QtCore + QtGui + QtWidgets)4.3M3.2M
SensorTag (QtCore + QtGui + QtWidgets + QtQuick + QtCharts)8.6M6.3M

Die Komprimierung erfolgt in der Regel auf der Seite des Webservers unter Verwendung von Standard-Komprimierungsfunktionen: Der Server komprimiert automatisch oder greift auf vorkomprimierte Versionen der Dateien zurück. Eine besondere Behandlung von Wasm-Dateien ist im Allgemeinen nicht erforderlich.

Weitere Informationen finden Sie unter Minimierung der Größe von Binärdateien.

Beispiele

Externe Ressourcen

Lizenz

Qt für WebAssembly ist unter kommerziellen Lizenzen von The Qt Company erhältlich. Darüber hinaus ist es unter der GNU General Public License, Version 3, erhältlich. Siehe Qt-Lizenzierung für weitere Details.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.