Qt最新事情-QtでWebKitを使ってみよう

第3回 Qtの基本プログラミング~オブジェクトモデル

この記事を読むのに必要な時間:およそ 6 分

ダイナミックキャスト

QObjectの継承クラスに対して,RTTI(Run Time Type Information)を使わずにダイナミックキャストを可能にしています。dynamic_castと異なり,共有ライブラリーを跨いで使用できるが利点です。以下のように扱い方は dynamic_castと同じです。

QObject* object = new QLabel;
if (QLabel* label = qobject_cast<QLabel*>(object)) {
    label->setText("Hi!");
}

メタオブジェクトシステム

これまで説明した機能の実現を支えているのがメタオブジェクトシステムです。QObject::metaObject()メソッドはメタオブジェクトを返し,クラス名,シグナルとスロットのメソッド,プロパティ,列挙型と集合型,スーパークラスの情報などのオブジェクトのクラスのメタ情報にアクセスできるようにしています。

metaObject()が返すのは変数staticMetaObjectのアドレスで,metaObject()の実行コードとstaticMetaObjectの初期コードは,moc (Meta Object Compiler) がクラス宣言をパーズして生成されます。この他にmocは,ダイナミックキャストのためのqt_metacast(),シグナルとスロットのメソッド呼び出しのためのqt_metacall(),シグナルの実装コードなども生成します。

リスト5は,列挙型,シグナルとスロットのメソッド,プロパティに,メタオブジェクトを使ってアクセスをするコードの例です。

リスト5 メタ情報へのアクセスコード

const QMetaObject* metaObject = &Class::staticMetaObject;

int count;

qDebug() << endl << "Enumerators:";

qDebug() << "enumeratorCount() =" << metaObject->enumeratorCount();
qDebug() << "enumeratorOffset() =" << metaObject->enumeratorOffset();

count = metaObject->enumeratorCount();
for (int i = 0; i < count; i++) {
    QMetaEnum enumerator = metaObject->enumerator(i);
    qDebug() << "i =" << i;
    qDebug() << enumerator.name();
    for (int k = 0; k <  enumerator.keyCount(); ++k) {
        qDebug() << "\t" << enumerator.key(k);
    }
}

qDebug() << endl << "Methods:";

qDebug() << "methodCount() =" << metaObject->methodCount();
qDebug() << "methodOffset() =" << metaObject->methodOffset();

count = metaObject->methodCount();
for (int i = 0; i < count; i++) {
    QMetaMethod method = metaObject->method(i);
    qDebug() << "i =" << i;
    qDebug() << method.signature() << "," << method.typeName();
    qDebug() << method.parameterNames();
    qDebug() << method.parameterTypes();
}

qDebug() << endl << "Properties:";

qDebug() << "propertyCount() =" << metaObject->propertyCount();
qDebug() << "propertyOffset() =" << metaObject->propertyOffset();

count = metaObject->propertyCount();
for (int i = 0; i < count; i++) {
    QMetaProperty property = metaObject->property(i);
    qDebug() << "i =" << i;
    qDebug() << property.name();
    qDebug() << property.typeName();
}

リスト6のクラス宣言に対しての実行結果はリスト7のようになります。

リスト6 クラス宣言

    Q_ENUMS( Enum1 )
    ...
    Q_PROPERTY( int prop1 READ prop1 WRITE setProp1 )
    ...
public slots:
    void setProp1( int value );
    ...
signals:    
    ...
    void sigC( const QString& );
    ...

リスト7 メタ情報へのアクセス結果

Enumerators:
enumeratorCount() = 3 
enumeratorOffset() = 0 
==== i = 0 
Enum1 
         Enum1A 
         Enum1B 
         Enum1C 
...

Methods:
methodCount() = 16 
methodOffset() = 4 
i = 0 
destroyed(QObject*) ,  
("") 
...
==== i = 6 
sigC(QString) ,  
("") 
("QString") 
==== i = 7 
setProp1(int) ,  
("value") 
("int") 
...

Properties:
propertyCount() = 7 
propertyOffset() = 1 
i = 0 
objectName 
QString 
i = 1 
prop1 
int 
...

アクセス結果にシグナルとスロットのメソッドがあることに注目しましょう。シグナルとスロットのconnect()での接続時には,このメタ情報によってオブジェクト間を接続し,その情報をオブジェクトに設定しています。そして,シグナルを送信するというのは,シグナルメソッドが呼び出され(スレッド間の場合にはイベントループを介します⁠⁠,接続先のオブジェクトのスロットを呼び出すことなのです。

QMetaObject::invokeMethod()は,シグナルとスロットを呼び出すために公開されているAPIです。

QMetaObject::invokeMethod(slider, "valueChanged", Q_ARG(int, value));

このようにするとQSliderのシグナルvalueChanged(int)が送信され,接続されたスロットでシグナルが受け取られます。強制的にシグナルを送信しているので,sliderの実際の値とは異なる値でシグナルが送信されることに注意しましょう。同様にしてスロットの呼び出しもできます。

まとめと次回の予告

今回はQtのアーキテクチャの基礎となっているオブジェクトモデルについて説明しました。次回は,Qtの基本プログラミングの最後として,ウィジェットを配置するレイアウトマネージメントを普通とは違う視点で説明します。

著者プロフィール

杉田研治(すぎたけんじ)

1955年生まれ。東京都出身。株式会社SRAに勤務。プログラマ。

仕事のほとんどをMac OS XとKubuntu KDE 4でQtと供に過ごす。