QML型コンパイラ

QML タイプコンパイラ(qmltc )は Qt に同梱されているツールで、QML タイプを C++ タイプに変換しユーザーコードの一部としてコンパイルします。qmltc を使用すると、QQmlComponent ベースのオブジェクト生成に比べて、コンパイラが最適化できる機会が増えるため、ランタイムパフォーマンスが向上します。qmltc はQt Quick Compilerツールチェーンの一部です。

設計上、qmltcはユーザー向けのコードを出力します。このコードはC++アプリケーションによって直接利用されることになっています。この生成されたコードは、QML文書からオブジェクトを生成するためのQQmlComponent とそのAPIを本質的に置き換えるものです。詳しくは「QMLアプリケーションでのqmltcの使用」と「生成された出力の基本」を参照してください。

qmltcを有効にするには

  • アプリケーション用に適切なQMLモジュールを作成する。
  • CMakeAPIなどでqmltcを呼び出してください。
  • #include 生成されたヘッダファイルをアプリケーションのソースコードに追加する。
  • 生成された型のオブジェクトをインスタンス化する。

このワークフローでは、qmltcは通常ビルドプロセス中に実行されます。そのため、qmltcがQMLドキュメントを拒否した場合(エラーや警告によるものであれ、qmltcがまだサポートしていないコンストラクトによるものであれ)、ビルドプロセスは失敗します。これは、QMLモジュール作成時にリントターゲットの自動生成を有効にし、qmllintを実行するために "ビルド "しようとしたときにqmllintエラーが発生するのと似ています。

警告: qmltcは現在技術プレビューの段階であり、任意のQMLプログラムをコンパイルできない可能性があります(詳細は既知の制限を参照してください)。qmltcが失敗した場合、アプリケーションはqmltcの出力を適切に使用できないため、何も生成されません。もしプログラムにエラー(あるいは解決不可能な警告)が含まれている場合、コンパイルできるように修正する必要があります。一般的なルールはベストプラクティスを守り、qmllintのアドバイスに従うことです。

注意: qmltc は、生成されたC++が過去または将来のバージョン間でAPI、ソース、バイナリの互換性を保つことを保証しません。さらに、Qt の QML モジュールを使用して qmltc コンパイルされたアプリは、Qt のプライベート API に対してリンクする必要があります。これは、Qt の QML モジュールは、主に QML を通して使用されるため、通常はパブリックな C++ API を提供しないからです。

QMLアプリケーションでqmltcを使う

ビルドシステムの観点からは、qmltcコンパイルを追加することは、qmlキャッシュ生成を追加することと大差ありません。直感的に言えば、ビルドプロセスは次のようになります:

実際のコンパイルプロセスはもっとトリッキーですが、この図はqmltcが使用するコアコンポーネント、つまりQMLファイルそのものとqmltypes情報を持つqmldirをキャプチャしたものです。より単純なアプリケーションの場合、qmldirはむしろ原始的なものですが、一般的にはqmldirは複雑で、qmltcがQMLからC++への正しい変換を行うために必要な型情報を提供します。

とはいえ、qmltcの場合、ビルドステップを追加するだけでは十分ではありません。アプリケーションのコードも、QQmlComponent やその上位レベルの代替ではなく、qmltc が生成したクラスを使用するように変更する必要があります。

qmltcによるQMLコードのコンパイル

Qt 6 以降、Qt は CMake を使って様々なコンポーネントをビルドします。ユーザープロジェクトもCMakeを使ってQtコンポーネントをビルドすることができます。あなたのプロジェクトにqmltcのコンパイルサポートを追加するには、CMakeによるビルドフローが必要になります。

qmltcコンパイルを追加する簡単な方法は、アプリケーションのQMLモジュール作成の一部として、専用のCMake APIを使用することです。簡単なアプリケーションのディレクトリ構造を考えてみましょう:

.
├── 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

その場合、CMakeのコードは通常以下のようになります:

# 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
)

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

生成されたC++を使う

QQmlComponent 。インスタンス化の場合とは異なり、qmltcの出力はC++コードであるため、アプリケーションによって直接使用されます。一般に、C++で新しいオブジェクトを作成することは、QQmlComponent::create ()を使って新しいオブジェクトを作成することと同じである。一度作成されたオブジェクトは、C++から操作したり、QQuickWindow と組み合わせて画面に描画したりすることができる。

コンパイルされた型がいくつかの必要なプロパティを公開している場合、 `qmltc` は生成されるオブジェクトのコンストラクタでそれらのプロパティの初期値を要求する。

さらに、qmltcオブジェクトのコンストラクタには、コンポーネントのプロパティの初期値を設定するためのコールバックを指定することができます。

myApp.qml 、アプリケーションコードは次のようになります:

#include <QtQml/qqmlcomponent.h>

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

QQmlEngine e;
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;
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エンジン

生成されたコードはQQmlEngine 、QML文書の動的な部分(主にJavaScriptのコード)と相互作用します。このために特別な準備は必要ありません。qmltc が生成するクラスオブジェクトのコンストラクタに渡されるQQmlEngine インスタンスはQQmlComponent(engine) と同様に正しく動作するはずです。このことは、QMLの動作に影響を与えるQQmlEngine methods 。ただし、注意点があります。QQmlComponent ベースのオブジェクト生成とは異なり、qmltc自身はC++ へのコンパイル時にQQmlEngine に依存しません。例えば、QQmlEngine::addImportPath("/foo/bar/") - 通常はスキャンするインポートパスが追加されますが、qmltcの先読みプロシージャでは完全に無視されます。

