Qt Quick 入門 第7回: レイアウト

前回までの「Qt Quick 入門」で簡単な UI を Qt Quick で作成してアニメーションを設定する方法までを説明しました。やや駆け足で進めてきましたが、UI 作成の流れを理解していただけたでしょうか。今回からは、これまでの記事では説明を省略していた箇所や、説明に出てこなかった Qt Quick の詳細を学んでいただこうと思います。まずは QML における各要素のレイアウト方法を説明します。

QMLのレイアウト

QML でレイアウトを行う際には大きく3つの方法があります。

実際に UI を作成する際には、それぞれの方法を組み合わせて使用することになると思います。それぞれについて、下記で説明します。

各要素の座標やサイズを指定する

各要素が持つ [qml '' x e=item], [qml '' y e=item], [qml '' width e=item], [qml '' height e=item] プロパティを使用してその位置やサイズを指定する方法です。指定できる値には数値だけでなく、JavaScript の式を書くことが出来ますので、柔軟な指定も可能です。なお、x, yには親要素(parent)の左上の基準点 (0, 0) からの相対座標を指定します。
これらのプロパティで指定する例(リストA)を掲載します。

// リストA
Rectangle {
width: 100
height: 100
color: "blue"
Rectangle {
x: parent.width * 0.1
y: 10
width: parent.width * 0.8
height: parent.height - 20
color: "red"
}
}

親子関係にある二つの [qml Rectangle] 要素を作成し、そのレイアウトを x, y, width, height で指定しています。x と width は親要素の値に対する割合で指定し、y と height は固定値と親要素から固定値を減算した値を指定しています。この QML を実行すると以下の図のようになります。

 

リストAの実行結果

 

X 方向も Y 方向も結果的には同じ値を指定しているため、赤い Rectangle 要素のサイズは同じだけ小さくなっていますが、リサイズしてみると X 方向の青い部分はサイズに従ってその幅が変動するのが分かると思います。逆に Y 方向の青い部分の幅はサイズが変動しても変わりません。

アンカーレイアウトを使用する

[qml 'アンカーレイアウト' e=anchor-layout]はある要素と他の要素の関係を記述することでレイアウトを指定する方法です。考え方は簡単です。ある要素を別の要素(element1)の右に置きたい場合には

anchors.left: element1.right

と、その要素の左端を element1 の右端と結びつけます。右端をどこかと関連づけたい場合には同じように anchros.right を使って右端と結びつける位置を指定します。左右だけの指定では上下は決定していませんので、必要ならば anchors.top や anchors.bottom を使って上下方向も指定します。
アンカーレイアウトで使えるアンカーには以下のものがあります。

 

アンカーレイアウト: エッジ

 

  • [qml '' anchors.top e=item]: その要素の上端をアンカーで指定します
  • [qml '' anchors.bottom e=item]: その要素の下端をアンカーで指定します
  • [qml '' anchors.verticalCenter e=item]: その要素の縦方向の中心位置をアンカーで指定します
  • [qml '' anchors.left e=item]: その要素の左端をアンカーで指定します
  • [qml '' anchors.right e=item]: その要素の右端をアンカーで指定します
  • [qml '' anchors.horizontalCenter e=item]: その要素の横方向の中心位置をアンカーで指定します
  • [qml '' anchors.baseline e=item]: その要素の(テキストの)ベースラインをアンカーで指定します

ボタンを作る際に、[qml MouseArea] 要素を Rectangle 要素と同じサイズに指定することがよくありますが、アンカーレイアウトを使うと下記のリストのようになります。

Rectangle {
width: 100
height: 100
MouseArea {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
}
}

また、アンカーレイアウトではよく使う複数のアンカーをまとめて指定することも出来ます。

  • [qml '' anchors.fill e=item]: 上下左右の全てのアンカーを指定した要素と同じにします。top, bottom, left, right の全てのアンカーを指定した要素の同じ位置のアンカーにかけたのと同じです
  • [qml '' anchors.centerIn e=item]: ある要素の中心点を、指定した要素と同じにします。verticalCenter, horizontalCenter の双方を指定した要素にアンカーするのと同じです

fill や centerIn で指定する場合には ':' の右側に来る値は top や bottom のようなアンカーではなく、要素の id そのものであることに注意してください。たとえば、先ほどの四カ所を anchors で指定した例を anchors.fill を使って書き直すと以下のようになります。こちらの方がおなじみの方もいらっしゃるかと思います。

Rectangle {
width: 100
height: 100
MouseArea {
anchors.fill: parent
}
}

アンカーレイアウトで特に指示をしない場合には互いの要素に間隔が発生しませんが、マージンを指定することで各要素間に空間を作成することが出来ます。

 

アンカーレイアウト: マージン

 

  • [qml '' anchors.topMargin e=item]: その要素の上端のアンカーのマージンを指定します
  • [qml '' anchors.bottomMargin e=item]: その要素の下端のアンカーのマージンを指定します
  • [qml '' anchors.leftMargin e=item]: その要素の左端のアンカーのマージンを指定します
  • [qml '' anchors.rightMargin e=item]: その要素の右端のアンカーのマージンを指定します

上下左右の全ての方向で同じ値を使うのであれば margins を用います。

  • [qml '' anchors.margins e=item]: その要素の上下左右の四方向のアンカーのマージンをまとめて指定します

最初に出てきた リストA をアンカーレイアウトを使って書き直してみましょう。

