Auf dieser Seite

QML-Typ-Compiler

Der QML-Typ-Compiler, qmltc, ist ein mit Qt geliefertes Tool zur Übersetzung von QML-Typen in C++-Typen, die als Teil des Benutzercodes vorab kompiliert werden. Die Verwendung von qmltc kann zu einer besseren Laufzeitleistung führen, da dem Compiler im Vergleich zur Objekterstellung auf Basis von „ QQmlComponent “ mehr Optimierungsmöglichkeiten zur Verfügung stehen. qmltc ist Teil der „Qt Quick “-Compiler -Toolchain.

qmltc gibt grundsätzlich benutzerseitigen Code aus. Dieser Code soll direkt von der C++-Anwendung genutzt werden, andernfalls ergibt sich kein Vorteil. Der generierte Code ersetzt im Wesentlichen „ QQmlComponent “ und dessen APIs zur Erstellung von Objekten aus QML-Dokumenten. Weitere Informationen finden Sie unter „Verwendung von qmltc in einer QML-Anwendung “ und „Grundlagen der generierten Ausgabe“.

So aktivieren Sie qmltc:

  • Erstellen Sie ein geeignetes QML-Modul für Ihre Anwendung.
  • Rufen Sie qmltc auf, beispielsweise über die CMake-API.
  • #include Fügen Sie die generierten Header-Dateien in den Anwendungsquellcode ein.
  • Instanziieren Sie ein Objekt des generierten Typs.

In diesem Arbeitsablauf wird qmltc in der Regel während des Build-Prozesses ausgeführt. Wenn qmltc also ein QML-Dokument ablehnt (sei es aufgrund von Fehlern oder Warnungen oder wegen Konstrukten, die qmltc noch nicht unterstützt), schlägt der Build-Prozess fehl. Dies ähnelt der Situation, in der Sie qmllint-Fehler erhalten, wenn Sie bei der Erstellung von QML-Modulen die automatische Generierung von Linting-Zielen aktivieren und anschließend versuchen, diese zu „bauen“, um qmllint auszuführen.

Warnung: qmltc befindet sich derzeit in der Tech-Preview-Phase und kann möglicherweise nicht jedes beliebige QML-Programm kompilieren (weitere Details finden Sie unter „Bekannte Einschränkungen“ ). Wenn qmltc fehlschlägt, wird nichts generiert, da Ihre Anwendung die qmltc-Ausgabe nicht sinnvoll nutzen kann. Wenn Ihr Programm Fehler (oder unlösbare Warnungen) enthält, sollten diese behoben werden, um die Kompilierung zu ermöglichen. Als allgemeine Regel gilt, sich an die Best Practices zu halten und die Empfehlungen von qmllint zu befolgen.

Hinweis: „ qmltc “ garantiert nicht, dass das generierte C++ zwischen früheren und zukünftigen Versionen – selbst Patch-Versionen – API-, Quellcode- oder Binärkompatibilität aufweist. Darüber hinaus erfordern mit qmltc kompilierte Anwendungen, die Qt-Qml-Module verwenden, eine Verknüpfung mit der privaten Qt-API. Dies liegt daran, dass Qt-Qml-Module in der Regel keine öffentliche C++-API bereitstellen, da sie in erster Linie über Qml genutzt werden.

Verwendung von qmltc in einer QML-Anwendung

Aus Sicht des Build-Systems unterscheidet sich das Hinzufügen der qmltc-Kompilierung kaum vom Hinzufügen der QML-Cache-Generierung. Vereinfacht lässt sich der Build-Prozess wie folgt beschreiben:

Das Flussdiagramm zeigt, wie man QML- und C++-Dateien mit dem QML-Typ-Compiler kompiliert.

Zwar ist der tatsächliche Kompilierungsprozess wesentlich komplexer, doch dieses Diagramm veranschaulicht die Kernkomponenten, die qmltc nutzt: die QML-Dateien selbst und qmldir mit den qmltypes-Informationen. Einfachere Anwendungen verfügen in der Regel noch über ein eher primitives „qmldir“, doch im Allgemeinen kann „qmldir“ komplex sein und wesentliche, übersichtlich strukturierte Typinformationen bereitstellen, auf die sich „qmltc“ stützt, um eine korrekte Übersetzung von QML nach C++ durchzuführen.

Dennoch reicht es im Fall von qmltc nicht aus, einen zusätzlichen Build-Schritt hinzuzufügen. Der Anwendungscode muss ebenfalls angepasst werden, um qmltc-generierte Klassen anstelle von ` QQmlComponent ` oder dessen übergeordneten Alternativen zu verwenden.

Kompilieren von QML-Code mit qmltc