注意: qmltcコンパイルにインポートパスを追加するには、CMakeコマンドの関連する引数を使用することを検討してください。

一般的には、このように考えることができます。QQmlEngine は実行するアプリケーションプロセスに関与しますが、qmltc はアプリケーションがコンパイルされる前に動作するため、関与しません。qmltcはアプリケーションのC++ソースコードをイントロスペクトしようとしないので、qmltcはユーザが行うQMLの操作について知ることができません。QQmlEngine や関連するランタイムルーチンを使ってQMLに型を公開したり、インポートパス を追加したりする代わりに、実質的には、うまく動作するQMLモジュールを作成し、宣言的なQML型登録を使用することが要求されます。

警告 qmltc はQQmlEngine と密接に連携して C++ コードを生成しますが、生成されたクラスはQQmlComponent を通して QML に公開したり、利用したりすることはできません。

生成出力の基本

qmltc qmltcは既存のQML実行モデルとの互換性を目指しています。つまり、生成されるコードは の内部設定ロジックとほぼ等価であり、QML型の動作、セマンティクス、APIを現在と同じように、つまり対応するQMLドキュメントを視覚的に検査することで理解することができるはずです。QQmlComponent

しかし、生成されたコードは、特にアプリケーションがC++側でqmltcの出力を直接使用しなければならないことを考えると、まだ多少混乱しています。生成されたコードには2つの部分があります:CMakeビルドファイルの構造と、生成されたC++フォーマットです。前者はqmltcのCMake APIでカバーされ、後者はここでカバーされる。

単純なHelloWorld型で、hello プロパティと、そのプロパティを表示する関数と、その型のオブジェクトが生成されたときに発せられるシグナルがあるとする:

// 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();
}

この QML 型の C++ 代替を提供する場合、C++ クラスにはQML 固有のメタオブジェクトシステムマクロhello プロパティ用のQ_PROPERTY 飾り、Q_INVOKABLE C++ 印刷関数、そして通常の Qt シグナル定義が必要になります。同様に、qmltcは与えられたHelloWorld型をおおよそ次のように変換します:

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

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

Q_SIGNALS:
    void created();

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

    // ...
};

生成される型の具体的な詳細は異なっても、普遍的な部分は残ります。例えば

  • ドキュメント内のQML型は、コンパイラから見える情報に従ってC++型に変換されます。
  • プロパティは、Q_PROPERTY 宣言を持つ C++ プロパティに変換されます。
  • JavaScript の関数はQ_INVOKABLE C++ の関数になります。
  • QML シグナルは C++ Qt シグナルに変換されます。
  • QML の列挙は、Q_ENUM 宣言によって C++ の列挙に変換されます。

qmltc はクラス名を生成します。与えられたQML型のクラス名は、その型を定義するQML文書から自動的に推測されます。拡張子を除いたQMLファイル名(最初の. (ベース名とも呼ばれる)までのファイル名)がクラス名となります。ファイル名の大文字と小文字は区別されます。従って、HelloWorld.qmlclass HelloWorld となり、helloWoRlD.qmlclass helloWoRlD となります。 QMLの慣例に従い、QML文書のファイル名が小文字で始まる場合、生成される C++クラスは匿名クラスとみなされ、QML_ANONYMOUS とマークされます。

今のところ、生成されたコードはC++アプリケーション側から利用することができ ますが、生成されたAPIを呼び出すことは制限してください。その代わりに、アプリケーションロジックを QML/JavaScript で実装し、QML に公開される C++ の型を手書きし、qmltc で生成されたクラスを単純なオブジェクトのインスタンス化に使用することをお勧めします。生成されたC++はQMLで定義された型の要素に直接(そして通常はより速く)アクセスすることができますが、そのようなコードを理解することは難しいかもしれません。

既知の制限事項

qmltcは多くの一般的なQMLの機能をカバーしているにもかかわらず、まだ開発の初期段階にあり、サポートされていないものもあります。

QML で定義された型(例えばQtQuick.Controls など)で構成される QML モジュールをインポートした場合、qmltc でコンパイルされたとしても正しくコンパイルされないことがあります。現在のところ、QtQmlQtQuick のモジュールは、QML に公開されている C++クラスのみを含む他の QML モジュールと同様に、確実に使用することができます。

これに加えて、もっと基本的な特殊性を考慮する必要があります:

  • QtのQMLモジュールは通常C++ライブラリに依存しています。Qt の QML モジュールは通常 C++ ライブラリに依存しています。多くの場合、これらの ライブラリは公開 C++ API を提供していません(主な用途が QML であるため)。qmltcのユーザーにとって、これはアプリケーションがプライベートなQtライブラリに対してリンクする必要があることを意味します。
  • qmltcのコード生成の性質上、QMLプラグインはコンパイルの目的には使えません。代わりに、プラグインを使用するQMLモジュールは、コンパイル時にプラグインのデータにアクセスできるようにしなければなりません。そのようなQMLモジュールは、オプションのプラグインを持つことになります。ほとんどの場合、コンパイル時の情報はヘッダーファイル(C++宣言付き)やリンク可能なライブラリ(C++定義付き)を通して提供することができます。ユーザーコードは、ヘッダーファイルへのパスを含め、QMLモジュールライブラリにリンクする責任を(通常はCMakeを通して)負います。

注意: このコンパイラは技術プレビュー版であるため、qmltcや生成されるコード、その他関連する部分でバグが発生する可能性があります。このような場合はバグレポートを提出することをお勧めします。

©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。