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

第4回 Qtの基本プログラミング~レイアウトマネージメント

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

レイアウト内に配置されるオブジェクト

インスタンス生成に時間がかかったり再生成が困難なウィジェットがある場合には,そのウィジェットを1つ用意して,複数のウィンドウで使い回したくなります。そのためには,オブジェクトツリーとレイアウト構造の把握が必要です。

レイアウト内には,3種類のオブジェクトが存在します。

  • ウィジェット
    ラベルやボタンなどのウィジェット。addWidget()でレイアウトに入れる。
  • レイアウト
    レイアウト自体も並べられる,つまりネストしたレイアウト。addLayout()でレイアウトに入れる。
  • スペーサー
    QSpacerItemのインスタンス。縦横の広がりを持つ空白で表示はされない。固定サイズのスペーサーはaddSpacing(),伸縮するスペーサーはaddStretch()でレイアウトに入れる。

リスト3は固定サイズのボタンと伸縮するスペースに合わせて伸縮するボタンを並べたレイアウト記述で,図1が実行結果となります。

リスト3 レイアウト内オブジェクトの確認コード

QPushButton* button11 = new QPushButton("Button 11");
QPushButton* button12 = new QPushButton("Button 12");
QPushButton* button13 = new QPushButton("Button 13");
QPushButton* button14 = new QPushButton("Button 14");

QHBoxLayout* buttonLayout1 = new QHBoxLayout;
buttonLayout1->addWidget(button11);
buttonLayout1->addSpacing(12);        // 固定サイズのスペース
buttonLayout1->addWidget(button12);
buttonLayout1->addWidget(button13);
buttonLayout1->addStretch();            // 伸縮するスペース
buttonLayout1->addWidget(button14);

QPushButton* button21 = new QPushButton("Button 21");
QPushButton* button22 = new QPushButton("Button 21");

QHBoxLayout* buttonLayout2 = new QHBoxLayout;
buttonLayout2->addWidget(button21);
buttonLayout2->addWidget(button22);

QVBoxLayout* topLayout = new QVBoxLayout;
topLayout->addLayout(buttonLayout1);
topLayout->addLayout(buttonLayout2);

QWidget top;
top.setLayout(topLayout);

図1 レイアウト確認サンプル

図1 レイアウト確認サンプル

オブジェクトツリーは図2のようになり,レイアウト内のオブジェクトのリンク構造は図3のようになります。図3ではマージンによる空白は省略しています。

図2 リスト3の記述で作成されるオブジェクトツリー

図2 リスト3の記述で作成されるオブジェクトツリー

図3 リスト3の記述で作成されるレイアウト内のリンク構造

図3 リスト3の記述で作成されるレイアウト内のリンク構造

レイアウト内にどのようにオブジェクトが存在しているかを把握するのは,一段上のステップに上がるために必要な知識です。QLayout::itemAt(),QLayoutItem::widget(),QLayoutItem::layout(),QLayoutItem::spacerItem()を使うと,図2や図3のようになっているのを確かめられます。

いろいろなスペースとサイズ制約

いろいろなスペースの取り方やウィジェットのサイズ制約方法によって,レイアウト機能が柔軟になり,複雑なレイアウト要求に答えられるようになります。

いろいろなスペース

スペースには,属性としてのスペースとオブジェクトとして存在するものの2つがあります。

表4 スペースの種類

種類説明
マージンレイアウトに並べたウィジェットの上下左右に取られるスペース。QLayout::getContentsMargins()/setContentsMargins()で参照と設定。
スペーシングレイアウトに並べるウィジェットの間隔。レイアウトオブジェクトのspacingプロパティで,すべてのウィジェット間に適用される。
可変スペースレイアウトに配置された可変サイズのQSpacerItemオブジェクト。
固定スペースレイアウトに配置された固定サイズのQSpacerItemオブジェクト。

サイズヒントとサイズポリシー

サイズヒントは,QWidget::sizeHint()が返すウィジェットの最適なサイズです。最適というのは,たとえばボタンであれば,ボタンのテキストが欠けることなく表示され,そのテキストの周りに適度なスペースが取られたサイズです。

グラフィカルなウィジェットには,最適なサイズを決められるものと決められないものがあります。アナログ時計ウィジェットのように,最適サイズのないウィジェットは,適度なサイズを返すようにsizeHint()を実装して,レイアウトマネージャにそのサイズがどれくらいになるかわかるように知らせないと,サイズが決められず,表示されないことがあります。

