Qt 5.15でのQMLタイプ登録

Qt 5.15では、C ++をQML内で利用する方法が大幅に改善されています。モジュール名とバージョンを一か所で指定できるようになり、マイナーバージョンやリビジョンを指定する必要がなくなりました。さらに、QMLタイプ登録の詳細をC ++クラス宣言で宣言できるようになりました。

これまでにQMLでC ++を使用できるようにする一般的な方法は、qqml.hヘッダーで提供される登録関数を使用することでした。すなわち、qmlRegisterType()、qmlRegisterSingletonType()、qmlRegisterUncreatableType()などです。ただ、このアプローチには欠点がありました。

タイプの登録を実際のタイプと常に同期させる必要があったのです。リビジョンを使用して、インポートの特定のバージョンでプロパティを使用できるようにする場合、これは特に面倒です。そうでなくても、タイプとは別に登録を指定する必要があるという事実は、どのモジュールにどのタイプをどのように登録したかを簡単に把握できなくなるため、負担となります。

さらに、タイプを手続き的に登録するとき、QMLツールはどのタイプがどのインポートで使用可能かを自動的に判断できません。 Qt Creatorには、C ++コードの一般的な登録パターンを推測して検出しようとする仕組みがありますが、すべてのパターンを検出できるわけではありません。特定の登録がプログラムによって実行されるかどうかを判断することは、「停止性問題」を解決することと同じく不可能な問題です。qmllintやqmlformatなどの単純なツールには、C ++コードに関する情報がないため、QMLコードを単独で分析する必要があります。したがって、C ++から登録されたタイプに関する情報を得ることができません。 (部分的に)この問題を解決するために、「qmltypes」ファイルが導入されました。 QMLプラグインを開発するときは、プラグインバイナリの横に「plugins.qmltypes」というファイルを配置することをお勧めします。 qmltypesファイルには、プラグインによって登録されたタイプに関するメタ情報が含まれています。 Qt Creatorおよび他のツールは、この情報を参照して、コードのより良く分析することができます。ただこれは、プラグインにのみ有効ですので、メインプログラムから直接タイプを登録する場合はまだ同じ問題に直面してしまいます。また、タイプを2回指定しなければなりません。1回はC ++で、もう1回はqmltypes形式で指定します。冗長なタイプ指定の問題を(部分的に)解決するために、「qmlplugindump」と呼ばれるツールが利用可能です。このツールは、QMLエンジンが読み込むのと同じ方法でプラグインを読み込みます。次に、それに含まれるすべてのタイプに関する情報を抽出して、plugins.qmltypesファイルを生成します。ただし、これはプラグインで無関係なコードも実行し、qmlplugindumpが実行されているのと同じプラットフォーム用にプラグインをコンパイルしている場合にのみ機能します。実際には、クロスコンパイルされたビルドでは機能しません。

これらすべての障害を考慮して、タイプ登録のシステムはQt 5.15で再設計されました。 qmlRegisterType()とその関連タイプを手続き的に呼び出す必要はもうありません。代わりに、クラス宣言に追加できる一連のマクロがqqml.hで提供されます。これらのマクロは、タイプをQMLにエクスポートされたものとしてマークします。最も重要なのは「QML_ELEMENT」です。 QMLの周囲のクラスを、C ++クラス名と同名のエレメントとして提供します。メタオブジェクトコンパイラ(moc)は、この方法で与えられた情報を見つけます。オプション "--output-json"を渡すと、すべてのメタタイプ情報を含むJSONファイルがマシンに依存せず、人が読める形で出力します。 qmakeプロジェクトファイルで「CONFIG + = metatypes」を指定すると、この動作をトリガーできます。

次に、「qmltyperegistrar」と呼ばれる新しいツールは、このmetatypesファイルと関連する可能性のあるものを読み取り、関連するマクロでマークされたすべてのタイプを登録する関数を含むC ++ファイルと、アプリケーションで定義されたQMLモジュールに関連するタイプ情報のみを含むqmltypesファイルを生成します。これを行うには、モジュール名とメジャーバージョンを知る必要があります。変数「QML_IMPORT_NAME」および「QML_IMPORT_MAJOR_VERSION」を使用して、qmakeプロジェクトファイルでこれを指定できます。マイナーバージョンを指定する必要はありません。 qmltyperegistrarは、メタタイプデータで改訂されたプロパティとメソッドを見つけ、それに応じて異なるバージョンのタイプを登録します。これは、モジュールが使用可能なマイナーバージョンを定義します。 「QML_ADDED_IN_MINOR_VERSIONx)」マクロを追加して、特定のマイナーバージョンからタイプを使用可能にすることもできます。

ビルドシステムがこのように設定されたら、「CONFIG + = qmltypes」を追加して実際の登録をトリガーできます。 「qmltypes」CONFIGオプションは、「メタタイプ」を意味します。両方を明示的に指定する必要はありません。

このようなQMLのC ++タイプの静的登録は、プラグイン向けのqmltypesファイルだけでなく、メインアプリケーション向けにも生成します。後者のファイルは「app.qmltypes」と呼ばれ、参照するプラグインの隣に「plugins.qmltypes」を配置するのと同じように、アプリケーションバイナリの隣に配置する必要があります。

これを説明するために、タイプを動的に登録するアプリケーションを、同じタイプを静的に登録するアプリケーションに変換してみます。これは、すべてのQMLおよびQtQuickのサンプルプログラムで既に行われています。基本的な「タイプの追加」の例を見てみましょう。 Qt 5.14以前では、例のmain.cppファイルに次のコード行がありました。

qmlRegisterType<Person>("People", 1,0, "Person");

これはC ++クラス「Person」を、「Person」と呼ばれるQML要素として、バージョン1.0の「People」モジュールに登録します。この行はQt 5.15で不要になりました。代わりに、次の行がadding.proファイルに追加されています:


CONFIG += qmltypes
QML_IMPORT_NAME = People
QML_IMPORT_MAJOR_VERSION = 1

これらの行は、QMLに公開されるすべてのタイプのインポート名とメジャーバージョンを指定します。最後に、最も重要な変更はperson.hファイルに追加された「QML_ELEMENT」マクロです。

class Person : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
    QML_ELEMENT
public:
    Person(QObject *parent = nullptr);
    [...]
};

この「QML_ELEMENT」は、タイプを登録するようにqmltyperegistrarに指示します。ただそれだけです。バージョンとリビジョンのインポート名を誤入力することなく、好きなだけ「QML_ELEMENT」を指定できるようになりました。 C ++クラス名とは異なるQML要素名を提供したり、シングルトンを作成したりするなど、特定の方法でタイプを登録する方法の詳細は、ドキュメントに記載されています。


Blog Topics:

Comments