はじめに
前回は、
オブジェクトモデル
Qtのオブジェクトモデルでは、
シグナルとスロット | タイプセーフコールバック、 |
---|---|
オブジェクトプロパティ | 問い合わせ可能なプロパティ |
イベントとイベントフィルター | イベント処理とイベントフック |
国際化用テキスト文字列の翻訳 | クラスをコンテキストとするテキスト文字列翻訳 |
インターバルタイマー | イベント処理と統合されたタイマー |
オブジェクトツリー | オブジェクトの階層化と検索 |
ガーディドポインター | QPointer。Qt 4. |
ダイナミックキャスト | ライブラリ境界制限のないダイナミックキャスト |
これらの機能は、
オブジェクトツリー

図1のように、
- ウィジエットがどのウィジェット上に置かれているか
- ウィジェットをどのように並べるか
- ウィジェットの表示/
非表示の制御 - ウィジェットの有効化/
無効化の制御 - ウィジェットのメモリ割り当てと解放の制御
Qtでは、
図1のコンポジットウィジェットのオブジェクトツリーは、

QFrameとQGroupBoxの親オブジェクトはQWidget、
ウィジェットの親子関係に着目したコードはリスト1のようになります。
class ColorChooser : public QWidget
{
...
private:
QFrame* colorFrame;
QSlider* redSlider;
QSlider* greenSlider;
QSlider* blueSlider;
}
ColorChooser::ColorChooser( QWidget* parent )
: QWidget( parent )
{
...
colorFrame = new QFrame(this);
QGroupBox* colorGroupBox = new QGroupBox("RGB", this);
QLabel* redLabel = new QLabel("&Red", groupBox);
QLabel* greenLabel = new QLabel("&Green", groupBox);
QLabel* blueLabel = new QLabel("&Blue", groupBox);
redSlider = new QSlider(Qt::Horizontal, colorGroupBox);
greenSlider = new QSlider(Qt::Horizontal, colorGroupBox);
blueSlider = new QSlider(Qt::Horizontal, colorGroupBox);
...
}
ColorChooser::~ColorChooser()
{
// 子ウィジェットのメモリ解放は不要。
}
ウィジェットのインスタンス生成時に親ウィジェットを指定しているのがポイントです。QObject またはその継承クラスのインスタンス生成では、
1.子ウィジェットのメモリ解放の自動化
親オウィジェットのメモリ解放時にオブジェクトツリーを辿って、
デストラクタでのウィジェットのメモリ解放が不要となり、
QTimerクラスなどのQObjectを継承した非GUIクラスも親ウィジェットを指定してインスタンス生成して、
2.オブジェクトツリー内のオブジェクト検索
以下のようにして、
QLabel* label = colorChooser->findChild<QLabel*>("redLabel");
QList<QLabel*> labels = colorChooser->findChildren<QLabel*>();
// ここでラベルのプロパティを変更する
ここで、
redLabel->setObjectName("redLabel");
3 親ウィジェットの表示/非表示に応じて、配下のウィジェットを表示/非表示する。
show()を呼び出して親ウィジェットを表示状態にしたときに、
4 親ウィジェットの有効化/無効化に応じて、配下のウィジェットを有効化/無効化する。
setEnabled(true)を呼び出して親ウィジェットを有効化したときに、
表示/
子ウィジェットの自動メモリ解放に関連して、
QBitArray QKeySequence QSet
QBitmap QLinkedList QSqlField
QBrush QList QSqlQuery
QByteArray QLocale QSqlRecord
QCache QMap QStack
QCursor QMultiHash QString
QDir QMultiMap QStringList
QFileInfo QPainterPath QTextBoundaryFinder
QFont QPalette QTextCursor
QFontInfo QPen QTextDocumentFragment
QFontMetrics QPicture QTextFormat
QFontMetricsF QPixmap QUrl
QGLColormap QPolygon QVariant
QGradient QPolygonF QVector
QHash QQueue QX11Info
QIcon QRegExp
QImage QRegion
オブジェクトプロパティ
QObjectの継承クラスに任意のプロパティを持たせることができ、
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName);
Q_PROPERTY(int age READ age WRITE setAge);
public:
explicit Person(QObject* parent = 0);
QString name() const;
int age() const;
public slots:
void setName(const QString& name);
void setAge(int age);
...
};
Q_PROPERTY(type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool])
リスト2では、
リスト4 プロパティにアクセスする例
QListIterator<PersonPtr> it(personList);
while (it.hasNext()) {
PersonPtr p = it.next();
QVariant name = p->property("name");
QVariant age = p->property("age");
QVariant weight = p->property("weight");
qDebug() << (name.isValid() ? name.toString() : "----")
<< (age.isValid() ? age.toInt() : -1 )
<< (weight.isValid() ? weight.toInt() : -1 );
if ( age.isValid() )
p->setProperty("age", p->property("age").toInt() * 2 );
}
weightプロパティは宣言されていないので、
QVariantは、
ダイナミックキャスト
QObjectの継承クラスに対して、
QObject* object = new QLabel;
if (QLabel* label = qobject_cast<QLabel*>(object)) {
label->setText("Hi!");
}
メタオブジェクトシステム
これまで説明した機能の実現を支えているのがメタオブジェクトシステムです。QObject::metaObject()メソッドはメタオブジェクトを返し、
metaObject()が返すのは変数staticMetaObjectのアドレスで、
リスト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のようになります。
Q_ENUMS( Enum1 )
...
Q_PROPERTY( int prop1 READ prop1 WRITE setProp1 )
...
public slots:
void setProp1( int value );
...
signals:
...
void sigC( const QString& );
...
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()は、
QMetaObject::invokeMethod(slider, "valueChanged", Q_ARG(int, value));
このようにするとQSliderのシグナルvalueChanged(int)が送信され、
まとめと次回の予告
今回はQtのアーキテクチャの基礎となっているオブジェクトモデルについて説明しました。次回は、