「QMLの基礎 - カスタムQMLタイプ」の版間の差分

(ページの作成:「== 概要 == 設計者は、既存のQMLタイプを使用して、独自の機能を追加したカスタムQMLタイプを作成することができる。<br> <br><br> == カスタムQMLタイプの作成 == # Qt Creatorを起動して、[ファイル]メニューバー - [New File...]を選択する。 # 次に、[New File]ダイアログの左ペインにある[Qt] - 右ペインにある[QMLファイル(Qt Quick 2)]を選択する。 # 作成するQMLファ…」)
 
 
(同じ利用者による、間の9版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
設計者は、既存のQMLタイプを使用して、独自の機能を追加したカスタムQMLタイプを作成することができる。<br>
設計者は、既存のQMLタイプを使用して、独自の機能を追加したカスタムQMLタイプを作成することができる。<br>
<br>
カスタムQMLタイプは、コンポーネントと呼ぶこともある。<br>
コンポーネントとは、複数のQMLタイプをまとめて1つのQMLタイプとして扱う仕組みである。<br>
<br>
カスタムQMLタイプ以外にも、Componentタイプを使用してコンポーネントを作成することができる。<br>
<br><br>
<br><br>


20行目: 25行目:
<br><br>
<br><br>


== サンプルコード 1 ==
== カスタムQMLタイプの例 ==
以下の例では、押下するたびに乱数を発生させて、テキストに乱数値を表示するカスタムQMLタイプを作成および使用している。<br>
以下の例では、押下するたびに乱数を発生させて、テキストに乱数値を表示するカスタムQMLタイプを作成および使用している。<br>
なお、カスタムQMLファイル名は"CustomItem.qml"として、Qtプロジェクトのトップディレクトリに配置している。<br>
なお、カスタムQMLファイル名は"CustomItem.qml"として、Qtプロジェクトのトップディレクトリに配置している。<br>
68行目: 73行目:
           height: 50
           height: 50
       }
       }
    }
}
</syntaxhighlight>
<br><br>
== コンポーネントの作成 ==
まず、カスタムQMLタイプを作成する。<br>
# Qt Creatorを起動して、[ファイル]メニューバー - [New File...]を選択する。
# 次に、[New File]ダイアログの左ペインにある[Qt] - 右ペインにある[QMLファイル(Qt Quick 2)]を選択する。
# 作成するQMLファイル名を入力して、[次へ]ボタンを押下する。
# [プロジェクト管理]画面にある[完了]ボタンを押下する。
<br>
カスタムQMLファイル名には、英数字や<code>_</code>(アンダーバー)が使用できる。<br>
<u>ただし、ファイル名の1文字目は大文字のアルファベットで始まる必要がある。</u><br>
<br>
コンポーネントは、以下に示すいずれかの手順により、オブジェクトを動的に生成することができる。<br>
* <code>Loader</code>タイプを使用する。
* <code>Component</code>タイプの<code>createObject</code>関数を使用する。
<br>
オブジェクトを必要に応じて生成する場合、または、パフォーマンス性からオブジェクトを不必要に削除したくない場合等に使用する。<br>
<br><br>
== コンポーネントの例 ==
==== Loaderタイプを使用する場合 ====
<code>Loader</code>タイプは、カスタムQMLファイルを読み込み、オブジェクトを動的に生成することができる。<br>
カスタムQMLファイルのオブジェクトを生成するには、<code>source</code>プロパティにカスタムQMLファイルのパスを指定する。<br>
<br>
生成したオブジェクトを削除する場合は、<code>source</code>プロパティを空に指定する。<br>
<br>
<code>Loader</code>タイプは<code>Item</code>タイプを継承しているため、<code>x</code>プロパティ、<code>y</code>プロパティ、<code>anchors</code>プロパティを使用して、<br>
動的に生成したカスタムQMLファイルのオブジェクトのレイアウトを設定することができる。<br>
デフォルトの<code>x</code>プロパティおよび<code>y</code>プロパティの値は、<code>0</code>であるため、そのオブジェクトは左上に表示される。<br>
<syntaxhighlight lang="qml">
// 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
    }
}
</syntaxhighlight>
<br>
また、読み込んだカスタムQMLタイプがシグナルを送信してそれを呼び出し側が受信する場合、<code>Connections</code>タイプを<code>Loader</code>タイプの<code>item</code>プロパティと一緒に使用する必要がある。<br>
また、読み込んだカスタムQMLタイプが破棄された場合、カスタムQMLタイプが再び生成されるまで、全てのシグナルハンドラは無視される。<br>
<syntaxhighlight lang="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 = ""
    }
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="qml">
// 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!")
      }
    }
}
</syntaxhighlight>
<br>
==== Componentタイプを使用する場合 ====
Componentタイプは、複数のQMLオブジェクトをまとめてコンポーネント化して、再利用可能なオブジェクトとして部品化する。<br>
<syntaxhighlight lang="qml">
Component {
    // ...略
    <任意のカスタムQMLタイプ名  例. CustomItem> {
      // ...略
    }
}
</syntaxhighlight>
<br>
<code>Component</code>タイプは、Itemタイプを継承していないため、<code>x</code>プロパティ、<code>y</code>プロパティ、<code>anchors</code>プロパティ、レイアウトの設定は使用できない。<br>
レイアウトの設定が必要な場合、カスタムQMLタイプのプロパティを変更して、レイアウトを設定する必要がある。<br>
<br>
<code>Component</code>タイプ内に定義したカスタムQMLタイプをウインドウに表示する場合は、<code>Component</code>タイプを<code>Loader</code>タイプの<code>sourceComponent</code>プロパティに設定する。<br>
また、<code>Loader</code>タイプの<code>sourceComponent</code>プロパティに<code>undefined</code>を指定することにより、読み込み済みのオブジェクトを削除することができる。<br>
<br>
以下の例では、カスタムQMLタイプ(CustomItem)をコンポーネント化したComponentタイプを読み込んで表示している。<br>
<syntaxhighlight lang="qml">
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
      }
    }
}
</syntaxhighlight>
<br>
<code>Component</code>タイプの重要な機能として、<code>completed</code>と<code>destruction</code>という2つのアタッチドシグナルが存在する。<br>
* completed
*: インスタンスが生成された直後に送信される。
*: シグナルハンドラである<code>onCompleted</code>は、インスタンスの生存確認およびインスタンスの生成直後に何らかの処理が必要な場合に使用するため、使用頻度が高い。
* destruction
*: インスタンスが削除された時に送信される。
<br>
==== ComponentタイプのcreateObject関数を使用する場合 ====
グローバル関数である<code>Qt.createComponent</code>関数を使用することにより、<code>Component</code>タイプのインスタンスを生成することができる。<br>
このインスタンスから、<code>Component</code>タイプにある<code>createObject</code>関数を使用して、表示可能なインスタンスを動的に生成することができる。<br>
<br>
また、<code>createObject</code>関数の第2引数にカスタムQMLタイプに渡すプロパティを指定することにより、インスタンスの生成と同時にプロパティを設定することができる。<br>
<br>
以下の例では、<code>Qt.createComponent</code>関数と<code>createObject</code>関数を使用して、カスタムQMLタイプ(CustomItem)を動的に生成している。<br>
<syntaxhighlight lang="qml">
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})
    }
}
</syntaxhighlight>
<br>
<code>Component</code>タイプは、<code>Loader</code>タイプと比較して手順が複雑であるが複数のインスタンスを生成できる等、<code>Loader</code>タイプには無いメリットが存在する。<br>
<br><br>
== ダイアログの例 ==
ダイアログは、カスタムQMLタイプとして定義することができない。<br>
ただし、<code>Window</code>タイプまたは<code>ApplicationWindow</code>タイプを定義したカスタムQMLタイプを、<code>createComponent</code>関数を使用して読み込むことにより実現できる。<br>
<br>
また、ダイアログから戻り値を取得する場合は、カスタムQMLタイプに<code>property</code>を定義して、<code>Connections</code>タイプから取得する。<br>
<syntaxhighlight lang="qml">
// 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);     
      }
    }
}
// ...略
</syntaxhighlight>
<br>
<syntaxhighlight lang="qml">
// 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();
                    }
                }
            }
        }
     }
     }
  }
  }

