「QMLの基礎 - モデルとビュー」の版間の差分
編集の要約なし |
|||
198行目: | 198行目: | ||
<br> | <br> | ||
[[ファイル:QML Model and View 3.png|フレームなし|中央]] | [[ファイル:QML Model and View 3.png|フレームなし|中央]] | ||
<br><br> | |||
== ビューのデリゲート == | |||
ビューは、リストの中のアイテムを視覚的に表現するために、デリゲートが必要となる。<br> | |||
<br> | |||
ビューは、デリゲートにより定義されたテンプレートにしたがって、各アイテムのリストを生成する。<br> | |||
モデル内のアイテムは、アイテムのプロパティだけでなく、インデックスのプロパティにもアクセスできる。<br> | |||
[[ファイル:QML Model and View 4.png|フレームなし|中央]] | |||
<br> | |||
<syntaxhighlight lang="qml"> | |||
Component { // delegate: <アイテム名> { を使用することもできる | |||
id: petdelegate | |||
Text { | |||
id: label | |||
font.pixelSize: 24 | |||
text: index === 0 ? type + " (default)" : type | |||
required property int index | |||
required property string type | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<br> | |||
==== デリゲートからのビューやモデルへのアクセス ==== | |||
デリゲートがバインドされているビューは、<code>view</code>プロパティを介してデリゲートからアクセスできる。(例. <code>ListView.view</code>, <code>GridView.view</code>等)<br> | |||
したがって、対応するモデルとそのプロパティは、<code>view.model</code>を通じて使用できる。(例. <code>ListView.view.model</code>, <code>GridView.view.model</code>等)<br> | |||
<br> | |||
さらに、モデルに定義されているシグナルやメソッドもアクセス可能である。<br> | |||
例えば、複数のビューに同じデリゲートを使用したいが、装飾等の機能は各ビューごとに異なるものにする場合や他の異なる設定を各ビューのプロパティにする場合に活用できる。<br> | |||
同様に、モデルの各プロパティにアクセスおよび表示することもできる。<br> | |||
<br> | |||
以下の例では、デリゲート内の1つのTextアイテムは、<code>ListModel</code>内のlanguageプロパティを表示している。<br> | |||
また、デリゲート内の1つのTextアイテムの色は、<code>ListView</code>内のfruit_colorプロパティに依存している。<br> | |||
<syntaxhighlight lang="qml"> | |||
Rectangle { | |||
width: 200 | |||
height: 200 | |||
ListView { | |||
model: fruitModel | |||
delegate: fruitDelegate | |||
anchors.fill: parent | |||
property color fruit_color: "green" | |||
} | |||
ListModel { | |||
id: fruitModel | |||
property string language: "en" | |||
ListElement { | |||
name: "Apple" | |||
cost: 2.45 | |||
} | |||
ListElement { | |||
name: "Orange" | |||
cost: 3.25 | |||
} | |||
ListElement { | |||
name: "Banana" | |||
cost: 1.95 | |||
} | |||
} | |||
Component { | |||
id: fruitDelegate | |||
Row { | |||
id: fruit | |||
Text { | |||
text: " Fruit: " + name | |||
color: fruit.ListView.view.fruit_color | |||
} | |||
Text { | |||
text: " Cost: $" + cost | |||
} | |||
Text { | |||
text: " Language: " + fruit.ListView.view.model.language | |||
} | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== モデル == | |||
データは、デリゲートがバインドすることができる名前付きのデータロールを介してデリゲートに提供される。<br> | |||
<br> | |||
以下の例では、typeとageという2つのロールを持つListModel、および、これらのロールにバインドして値を表示するデリゲートを持つListViewを作成している。<br> | |||
<syntaxhighlight lang="qml"> | |||
import QtQuick 2.15 | |||
Item { | |||
width: 200 | |||
height: 250 | |||
ListModel { | |||
id: myModel | |||
ListElement { | |||
type: "Dog" | |||
age: 8 | |||
} | |||
ListElement { | |||
type: "Cat" | |||
age: 5 | |||
} | |||
} | |||
Component { | |||
id: myDelegate | |||
Text { text: type + ", " + age } | |||
} | |||
ListView { | |||
anchors.fill: parent | |||
model: myModel | |||
delegate: myDelegate | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
ロールのアクセスをより細かく制御する場合、および、デリゲートをビュー外部で使用する場合は、以下のように記述する。<br> | |||
<syntaxhighlight lang="qml"> | |||
required property <データ型> <プロパティ名> | |||
</syntaxhighlight> | |||
<br> | |||
デリゲートに必須プロパティが含まれている場合、名前付きのロールは提供されない。<br> | |||
代わりに、QMLエンジンは、まず、必須プロパティ名がモデルロールの名前と一致するかどうかをチェックして、一致する場合、その必須プロパティはモデルの対応する値にバインドされる。<br> | |||
<syntaxhighlight lang="qml"> | |||
import QtQuick 2.15 | |||
Item { | |||
width: 200 | |||
height: 250 | |||
ListModel { | |||
id: myModel | |||
ListElement { | |||
type: "Dog" | |||
age: 8 | |||
noise: "meow" | |||
} | |||
ListElement { | |||
type: "Cat" | |||
age: 5 | |||
noise: "woof" | |||
} | |||
} | |||
component MyDelegate : Text { | |||
required property string type | |||
required property int age | |||
text: type + ", " + age | |||
// 間違った記述: Component.onCompleted: () => console.log(noise) | |||
// 必須プロパティであるnoiseがないため、ReferenceErrorが発生する | |||
// 必要プロパティが存在することで、スコープにノイズが注入されるのを防ぐことができる | |||
} | |||
ListView { | |||
anchors.fill: parent | |||
model: myModel | |||
delegate: MyDelegate {} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
モデルのプロパティとデリゲートのプロパティの間に名前の衝突がある場合、代わりに修飾されたモデル名でロールにアクセスすることができる。<br> | |||
<br> | |||
例えば、モデル内とデリゲート内の両方に同名のプロパティが存在する時、<br> | |||
デリゲート内のプロパティではなく、モデル内のプロパティを参照する場合、<code><モデルのID名>.<プロパティ名></code>と記述することにより参照できる。<br> | |||
<br> | |||
また、モデル内のインデックスを含む特別なインデックスロールもデリゲートで使用できる。<br> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>このインデックスは、アイテムがモデルから削除されると<code>-1</code>に設定されることに注意すること。</u><br> | |||
<u>インデックスロールにバインドする場合、インデックスが<code>-1</code>になる可能性、つまり、アイテムが有効でなくなる可能性を考慮したロジックにする。</u><br> | |||
<u>(通常、アイテムはすぐに破棄されるが、一部のビューでは<code>delayRemove</code>プロパティによりデリゲートの破棄を遅らせることができる)</u><br> | |||
<br> | |||
名前付きロールを持たないモデル(例. <code>ListModel</code>等)は、モデルデータロールによりデータが提供される。<br> | |||
モデルデータロールは、1つのロールしか持たないモデルにも提供される。この場合、モデルデータロールには、名前付きロールと同じデータが含まれる。<br> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>モデルロール、モデルデータロール、インデックスロールにおいて、デリゲートが必須プロパティを含んでいる場合、プロパティ名が一致しなければアクセスできないことに注意する。</u><br> | |||
<br> | |||
QMLには、いくつかのデータモデルが用意されており、それらはQMLの組み込み型に含まれている。<br> | |||
加えて、モデルはQt C++で作成することができ、そのモデルを<code>QQmlEngine</code>クラスを使用してQMLコンポーネントから利用することもできる。<br> | |||
<br> | |||
モデルの作成については、[[QMLの基礎 - モデルの作成]]や[[QMLの基礎 - QMLタイプの作成]]を参照すること。<br> | |||
<br> | |||
モデルからアイテムを配置する場合は、<code>Repeater</code>を使用することで実現できる。<br> | |||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] |
2021年10月23日 (土) 01:23時点における版
概要
ソフトウェアはデータを生成して、データを表示する必要がある。
Qt Quickには、データを表示するためのモデル、ビュー、デリゲートという概念が存在する。
モデル、ビュー、デリゲートは、データの可視化をモジュール化し、開発者がデータの様々な側面をコントロールできる。
また、データにほとんど変更を加えることなく、リストビューとグリッドビューを入れ替えることができる。
同様に、データのインスタンスをデリゲートにカプセル化することで、データの表示方法や処理方法を決定することができる。
- モデル
- データとその構造を含む。
- モデルを作成するためのQMLタイプがいくつかある。
- ビュー
- データを表示するコンテナである。
- ビューは、リストやグリッドでデータを表示する。
- デリゲート
- データがビューにどのように表示されるかを指定する。
- デリゲートはモデルの各データを受け取り、それをカプセル化する。データはデリゲートを通してアクセスできる。
- デリゲートは、データを編集可能なモデルに書き戻すこともできる。(例.
TextField
のonAccepted
ハンドラ内) - データを可視化するには、ビューの
model
プロパティをモデル、delegate
プロパティをコンポーネントやその他の互換性のあるタイプにバインドする。
ビューによるデータの表示
ビューは、アイテムのコレクションのためのコンテナである。
豊富な機能を持ち、スタイルや動作の要求に合わせてカスタマイズすることができる。
Qt Quickの基本セットには、以下に示す標準的なビューが用意されている。
- ListView
- 水平または垂直のリストにアイテムを配置する。
- GridView
- 利用可能なスペース内のグリッドにアイテムを配置する。
- PathView
- アイテムをパス上に配置する。
上記のビューには、それぞれ専用のプロパティと動作がある。
ビューの装飾
ビューでは、header
、footer
、section
等の装飾用プロパティを使用して、見た目をカスタマイズすることができる。
これらのプロパティに対して、オブジェクト(通常は別のビジュアルオブジェクト)をバインドすることにより、ビューを装飾することができる。
footerには、ボーダーを表示するRectangleアイテムや、ビューの上にロゴを表示するヘッダ等がある。
以下の例では、あるクラブがメンバーリストを自社のブランドカラーで装飾している。
メンバーリストはモデルの中にあり、デリゲートはモデルのコンテンツを表示している。
クラブは、ビジュアルアイテムをheader
プロパティとfooter
プロパティにバインドすることにより、メンバーリストを装飾することができる。
ビジュアルアイテムは、同一ファイル、他のファイル、Component
アイテムで定義することができる。
ListModel {
id: nameModel
ListElement { name: "Alice" }
ListElement { name: "Bob" }
ListElement { name: "Jane" }
ListElement { name: "Harry" }
ListElement { name: "Wendy" }
}
Component {
id: nameDelegate
Text {
text: name;
font.pixelSize: 24
}
}
ListView {
anchors.fill: parent
clip: true
model: nameModel
delegate: nameDelegate
header: bannercomponent
footer: Rectangle {
width: parent.width
height: 30;
gradient: clubcolors
}
highlight: Rectangle {
width: parent.width
color: "lightgray"
}
}
Component { // instantiated when header is processed
id: bannercomponent
Rectangle {
id: banner
width: parent.width
height: 50
gradient: clubcolors
border {
color: "#9EDDF2"
width: 2
}
Text {
anchors.centerIn: parent
text: "Club Members"
font.pixelSize: 32
}
}
}
Gradient {
id: clubcolors
GradientStop {
position: 0.0
color: "#8EE2FE"
}
GradientStop {
position: 0.66
color: "#7ED2EE"
}
}
マウスとタッチの操作
ビューは、コンテンツのドラッグやフリックを処理するが、個々のデリゲートとのタッチ操作は処理しない。
デリゲートがタッチ入力に反応してcurrentIndex
プロパティを設定するためには、適切なタッチ処理を持つMouseArea
アイテムをデリゲートに定義する必要がある。
※注意
highlightRangeMode
プロパティがStrictlyEnforceRange
に設定されている場合、ビューは常にcurrentIndex
プロパティが指定されたハイライト範囲内にあることを保証するため、
currentIndex
プロパティはビューをドラッグおよびフリックしても影響を受けない。
ListViewのセクション
ListView
のコンテンツはセクションにグループ化されるため、関連するリストアイテムは、そのセクションに応じてラベル付けされる。
また、セクションは、デリゲートを使用して装飾することもできる。
以下の例では、ListView
に、人名とチームの所属を示すリストを作成している。
ListView
には、隣接するアイテムや関連するアイテムをセクションにまとめることができるproperty
プロパティが存在する。
property
プロパティは、どのリストタイプのプロパティをセクションとして使用するかを決定する。
cliteria
プロパティは、セクション名の表示方法を決定する。
delegate
プロパティは、ListView
等のdelegate
プロパティと同様である。
ListModel {
id: nameModel
ListElement { name: "Alice"; team: "Crypto" }
ListElement { name: "Bob"; team: "Crypto" }
ListElement { name: "Jane"; team: "QA" }
ListElement { name: "Victor"; team: "QA" }
ListElement { name: "Wendy"; team: "Graphics" }
}
Component {
id: nameDelegate
Text {
text: name;
font.pixelSize: 24
anchors.left: parent.left
anchors.leftMargin: 2
}
}
ListView {
anchors.fill: parent
model: nameModel
delegate: nameDelegate
focus: true
highlight: Rectangle {
color: "lightblue"
width: parent.width
}
section {
property: "team"
criteria: ViewSection.FullString
delegate: Rectangle {
color: "#b0dfb0"
width: parent.width
height: childrenRect.height + 4
Text {
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 16
font.bold: true
text: section
}
}
}
}
ビューのデリゲート
ビューは、リストの中のアイテムを視覚的に表現するために、デリゲートが必要となる。
ビューは、デリゲートにより定義されたテンプレートにしたがって、各アイテムのリストを生成する。
モデル内のアイテムは、アイテムのプロパティだけでなく、インデックスのプロパティにもアクセスできる。
Component { // delegate: <アイテム名> { を使用することもできる
id: petdelegate
Text {
id: label
font.pixelSize: 24
text: index === 0 ? type + " (default)" : type
required property int index
required property string type
}
}
デリゲートからのビューやモデルへのアクセス
デリゲートがバインドされているビューは、view
プロパティを介してデリゲートからアクセスできる。(例. ListView.view
, GridView.view
等)
したがって、対応するモデルとそのプロパティは、view.model
を通じて使用できる。(例. ListView.view.model
, GridView.view.model
等)
さらに、モデルに定義されているシグナルやメソッドもアクセス可能である。
例えば、複数のビューに同じデリゲートを使用したいが、装飾等の機能は各ビューごとに異なるものにする場合や他の異なる設定を各ビューのプロパティにする場合に活用できる。
同様に、モデルの各プロパティにアクセスおよび表示することもできる。
以下の例では、デリゲート内の1つのTextアイテムは、ListModel
内のlanguageプロパティを表示している。
また、デリゲート内の1つのTextアイテムの色は、ListView
内のfruit_colorプロパティに依存している。
Rectangle {
width: 200
height: 200
ListView {
model: fruitModel
delegate: fruitDelegate
anchors.fill: parent
property color fruit_color: "green"
}
ListModel {
id: fruitModel
property string language: "en"
ListElement {
name: "Apple"
cost: 2.45
}
ListElement {
name: "Orange"
cost: 3.25
}
ListElement {
name: "Banana"
cost: 1.95
}
}
Component {
id: fruitDelegate
Row {
id: fruit
Text {
text: " Fruit: " + name
color: fruit.ListView.view.fruit_color
}
Text {
text: " Cost: $" + cost
}
Text {
text: " Language: " + fruit.ListView.view.model.language
}
}
}
}
モデル
データは、デリゲートがバインドすることができる名前付きのデータロールを介してデリゲートに提供される。
以下の例では、typeとageという2つのロールを持つListModel、および、これらのロールにバインドして値を表示するデリゲートを持つListViewを作成している。
import QtQuick 2.15
Item {
width: 200
height: 250
ListModel {
id: myModel
ListElement {
type: "Dog"
age: 8
}
ListElement {
type: "Cat"
age: 5
}
}
Component {
id: myDelegate
Text { text: type + ", " + age }
}
ListView {
anchors.fill: parent
model: myModel
delegate: myDelegate
}
}
ロールのアクセスをより細かく制御する場合、および、デリゲートをビュー外部で使用する場合は、以下のように記述する。
required property <データ型> <プロパティ名>
デリゲートに必須プロパティが含まれている場合、名前付きのロールは提供されない。
代わりに、QMLエンジンは、まず、必須プロパティ名がモデルロールの名前と一致するかどうかをチェックして、一致する場合、その必須プロパティはモデルの対応する値にバインドされる。
import QtQuick 2.15
Item {
width: 200
height: 250
ListModel {
id: myModel
ListElement {
type: "Dog"
age: 8
noise: "meow"
}
ListElement {
type: "Cat"
age: 5
noise: "woof"
}
}
component MyDelegate : Text {
required property string type
required property int age
text: type + ", " + age
// 間違った記述: Component.onCompleted: () => console.log(noise)
// 必須プロパティであるnoiseがないため、ReferenceErrorが発生する
// 必要プロパティが存在することで、スコープにノイズが注入されるのを防ぐことができる
}
ListView {
anchors.fill: parent
model: myModel
delegate: MyDelegate {}
}
}
モデルのプロパティとデリゲートのプロパティの間に名前の衝突がある場合、代わりに修飾されたモデル名でロールにアクセスすることができる。
例えば、モデル内とデリゲート内の両方に同名のプロパティが存在する時、
デリゲート内のプロパティではなく、モデル内のプロパティを参照する場合、<モデルのID名>.<プロパティ名>
と記述することにより参照できる。
また、モデル内のインデックスを含む特別なインデックスロールもデリゲートで使用できる。
※注意
このインデックスは、アイテムがモデルから削除されると-1
に設定されることに注意すること。
インデックスロールにバインドする場合、インデックスが-1
になる可能性、つまり、アイテムが有効でなくなる可能性を考慮したロジックにする。
(通常、アイテムはすぐに破棄されるが、一部のビューではdelayRemove
プロパティによりデリゲートの破棄を遅らせることができる)
名前付きロールを持たないモデル(例. ListModel
等)は、モデルデータロールによりデータが提供される。
モデルデータロールは、1つのロールしか持たないモデルにも提供される。この場合、モデルデータロールには、名前付きロールと同じデータが含まれる。
※注意
モデルロール、モデルデータロール、インデックスロールにおいて、デリゲートが必須プロパティを含んでいる場合、プロパティ名が一致しなければアクセスできないことに注意する。
QMLには、いくつかのデータモデルが用意されており、それらはQMLの組み込み型に含まれている。
加えて、モデルはQt C++で作成することができ、そのモデルをQQmlEngine
クラスを使用してQMLコンポーネントから利用することもできる。
モデルの作成については、QMLの基礎 - モデルの作成やQMLの基礎 - QMLタイプの作成を参照すること。
モデルからアイテムを配置する場合は、Repeater
を使用することで実現できる。