Qt verwendet ab Version 6 CMake zum Erstellen seiner verschiedenen Komponenten. Benutzerprojekte können – und sollten – ebenfalls CMake nutzen, um ihre Komponenten mit Qt zu erstellen. Das Hinzufügen einer sofort einsatzbereiten qmltc-Kompilierungsunterstützung zu Ihrem Projekt würde ebenfalls einen CMake-gesteuerten Build-Ablauf erfordern, da dieser Ablauf auf korrekten QML-Modulen und deren Infrastruktur basiert.

Der einfachste Weg, die qmltc-Kompilierung hinzuzufügen, ist die Verwendung der speziellen CMake-API als Teil der Erstellung eines QML-Moduls für die Anwendung. Betrachten wir eine einfache Verzeichnisstruktur für eine Anwendung:

.
├── CMakeLists.txt
├── myspecialtype.h     // C++ type exposed to QML
├── myspecialtype.cpp
├── myApp.qml           // main QML page
├── MyButton.qml        // custom UI button
├── MySlider.qml        // custom UI slider
└── main.cpp            // main C++ application file

Dann würde der CMake-Code in der Regel in etwa wie folgt aussehen:

# Use "my_qmltc_example" as an application name:
set(application_name my_qmltc_example)

# Create a CMake target, add C++ source files, link libraries, etc...

# Specify a list of QML files to be compiled:
set(application_qml_files
    myApp.qml
    MyButton.qml
    MySlider.qml
)

# Make the application into a proper QML module:
qt6_add_qml_module(${application_name}
    URI QmltcExample
    QML_FILES ${application_qml_files}

    # Compile qml files (listed in QML_FILES) to C++ using qmltc and add these
    # files to the application binary:
    ENABLE_TYPE_COMPILER
    NO_GENERATE_EXTRA_QMLDIRS
)

# (qmltc-specific) Link *private* libraries that correspond to QML modules:
find_package(Qt6 COMPONENTS QmlPrivate QuickPrivate)
target_link_libraries(${application_name} PRIVATE Qt::QmlPrivate Qt::QuickPrivate)

Verwendung des generierten C++-Codes

Anders als bei der Instanziierung von ` QQmlComponent ` wird die Ausgabe von `qmltc` – also C++-Code – direkt von der Anwendung verwendet. Im Allgemeinen entspricht das Erstellen eines neuen Objekts in C++ dem Erstellen eines neuen Objekts über ` QQmlComponent::create()`. Nach der Erstellung kann das Objekt von C++ aus bearbeitet oder beispielsweise mit ` QQuickWindow ` kombiniert werden, um es auf dem Bildschirm darzustellen.

Wenn ein kompilierter Typ bestimmte erforderliche Eigenschaften offenlegt, verlangt `qmltc` im Konstruktor des generierten Objekts einen Anfangswert für diese Eigenschaften.

Zudem kann der Konstruktor eines `qmltc`-Objekts mit einem Callback versehen werden, um Anfangswerte für die Eigenschaften der Komponente festzulegen.

Bei einer „ myApp.qml “-Datei würde der Anwendungscode (in beiden Fällen) typischerweise wie folgt aussehen:

#include <QtQml/qqmlcomponent.h>

QGuiApplication app(argc, argv);
app.setApplicationDisplayName(QStringLiteral("This example is powered by QQmlComponent :("));

QQmlEngine e;
// If the root element is Window, you don't need to create a Window.
// The snippet is for the cases where the root element is not a Window.
QQuickWindow window;

QQmlComponent component(&e);
component.loadUrl(
            QUrl(QStringLiteral("qrc:/qt/qml/QmltcExample/myApp.qml")));

QScopedPointer<QObject> documentRoot(component.create());
QQuickItem *documentRootItem = qobject_cast<QQuickItem *>(documentRoot.get());

documentRootItem->setParentItem(window.contentItem());
window.setHeight(documentRootItem->height());
window.setWidth(documentRootItem->width());
// ...

window.show();
app.exec();
#include "myapp.h" // include generated C++ header

QGuiApplication app(argc, argv);
app.setApplicationDisplayName(QStringLiteral("This example is powered by qmltc!"));

QQmlEngine e;
// If the root element is Window, you don't need to create a Window.
// The snippet is for the cases where the root element is not a Window.
QQuickWindow window;

QScopedPointer<QmltcExample::myApp> documentRoot(
    new QmltcExample::myApp(&e, nullptr, [](auto& component){
        component.setWidth(800);
}));

documentRoot->setParentItem(window.contentItem());
window.setHeight(documentRoot->height());
window.setWidth(documentRoot->width());
// ...

window.show();
app.exec();

QML-Engine

