はじめに
前回は,C++のオブジェクトに機能拡張を施したQtのオブジェクトモデルについて説明しました。今回は,ウィジェット作成での基本的機能であるレイアウトマネージメントについて説明します。ただウィジェットを並べてレイアウトする方法を説明するのではなく,レイアウトマネージメントの考え方や,Qtのレイアウトマネージメントがどのような作りになっているかについても触れたいと思います。
レイアウトマネージメントとは
表1のように,ウィジェットは2種類に分類できます。
表1 ウィジェットの種類
| 種類 | 説明 |
|---|---|
| コンポジットウィジェット | ウィジェットを配置して作成するウィジェット |
| グラフィカルウィジェット | アナログ時計やVUメーターのようなウィジェット |
グラフィカルウィジェットは,2D描画機能を使ってウィジェットの外観を実装し,イベントを処理してユーザ操作を実装します。ボタン,ラベル,スライダーなどもそのようにして実装されています。コンポジットウィジェットは,既存のウィジェットを一般には平面状に配置したウィジェットです。コンポジットウィジェット自体もその構成ウィジェットになります。一般にコンポジットウィジェットでは,ウィジェットの配置方法を2種類持っています。
表2 ウィジェットの配置方法
| 方法 | 説明 |
|---|---|
| 位置とサイズ指定 | ウィジェットの絶対位置とサイズを指定して並べる機能。どのGUIツールキットも必ず持っているウィジェット配置機能。 |
| レイアウトマネージメント | ウィジェットの相対関係を指定してウィジェットを並べたり,空き領域にウィジェットを詰込んで行くというように,レイアウトポリシーに従ってウィジェットを配置する機能。 |
位置とサイズは,setGeometry(),resize(),move()で指定します。しかし,この方法のみでは,以下のような変更に追従する際に,アプリケーション側でそのための処理を書かなければならず,アプリケーション作成の度に何度も煩雑な手間が繰り返されます。
- 日本語や英語などの言語の変更
- ウィンドウサイズの変更
- フォントの種類やフォントサイズの変更
そこで,ウィンドウシステムが現れてから,いろいろなレイアウトポリシーでウィジットを並べる方法が考案されています。
Qtのレイアウトポリシー
Qtでは,表3にあるような並べ方が用意されています。
表3 Qtのレイアウトクラス
| レイアウトクラス | 説明 |
|---|---|
| QBoxLayout | 縦(上から下,下から上)または横(左から右,右から左)に並べる。 |
| QHBoxLayout | QBoxLayoutを継承し,左から右に横に並べる。 |
| QVBoxLayout | QBoxLayoutを継承し,上から下に縦に並べる。 |
| QGridLayout | 格子状に並べる。 |
| QFormLayout | n行2列に並べる。ラベル付きで入力ウィジェットを並べることに特化。Qt 4.4で追加された並べ方。 |
| QStackedLayout | 前後に重ね合わせて,その内の1つを表示中にする。 |
いろいろありますが,機能を分類して整理するとQHBoxLayoutとQVBoxLayoutはQBoxLayoutの機能を部分的に使うクラスで,実際にはコンストラクタしか実装されていません。QFormLayoutとQStackedLayoutは,補助的なレイアウト機能なので,QBoxLayoutとQGridLayoutの2つのみ,つまり,直線状か格子状に並べるのがQtのレイアウトポリシーです。ただし,実際のコードでは,QHBoxLayout,QVBoxLayout,QGridLayoutの3つが使われ,QBoxLayoutは殆ど使いません。
レイアウトマネージャは,レイアウトポリシーに従って位置(move())やサイズ調整(resize())をしてウィジェットを並べています(つまり,どのようなレイアウトマネージメント機能も最終的にはウィジェットの位置とサイズを指定しています)。そうすることで,ウィンドウサイズが変更されたり,フォントが変更されてもウィジェットをきれいに並べられるようになります。
レイアウトの作成方法
ベースのウィジェット上にウィジェットを配置するには,ベースのウィジェットを親オブジェクトにして,レイアウトクラスのインスタンスを作成し,addWidget()でレイアウトに入れます。レイアウトにレイアウトをネストさせるには,addLayout()を使います。リスト1では,トップダウンにウィジェットを並べています。
リスト1 トップダウンなレイアウト
QVBoxLayout* topLayout = new QVBoxLayout(this);
QLabel* messageLabel = new QLabel(this);
topLayout->addWidget(messageLabel);
QPushButton* okButton = new QPushButton("Ok", this);
QPushButton* cancelButton = new QPushButton("Cancel", this);
QHBoxLayout* buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
topLayout->addLayout(buttonLayout);
リスト2は,Qt 4で追加されたボトムアップなレイアウト記述方法で,ウィジェットのインスタンス生成で親ウィジェットを指定していないので簡潔な記述になっています。setLayout()の呼び出しで,レイアウトがベースのウィジェットに設定され,topLayout 内にある子ウィジェットの親ウィジェットは,QObject::setParent()でベースのウィジェットに設定されます。記述方法はリスト1と異なりますが,オブジェクトツリーとレイアウト内に配置されるオブジェクトの並びは,リスト1と同じです。レイアウトで何が行われているかは掴みにくいですが,慣れると使いやすく修正もしやすい記述方法です。
リスト2 ボトムアップなレイアウト
QLabel* messageLabel = new QLabel(this);
QPushButton* okButton = new QPushButton("Ok");
QPushButton* cancelButton = new QPushButton("Cancel");
QHBoxLayout* buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
QVBoxLayout* topLayout = new QVBoxLayout;
topLayout->addWidget(messageLabel);
topLayout->addLayout(buttonLayout);
setLayout(topLayout);