コンポジットウィジェットは,レイアウトを使用していれば,レイアウトが配下のウィジェットのサイズヒントを調べてコンポジットウィジェットのサイズヒントを自動的に求めます。レイアウトを使用していないコンポジットウィジェットの場合には,グラフィカルなウィジェットと同じようにsizeHint()を実装します。

サイズポリシーは,QWidget::sizePolicy()が返すQSizePolicyオブジェクトで,ウィジェットが縦または横にどのように伸縮できるかというポリシーです。ボタンは縦には伸縮させないものなので,縦のサイズポリシーはFixedで,横には伸ばしても良いけれど,ボタンのテキストは表示されなければならないので,横のサイズポリシーはMinimumに設定されています。

表5 よく使われるサイズポリシー

種類説明
QSizePolicy::Fixedサイズヒントに常に固定。
QSizePolicy::Minimumサイズヒントが最小サイズで,サイズヒントよりも拡大可能。
QSizePolicy::Maximumサイズヒントが最大サイズで,最小サイズ迄縮小可能。
QSizePolicy::Preferredサイズヒントが最適サイズで,伸縮可能。
QSizePolicy::Expanding伸縮できるが拡大の方が適している。

スペーサーにもサイズヒントとサイズポリシーがあり,最小幅が12ピクセルで,横方向に延ばせるというようなスペースも作れます。

サイズ制約

QWidgetのminimumSizeとmaximumSizeのプロパティが最小サイズと最大サイズで,setFixedSize()で最小サイズと最大サイズを同一にすることで固定サイズとなります。

ストレッチ

サイズポリシーには,ベースウィジェットの横幅または高さをどのような割合で子ウィジェットに配分するかを指定するストレッチファクターがあり,QLayout::addWidget(widget, factor)の2番目の引数でストレッチファクターを指定すると,ウィジェットのサイズポリシーに設定されます。例えば,2つの横方向に並ぶウィジェットに2と3のストレッチファクターを指定すると,ベースウィジェットの横幅を2:3で分割した幅が各ウィジェットに配分されます。ただし,ウィジェットは最小サイズよりは小さくなりません。デフォルトは0でストレッチファクターは影響しません。スペーサーにもストレッチファクターがあり,ウィジェットと同様に幅と高さが割り当てられます。

ストラット

レイアウトストラットとグローバルストラットの2種類のストラットがあります。レイアウトストラットはaddStrut(int)でで設定し,QHBoxLayoutの場合には,レイアウト領域の高さは指定した値より小さくなりません。逆に,QVBoxLayout の場合には,幅が指定した値より小さくなりません。

グローバルストラットは,QApplication::setGlobalStrut(QSize)で指定する矩形で,マウスで操作するボタンやスライダーなどの大きさは,指定した矩形よりも小さくならないようになります。タッチパネルなどの操作に適するようにウィジェットのサイズを調整できます。

heightForWidth()

ウィジェットやレイアウトの幅に対応して高さが定まるような場合に使われます。たとえば,QLabelのテキストが折り返されるときにその高さを求めて返しています。

レイアウトマネージメントの補足事項

  • 必ずしもすべてのウィジェットをレイアウトマネージメント機能の配下に置く必要はありません。インジケータのようなウィジェットを浮かせて,他のウィジェットの上に部分的に重ねることもあります。そのようなウィジェットは,レイアウトには入れずに使います。
  • Qt Designerのようにグラフィカルにウィジェットを編集するツールは,レイアウトコードを使いこなせるようになってから使ってこそ効果的です。レイアウトがどのようなことをしているかを本質的に掴めるようになるには,コードで記述できることが必須です。GUI操作では使えるレイアウト機能が部分的であったり,GUI操作のために便宜的な操作方法を取っていたりしているため,本質的なことを掴みにくいからです。
  • レイアウト機能で並べているウィジェットに,位置やサイズの直接指定はできません。たとえば,横に並んでいるウィジェットの片方の端のウィジェットをmove()で呼び出して,もう一方の端に位置を変えようとすれば,レイアウトマネージャが並べようとしていることと矛盾してしまいます。
  • 矛盾するレイアウト要求もあります。レイアウトマネージャが常に全ウィジェットの要求を満たせるとは限りません。たとえば,80×200の固定サイズのベースウィジェット上に,60×50の固定サイズの子ウィジェットを縦に5つは並べられません。子ウィジェットの高さは50よりも小さくなった状態で並べられるでしょう。

まとめと次回の予告

Qtのアーキテクチャの中から,Qtを特徴付ける基本的な機能を普通とは少し違う視点で説明してきました。次回は,いよいよ本連載のテーマであるQt 4.4で追加された大きな機能Qt WebKitについて説明です。

著者プロフィール

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

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

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