Der generierte Code nutzt „ QQmlEngine “, um mit dynamischen Teilen eines QML-Dokuments zu interagieren – hauptsächlich mit dem JavaScript-Code. Damit dies funktioniert, sind keine besonderen Vorkehrungen erforderlich. Jede „ QQmlEngine “-Instanz, die an den Konstruktor eines von qmltc generierten Klassenobjekts übergeben wird, sollte ebenso korrekt funktionieren wie „ QQmlComponent(engine) “. Das bedeutet auch, dass Sie „ QQmlEngine methods “ verwenden können, die das QML-Verhalten beeinflussen. Es gibt jedoch einige Einschränkungen. Im Gegensatz zur Objekterstellung auf Basis von QQmlComponentstützt sich qmltc selbst beim Kompilieren des Codes nach C++ nicht auf QQmlEngine. So würde beispielsweise QQmlEngine::addImportPath("/foo/bar/") – was normalerweise zu einem zusätzlichen Importpfad führt, der gescannt werden muss – vom Ahead-of-Time-Verfahren von qmltc vollständig ignoriert werden.

Hinweis: Um Importpfade zur qmltc-Kompilierung hinzuzufügen, sollten Sie stattdessen die Verwendung eines entsprechenden Arguments des CMake-Befehls in Betracht ziehen.

Im Allgemeinen können Sie sich das so vorstellen: Bei „ QQmlEngine “ wird der Anwendungsprozess ausgeführt, während dies bei „qmltc“ nicht der Fall ist, da es bereits vor der Kompilierung Ihrer Anwendung arbeitet. Da „qmltc“ keinen Versuch unternimmt, den C++-Quellcode Ihrer Anwendung zu analysieren, kann es bestimmte Arten von QML-Manipulationen, die Sie als Benutzer vornehmen, nicht erkennen. Anstatt „ QQmlEngine “ und zugehörige Laufzeitroutinen zu verwenden, um Typen für QML verfügbar zu machen, Importpfade hinzuzufügen usw., musst du praktisch gut funktionierende QML-Module erstellen und die deklarative QML-Typregistrierung nutzen.

Warnung: Obwohl qmltc eng mit „ QQmlEngine “ zusammenarbeitet und C++-Code generiert, können die erzeugten Klassen nicht weiter für QML freigegeben und über „ QQmlComponent “ verwendet werden.

Grundlagen der generierten Ausgabe

qmltc zielt darauf ab, mit dem bestehenden QML-Ausführungsmodell kompatibel zu sein. Dies bedeutet, dass der generierte Code in etwa der internen Einrichtungslogik von QQmlComponent entspricht und Sie daher das Verhalten, die Semantik und die API Ihres QML-Typs auf dieselbe Weise verstehen sollten wie bisher – durch visuelle Überprüfung des entsprechenden QML-Dokuments.

Allerdings ist der generierte Code immer noch etwas verwirrend, insbesondere da Ihre Anwendung die qmltc-Ausgabe auf der C++-Seite direkt verwenden sollte. Der generierte Code besteht aus zwei Teilen: der Struktur der CMake-Build-Dateien und dem generierten C++-Format. Ersteres wird in der CMake-API von qmltc behandelt, Letzteres hier.

Betrachten wir einen einfachen „HelloWorld“-Typ, der über eine Eigenschaft „ hello “, eine Funktion zum Ausgeben dieser Eigenschaft und ein Signal verfügt, das bei der Erstellung eines Objekts dieses Typs ausgelöst wird:

// HelloWorld.qml
import QtQml

QtObject {
    id: me
    property string hello: "Hello, qmltc!"

    function printHello(prefix: string, suffix: string) {
        console.log(prefix + me.hello + suffix);
    }

    signal created()
    Component.onCompleted: me.created();
}

Bei der Bereitstellung einer C++-Alternative für diesen QML-Typ benötigte die C++-Klasse ein QML-spezifisches Makro des Metaobjektsystems, eine „ Q_PROPERTY “-Dekoration für die Eigenschaft „ hello “, eine C++-Ausgabefunktion „ Q_INVOKABLE “ sowie eine reguläre Qt-Signaldefinition. In ähnlicher Weise würde qmltc den angegebenen „HelloWorld“-Typ in etwa wie folgt übersetzen:

class HelloWorld : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    Q_PROPERTY(QString hello READ hello WRITE setHello BINDABLE bindableHello)

public:
    HelloWorld(QQmlEngine* engine, QObject* parent = nullptr, [[maybe_unused]] qxp::function_ref<void(PropertyInitializer&)> initializer = [](PropertyInitializer&){});

Q_SIGNALS:
    void created();

