Compiling QML to C++: Qt Quick Controls and optional imports

This is the sixth installment in the series of blog posts on how to adjust your QML application to take the maximum advantage of qmlsc. In the first post we've set up the environment. You should read that post first in order to understand the others.

This time I will show how to deal with the optional imports provided by Qt's own modules. When compiling any QML file that imports QtQuick.Controls you are greeted with a message of this kind:

Warning: QtQuick.Controls uses optional imports which are not supported. Some types might not be found.

Indirectly, among other problems, this leads to:

Warning: CategoryLabel.qml:145:9: unknown grouped property scope anchors.
    anchors.verticalCenter: txt.verticalCenter

As qmlsc doesn't know the QtQuick.Controls types, it doesn't know they provide anchors. This one is an ImageToolButton, derived ToolButton as probided by QtQuick.Controls. As qmlsc doesn't know the type of the property the binding is assigned to, it cannot compile the binding.

Profiling using QML Profiler reveals that the 26 calls calls to the binding on anchors.verticalCenter take 54.2µs, of which 29.5µs are spent on JavaScript execution.

What does the warning mean? QtQuick.Controls uses different styles, depending on your platform. On windows it usually uses a "windows" style, on macOS a "macOS" style, etc. However, you can override the automatic style selection at run time using the QT_QUICK_CONTROLS_STYLE environment variable, via a configuration file, and in a number of other ways. This means, when QtQuick.Controls is imported, qmlsc does not know what types it will provide. It could be the types from either of the styles available.

We can fix this by nailing down the style to be used at compile time. Since our Timeline module uses QtQuick.Controls practically everywhere, the easiest way to do it is to just import the specific style we want to use from the module definition in the CMakeLists.txt. There we can use CMake to choose a different style depending on platform or other criteria we may need. We change our module as follows and drop all the QtQuick.Controls imports from the individual QML files:

if(WIN32)
  set(QUICK_CONTROLS "QtQuick.Controls.Windows")
elseif(APPLE)
  set(QUICK_CONTROLS "QtQuick.Controls.macOS")
else()
  set(QUICK_CONTROLS "QtQuick.Controls.Basic")
endif()

qt_add_qml_module(Tracing
  URI "QtCreator.Tracing"
  VERSION "1.0"
  NO_PLUGIN
  DEPENDENCIES
    QtQuick
  IMPORTS
    ${QUICK_CONTROLS}
  QML_FILES
    ${TRACING_QML_FILES}
  RESOURCES
    ${TRACING_QML_RESOURCES}
  SOURCES
    ${TRACING_CPP_SOURCES}
)

In reality, we cannot do this to Qt Creator because people value the choice they have in customizing Qt Creator's look and feel to whatever desktop environment they are using. If you are developing a commercial application that tightly controls its look and feel, and possibly ships its own style, you may want to fix the style at compile time anyway, though. In that case, you can reap the benefits of compiling the QML code that uses it to C++ in addition.

After applying this change, the binding on anchors.verticalCenter can be compiled to C++. Now the 26 calls take 29µs, of which 13.7µs are spent on JavaScript execution. That's a rather significant improvement from the 54.1µs and 29.5µs we've seen before.

Compatibility

Module-level imports have been available since Qt 5.14, but before Qt 6 they have a number of flaws. First, in Qt 5 you don't get to use qt_add_qml_module(). Therefore you have to write your qmldir files manually or better, write your own build system code to generate them. Then, the qmldir "import" directive in Qt 5 will only behave reasonably if:

  1. The module in question also declares some QML types itself.
  2. All of the other types are C++-based (not .qml files).
  3. The module has a plugin. You cannot directly compile it into your application.

Blog Topics:

Comments

David Boosalis
2 points
34 months ago

6 blog post on compiling QML spells over complicated.

Felix
0 points
34 months ago

How so? This probably one of the most significant innovations the company has produced, IMHO (ehh maybe RHI). And it fundamentally changes how you should code for QML(if you want max performance, if you don't care nothing changes). 6 blog posts are definitely warranted. Aside from these blog posts QMLLint should also help devs in catching mistakes, and also the IDE now gives warnings about what it could not compile, so it's not like we are in the dark. And even then all of this is just for those who want to get the best performance. If it doesn't compile ahead of time it will just revert to how qmlcachegen worked previously

E
ekke
1 point
34 months ago

I really like this blog series and looking forward to the next parts.

Felix
0 points
34 months ago

Could you in some next installments show some example of a UI concept that didn't scale up well traditionally, but is now feasible with qmlsc? That way we can get some ideas on what is now possible to build that used to be impractical/underperforming. Love reading the series, thanks!

M
Maarten de Boer
0 points
28 months ago

How does this work in combination with setting a custom style at run-time? I am trying to create a custom style to be set at run-time, with a fallback style, but I can't get this to work.Edit: I got it to work by combining both IMPORTS in qt_add_qml_modules with a specific style and import QtQuick.Controls in the QML code. The specific style set with IMPORTS is not actually used when I use QQuickStyle::setStyle and QQuickStyle::setFallbackStyle. And I get a bunch of warnings on import QtQuick.Controls:

Warning: QtQuick.Controls uses optional imports which are not supported. Some types might not be found. Info: main.qml:2:1: Unused import at main.qml:2:1 import QtQuick.Controls

This makes me think I am not doing this correctly, but I don't see any other way. Examples and documentation on using styles seems a bit incomplete or contradictory.

Boris Posavec (akiko)
0 points
22 months ago

Thanks a bunch for this. I managed to move past the optional import errors and warnings by following the instructions above. However I get now another compiler error later in the process, that I can't find a way to fix:

fatal error: 'private/qquickcontrol_p_p.h' file not found [build] #include "private/qquickcontrol_p_p.h" [build] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Do I need some extra cmake directive for this to be found?

Dmitry Sazonov (SaZ)
0 points
17 months ago

Trying to follow this, but got the error:

Error: /Users/saz/Documents/Projects/xxxx/qml/main.qml:2:1: Warnings occurred while importing module "QtQuick.Controls": [import] import QtQuick.Controls

^^^^^^

Error: QtQuick.Controls uses optional imports which are not supported. Some types might not be found. [import]

Error: /Users/saz/Documents/Projects/xxxx/qml/main.qml:4:1: ApplicationWindow was not found. Did you add all import paths? [import] ApplicationWindow { ^^^^^^^^^^^^^^^^^ Info: Did you mean "Application"? Application { ^^^^^^^^^^^ Error: /Users/saz/Documents/Projects/xxxx/qml/main.qml:4:1: 'ApplicationWindow' is used but it is not resolved [unresolved-type] ApplicationWindow {

It's just a "hello world" that shows ApplicationWindow