{"id":9069,"date":"2024-04-26T09:37:58","date_gmt":"2024-04-26T07:37:58","guid":{"rendered":"https:\/\/www.basyskom.de\/?p=9069"},"modified":"2024-07-17T17:57:21","modified_gmt":"2024-07-17T15:57:21","slug":"how-to-use-modern-qml-tooling-in-practice","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/how-to-use-modern-qml-tooling-in-practice\/","title":{"rendered":"How To Use Modern QML Tooling in Practice"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"9069\" class=\"elementor elementor-9069\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2fee280 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-parent\" data-id=\"2fee280\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ddf2646 elementor-widget elementor-widget-heading\" data-id=\"ddf2646\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Creating the main application QML module<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8970763 elementor-widget elementor-widget-text-editor\" data-id=\"8970763\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Your typical embedded device HMI consists of its main application UI, a bunch of shared UI components, as well as back-end glue code to talk to some hardware. While shared QML components are usually implemented as proper QML plug-ins already, the main UI is often bundled in the application binary loaded through a <i>qrc:\/qml\/main.qml<\/i> URL on startup. With QML modules, however, Qt wants to move away from loading QML files in an ad-hoc fashion like this. Every QML file that is used should be explicitly declared. This means that even the main application UI should become its own QML module:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-165d6b5 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"165d6b5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-clike'>qt6_add_executable(myhmi main.cpp ...)\nqt6_add_qml_module(myhmi\n    URI com.basyskom.app\n    QML_FILES\n        Main.qml\n        MainPage.qml\n        ...\n    IMPORTS\n        QtQuick\n) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-187e63d elementor-widget elementor-widget-text-editor\" data-id=\"187e63d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The <em>Main.qml<\/em> file will be added to the resource system automatically in <i>qrc:\/qt\/qml\/com\/basyskom\/app\/Main.qml<\/i> but you will rather be loading it by its module and type name:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c6f1f20 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"c6f1f20\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>QQmlApplicationEngine engine;\nengine.loadFromModule(&quot;com.basyskom.app&quot;, &quot;Main&quot;); <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b900d82 elementor-widget elementor-widget-text-editor\" data-id=\"b900d82\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Likewise from QML:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5eb37ce elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"5eb37ce\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-javascript'>Qt.createComponent(&quot;com.basyskom.app&quot;, &quot;Main&quot;); <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-633158d elementor-widget elementor-widget-text-editor\" data-id=\"633158d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tNow it doesn\u2019t matter whether it\u2019s an actual QML file somewhere on your hard drive, in your application resources, or has been compiled to proper C++. If the CMake target already exists, the QML module is added to it, otherwise a library target is created automatically.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d267664 elementor-widget elementor-widget-heading\" data-id=\"d267664\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Adding another QML module<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cccb226 elementor-widget elementor-widget-text-editor\" data-id=\"cccb226\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tUnfortunately, every CMake target can only be assigned a single QML module. If you want to keep using separate QML imports within the same binary, you\u2019ll need to create a static library for each module and link it to the main application:\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3d95fd2 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"3d95fd2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-clike'>qt6_add_qml_module(style_module\n    STATIC\n    URI com.basyskom.style\n    QML_FILES\n        Style.qml\n    IMPORTS\n        QtQuick\n)\ntarget_link_libraries(myhmi PRIVATE style_moduleplugin) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e3cfc35 elementor-widget elementor-widget-text-editor\" data-id=\"e3cfc35\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tWe then also want to link the \u201cplugin\u201d rather than its backing library. Since the compiler is free to eliminate unreferenced symbols, it needs to be explicitly mentioned in the main application. The plugin class name is based on the URI with \u201cPlugin\u201d appended to it:\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-33f539b elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"33f539b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>#include &lt;QQmlExtensionPlugin&gt;\nQ_IMPORT_QML_PLUGIN(com_basyskom_stylePlugin) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e2295cd elementor-widget elementor-widget-text-editor\" data-id=\"e2295cd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tThere\u2019s a catch: by default, for executable targets, the path components based on its URI (e.g. com\/basyskom\/app) are automatically appended to the QML module\u2019s binary location whereas for library targets it\u2019s not! The rationale being that an application can control its import paths and search for QML imports in its binary directory whereas a library has no control over its environment. It\u2019s your responsibility to deploy it correctly and set the QML engine\u2019s import paths accordingly. That\u2019s why in the above example the CMakeLists.txt has to be placed  in a com\/basyskom\/style subfolder. This ensures that the resulting binary folder structure matches the structure the QML engine expects by default.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0f2a9d1 elementor-widget elementor-widget-text-editor\" data-id=\"0f2a9d1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tRealistically, for existing projects, matching the source code directory structure to the QML import path structure is not feasible. Luckily, you can force QML module code generation for all targets to happen in a specific directory:\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5a6cda2 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"5a6cda2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-clike'>set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}\/qml) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e16274e elementor-widget elementor-widget-text-editor\" data-id=\"e16274e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tOnce an <i>OUTPUT_DIRECTORY<\/i> is set, the default rule no longer applies and the URI path components are always appended. Confusing, I know. Sadly, just because the application runs, it doesn\u2019t mean that the QML tooling finds its imports. The application is linked against the plug-in and its resource files and can access it just fine at runtime whereas QML tooling needs to look for \u201cphysical\u201d files describing the modules without execution. Qt Creator for instance checks the <i>QML_IMPORT_PATH<\/i> from CMakeCache.txt:\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-746048b elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"746048b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-clike'>set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} CACHE PATH &quot;Extra QML import paths to make Qt Creator happy&quot;) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-417e028 elementor-widget elementor-widget-text-editor\" data-id=\"417e028\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tYou might have to reset the QML code model for Qt Creator to find newly added QML components.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d0f330b elementor-widget elementor-widget-heading\" data-id=\"d0f330b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Benefits<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-10fd439 elementor-widget elementor-widget-text-editor\" data-id=\"10fd439\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tOnce you have added the relevant QML files to your new application module, the application will run pretty much as before. The good news is that the two worlds can co-exist. You can keep your manual calls to <i>qmlRegisterType<\/i> and gradually migrate as you add new features or refactor existing code. Thus, the big question right now is: what are the benefits of this new approach?\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3b1ad57 elementor-widget elementor-widget-text-editor\" data-id=\"3b1ad57\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tFirst of all, the <i>qmldir<\/i> file that describes your QML module is automatically generated. Usually, <i>qmldir<\/i> was only maintained when absolutely necessary, such as when implementing a singleton type, with applications instead relying on the implicit import that makes types in the same directory available automatically. Likewise for the <i>qmltypes<\/i> type description file that an IDE like Qt Creator uses to figure out which C++ types are accessible in the module and which properties they expose.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-47887a0 elementor-widget elementor-widget-text-editor\" data-id=\"47887a0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tThere\u2019s also no need to tinker with resource files as QML files and their assets get automatically bundled. That alone makes it worth it as there is now a single source of truth about where to add a new QML file. Having assets, such as icons, addressable relative to the QML files that use them also makes them more easily viewable standalone, such as from the <i>qml<\/i> tool (think <i>qmlscene<\/i>) or Qt Creator\u2019s QML Designer. Sadly, bundling many assets alongside QML files also has an impact on compilation times. A couple of seconds penalty whenever changing a single line of QML because of assets being recompiled can be quite irritating.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5565bac elementor-widget elementor-widget-text-editor\" data-id=\"5565bac\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tThe most game-changing part of QML modules, however, is the tooling it unlocks: The QML Linter (qmllint) knows exactly what types and properties are available and can warn of unqualified property access, vague type descriptions, unused imports. The types of bugs that only show at runtime and are therefore hard to spot in advance. The QML Language Server (<i>qmlls<\/i>) then supplies an LSP-capable editor with information collected by qmllint for a richer development experience even outside a fully-featured IDE as well as invoke the QML Formatter (<i>qmlformat<\/i>) to format your code. Finally, the QML compilers (<i>qmlcachegen<\/i> or <i>qmlsc<\/i>, the <i>QML script compiler<\/i>) can produce more efficient code if they know in advance what types are being used in a property binding or function call, since JavaScript is a very dynamic language. Of course, thanks to QML files being processed at build time, we also get build failures for syntax errors in them.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-44c230a elementor-widget elementor-widget-text-editor\" data-id=\"44c230a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tThe <i>qt_add_qml_module<\/i> CMake function automatically generates qmllint targets for your libraries and executables. Just build the relevant <i>myapp_qmllint<\/i> or convenience <i>all_qmllint<\/i> targets and look at the results.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e9e6bd9 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"e9e6bd9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-bash'>$ cmake --build . --target all_qmllint\n...\nWarning: app\/qml\/Main.qml:52:42: Member &quot;title&quot; not found on type &quot;QQuickItem&quot; [missing-property]\n                text: stack.currentItem.title\n                                        ^^^^^\n\nInfo: app\/qml\/pages\/MainPage.qml:7:1: Unused import [unused-imports]\nimport QtQuick\n\nWarning: app\/com\/basyskom\/style\/Style.qml:24:31: Member &quot;colorScheme&quot; not found on type &quot;QObject&quot; [missing-property]\n            if (Qt.styleHints.colorScheme === Qt.ColorScheme.Dark) {\n                              ^^^^^^^^^^^\n\nninja: build stopped: subcommand failed. <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-185a05e elementor-widget elementor-widget-text-editor\" data-id=\"185a05e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The linter target \u201cfailed\u201d because there were some warnings, making it an ideal candidate for inclusion in a CI system. Naturally, all the linter sees is the \u201ccurrentPage\u201d property of StackView which is declared \u201c<em>QQuickItem<\/em>\u201d. It doesn\u2019t know that in our application we will only ever push an implementation of <i>AbstractPage<\/i>. We can fix that by using a type assertion:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1af5153 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"1af5153\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-javascript'>text: (stack.currentItem as Controls.AbstractPage).title <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-55671b7 elementor-widget elementor-widget-text-editor\" data-id=\"55671b7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Think of a type assertion (the \u201c<b>as<\/b>\u201d cast) as a \u201c<b>qobject_cast<\/b>\u201d: if the type is not (an implementation of) an <i>AbstractPage<\/i>, it will return <i>null<\/i>. Unfortunately, there\u2019s a couple of APIs in Qt itself where the return type is generic, which we cannot fix on our side. <i>Qt.styleHints<\/i> returns a generic <i>QObject<\/i>, not the <i>StyleHints<\/i> type. Therefore, we should use the \u201cstyleHints\u201d property of the <i>Application<\/i> singleton instead.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-001ba01 elementor-widget elementor-widget-heading\" data-id=\"001ba01\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Conclusion<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0bbf042 elementor-widget elementor-widget-text-editor\" data-id=\"0bbf042\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>We have seen how to transform our existing main application UI into proper QML modules to take full advantage of modern QML tooling. While it\u2019s nice for the build system to be more knowledgeable about our code base now, as a developer you want this information available at your fingertips while actually writing code. Stay tuned for our next issue where we are going to have a look at the QML Language Server and how to set it up, and what you can do to help the QML Compiler generate more efficient code.<\/p><p><strong>Further reading:<\/strong><\/p><ul><li><a class=\"external-link\" href=\"https:\/\/doc.qt.io\/qt-6\/qtqml-cppintegration-definetypes.html\" target=\"_blank\" rel=\"noopener\">Automatic Type Registration<\/a><\/li><li><a class=\"external-link\" href=\"https:\/\/doc.qt.io\/qt-6\/qqmlengine.html#QML_ELEMENT\" target=\"_blank\" rel=\"noopener\">QML_ELEMENT<\/a><\/li><li><a class=\"external-link\" href=\"https:\/\/doc.qt.io\/qt-6\/qtqml-writing-a-module.html#multiple-qml-modules-in-one-binary\" target=\"_blank\" rel=\"noopener\">Writing QML Modules<\/a><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Qt 5.15 introduced \u201cAutomatic Type Registration\u201d. With it, a C++ class can be marked as \u201cQML_ELEMENT\u201d to be automatically registered to the QML engine. Qt 6 takes this to the next level and builds all of its tooling around the so-called QML Modules. Let\u2019s talk about what this new infrastructure means to your application in practice and how to benefit from it in an existing project.<\/p>","protected":false},"author":23,"featured_media":4011,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1,2,720,8],"tags":[126,15],"class_list":["post-9069","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","category-blog","category-newsletter","category-qt","tag-qml","tag-qt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/9069","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/users\/23"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=9069"}],"version-history":[{"count":17,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/9069\/revisions"}],"predecessor-version":[{"id":10736,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/9069\/revisions\/10736"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/4011"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=9069"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=9069"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=9069"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}