public:
    QString hello();
    void setHello(const QString& hello_);
    QBindable<QString> bindableHello();
    Q_INVOKABLE void printHello(passByConstRefOrValue<QString> prefix, passByConstRefOrValue<QString> suffix);

    // ...
};

Auch wenn sich die spezifischen Details des generierten Typs unterscheiden könnten, bleiben die allgemeinen Aspekte bestehen. Zum Beispiel:

  • QML-Typen innerhalb eines Dokuments werden entsprechend den für den Compiler sichtbaren Informationen in C++-Typen übersetzt.
  • Eigenschaften werden in C++-Eigenschaften mit „ Q_PROPERTY “-Deklarationen übersetzt.
  • JavaScript-Funktionen werden zu C++-Funktionen mit ` Q_INVOKABLE `.
  • Qt-Signale werden in Qml-Signale umgewandelt.
  • QML-Aufzählungen werden in C++-Aufzählungen mit „ Q_ENUM “-Deklarationen umgewandelt.

Ein weiteres Detail ist die Art und Weise, wie „ qmltc “ Klassennamen generiert. Ein Klassenname für einen bestimmten QML-Typ wird automatisch aus dem QML-Dokument abgeleitet, das diesen Typ definiert: Der QML-Dateiname ohne Erweiterungen (bis einschließlich des ersten „ . “, auch als Basisname bezeichnet) wird zum Klassennamen. Die Groß-/Kleinschreibung des Dateinamens bleibt erhalten. So würde „ HelloWorld.qml “ zu „ class HelloWorld “ und „ helloWoRlD.qml “ zu „ class helloWoRlD “ führen. Gemäß der QML-Konvention wird, wenn der Dateiname eines QML-Dokuments mit einem Kleinbuchstaben beginnt, davon ausgegangen, dass die generierte C++-Klasse anonym ist und mit „ QML_ANONYMOUS “ gekennzeichnet wird.

Obwohl der generierte Code derzeit bereits für die Verwendung auf der C++-Anwendungsseite bereit ist, sollten Sie den Aufruf der generierten APIs generell einschränken. Implementieren Sie die Anwendungslogik stattdessen vorzugsweise in QML/JavaScript und in manuell geschriebenen C++-Typen, die für QML verfügbar sind, und nutzen Sie die von qmltc erstellten Klassen für die einfache Instanziierung von Objekten. Der generierte C++-Code bietet Ihnen zwar direkten (und in der Regel schnelleren) Zugriff auf die in QML definierten Elemente des Typs, doch das Verständnis dieses Codes kann eine Herausforderung darstellen.

Bekannte Einschränkungen

Obwohl qmltc viele gängige QML-Funktionen abdeckt, befindet es sich noch in einem frühen Entwicklungsstadium, sodass einige Funktionen noch nicht unterstützt werden.

Importierte QML-Module, die aus QML-definierten Typen bestehen (wie z. B. QtQuick.Controls), werden möglicherweise nicht korrekt kompiliert, selbst wenn diese QML-definierten Typen von qmltc kompiliert wurden. Derzeit können Sie die Module QtQml und QtQuick sowie alle anderen QML-Module, die ausschließlich für QML bereitgestellte C++-Klassen enthalten, zuverlässig verwenden.

Darüber hinaus gibt es noch einige grundlegendere Besonderheiten zu beachten:

  • Die Qml-Module von Qt stützen sich in der Regel auf C++-Bibliotheken, um die Hauptarbeit zu erledigen. Oftmals stellen diese Bibliotheken keine öffentliche C++-API bereit (da sie in erster Linie über Qml genutzt werden). Für Nutzer von qmltc bedeutet dies, dass ihre Anwendungen gegen private Qt-Bibliotheken verknüpft werden müssen.
  • Aufgrund der Art der qmltc-Codegenerierung sind QML-Plugins für Kompilierungszwecke unbrauchbar. Stattdessen müssen QML-Module, die ein Plugin verwenden, sicherstellen, dass die Plugin-Daten zur Kompilierungszeit zugänglich sind. Solche QML-Module würden dann über optionale Plugins verfügen. In den meisten Fällen können die Informationen zur Kompilierungszeit über eine Header-Datei (mit C++-Deklarationen) und eine verknüpfbare Bibliothek (mit C++-Definitionen) bereitgestellt werden. Der Anwendercode ist (in der Regel über CMake) dafür verantwortlich, einen Pfad zur Header-Datei einzubinden und eine Verknüpfung mit der QML-Modulbibliothek herzustellen.

Hinweis: Da sich der Compiler noch im Tech-Preview-Statusbefindet , können auch Fehler in qmltc, im generierten Code oder in anderen damit zusammenhängenden Teilen auftreten. Wir bitten Sie, in diesem Fall einen Fehlerbericht einzureichen.

© 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.