Qt pour WebAssembly
Qt for Webassembly vous permet d'exécuter des applications Qt sur le web.
WebAssembly (abrégé Wasm) est un format d'instruction binaire destiné à être exécuté dans une machine virtuelle, par exemple dans un navigateur web.
Avec Qt for WebAssembly, vous pouvez distribuer votre application en tant qu'application web qui s'exécute dans le bac à sable d'un navigateur. Cette approche convient aux applications distribuées sur le web qui ne nécessitent pas un accès complet aux capacités de l'appareil hôte.
Remarque : Qt for WebAssembly est une plate-forme prise en charge, mais certains modules ne sont pas encore pris en charge ou sont en avant-première technique. Voir Modules Qt supportés.
Démarrer avec Qt pour WebAssembly
La création d'applications Qt pour WebAssembly est similaire à la création de Qt pour d'autres plateformes. Vous devez installer un SDK (Emscripten), installer Qt (ou construire Qt à partir des sources), et enfin, construire l'application. Il existe quelques différences, par exemple, Qt pour WebAssembly prend en charge moins de modules et moins de fonctionnalités que les autres versions de Qt.
Installation d'Emscripten
Emscripten est une chaîne d'outils pour la compilation vers WebAssembly. Il vous permet d'exécuter Qt sur le web à une vitesse proche de la vitesse native sans plugins de navigateur.
Chaque version mineure de Qt cible une version spécifique d'Emscripten, qui reste inchangée dans les versions correctives. Les paquets binaires de Qt sont construits en utilisant la version cible d'Emscripten. Les applications doivent utiliser la même version, car Emscripten ne garantit pas la compatibilité ABI entre les versions.
La version d'Emscripten supportée pour la version 6.11.0 est Emscripten 4.0.7.
Reportez-vous à la documentation d'Emscripten pour plus d'informations sur l'installation du SDK d'Emscripten.
Utilisez emsdk pour installer des versions spécifiques de Emscripten. Par exemple, pour l'installer pour la version 6.11.0, entrez :
- ./emsdk install 4.0.7
- ./emsdk activate 4.0.7
Après l'installation, vous devriez avoir le compilateur Emscripten dans votre chemin. Vérifiez-le avec la commande suivante :
em++ --version
Sous Windows, Emscripten se trouve dans votre chemin d'accès après l'installation. Sous macOS ou Linux, vous devez l'ajouter à votre chemin, comme ceci :
source /path/to/emsdk/emsdk_env.sh
Vérifiez-le avec la commande suivante :
em++ --version
Vous pouvez compiler Qt à partir des sources si vous avez besoin de plus de flexibilité lors de la sélection de la version d'Emscripten. Dans ce cas, les versions ci-dessus sont des versions minimales. Les versions ultérieures sont censées fonctionner, mais peuvent introduire des changements de comportement qui nécessitent de modifier Qt.
Installation de Qt
Téléchargez Qt à partir de la section Téléchargements de votre compte Qt. Nous fournissons des versions pour Linux, macOS et Windows comme plateformes de développement.
Les versions binaires sont conçues pour fonctionner sur le plus grand nombre de navigateurs possible, et sont disponibles en versions mono et multithread. Les fonctionnalités non standard telles que Wasm SIMD et Wasm exceptions ne sont pas prises en charge par les versions binaires.
Construire Qt à partir des sources
La compilation à partir des sources vous permet de définir les options de configuration de Qt comme la prise en charge des threads, le niveau d'OpenGL ES ou la prise en charge SIMD. Téléchargez les sources de Qt à partir de la section Téléchargements de votre compte Qt.
Configurez Qt en tant que compilation croisée pour la plateforme wasm-emscripten. Cela définit les options de configuration -static, -no-feature-thread, et -no-make examples. Vous pouvez activer la prise en charge des threads avec l'option de configuration -feature-thread. Les compilations de bibliothèques partagées ne sont pas prises en charge.
Vous avez besoin d'une version hôte de la même version de Qt. Définissez également le chemin vers la version hôte dans la variable CMake QT_HOST_PATH ou en utilisant l'argument de configuration -qt-host-path.
./configure -qt-host-path /path/to/Qt -platform wasm-emscripten -prefix $PWD/qtbase
Note : configure utilise toujours l'outil de génération et de construction Ninja si un exécutable ninja est disponible. Ninja est multiplateforme, riche en fonctionnalités, performant et recommandé sur toutes les plateformes. L'utilisation d'autres générateurs peut fonctionner mais n'est pas officiellement supportée.
Sous Windows, assurez-vous d'avoir Mingw-w64 dans votre PATH et configurez avec ce qui suit :
configure -qt-host-path C:\Path\to\Qt -no-warnings-are-errors -platform wasm-emscripten -prefix %CD%\qtbase
Construisez ensuite les modules requis :
cmake --build . -t qtbase -t qtdeclarative [-t another_module]
Construire des applications en ligne de commande
Qt for WebAssembly permet de construire des applications en utilisant qmake et make, ou CMake avec ninja ou make.
$ /path/to/qt-wasm/qtbase/bin/qt-cmake . $ cmake --build .
Note : Lorsque vous utilisez CMake (par opposition à qt-cmake sur Linux ou qt-cmake.bat sur Windows), n'oubliez pas de spécifier un fichier de chaîne d'outils avec "-DCMAKE_TOOLCHAIN_FILE", comme pour n'importe quelle autre compilation multiplateforme. Pour plus de détails, voir ici : Démarrer avec CMake.
La construction de l'application génère plusieurs fichiers de sortie, notamment un fichier .wasm qui contient l'application et le code Qt (lié statiquement), un fichier .html qui peut être ouvert dans le navigateur pour exécuter l'application.
Remarque : Emscripten produit des fichiers .wasm relativement volumineux au niveau de débogage "-g". Envisagez de lier avec "-g2" pour les constructions de débogage.
Exécution des applications
L'exécution de l'application nécessite un serveur web. Les fichiers de sortie de la compilation sont tous statiques, donc n'importe quel serveur web fera l'affaire. Certains cas d'utilisation peuvent nécessiter une configuration spéciale du serveur, comme la fourniture de certificats https ou la définition des en-têtes http nécessaires à la prise en charge du multithreading.
Emrun
Emscripten fournit l'utilitaire emrun pour tester les applications. Emrun démarre un serveur web, lance un navigateur et capture et transmet les données stdout/stderr (qui sont normalement envoyées à la console JavaScript).
/path/to/emscripten/emrun --browser=firefox appname.html
Python http.server
Une autre option consiste à démarrer un serveur web de développement, puis à lancer le navigateur web séparément. L'une des options les plus simples est http.server de Python :
python -m http.server
Notez qu'il ne s'agit que d'un simple serveur web et qu'il ne prend pas en charge le SharedArrayBuffer requis pour le threading, car les en-têtes COOP et COED mentionnés ci-dessous ne sont pas envoyés.
qtwasmserver
Qt fournit un serveur web de développement qui utilise mkcert pour générer des certificats https. Cela permet de tester les fonctionnalités web qui nécessitent un contexte sécurisé. Notez que la livraison sur http://localhost est également considérée comme sûre, sans nécessiter de certificat.
Le serveur web définit également les valeurs des en-têtes COOP et COEP, ce qui permet la prise en charge de SharedArrayBuffer et du multithreading.
Le script qtwasmserver démarre un serveur qui se lie à localhost par défaut. Vous pouvez ajouter des adresses supplémentaires en utilisant l'argument de ligne de commande -a, ou utiliser --all pour se lier à toutes les adresses disponibles.
Le script qtwasmserver se trouve dans les sources à l'adresse suivante
qtbase/util/wasm/qtwasmserver/qtwasmserver.py
ou peut être installé à l'aide de pip :
pip install qtwasmserver
Pour plus d'informations sur qtwasmserver qtwasmserver
Utilisation de qtwasmserver :
python /path/to/qtbase/util/wasm/qtwasmserver/qtwasmserver.py --all
Construire des applications en utilisant Qt Creator
Qt Creator:Créer des applications pour le Web.
Déployer des applications sur le web
La construction d'une application génère plusieurs fichiers (remplacer "app" par le nom de l'application dans le tableau suivant).
| Fichier généré | Brève description |
|---|---|
| app.html | Conteneur HTML |
| qtloader.js | API JavaScript pour le chargement des applications Qt |
| app.js | moteur d'exécution JavaScript généré par Emscripten |
| app.wasm | binaire de l'application |
Vous pouvez déployer app.html tel quel, ou le supprimer en faveur d'un fichier HTML personnalisé. Il est également possible de procéder à des ajustements plus mineurs, tels que le remplacement de l'image de l'écran d'accueil par le logo de l'application au lieu du logo Qt. Dans les deux cas, qtloader.js fournit une API JavaScript pour charger l'application.
Compresser le fichier Wasm à l'aide de gzip ou brotli avant de le déployer, car ils offrent un meilleur taux de compression que les autres outils. Voir Minimiser la taille des binaires pour plus d'informations.
L'activation de certaines fonctionnalités, telles que le multithreading et le SIMD, produit des fichiers binaires .wasm incompatibles avec les navigateurs qui ne prennent pas en charge la fonctionnalité activée. Il est possible de contourner cette limitation en construisant plusieurs fichiers .wasm et en utilisant ensuite la détection de fonctionnalité JavaScript pour sélectionner le bon fichier, mais notez que Qt ne fournit aucune fonctionnalité pour ce faire.
Utilisation de qtloader
Qt fournit une API JavaScript pour le téléchargement, la compilation et l'instanciation des applications Qt for WebAssembly. Cette API de chargement reprend la fonctionnalité de chargement fournie par Emscripten et offre des fonctionnalités supplémentaires utiles pour les applications basées sur Qt. Elle est implémentée dans le fichier qtloader.js. Une copie de ce fichier est écrite dans le répertoire de construction au moment de la construction.
L'utilisation typique ressemble à ce qui suit :
const app_container_element = ...; const instance = await qtLoad({ qt: { containerElements: [ app_container_element ], onLoaded: () => { /* handle application load completed */ }, onExit: () => { /* handle application exit */ }, } });
Le code appelle la fonction de chargement qtLoad() avec un objet de configuration. Cet objet de configuration peut contenir n'importe quelle option de configuration d'emscripten, ainsi qu'un objet de configuration spécial "qt". L'objet de configuration qt prend en charge les propriétés suivantes :
| Propriété | Brève description |
|---|---|
| containerElements | Tableau d'éléments de conteneur HTML. L'application les considère comme des QScreens. |
| onLoaded | Rappel lorsque le chargement de l'application est terminé. |
| onExit | Rappel pour la sortie de l'application. |
Le tableau containerElements est l'interface principale entre Qt Location et la page web, où les éléments html de ce tableau (typiquement des éléments <div>) spécifient l'emplacement du contenu de l'application sur la page web.
L'application considère chaque élément du conteneur comme une instance QScreen et peut placer les fenêtres de l'application sur les instances d'écran comme d'habitude. Les fenêtres ayant l'état Qt::WindowFullScreen utilisent toute la surface de l'écran, tandis que les fenêtres qui ne sont pas "plein écran" ont des décorations de fenêtre.
La fonction qtLoad() renvoie une promesse, qui crée une instance Emscripten lorsqu'elle est attendue. Cette instance permet d'accéder aux fonctions exportées par Embind. Qt exporte plusieurs de ces fonctions, qui constituent l'API de l'instance.
Utilisation de l'API d'instance Qt
Qt fournit plusieurs fonctions d'instance. Actuellement, elles permettent d'ajouter et de supprimer des éléments de conteneur au moment de l'exécution.
| Propriété | Brève description |
|---|---|
| qtAddContainerElement | Ajoute un élément de conteneur. L'ajout d'un élément ajoutera un nouveau QScreen. |
| qtRemoveContainerElement | Supprime un élément conteneur et l'écran correspondant. |
| qtSetContainerElements | Définit tous les éléments du conteneur |
| qtResizeContainerElement | Fait en sorte que Qt prenne en compte les changements de taille de l'élément conteneur. |
Portage vers le qtloader de Qt 6.6
Qt 6.6 inclut un nouveau qtloader avec une implémentation simplifiée et un champ d'application plus restreint. Cela inclut des changements d'API qui peuvent nécessiter le portage du code JavaScript de l'application. Qt fournit une API de compatibilité pour faciliter la transition. Selon le cas d'utilisation, il y a plusieurs façons de procéder :
- Si vous utilisez directement le fichier
app.htmlgénéré, ce fichier sera également mis à jour au moment de la construction. Aucune action n'est nécessaire. - Si vous utilisez les fonctionnalités de base de qtloader, vous pouvez utiliser l'API de compatibilité incluse dans Qt 6.6 comme mesure temporaire. Cette API sera supprimée dans une prochaine version ; vous devez prévoir une mise à jour pour utiliser le nouveau qtloader. Le portage de l'étape 1 ci-dessous est nécessaire.
- Si vous utilisez des fonctionnalités avancées (telles que l'ajout d'éléments de conteneur au moment de l'exécution), le portage vers le nouveau chargeur ou la nouvelle API d'instance est nécessaire. Les étapes de portage 1 et 2 ci-dessous sont nécessaires.
Etapes du portage
- Inclure le
app.js(moteur d'exécution JavaScript généré par Emscripten) dans le fichier html de chargement.<script src="app.js"></script>
Avant Qt 6.6, qtloader chargeait et évaluait ce fichier JavaScript. Ce n'est plus le cas, et le fichier doit être inclus à l'aide d'une balise <script>.
- Portage vers l'utilisation de la nouvelle API JavaScript et instance.
Voir les sections de documentation ci-dessus.
Navigateurs pris en charge
Bureau
Qt for WebAssembly est développé et testé sur les navigateurs suivants :
- Chrome
- Firefox
- Safari
- Edge
Qt devrait fonctionner si le navigateur supporte WebAssembly. Qt a un besoin fixe de WebGL, même si l'application elle-même n'utilise pas de graphiques accélérés par le matériel. Les navigateurs qui supportent WebAssembly supportent souvent WebGL, bien que certains navigateurs mettent sur liste noire les GPU plus anciens ou non supportés. s/qtloader.js fournit des API pour vérifier si WebGL est disponible.
Qt n'utilise pas directement les caractéristiques des systèmes d'exploitation et cela ne fait aucune différence si, par exemple, FireFox fonctionne sous Windows ou macOS. Qt utilise certaines adaptations du système d'exploitation, par exemple pour la gestion des touches ctrl/cmd sur macOS.
Mobile
Les applications Qt for WebAssembly fonctionnent sur des navigateurs mobiles tels que Safari et Android Chrome.
Modules Qt supportés
Qt for WebAssembly supporte un sous-ensemble de modules et de fonctionnalités de Qt. Les modules testés sont listés ci-dessous, d'autres modules peuvent fonctionner ou non.
- Qt Core
- Qt GUI
- Qt Network
- Qt Widgets
- Qt Qml
- Qt Quick
- Qt Quick Contrôles
- Qt Quick Mises en page
- Qt 5 Core Compatibility APIs
- Qt Image Formats
- Qt OpenGL
- Qt SVG
- Qt WebSockets
- Qt Concurrent
- Qt Charts
- Qt Graphs
- Qt Quick 3D
Les modules suivants sont en avant-première technologique. Ils peuvent avoir des fonctionnalités limitées ou être modifiés de manière significative dans les prochaines versions.
Dans tous les cas, le support des modules peut ne pas être complet et il peut y avoir des limitations supplémentaires, soit en raison du bac à sable du navigateur, soit en raison de l'incomplétude du portage de la plate-forme Qt. Voir Developing with Qt for WebAssembly pour plus d'informations.
Développer avec Qt pour WebAssembly
Construire avec CMake
Si une configuration spécifique à Emscripten est nécessaire dans CMake, le code suivant peut être utilisé :
if(EMSCRIPTEN)
# WebAssembly specific code
else()
# other platforms
endif()Ce code permet de prendre en compte les configurations spécifiques à Emscripten tout en assurant la compatibilité avec les autres plates-formes.
OpenGL et WebGL
Qt for WebAssembly supporte le rendu accéléré par le matériel en utilisant https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API WebGL.
WebGL se conforme étroitement à OpenGL ES, avec la correspondance de version suivante :
| OpenGL | WebGL |
|---|---|
| OpenGL ES 2.0 | WebGL 1 |
| OpengL ES 3.0 | WebGL 2 |
Qt utilise la plus haute version disponible de WebGL. Il s'agit généralement de WebGL 2 sur les navigateurs actuels, mais il peut s'agir de WebGL 1 si le matériel est limité. Nous recommandons de cibler les appareils qui supportent WebGL 2 avec Qt for WebAssembly.
Les différences entre WebGL et OpenGL de bureau sont documentées dans WebGL et OpenGL Differences. Il existe des différences supplémentaires entre WebGL 1.0 et WebGL 2.0, documentées dans la spécification WebGL 2.0.
Un sous-ensemble d'ES2 (et ES3) adapté à WebGL est utilisé par défaut. Si vous avez besoin d'utiliser glDrawArrays et glDrawElements sans tampons liés, vous pouvez activer le support complet d'ES2 en ajoutant
target_link_options(<your target> PRIVATE -s FULL_ES2=1)
et/ou l'émulation complète de ES3 en ajoutant
target_link_options(<your target> PRIVATE -s FULL_ES3=1)
à la page CMakeLists.txt de votre projet.
Pour plus d'informations sur le support OpenGL d'Emscripten, visitez le site https://emscripten.org/docs/porting/multimedia_and_graphics/OpenGL-support.html.
Limitations du contexte OpenGL
WebGL ne supporte pas les contextes multiples par surface. Cela a des implications pour les applications qui utilisent QOpenGLContext directement ou indirectement via d'autres classes telles que QOpenGLWidget.
Chaque instance de QOpenGLContext ne doit être utilisée qu'avec une seule surface. En pratique, le contexte est associé à la surface lors du premier appel à makeCurrent(). L'appel à makeCurrent() sur une surface différente par la suite, ou à partir d'un autre site QOpenGLContext avec la même surface, n'est pas pris en charge.
Le partage de contexte OpenGL n'est pas supporté. L'appel à QOpenGLContext::setShareContext() n'a aucun effet, et QOpenGLContext::shareContext() renvoie toujours nullptr.
La destruction d'une surface (par exemple, une QWindow) entraîne la perte du contexte associé. L'application doit gérer cela en recréant le contexte.
QOpenGLWidget et les autres classes qui utilisent le partage de contexte en interne ne sont pas prises en charge.
Multithreading
Qt pour WebAssembly supporte le multithreading en utilisant le support Pthreads d'Emscripten, où chaque thread est soutenu par un web worker. Activez le multithreading en installant le composant "WebAssembly (multi-threaded)" à partir de Qt Maintenance Tool, ou en compilant Qt à partir des sources et en passant l'option "-feature-thread" à la configuration.
Le code de threading existant peut généralement être réutilisé, mais il peut être nécessaire de le modifier pour tenir compte des spécificités de l'implémentation de pthread. Certaines fonctionnalités d'Emscripten et de Qt ne sont pas prises en charge, notamment la fonction de proxy de thread et la boucle de rendu threadée Qt Quick.
Il est particulièrement important de ne pas bloquer le thread principal de Qt pour WebAssembly, car le thread principal peut être nécessaire pour répondre aux demandes des threads secondaires. Par exemple, toutes les minuteries de Qt sont programmées sur le thread principal et ne se déclencheront pas si le thread principal est bloqué. Un autre exemple est que la création d'un nouveau web worker (pour un thread) ne peut se faire qu'à partir du thread principal.
Emscripten propose des solutions pour pallier ce problème. Les attentes à court terme, telles que l'acquisition d'un verrou de mutex, sont prises en charge par le busy-waiting et le traitement des événements pendant l'attente du verrou. Les attentes plus longues sur le thread principal doivent être évitées. En particulier, la pratique courante consistant à appeler QThread::wait() ou pthread_join() pour attendre un thread secondaire ne fonctionnera pas, sauf si l'application peut garantir que le thread (et le travailleur web) a déjà été démarré et qu'il sera en mesure de terminer sans l'aide du thread principal au moment où l'appel wait() ou join() est effectué.
La fonction de multithreading nécessite la prise en charge par le navigateur de l'API SharedArrayBuffer. (Normalement, Emscripten stocke le tas dans un objet ArrayBuffer. Pour le multithreading, le tas doit être partagé avec les travailleurs web et un SharedArrayBuffer est nécessaire). Cette API est généralement disponible dans tous les navigateurs modernes, mais peut être désactivée si certaines exigences de sécurité ne sont pas respectées. Les binaires WebAssembly dont le support des threads est activé ne s'exécuteront alors pas, même si le binaire ne démarre pas réellement un thread.
L'activation de SharedArrayBuffer nécessite un contexte de navigation sécurisé (où la page est servie sur https:// ou http://localhost), et que la page soit en mode isolé inter-origines. Cette dernière condition peut être remplie en définissant les en-têtes COOP et COEP sur le serveur web :
- Cross-Origin-Opener-Policy : same-origin
- Cross-Origin-Embedder-Policy : require-corp
SIMD
Emscripten prend en charge WebAssembly SIMD, qui fournit des types et des opérations SIMD 128 bits pour WebAssembly.
Construisez Qt à partir des sources et configurez-le avec l'option -feature-wasm-simd128 pour l'activer ; cela passera l'option -msimd128 au moment de la compilation et de l'édition de liens. Notez que Qt ne contient pas de chemins de code optimisés par wasm-simd à ce stade, mais l'activation de wasm-simd activera l'auto-vectorisation du compilateur là où le compilateur peut utiliser les instructions SIMD.
Vous pouvez cibler WebAssembly SIMD directement en utilisant les extensions vectorielles SIMD de GCC/Clang ou les intrinsèques SIMD128 de WASM. Pour plus d'informations, voir la documentation SIMD d'Emscripten .
En outre, Emscripten prend en charge l'émulation/la traduction des instructions SSE x86 en instructions SIMD Wasm. Qt n'utilise pas cette émulation, car l'utilisation d'instructions SSE SIMD qui n'ont pas d'équivalent natif Wasm SIMD peut entraîner une réduction des performances.
Notez que les binaires SIMD sont incompatibles avec les navigateurs qui ne supportent pas WebAssembly SIMD, même si les chemins de code SIMD ne sont pas appelés au moment de l'exécution. Il peut être nécessaire d'activer le support SIMD dans les configurations avancées des navigateurs, telles que 'about:config' ou 'chrome:flags'
Mise en réseau
Qt Network fournit un support limité pour la mise en réseau. En général, les protocoles réseau qui sont déjà utilisés sur le web peuvent être utilisés également à partir de Qt Network, tandis que d'autres ne sont pas directement disponibles en raison de la sandbox web.
Les protocoles suivants sont pris en charge :
- QNetworkAccessManager les requêtes http vers le serveur d'origine de la page web, ou vers un serveur qui supporte CORS. Cela inclut XMLHttpRequest de QML.
- QWebSocket les connexions à n'importe quel hôte. Notez que les pages web servies par le protocole sécurisé https n'autorisent les connexions websockets que par le protocole sécurisé wss.
- Sockets TCP POSIX émulées sur WebSockets, en utilisant la fonctionnalité fournie par Emscripten. Cela nécessite l'exécution d'un serveur de transfert qui gère la traduction des sockets.
Tous les autres protocoles réseau ne sont pas pris en charge.
Remarque : QWebSocketServer n'est pas pris en charge en raison des limitations des navigateurs. Les navigateurs limitent la fonctionnalité de socket côté serveur afin de garantir la sécurité dans le bac à sable web. Par conséquent, toute fonctionnalité reposant sur QWebSocketServer pour accepter les connexions réseau entrantes ne peut être utilisée dans l'environnement web.
Note : Les modulesQtMqtt et QtRemoteObjects peuvent être utilisés avec QtWebSockets comme transport. Ils ne sont pas officiellement pris en charge et peuvent fonctionner ou non, ou présenter des fonctionnalités manquantes. Voir QMqttClient::connectToHostWebSocket et l'exemple QtRemoteObjects WebSockets Applications.
Partage de ressources entre origines (CORS) et politique (CORP)
Les applications WebAssembly qui utilisent la mise en réseau peuvent nécessiter que les en-têtes de réponse CORS (Cross-Origin Resource Sharing) et CORP (Cross-Origin Resource Policy) soient définis par le serveur. Ces en-têtes limitent les requêtes HTTP provenant de domaines différents, ce qui présente un risque pour la sécurité.
À l'aide de QHttpServer, voici un exemple de définition de ces en-têtes :
auto headers = response.headers(); headers.append("Access-Control-Allow-Origin", "*"); headers.append("Access-Control-Allow-Origin", "localhost"); headers.append("Access-Control-Allow-Methods", "POST", "GET","OPTIONS"); headers.append("Cross-Origin-Opener-Policy", "same-origin"); headers.append("Cross-Origin-Embedder-Policy", "require-corp"); headers.append("Cross-Origin-Resource-Policy", "cross-origin");
D'autres serveurs bien connus ont leur propre façon de configurer l'envoi de ces en-têtes.
L'utilisation d'un caractère générique "*" pour Access-Control-Allow-Origin entraînera une erreur s'il est utilisé avec Access-Control-Allow-Credentials.
Le script utilitaire qtwasmserver.py inclus définira ces en-têtes si l'option -cross-origin-isolation est utilisée.
Accès aux fichiers locaux
L'accès au système de fichiers est limité sur le web, ce qui a des implications sur la manière dont l'application fonctionne avec les fichiers. La plate-forme Web fournit des API pour accéder au système de fichiers local sous le contrôle de l'utilisateur, ainsi que des API pour accéder au stockage persistant. Emscripten et Qt intègrent ces fonctionnalités et fournissent des API plus faciles à utiliser à partir d'applications basées sur C++ et Qt.
La plateforme web fournit des fonctionnalités permettant d'accéder aux fichiers locaux et au stockage permanent :
- <input type="file"> pour afficher une boîte de dialogue native d'ouverture de fichier où l'utilisateur peut choisir un fichier.
- IndexedDB fournit un stockage local persistant (non accessible en dehors du navigateur).
Emscripten fournit plusieurs systèmes de fichiers avec une API de type POSIX. Il s'agit notamment de
- le système de fichiers éphémère MEMFS qui stocke les fichiers en mémoire
- le système de fichiers persistants IDBFS, qui stocke les fichiers à l'aide d'IndexedDB
Emscripten monte un système de fichiers MEMFS temporaire sur "/" au démarrage de l'application. Cela signifie que QFile peut être utilisé et qu'il lira et écrira des fichiers en mémoire par défaut. Il n'est pas préservé lors d'un rechargement du navigateur.
Comme les pages web n'ont pas d'accès direct au système de fichiers local, Qt fournit une API QFileDialog à utiliser avec Qt for WebAssembly.
- QFileDialog::getOpenFileContent() ouvre une boîte de dialogue native dans laquelle l'utilisateur peut choisir un fichier
- QFileDialog::saveFileContent() enregistre un fichier sur le système de fichiers local via le téléchargement de fichiers.
Accès au presse-papiers
Qt supporte le copier-coller de texte, d'url, de types de fichiers connus et d'images avec le presse-papiers du système, avec quelques différences dues au bac à sable du web. Il ne prend pas en charge les données binaires arbitraires de type application/octet-stream. En général, l'accès au presse-papiers nécessite l'autorisation de l'utilisateur, qui peut être obtenue en gérant un événement d'entrée (par exemple, CTRL+c) ou en utilisant l'API du presse-papiers.
Les navigateurs qui prennent en charge l'API du presse-papiers sont préférables. Notez que cette API exige que la page web soit servie via une connexion sécurisée (par exemple https), et que certains navigateurs peuvent nécessiter la modification des drapeaux de configuration.
- Chrome version 66 et Safari version 13.1 prennent en charge l'API Presse-papiers.
- La version 90 de Firefox prend en charge l'API Presse-papiers si vous activez les options suivantes dans 'about:config' :
dom.events.asyncClipboard.read dom.events.asyncClipboard.clipboardItem
Polices
Le module Qt WASM contient 3 polices intégrées : "Bitstream Vera Sans" (police de secours), "DejaVu Sans", "DejaVu Sans Mono".
Ces polices fournissent un jeu de caractères limité. Qt propose plusieurs options pour ajouter des polices supplémentaires :
L'une d'entre elles consiste à utiliser FontLoader dans Qt Qml, qui peut soit récupérer une police par URL, soit utiliser le système de ressources Qt (de la même manière que les applications de bureau habituelles).
L'autre façon d'utiliser une police est de l'ajouter via QFontDatabase::addApplicationFontFromData.
Accessibilité et lecteurs d'écran
Qt for WebAssembly fournit un support de base pour les lecteurs d'écran. Les éléments d'interface utilisateur simples tels que les boutons et les cases à cocher fonctionnent, tandis que les éléments d'interface utilisateur plus complexes tels que les tableaux ou les arborescences peuvent ne pas être pris en charge. Les sites Qt Widgets et Qt Quick sont tous deux pris en charge.
Les configurations suivantes de lecteurs d'écran et de navigateurs ont été testées et sont reconnues comme étant fonctionnelles. D'autres navigateurs et lecteurs d'écran peuvent fonctionner.
- VoiceOver avec Safari sur macOS
- VoiceOver avec Chrome sur macOS
L'implémentation de l'accessibilité fonctionne en créant des éléments html "shadow" qui fournissent les informations d'accessibilité pour les éléments de l'interface utilisateur Qt. Cette fonctionnalité est désactivée par défaut. L'utilisateur final peut l'activer en sélectionnant un bouton "activer le lecteur d'écran" à l'aide du lecteur d'écran. Une fois activée, la page web sera remplie d'éléments d'accessibilité.
Démarrage de l'application et boucle d'événements
Qt for WebAssembly prend en charge l'approche de démarrage standard de Qt, où l'application crée un objet QApplication et appelle la fonction exec :
int main(int argc, char **argv) { QApplication app(argc, argv); QWindow appWindow; return app.exec(); }
L'appel exec() ci-dessus bloque et traite normalement les événements jusqu'à l'arrêt de l'application. Malheureusement, cela n'est pas possible sur la plateforme web où le blocage du thread principal n'est pas autorisé. Au lieu de cela, le contrôle doit être renvoyé à la boucle d'événements du navigateur après le traitement de chaque événement.
Qt contourne ce problème en faisant en sorte que exec() renvoie le contrôle du thread principal au navigateur, tout en préservant la pile. Du point de vue du code de l'application, la fonction exec() est saisie et le traitement des événements se déroule comme d'habitude. Cependant, l'appel à exec() ne revient jamais, pas plus qu'à la sortie de l'application.
Ce comportement est généralement acceptable puisque le navigateur libère la mémoire de l'application au moment de l'arrêt de celle-ci. Cela signifie que le code d'arrêt ne s'exécute pas, puisque l'objet de l'application est perdu et que son destructeur ne s'exécute pas.
Vous pouvez éviter cela en réécrivant main() pour qu'il soit asynchrone, ce qui est possible puisque Emscripten ne quitte pas le runtime lorsque main() revient. Le code de l'application omet alors de faire l'appel à exec() et peut arrêter Qt proprement en supprimant la fenêtre de premier niveau et les objets de l'application.
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; }
Asynchronisation et JSPI
La version par défaut de Qt pour WebAssembly ne permet pas de réintégrer la boucle d'événements, par exemple en appelant QEventLoop::exec() ou QDialog::exec(), en raison des restrictions de la plateforme web qui empêchent le code C++ synchrone d'invoquer les API JavaScript asynchrones.
Les fonctionnalités qui requièrent asyncify/JSPI sont les suivantes :
- QDialogs, QMessageBoxes avec valeurs de retour.
- Glisser-déposer (en particulier glisser).
- Boucles d'événements imbriquées/secondaires exec().
Emscripten permet de contourner ces limitations grâce à la fonction Asyncify. Cette fonctionnalité est disponible en deux versions :
- Asyncify, implémentée via une étape de post-traitement du code WebAssembly.
- JSPI, implémentée en utilisant la fonctionnalité WebAssembly JS Promise Integration.
Voir les sections ci-dessous pour plus de détails sur la façon d'activer chaque option et les compromis impliqués. En résumé, Asyncify est disponible aujourd'hui mais introduit des surcoûts sous la forme de temps de construction plus longs, de tailles binaires plus importantes et de coûts de performance d'exécution. JSPI a un surcoût minimal ou nul, mais il n'est pas encore pris en charge par tous les navigateurs.
Asyncify
Activez l'asynchronisme en ajoutant l'option "-sASYNCIFY -Os" aux options de l'éditeur de liens :
CMake :
target_link_options(<your target> PUBLIC -sASYNCIFY -Os)
qmake :
QMAKE_LFLAGS += -sASYNCIFY -Os
L'activation d'asyncify ajoute une surcharge sous la forme d'une augmentation de la taille des binaires et de l'utilisation du CPU. Construire avec des optimisations activées pour minimiser la surcharge.
JSPI (JS Promise Integration)
Contrairement à Asyncify, l'utilisation de JSPI nécessite de construire Qt à partir des sources. Passez les drapeaux -feature-wasm-jspi -feature-wasm-exceptions au script de configuration de Qt pour l'activer. (JSPI n'est pas compatible avec les exceptions émulées par défaut d'Emscripten).
Ensuite, activez JSPI pour votre application en ajoutant le drapeau -sJSPI aux options de l'éditeur de liens :
CMake :
target_link_options(<your target> PUBLIC -sJSPI)
qmake :
QMAKE_LFLAGS += -sJSPI
Débogage et profilage
Le débogage de Wasm se fait dans la console JavaScript du navigateur. Il n'est pas possible de déboguer des applications sur Wasm directement dans Qt Creator.
- La sortie de débogage et de journalisation de Qt est imprimée sur la console JavaScript, à laquelle on peut accéder via les "outils de développement" du navigateur ou un outil similaire.
- Les cartes de sources pour parcourir le code peuvent être créées en reconfigurant Qt avec l'option -device-option QT_WASM_SOURCE_MAP=1, et en construisant une version de débogage.
- Les symboles de débogage via DWARF sont également activés si le programme est lié avec le drapeau -g (testé avec Chrome).
- Cela nécessite cette extension : https://goo.gle/wasm-debugging-extension
- Voir aussi https://developer.chrome.com/blog/wasm-debugging-2020/
- Les navigateurs mobiles peuvent utiliser le débogage à distance
- Pour arrêter l'exécution sur une certaine ligne et faire apparaître le débogueur du navigateur par programme, vous pouvez ajouter la fonction emscripten_debugger() ; au code source de l'application.
- Le profilage peut être réalisé en utilisant une compilation de débogage et les fonctions de profilage de la console JavaScript. Qt ajoute -profiling-funcs aux arguments de l'éditeur de liens dans les versions de débogage, ce qui préserve les noms de fonctions dans le profilage
Vous pouvez ajouter plus de verbosité pour faciliter le débogage en utilisant les arguments de l'éditeur de liens Emscripten :
- -s LIBRARY_DEBUG=1 (impression des appels de bibliothèque)
- -s SYSCALL_DEBUG=1 (impression des appels sys)
- -s FS_LOG=1 (impression des opérations du système de fichiers)
- -s SOCKET_DEBUG (impression du transfert de données réseau)
CMake :
target_link_options(<your target> PRIVATE -s LIBRARY_DEBUG=1)
qmake :
QMAKE_LFLAGS_DEBUG += -s LIBRARY_DEBUG=1
Optimisation
Qt for WebAssembly utilise la chaîne d'outils Emscripten pour générer des binaires, et il existe de nombreux drapeaux qui peuvent avoir un impact sur les performances et la taille des binaires. Voir Emscripten : Optimisation du code pour plus d'informations.
Vous pouvez passer des drapeaux d'éditeur de liens et de compilateur comme pour les applications C++ normales :
target_compile_options(<your target> PRIVATE -oz -flto) target_link_options(<your target> PRIVATE -flto)
QMAKE_CXXFLAGS += -oz -flto QMAKE_LFLAGS += -flto
Minimiser la taille des fichiers binaires
Afin d'offrir à l'utilisateur une expérience transparente, il est important de réduire le temps de téléchargement et de chargement des applications WebAssembly. Un binaire d'application plus petit est l'un des aspects importants qui permet un téléchargement plus rapide. Utilisez les alternatives suivantes pour réduire la taille des binaires :
- Veillez à distribuer des versions release. Les versions de débogage contiennent des symboles de débogage et sont beaucoup plus volumineuses.
- Activez la compression sur un serveur. Les algorithmes les plus courants comme
gzipet Brotli fonctionnent bien sur les binaires Wasm et peuvent réduire considérablement leur taille. - Essayez les options du compilateur et de l'éditeur de liens qui peuvent permettre de générer des binaires plus petits (i.e. '-os', '-oz'). Les résultats varieront en fonction de l'application concernée.
- Désactiver les fonctionnalités non désirées lors de la compilation de Qt for WebAssembly à partir des sources (voir ci-dessous).
Désactiver des fonctionnalités
Par défaut, une application WebAssembly est liée statiquement aux bibliothèques Qt, ce qui permet au compilateur d'éliminer le code mort. Cependant, en raison de la nature dynamique de Qt, il n'est pas toujours possible pour le compilateur d'effectuer de telles optimisations.
Si vous compilez Qt pour WebAssembly à partir des sources, vous pouvez désactiver certaines fonctionnalités afin de réduire la taille des binaires Qt et, par conséquent, la taille des binaires .wasm. Qt désactive certaines fonctionnalités par défaut pour la plateforme WebAssembly, mais vous pouvez également désactiver les fonctionnalités que votre application n'utilise pas. Voir les fonctionnalités désactivées pour plus d'informations.
Vous pouvez désactiver les fonctionnalités suivantes pour réduire la taille des binaires (généralement de 10 à 15 %) :
| Configurer Argument | Brève description |
|---|---|
| -no-feature-cssparser | Analyseur pour les feuilles de style en cascade. |
| -no-feature-datetimeedit | Édition de dates et d'heures (dépend de datetimeparser). |
| -no-feature-datetimeparser | Analyse des textes de date et d'heure. |
| -no-feature-dockwidget | Accrocher des widgets à l'intérieur d'un QMainWindow ou les faire flotter en tant que fenêtres de premier niveau sur le bureau. |
| -no-feature-gestures | Cadre pour les gestes. |
| -no-feature-mimetype | Gestion des Mimetype. |
| -no-feature-qml-network | Transparence du réseau. |
| -no-feature-qml-list-model | ListModel Type QML. |
| -no-feature-qml-table-model | TableModel Type QML. |
| -no-feature-quick-canvas | Élément de canevas. |
| -no-feature-quick-path | Éléments de chemin d'accès. |
| -no-feature-quick-pathview | PathView élément. |
| -no-feature-quick-treeview | TreeView élément. |
| -no-feature-style-stylesheet | Style de widget configurable via CSS. |
| -no-feature-tableview | Implémentation par défaut d'un modèle/vue de table. |
| -no-feature-texthtmlparser | Analyseur de code HTML. |
| -no-feature-textmarkdownreader | Lecteur Markdown (CommonMark et GitHub). |
| -no-feature-textodfwriter | Rédacteur ODF. |
Exceptions Wasm
Qt est construit sans support d'exception par défaut, où le lancement d'une exception interrompt le programme. Les exceptions WebAssembly peuvent être activées en compilant à partir des sources et en passant l'option -feature-wasm-exceptions à Qt configure. Cela transmettra le drapeau -fwasm-exceptions au compilateur au moment de la compilation et de la liaison. Qt ne permet pas d'activer le support d'Emscripten pour l'ancienne implémentation des exceptions basée sur JavaScript.
Notez que l'appel à QApplication::exec() n'est pas pris en charge lorsque les exceptions sont activées, en raison de détails d'implémentation internes. Au lieu de cela, écrivez main() de manière à ce qu'il retourne rapidement et n'appelle pas exec(), comme décrit dans Démarrage de l'application et boucle d'événements.
Bibliothèques partagées et liaison dynamique (aperçu technologique)
Qt for WebAssembly utilise par défaut la liaison statique, où l'application est déployée sous la forme d'un seul fichier WebAssembly qui contient les bibliothèques Qt et le code de l'application. La liaison dynamique est un mode de construction alternatif dans lequel chaque bibliothèque et plugin est distribué individuellement.
Par exemple, une application qui utilise Qt Quick peut utiliser les bibliothèques et plugins suivants :
- <qtpath>/lib/libQt6Core.so
- <qtpath>/lib/libQt6Gui.so
- <qtpath>/lib/libQt6Qml.so
- <qtpath>/lib/libQt6Quick.so
- <qtpath>/plugins/imageformats/libqjpeg.so
- <qtpath>/plugins/imageformats/libqjgif.so
- <qtpath>/qml/QtQuick/Window/libquickwindowplugin.so
La prise en charge des liens dynamiques dans la version 6.11 est en avant-première technologique. L'implémentation est adaptée au prototypage et à l'évaluation, mais ne convient pas à une utilisation en production. Les limitations et restrictions actuelles sont les suivantes :
- Le multithreading n'est pas pris en charge.
- L'asynchronisme n'est pas pris en charge.
Les applications Qt construites avec la liaison dynamique nécessitent la présence de deux fichiers supplémentaires aux côtés des binaires : qt_plugins.json et qt_qml_imports.json. Ces fichiers spécifient une liste de bibliothèques partagées qui seront chargées au démarrage de l'application. Il existe un outil d'aide pour les générer : wasmdeployqt. Pour démontrer comment utiliser cet outil, vous pouvez le lancer avec l'option -help pour obtenir une vue d'ensemble des drapeaux nécessaires à l'exécution de l'outil et des exemples d'utilisation.
Le serveur web hébergeant l'application doit disposer des bibliothèques partagées Qt. Cela peut être réalisé en copiant le contenu du dossier d'installation de Qt sur le serveur web, ou en créant un lien avec le système de fichiers.
Démarrage rapide
La procédure de construction et de déploiement est légèrement différente de celle des constructions statiques de wasm et de bureau partagé. Envisagez de commencer par un petit exemple avant de passer à la construction d'une application complète.
- Construisez Qt à partir des sources, passez l'option "-shared" au script de configuration de Qt. Utilisez l'option "-prefix" pour définir le répertoire d'installation.
- Construisez votre application en utilisant Qt à partir de l'étape 1.
- Créez les listes de préchargement des plugins en exécutant l'outil de déploiement à partir du répertoire de construction de votre application.
- <qtpath>/qtbase/bin/wasmdeployqt -qt-wasm-dir=<Path to the Qt for WebAssembly directory> -qml-root-path=<Root directory for QML files> -qt-host-dir=<Path to the Qt host directory>.
Déploiement en profondeur des bibliothèques partagées
La version des bibliothèques partagées de Qt est déployée en deux étapes : la première étape permet de télécharger la version de Qt et de l'application à partir du serveur web, et la seconde étape permet de télécharger les plugins Qt et les importations Qt Quick nécessaires au démarrage de l'application.
Lors de la première étape, l'installation de Qt peut être téléchargée à partir du serveur web. Selon les spécificités de la configuration du serveur web, il peut y avoir différentes façons d'y parvenir. En général, le chargeur Qt s'attend à trouver les bibliothèques et les plugins Qt dans un répertoire nommé "qt", relativement au fichier html qui charge l'application.
Si vous copiez déjà l'application sur le serveur web dans le cadre du déploiement, il est possible de copier également Qt. Si vous servez l'application directement à partir de son répertoire de construction - ce qui est souvent le cas pendant les phases de développement - la création d'un lien symbolique vers Qt peut s'avérer efficace.
Préparez la deuxième étape en créant des listes de préchargement pour les composants Qt tels que les plugins et les importations Qt Quick. Le préchargement permet de s'assurer que tous les composants Qt nécessaires sont disponibles au démarrage de l'application. Le chargement différé, où les composants sont téléchargés à la demande, est également possible mais n'est pas abordé ici.
Le préchargement est mis en œuvre par le chargeur JavaScript de Qt, qui télécharge les fichiers du serveur web vers le système de fichiers en mémoire fourni par Emscripten. Les fichiers à télécharger sont spécifiés à l'aide de listes de téléchargement formatées en json. Qt Quick fournit un outil pour générer des listes de préchargement, voir la section Démarrage rapide ci-dessus.
Problèmes connus
- Les boucles d'événements imbriquées ne sont pas prises en charge. Les applications ne doivent pas appeler des API comme QDialog::exec() et QEventLoop::exec(). La fonctionnalité expérimentale Asyncify peut être utilisée.
- L'impression n'est pas prise en charge.
- QDnsLookup Les recherches et QSsl ne fonctionnent pas et ne sont pas prises en charge en raison de la sandbox web. Le navigateur gère les recherches DNS et les certificats SSL. DNS over HTTP pourrait être utilisé par des applications nécessitant des consultations DNS.
- QTcpSockets peut être utilisé mais toutes les fonctions POSIX sockets ne sont pas mandatées. Un proxy de serveur Websockets comme Websockify doit être utilisé.
- Toutes les classes Q*Server ne sont pas supportées par la plateforme.
- QWebSocket Les connexions sont prises en charge par Emscripten uniquement sur le thread principal.
- QWebSockets pour WebAssembly ne prend pas en charge l'envoi de trames ping ou pong, car l'API disponible pour les pages web et les navigateurs n'expose pas cette fonctionnalité.
- Pour utiliser QtWebsockets, le sous-protocole peut avoir besoin d'être réglé sur 'mqtt' pour utiliser QtMqtt. Utilisez QWebSocketHandshakeOptions lorsque vous ouvrez le site QWebSocket.
- Polices : Le bac à sable Wasm n'autorise pas l'accès aux polices du système. Les fichiers de polices doivent être distribués avec l'application, par exemple dans les ressources Qt ou en téléchargement. Qt for WebAssembly intègre lui-même une de ces polices.
- Il peut y avoir des artefacts de mémoire graphique non initialisée sur certains composants de Qt Quick Controls 2, tels que les cases à cocher. Ces artefacts sont parfois visibles sur les écrans HighDPi.
- Les styles natifs pour Windows et macOS ne sont pas supportés car Wasm en tant que plateforme ne fournit pas cette capacité.
- Une erreur de temps de liaison telle que "wasm-ld : error : initial memory too small", nécessite un ajustement de la taille de la mémoire initiale. Utilisez QT_WASM_INITIAL_MEMORY pour définir la taille initiale en Ko, qui doit être un multiple de 64 Ko (65536). La valeur par défaut est de 50 Mo. Dans CMakeLists.txt : set_target_properties(<target> PROPERTIES QT_WASM_INITIAL_MEMORY "150MB")
- add_executable dans CMakeLists.txt ne produit pas <target>.html ou ne copie pas qtloader.js. Utilisez qt_add_executable à la place.
Autres sujets
Référence des options de configuration de Qt
Les options de configuration suivantes sont pertinentes lors de la construction de Qt for WebAssembly à partir des sources.
| Argument de configuration | Brève description |
|---|---|
| -fonctionnalité-fil | Wasm multithread. |
| -fonctionnalité-wasm-simd128 | Active le support SIMD de WebAssembly. |
| -caractéristique-wasm-exceptions | Active le support des exceptions de WebAssembly. |
| -device-option QT_EMSCRIPTEN_ASYNCIFY=1 | Active la prise en charge de l'asynchronisme. |
| -device-option QT_EMSCRIPTEN_ASYNCIFY=2 | Active la prise en charge de l'asynchronisme (JSPI). |
Qt désactive certaines fonctionnalités par défaut pour la plateforme WebAssembly, afin de réduire la taille des binaires. Vous pouvez activer explicitement une fonctionnalité lorsque vous configurez Qt pour WebAssembly :
| Argument de configuration | Brève description |
|---|---|
| -fonctionnalité-topleveldomain | Permet de vérifier si un domaine est un domaine de premier niveau. |
Tailles typiques des téléchargements
Empreinte attendue (taille de téléchargement) : Les modules Wasm tels qu'ils sont produits par le compilateur peuvent être volumineux, mais ils se compressent bien :
| Exemple | gzip | brotli |
|---|---|---|
| helloglwindow (QtCore + QtGui) | 2.8M | 2.1M |
| wiggly widget (QtCore + QtGui + QtWidgets) | 4.3M | 3.2M |
| SensorTag (QtCore + QtGui + QtWidgets + QtQuick + QtCharts) | 8.6M | 6.3M |
La compression est généralement gérée du côté du serveur web, à l'aide de fonctions de compression standard : le serveur compresse automatiquement ou récupère des versions précompressées des fichiers. Il n'est généralement pas nécessaire d'avoir une gestion spéciale des fichiers Wasm.
Pour plus d'informations, voir Minimiser la taille des binaires.
Exemples d'utilisation
Exemples et démonstrations d'applications Qt fonctionnant dans un navigateur web : Démonstrations Qt
Ressources externes
Licence
Qt for WebAssembly est disponible sous licence commerciale auprès de The Qt Company. En outre, il est disponible sous la licence publique générale GNU, version 3. Voir Qt Licensing pour plus de détails.
Voir également https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS et https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy.
© 2026 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.