QMLの基礎 - ウインドウ
概要
子ウインドウ
静的宣言方式
静的宣言方式は、QMLファイル内で直接ウインドウやコンポーネントを宣言する方法である。
これは、直接的な実装を行う場合に適している。
静的宣言方式の特徴を以下に示す。
- コードが簡潔で可読性が良い。
- デザイン時にレイアウトが理解しやすい。
- アプリケーション起動時の初期化が速い。
- 宣言されたオブジェクトは常に存在するため、常にメモリを消費する。
以下の例では、静的宣言方式を使用して、ウインドウ間の表示・非表示をスイッチングしている。
- メインウインドウ
- 2つのボタンがあり、各ボタンを押下する時、メインウインドウが非表示になりサブウインドウが開く。
- 各サブウィンドウのボタンを押下する時、サブウインドウを閉じて、メインウインドウが再度表示される。
まず、QMLにおけるシグナルの構文を以下に示す。
シグナル(signal <シグナル名>
)を宣言して、シグナルの定義を記述する。
シグナルの定義名は、プレフィックスに"on
"という文字を付加して、シグナル名の先頭文字を大文字にする必要がある。
signal closeThisWindow
onCloseThisWindow: {
// ...略
}
// AnotherWindow.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window
Window {
id: anotherWindow
width: 480
height: 320
// シグナルは、サブウインドウの関数として呼び出す
signal signalExit // シグナルの宣言
// Button to open the main application window
Button {
text: qsTr("メインウインドウに戻る")
width: 180
height: 50
anchors.centerIn: parent
onClicked: {
anotherWindow.signalExit() // シグナルを発行する
}
}
}
// main.qmlファイル
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
id: mainWindow
width: 640
height: 480
title: qsTr("Switching between windows in QML")
visible: true
Rectangle {
anchors.fill: parent
color: "white"
GridLayout {
id: grid
anchors.fill: parent
rows: 2
columns: 1
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.column: 1
Layout.row: 1
// Button to open the first secondary application window
Button {
text: qsTr("サブウインドウ 1 を開く")
anchors.centerIn: parent
width: 300
height: 50
onClicked: {
firstWindow.show() // Open the first window
mainWindow.hide() // Hide the main window
}
}
}
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.column: 1
Layout.row: 2
// Button to open the second secondary application window
Button {
text: qsTr("サブウインドウ 2 を開く")
anchors.centerIn: parent
width: 300
height: 50
onClicked: {
secondWindow.show() // Open a second window
mainWindow.hide() // Hide the main window
}
}
}
}
}
AnotherWindow {
id: firstWindow
title: qsTr("サブウインドウ 1")
// The signal handler for the opening of the main window
onSignalExit: {
firstWindow.close() // Close the first window
mainWindow.show() // Shows the main window
}
}
AnotherWindow {
id: secondWindow
title: qsTr("サブウインドウ 2")
// The signal handler for the opening of the main window
onSignalExit: {
secondWindow.close() // We close the second window
mainWindow.show() // Shows the main window
}
}
}
動的生成方式
動的生成方式は、Qt.createComponent
メソッドおよびComponent.createObject
メソッドを使用して、動的にコンポーネントおよびオブジェクトを生成する方法である。
動的生成方式の特徴を以下に示す。
- 柔軟性が高い。
- 実行時にコンポーネントを生成できる。
- メモリ使用を最適化できる。(必要な時にのみオブジェクトを生成)
- ソースコードが複雑になる可能性がある。
動的生成方法でコンポーネントに引数を渡す場合は、コンポーネントにプロパティを定義して、それらのプロパティに値を渡すことで行う。
var component = Qt.createComponent("<QMLファイル名>");
var window = component.createObject(<親コンポーネントのID>,
// 各プロパティの設定
{ "生成するコンポーネントのプロパティ名 1": "値",
"生成するコンポーネントのプロパティ名 2": "値",
"生成するコンポーネントのプロパティ名 3": "値"
}
);
以下の例では、メインウインドウのボタンを押下する時、各プロパティに初期値を渡して別のウインドウを開いている。
// SecondWindow.qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
ApplicationWindow {
id: secondWindow
width: 400
height: 300
visible: true
property string windowTitle: "Default Title"
property color backgroundColor: "white"
property string message: "Default message"
title: windowTitle
color: backgroundColor
Column {
anchors.centerIn: parent
spacing: 20
Label {
text: message
font.pixelSize: 16
}
Button {
text: "Debug Button"
onClicked: {
console.log("Debug button clicked in Second Window")
console.log("Window Title:", windowTitle)
console.log("Background Color:", backgroundColor)
console.log("Message:", message)
}
}
}
}
// MainWindow.qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
ApplicationWindow {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Main Window")
Button {
anchors.centerIn: parent
text: "Open Second Window"
onClicked: {
var component = Qt.createComponent("SecondWindow.qml");
var window = component.createObject(mainWindow, {
"windowTitle": "Custom Title",
"backgroundColor": "lightblue",
"message": "Hello from Main Window!"
});
window.show();
}
}
}
シグナル / シグナルハンドラの定義
動的生成方式において、コンポーネントを生成して、そのコンポーネントにシグナルを定義して、シグナルハンドラで受信することもできる。
以下の例では、子ウインドウでwindowClosedシグナルを定義して、親ウインドウで動的に生成した子ウインドウのシグナルを接続・受信している。
親ウインドウのボタンを押下する時、子ウインドウが開く。
子ウインドウのボタンを押下する時、子ウインドウを閉じてシグナルが親ウインドウに送信される。
// DynamicWindow.qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: dynamicWindow
width: 400
height: 300
visible: true
title: windowTitle
property string windowTitle: "Dynamic Window"
signal windowClosed(string message)
Column {
anchors.centerIn: parent
spacing: 20
Button {
text: "Close Window"
onClicked: {
dynamicWindow.windowClosed("Window is closing")
dynamicWindow.close()
}
}
}
}
// MainWindow.qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Main Window")
Column {
anchors.centerIn: parent
spacing: 20
Button {
text: "Create Dynamic Window"
onClicked: {
var component = Qt.createComponent("DynamicWindow.qml");
if (component.status === Component.Ready) {
var dynamicWindow = component.createObject(mainWindow, {"windowTitle": "New Dynamic Window"});
// シグナルの接続
dynamicWindow.windowClosed.connect(function(message) {
console.log("Received signal:", message);
statusText.text = "Last signal: " + message;
});
}
}
}
Text {
id: statusText
text: "No signal received yet"
}
}
}
メソッドの定義 / 戻り値の受け取り
動的生成方式において、コンポーネントを生成して、そのコンポーネントに定義したメソッドの戻り値を受け取ることもできる。
以下の例では、子ウインドウにメソッドを定義して、親ウインドウで動的に生成した子ウインドウのメソッドを実行して戻り値を取得している。
親ウインドウのボタンを押下する時、子ウインドウが開く。
子ウインドウのボタンを押下する時、子ウインドウを閉じてシグナルが親ウインドウに送信される。
この方法では、親ウインドウから子ウィンドウの関数を直接呼び出し、あるいは、子ウィンドウから親ウィンドウの関数を呼び出すことで通信を行う。
シグナル / シグナルハンドラを使用しないため、コードの流れが直接的になる。
ただし、オブジェクト間の結合度が高くなるため、複雑なアプリケーションでは管理が難しくなる可能性があることに注意すること。
// DynamicWindow.qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: dynamicWindow
width: 400
height: 300
visible: true
title: windowTitle
property var parentWindow: null
property string windowTitle: "Dynamic Window"
function closeWindow() {
if (parentWindow) parentWindow.onChildWindowClosed("Window is closing")
dynamicWindow.close()
}
function getWindowInfo() {
return {
title: windowTitle,
width: width,
height: height
}
}
Column {
anchors.centerIn: parent
spacing: 20
Button {
text: "Close Window"
onClicked: {
closeWindow()
}
}
Button {
text: "Change Title"
onClicked: {
windowTitle = "Updated Title"
if (parentWindow) parentWindow.updateChildWindowInfo()
}
}
}
}
// MainWindow.qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Main Window")
property var dynamicWindow: null
Column {
anchors.centerIn: parent
spacing: 20
Button {
text: "Create Dynamic Window"
onClicked: {
var component = Qt.createComponent("DynamicWindow.qml");
if (component.status === Component.Ready) {
dynamicWindow = component.createObject(mainWindow, {
"windowTitle": "New Dynamic Window",
"parentWindow": mainWindow
});
updateChildWindowInfo()
}
}
}
Button {
text: "Update Window Info"
enabled: dynamicWindow !== null
onClicked: updateChildWindowInfo()
}
Text {
id: statusText
text: "No window closed yet"
}
Text {
id: infoText
text: "No window info yet"
}
}
function updateChildWindowInfo() {
if (dynamicWindow) {
var windowInfo = dynamicWindow.getWindowInfo()
console.log("Updated Window Info:", JSON.stringify(windowInfo))
infoText.text = "Window Info: " + JSON.stringify(windowInfo)
}
}
function onChildWindowClosed(message) {
console.log("Child window closed:", message)
statusText.text = "Last closed: " + message
dynamicWindow = null
}
}