QMLの基礎 - カスタムQMLタイプ
概要
設計者は、既存のQMLタイプを使用して、独自の機能を追加したカスタムQMLタイプを作成することができる。
カスタムQMLタイプは、コンポーネントと呼ぶこともある。
コンポーネントとは、複数のQMLタイプをまとめて1つのQMLタイプとして扱う仕組みである。
カスタムQMLタイプ以外にも、Componentタイプを使用してコンポーネントを作成することができる。
カスタムQMLタイプの作成
- Qt Creatorを起動して、[ファイル]メニューバー - [New File...]を選択する。
- 次に、[New File]ダイアログの左ペインにある[Qt] - 右ペインにある[QMLファイル(Qt Quick 2)]を選択する。
- 作成するQMLファイル名を入力して、[次へ]ボタンを押下する。
- [プロジェクト管理]画面にある[完了]ボタンを押下する。
QMLファイル名には、英数字や_
(アンダーバー)が使用できる。
ただし、ファイル名の1文字目は大文字のアルファベットで始まる必要がある。
もし、カスタムQMLファイルがQtプロジェクトのトップディレクトリと異なるディレクトリに存在する場合、該当ディレクトリをimport
する必要がある。
import "<カスタムQMLファイルが存在するディレクトリの相対パスまたは絶対パス>"
カスタムQMLファイルがQtプロジェクトのトップディレクトリに存在する場合は省略できる。
カスタムQMLタイプの例
以下の例では、押下するたびに乱数を発生させて、テキストに乱数値を表示するカスタムQMLタイプを作成および使用している。
なお、カスタムQMLファイル名は"CustomItem.qml"として、Qtプロジェクトのトップディレクトリに配置している。
// CustomItem.qmlファイル
import QtQuick 2.15
import QtQuick.Controls 2.15
Row {
Button {
id: btn
text: "Click"
function onClicked() {
textvalue.text = Math.random()
}
}
Text {
id: textvalue
text: "0"
}
}
// main.qmlファイル
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
visible: true
width: 640
height: 480
Column {
CustomItem {
height: 50
}
CustomItem {
height: 50
}
CustomItem {
height: 50
}
}
}
コンポーネントの作成
まず、カスタムQMLタイプを作成する。
- Qt Creatorを起動して、[ファイル]メニューバー - [New File...]を選択する。
- 次に、[New File]ダイアログの左ペインにある[Qt] - 右ペインにある[QMLファイル(Qt Quick 2)]を選択する。
- 作成するQMLファイル名を入力して、[次へ]ボタンを押下する。
- [プロジェクト管理]画面にある[完了]ボタンを押下する。
カスタムQMLファイル名には、英数字や_
(アンダーバー)が使用できる。
ただし、ファイル名の1文字目は大文字のアルファベットで始まる必要がある。
コンポーネントは、以下に示すいずれかの手順により、オブジェクトを動的に生成することができる。
Loader
タイプを使用する。Component
タイプのcreateObject
関数を使用する。
オブジェクトを必要に応じて生成する場合、または、パフォーマンス性からオブジェクトを不必要に削除したくない場合等に使用する。
コンポーネントの例
Loaderタイプを使用する場合
Loader
タイプは、カスタムQMLファイルを読み込み、オブジェクトを動的に生成することができる。
カスタムQMLファイルのオブジェクトを生成するには、source
プロパティにカスタムQMLファイルのパスを指定する。
生成したオブジェクトを削除する場合は、source
プロパティを空に指定する。
Loader
タイプはItem
タイプを継承しているため、x
プロパティ、y
プロパティ、anchors
プロパティを使用して、
動的に生成したカスタムQMLファイルのオブジェクトのレイアウトを設定することができる。
デフォルトのx
プロパティおよびy
プロパティの値は、0
であるため、そのオブジェクトは左上に表示される。
// main.qmlファイル
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
visible: true
width: 640
height: 480
MouseArea {
anchors.fill: parent
function onClicked() {
pageLoader.source = "CustomItem.qml"
}
}
Loader {
id: pageLoader
}
}
また、読み込んだカスタムQMLタイプがシグナルを送信してそれを呼び出し側が受信する場合、Connections
タイプをLoader
タイプのitem
プロパティと一緒に使用する必要がある。
また、読み込んだカスタムQMLタイプが破棄された場合、カスタムQMLタイプが再び生成されるまで、全てのシグナルハンドラは無視される。
// main.qmlファイル
Loader {
id: pageLoader
source: "CustomQML.qml"
}
Connections {
target: pageLoader.item
// Qt 5.14以前の場合
onMessage: {
pageLoader.source = ""
}
// Qt 5.15以降は、以下に示すシンタックスでも可能
function onMessage()
{
pageLoader.source = ""
}
}
// CustomQML.qmlファイル
import QtQuick 2.15
Rectangle {
id: root
width: 100
height: 100
signal message(string msg)
MouseArea {
anchors.fill: parent
onClicked: {
root.message("clicked!")
}
}
}
Componentタイプを使用する場合
Componentタイプは、複数のQMLオブジェクトをまとめてコンポーネント化して、再利用可能なオブジェクトとして部品化する。
Component {
// ...略
<任意のカスタムQMLタイプ名 例. CustomItem> {
// ...略
}
}
Component
タイプは、Itemタイプを継承していないため、x
プロパティ、y
プロパティ、anchors
プロパティ、レイアウトの設定は使用できない。
レイアウトの設定が必要な場合、カスタムQMLタイプのプロパティを変更して、レイアウトを設定する必要がある。
Component
タイプ内に定義したカスタムQMLタイプをウインドウに表示する場合は、Component
タイプをLoader
タイプのsourceComponent
プロパティに設定する。
また、Loader
タイプのsourceComponent
プロパティにundefined
を指定することにより、読み込み済みのオブジェクトを削除することができる。
以下の例では、カスタムQMLタイプ(CustomItem)をコンポーネント化したComponentタイプを読み込んで表示している。
import QtQuick 2.15
import QtQuick.Window 2.15
window {
visible: true
width: 640
height: 480
MouseArea {
anchors.fill: parent
onClicked: {
componentLoader.sourceComponent = customComponent
}
}
Loader {
id: componentLoader
}
Component {
id: customComponent
CustomItem {
id: customItem
}
}
}
Component
タイプの重要な機能として、completed
とdestruction
という2つのアタッチドシグナルが存在する。
- completed
- インスタンスが生成された直後に送信される。
- シグナルハンドラである
onCompleted
は、インスタンスの生存確認およびインスタンスの生成直後に何らかの処理が必要な場合に使用するため、使用頻度が高い。
- destruction
- インスタンスが削除された時に送信される。
ComponentタイプのcreateObject関数を使用する場合
グローバル関数であるQt.createComponent
関数を使用することにより、Component
タイプのインスタンスを生成することができる。
このインスタンスから、Component
タイプにあるcreateObject
関数を使用して、表示可能なインスタンスを動的に生成することができる。
また、createObject
関数の第2引数にカスタムQMLタイプに渡すプロパティを指定することにより、インスタンスの生成と同時にプロパティを設定することができる。
以下の例では、Qt.createComponent
関数とcreateObject
関数を使用して、カスタムQMLタイプ(CustomItem)を動的に生成している。
import QtQuick 2.15
import QtQuick.Window 2.15
window {
id: mainWindow
visible: true
width: 640
height: 480
Component.onCompleted {
var component = Qt.createComponent("CustomItem.qml")
component.createObject(mainWindow, {"anchors.fill": mainWindow})
}
}
Component
タイプは、Loader
タイプと比較して手順が複雑であるが複数のインスタンスを生成できる等、Loader
タイプには無いメリットが存在する。
ダイアログの例
ダイアログは、カスタムQMLタイプとして定義することができない。
ただし、Window
タイプまたはApplicationWindow
タイプを定義したカスタムQMLタイプを、createComponent
関数を使用して読み込むことにより実現できる。
また、ダイアログから戻り値を取得する場合は、カスタムQMLタイプにproperty
を定義して、Connections
タイプから取得する。
// main.qmlファイル
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
// ...略
Button {
text: "Popup MessageDialog"
onClicked: {
var component = Qt.createComponent("MessageDialog.qml");
if (component.status === Component.Ready) {
var messageDialog = component.createObject(parent, {someValue1: 1, someValue2: 2});
messageDialogConnection.target = dlg
messageDialog.show();
}
}
Connections {
id: messageDialogConnection
onVisibleChanged: {
if(!target.visible) {
console.log(target.returnValue);
}
}
}
// ...略
// MessageDialog.qmlファイル
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
ApplicationWindow {
property int someValue1: 0
property int someValue2: 0
property int returnValue: 0
id: messageDialog
title: "Some Title"
width: 600
height: 450
flags: Qt.Dialog
modality: Qt.WindowModal
onVisibleChanged: {
messageDialogBtnCancel.focus = true
}
ColumnLayout {
id: messageColumn
width: parent.width
spacing: 20
Label {
text: "Some Message"
width: parent.width
font.pointSize: 12
wrapMode: Label.WordWrap
horizontalAlignment: Label.AlignHCenter
verticalAlignment: Label.AlignVCenter
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: 20
Layout.bottomMargin: 20
}
RowLayout {
x: parent.x
width: parent.width
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 20
spacing: 20
Button {
id: messageDialogBtnOK
text: "OK"
Connections {
target: messageDialogBtnOK
function onClicked() {
returnValue = 0
messageDialog.close()
}
}
}
Button {
id: messageDialogBtnCancel
text: "Cancel"
focus: true
Connections {
target: messageDialogBtnCancel
function onClicked() {
returnValue = 1
messageDialog.close();
}
}
}
}
}
}