Rectangle {
width: 100
height: 100
color: "blue"
Rectangle {
anchors.left: parent.left
anchors.leftMargin: parent.width * 0.1
anchors.right: parent.right
anchors.rightMargin: parent.width * 0.1
anchors.top: parent.top
anchors.topMargin: 10
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
color: "red"
}
}

アンカーレイアウトではアンカーとマージンを別々に指定するため行数としては長くなるのですが、互いの要素の関係がわかりやすいのが座標を直接指定する場合と比べての特徴となります。また、要素同士を隣り合わせるような配置をする場合は、width や height を使って x や y を計算して指定するよりも、アンカーレイアウトを用いる方がパフォーマンスもよいです。
なお、センター系およびベースラインアンカーを使う際にはマージンではなく、オフセットを指定します。

  • [qml '' anchors.verticalCenterOffset e=item]: その要素の縦方向の中心位置からのオフセットを指定します
  • [qml '' anchors.horizontalCenterOffset e=item]: その要素の横方向の中心位置からのオフセットを指定します
  • [qml '' anchors.baselineOffset e=item]: その要素の(テキストの)ベースラインからのオフセットを指定します

注: アンカーは直接の親要素か、同レベルにある兄弟要素のみ指定することが出来ます。また、アンカーレイアウトでアンカー先に x や y プロパティを使ったり、逆に x, y プロパティでのレイアウト時にアンカーを指定することは出来ません。

レイアウト要素を使用する

[qml Row], [qml Column], [qml Grid], [qml Flow] といった要素は、よく使われるレイアウトを実現するための要素です。これらのレイアウト要素は、先ほどまでのプロパティによるレイアウトとは異なり自分自身のレイアウトではなく、複数の子要素のレイアウトを決定するのに使用します。子要素のサイズは逐次レイアウトに反映されますが、表示されない要素(width か height、[qml '' opacity e=item] が 0 である、[qml '' visible e=item] が false である)はレイアウトされません。

  • Row: 要素を横一列に並べます
  • Column: 要素を縦一列に並べます
  • Grid: 要素を格子状に並べます
  • Flow: 要素をあらかじめ指定された幅や高さに応じて並べます

これらのレイアウト要素に格納する子要素は基本的には x, y プロパティによる座標の指定やアンカーレイアウトを使ったレイアウトの指定はしないでください。正しくレイアウトすることが出来なくなります。

Row

[qml Row] 要素はその子要素を横に一列に並べるために使用します。各要素の間隔は [qml '' spacing e=row] で指定することが出来ます。

Row {
spacing: 2
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "blue"; width: 50; height: 20 }
}

 

Row  要素によるレイアウトの例

 

Column

[qml Column] 要素はその子要素を縦に一列に並べるために使用します。各要素の間隔は [qml '' spacing e=column] で指定することが出来ます。

Column {
spacing: 2
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "blue"; width: 50; height: 20 }
}

 

Column  要素によるレイアウトの例

 

Grid

[qml Grid] 要素ではその子要素を格子状に並べるために使用します。並べる子要素は、二次元の配列ではなく Row や Column と同様に一次元の要素の列として指定するため、列もしくは行の要素数を [qml '' columns e=grid] か [qml '' rows e=grid] で指定する必要があります。デフォルトでは並べ方を示す [qml '' flow e=grid] プロパティが Grid.LeftToRight になっているため、columns を指定した上で各要素は左上から行を埋める形で右に向けて配置されていきます。縦に並べたい場合には flow を Grid.TopToBottom に変更して rows を指定します。各要素の間隔は [qml '' spacing e=grid] で指定することが出来ます。

Grid {
columns: 3
spacing: 2
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { color: "green"; width: 20; height: 50 }
Rectangle { color: "blue"; width: 50; height: 20 }
Rectangle { color: "cyan"; width: 50; height: 50 }
Rectangle { color: "magenta"; width: 10; height: 10 }
}

 

Grid 要素によるレイアウトの例

 

Flow

[qml Flow] 要素は Grid 要素によるレイアウトに似ています。Grid 要素では各要素の数によってレイアウトを作成しますが、Flow 要素は各要素の幅に従ってレイアウトを作成します。Grid 要素と同様に各要素の配置方向を示す [qml '' flow e=flow] プロパティがあり、デフォルトは左上から右に向けて配置していく Flow.LeftToRight になっています。幅(flow プロパティが Flow.TopToBottom の場合には高さ)がレイアウトの計算に必要になるので、width プロパティやアンカーレイアウトなどを用いて Flow 要素の幅が定まっている必要があります。各要素の間隔は [qml '' spacing e=flow] で指定することが出来ます。

Flow {
anchors.fill: parent
anchors.margins: 4
spacing: 10

Text { text: "Text"; font.pixelSize: 40 }
Text { text: "items"; font.pixelSize: 40 }
Text { text: "flowing"; font.pixelSize: 40 }
Text { text: "inside"; font.pixelSize: 40 }
Text { text: "a"; font.pixelSize: 40 }
Text { text: "Flow"; font.pixelSize: 40 }
Text { text: "item"; font.pixelSize: 40 }
}

 

Flow 要素によるレイアウトの例

 

まとめ

QML のレイアウト、いかがでしたか。基本は簡単ですので、サンプルを実行してみたり、QML のコードを書いてみたり、Qt Quick デザイナで配置してみたりといろいろ試してみてください。

商用ライセンスのトライアルやオープンソースを利用したい方は、ぜひQtの無料トライアルを開始してみてください!また、Qtについてご質問あり場合は、お気軽にお問い合わせください。


Blog Topics:

Comments