import QtQuick 1.1

この記事は Qt Blog の “import QtQtuick 1.1” を翻訳したものです。
執筆: aalpert 2011年2月25日

QML の動向を追ってらっしゃる方々はご存知だと思いますが、Qt 4.7.1 では Qt Quick のマイナーバージョンの変更を可能にするために、QML で import する Qt Quick のモジュールを "QtQuick 1.0" に変更しました。Qt Quick のマイナーバージョンアップの準備もほとんど整い、Qt の 4.7 ブランチで次のリリース(4.7.3)を待つばかりとなっています。QtQuick 1.1 に向けて [qt 'いくつもの新機能' l=qtquick-whatsnew v=4.7-snapshot] を実装しましたが、その中の一つであるバージョン管理機能の改善に特に焦点を合わせて紹介したいと思います。

昨年、私の wordgame モジュールを例に QML のバージョン管理システムに関する記事の公開をするつもりであることを言及しました。その記事の作成中に、バージョン管理システムにいくつかの機能が欠けていることに気がつきました。それらの機能は Qt Quick 1.1 で実装され、バージョン管理システム全体をより良い機能にすることが出来ました。その内容についてお話しさせてください。

QML モジュールでは C++ で現状可能な方法よりも良いアップグレードパスを実現することを目指しました。アプリケーションの動作が壊れたり、モジュールの作者に些細な変更の制限をすることのないように奮闘しました。その核となる手段はモジュールをバージョン番号を指定してインポートすることです。それが、'QtQuck' モジュールをインポートする際にはモジュール名だけではなく、'QtQuick 1.1'とバージョン番号を付けなくてはいけない理由です。特定のバージョンを指定することによって、そのバージョンに限定した機能だけを利用することを可能にしました。そのモジュールが改良されて、新しい機能を持つバージョンであったとしてもです。この方法は、qmlRegisterType() の引数で指定するバージョン番号がまず必要です。ある型を導入する時、そのマイナーバージョンとメジャーバージョンを指定しなくてはなりません。アプリケーションがそのネームスペースに型を追加するには、最低でもその型を含むバージョンを指定してインポートしなくてはなりません。これによって、モジュールにシンボルの衝突を気にすることなく型を追加することが出来ます。

例として、wordgame モジュール のバージョン 1.1 で Letters 型を追加してみました。
以下のコードは、wordgame のバージョン 1.0 を利用したゲームの一部だと考えてください:

import QtQuick 1.0
import MyTypes 1.0
import Qt.labs.wordgame 1.0
Item{
BoardLogic{
...
}
Letters{
count: 26
...
}
}

上記のコードでは、'Letters' 型は 'MyTypes' モジュールに含まれる型の一つです。それと、wordgame モジュールに実装した新しい 'Letters' 型は似たような型かもしれません。その新しい 'Letters' 型が 'MyTypes' の ’Letters’ 型にあるプロパティを持たない場合、(バージョン管理システムを持たない場合、互換性のない ’Letters' 型を持つ wordgame バージョン 1.1 のモジュールをインポートしてしまうため)この QML を実行しようとすると、存在しないプロパティ "test" を割り当てようとしたとしてエラーになるでしょう。wordgame モジュールがシステムモジュールでこのゲーム本体とは独立にアップデートされる場合、ゲーム自体のアップデートが実施されるまでの間、このコードは実行が出来なくなります。

しかし、このコードではバージョン 1.1 ではなく、wordgame 1.0 をインポートしています。そのため、バージョン管理システムは wordgame モジュールの新しい Letters 型を追加しません。このゲームは今まで通りに MyTypes の Letters 型を使用して動きます。ゲームアプリケーションのメンテナは次のバージョンで機能のアップデートを行う際に、MyTypes の型名を変更したり、wordgame モジュールをネームスペースにインポート(import Qt.labs.wordgame 1.1 as WordGame)するなどして、この問題を簡単に修正できます。この際、アプリケーションの実行が不可能になる期間はありません。

開発に使用しているものよりも古いバージョンのライブラリをインポートしようとした場合、エラーが警告されることなく、またその古いバージョンのライブラリで実行が出来ない可能性があることに注意してください。その対処は簡単で、対象となるバージョンのライブラリで開発を行ってください。

より極端な例として、実装を完全に変更する場合をあげてみましょう(これは次の Qt.labs.particles のリリースで実際に行うことです)。異なる C++ のクラスを異なるバージョンの型として登録した場合、アプリケーションは新しい型をその型のバージョン以上を指定してインポートすることで利用できます。古いバージョン番号では元々のクラスが登録されているため、インポートするバージョン番号によって利用するクラスを指定することが出来ます。新しいアプローチが期待したように動かない場合でも、古いバージョンも利用できずに途方に暮れるようなことはなくなります。

しかし、これで全てではありません。QtQuick1.1 ではそれぞれのプロパティやシグナル、スロットといったレベルでのバージョン管理機能を追加しました。型がバージョンで管理されていること自体は素晴らしいことです。しかし、この方法ではバージョン毎に新しいクラスの作成が必要になるため、小さな変更を行う場合よりも新しい要素への変更や全面的な書き換え(実質的には新要素)のような大改革に適した機能です。Text 要素に lineCount プロパティを追加するだけのために、新しい C++ クラスの作成をしたくはありません。たとえば、 wordgame の Letters オブジェクトを BoardLogic で使用するために若干の手直しが必要だったとします。そのために既存のある一つのプロパティの挙動の調整だけが必要だとします。以下にその場合に boradlogic.h に必要なコードを示します。

//A Letters object is how you can set the individual letter frequencies and score
Q_PROPERTY(Letters* letters READ letters WRITE setLetters NOTIFY lettersChanged REVISION 1);
...
signals:
Q_REVISION(1) void lettersChanged(Letters* arg);

このゲッター(letters())とセッター(setLetters())は QML から直接呼び出す事の出来ないメソッドであったため、バージョン番号を必要としないことに注意してください。また、wordgame.cpp で新しいバージョンを登録するのを忘れないでください:

qmlRegisterType(uri,1,1,"BoardLogic");

これで BoardLogic の 'letters' プロパティは、wordgame のバージョン 1.1 をインポートした場合にだけ利用できるようになり、型について説明したのと同様に衝突を避けることが出来ます。そのモジュールを使用する全てのアプリケーションの実行を妨げることなく、安全かつ簡単に追加のプロパティ、シグナル、スロットを持ったシステムモジュールをアップデートできます。

このバージョン記述の手法によって、QtQuick 1.1 をどのコードの実行も妨げないマイナーリリースとすることが出来たと思います。新しいクラスを作成するという余分な作業が必要だった機能の不足が埋まり、必要なプロパティを追加するだけとなりました。また、新しい型もいくつか追加していますが、それはまた次の機会にします。


Blog Topics:

Comments