2023年1月11日 (水) 13:52時点における最新版

概要

設計者は、既存のQMLタイプを使用して、独自の機能を追加したカスタムQMLタイプを作成することができる。

カスタムQMLタイプは、コンポーネントと呼ぶこともある。
コンポーネントとは、複数のQMLタイプをまとめて1つのQMLタイプとして扱う仕組みである。

カスタムQMLタイプ以外にも、Componentタイプを使用してコンポーネントを作成することができる。


カスタムQMLタイプの作成

  1. Qt Creatorを起動して、[ファイル]メニューバー - [New File...]を選択する。
  2. 次に、[New File]ダイアログの左ペインにある[Qt] - 右ペインにある[QMLファイル(Qt Quick 2)]を選択する。
  3. 作成するQMLファイル名を入力して、[次へ]ボタンを押下する。
  4. [プロジェクト管理]画面にある[完了]ボタンを押下する。


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タイプを作成する。

  1. Qt Creatorを起動して、[ファイル]メニューバー - [New File...]を選択する。
  2. 次に、[New File]ダイアログの左ペインにある[Qt] - 右ペインにある[QMLファイル(Qt Quick 2)]を選択する。
  3. 作成するQMLファイル名を入力して、[次へ]ボタンを押下する。
  4. [プロジェクト管理]画面にある[完了]ボタンを押下する。


カスタム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タイプの重要な機能として、completeddestructionという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();
                    }
                }
            }
        }
    }
 }