Qt quickをサンプルプロジェクトから学ぶ
背景
Qt Quick、QMLを学習したいが参考書籍やYoutube動画が少ない。サンプルプロジェクトのGalleryがためになるという情報を入手したのでコードを閲覧しまとめる。
サンプルプロジェクト
今回ソースコードを確認するサンプルプロジェクトは、Qt Creatorを立ち上げ後にExampleから選択可能。
プロジェクトをビルドすると以下のようなwindowが表示される。
左上のハンバーガーメニューをクリックすると各コンポーネント名が表示される。
コンポーネントを選択しクリックすると、コンポーネント名に応じた画面が表示される。
以下はButton
を選択した場合の画面。
サンプルプロジェクトなので、当然全てのコードが閲覧可能である。従って各コンポーネントの記述方法・プロパティなどが確認できる。
ファイル構成
プロジェクトのファイル構成は、メイン画面となるgallery.qmlと各コンポーネント用にcomponent名.qmlがpageディレクトリに収められている。
gallery.qmlのコードは以下の通り。
import QtQuick import QtQuick.Layouts import QtQuick.Controls import Qt.labs.settings import "." as App ApplicationWindow { id: window width: 360 height: 520 visible: true title: "Qt Quick Controls" function help() { let displayingControl = listView.currentIndex !== -1 let currentControlName = displayingControl ? listView.model.get(listView.currentIndex).title.toLowerCase() : "" let url = "https://doc.qt.io/qt-5/" + (displayingControl ? "qml-qtquick-controls2-" + currentControlName + ".html" : "qtquick-controls2-qmlmodule.html"); Qt.openUrlExternally(url) } required property var builtInStyles Settings { id: settings property string style } Shortcut { sequences: ["Esc", "Back"] enabled: stackView.depth > 1 onActivated: navigateBackAction.trigger() } Shortcut { sequence: StandardKey.HelpContents onActivated: help() } Action { id: navigateBackAction icon.name: stackView.depth > 1 ? "back" : "drawer" onTriggered: { if (stackView.depth > 1) { stackView.pop() listView.currentIndex = -1 } else { drawer.open() } } } Shortcut { sequence: "Menu" onActivated: optionsMenuAction.trigger() } Action { id: optionsMenuAction icon.name: "menu" onTriggered: optionsMenu.open() } header: App.ToolBar { RowLayout { spacing: 20 anchors.fill: parent ToolButton { action: navigateBackAction } Label { id: titleLabel text: listView.currentItem ? listView.currentItem.text : "Gallery" font.pixelSize: 20 elide: Label.ElideRight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter Layout.fillWidth: true } ToolButton { action: optionsMenuAction Menu { id: optionsMenu x: parent.width - width transformOrigin: Menu.TopRight Action { text: "Settings" onTriggered: settingsDialog.open() } Action { text: "Help" onTriggered: help() } Action { text: "About" onTriggered: aboutDialog.open() } } } } } Drawer { id: drawer width: Math.min(window.width, window.height) / 3 * 2 height: window.height interactive: stackView.depth === 1 ListView { id: listView focus: true currentIndex: -1 anchors.fill: parent delegate: ItemDelegate { width: listView.width text: model.title highlighted: ListView.isCurrentItem onClicked: { listView.currentIndex = index stackView.push(model.source) drawer.close() } } model: ListModel { ListElement { title: "BusyIndicator"; source: "qrc:/pages/BusyIndicatorPage.qml" } ListElement { title: "Button"; source: "qrc:/pages/ButtonPage.qml" } ListElement { title: "CheckBox"; source: "qrc:/pages/CheckBoxPage.qml" } ListElement { title: "ComboBox"; source: "qrc:/pages/ComboBoxPage.qml" } ListElement { title: "DelayButton"; source: "qrc:/pages/DelayButtonPage.qml" } ListElement { title: "Dial"; source: "qrc:/pages/DialPage.qml" } ListElement { title: "Dialog"; source: "qrc:/pages/DialogPage.qml" } ListElement { title: "Delegates"; source: "qrc:/pages/DelegatePage.qml" } ListElement { title: "Frame"; source: "qrc:/pages/FramePage.qml" } ListElement { title: "GroupBox"; source: "qrc:/pages/GroupBoxPage.qml" } ListElement { title: "PageIndicator"; source: "qrc:/pages/PageIndicatorPage.qml" } ListElement { title: "ProgressBar"; source: "qrc:/pages/ProgressBarPage.qml" } ListElement { title: "RadioButton"; source: "qrc:/pages/RadioButtonPage.qml" } ListElement { title: "RangeSlider"; source: "qrc:/pages/RangeSliderPage.qml" } ListElement { title: "ScrollBar"; source: "qrc:/pages/ScrollBarPage.qml" } ListElement { title: "ScrollIndicator"; source: "qrc:/pages/ScrollIndicatorPage.qml" } ListElement { title: "Slider"; source: "qrc:/pages/SliderPage.qml" } ListElement { title: "SpinBox"; source: "qrc:/pages/SpinBoxPage.qml" } ListElement { title: "StackView"; source: "qrc:/pages/StackViewPage.qml" } ListElement { title: "SwipeView"; source: "qrc:/pages/SwipeViewPage.qml" } ListElement { title: "Switch"; source: "qrc:/pages/SwitchPage.qml" } ListElement { title: "TabBar"; source: "qrc:/pages/TabBarPage.qml" } ListElement { title: "TextArea"; source: "qrc:/pages/TextAreaPage.qml" } ListElement { title: "TextField"; source: "qrc:/pages/TextFieldPage.qml" } ListElement { title: "ToolTip"; source: "qrc:/pages/ToolTipPage.qml" } ListElement { title: "Tumbler"; source: "qrc:/pages/TumblerPage.qml" } } ScrollIndicator.vertical: ScrollIndicator { } } } StackView { id: stackView anchors.fill: parent initialItem: Pane { id: pane Image { id: logo width: pane.availableWidth / 2 height: pane.availableHeight / 2 anchors.centerIn: parent anchors.verticalCenterOffset: -50 fillMode: Image.PreserveAspectFit source: "images/qt-logo.png" } Label { text: "Qt Quick Controls provides a set of controls that can be used to build complete interfaces in Qt Quick." anchors.margins: 20 anchors.top: logo.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: arrow.top horizontalAlignment: Label.AlignHCenter verticalAlignment: Label.AlignVCenter wrapMode: Label.Wrap } Image { id: arrow source: "images/arrow.png" anchors.left: parent.left anchors.bottom: parent.bottom } } } Dialog { id: settingsDialog x: Math.round((window.width - width) / 2) y: Math.round(window.height / 6) width: Math.round(Math.min(window.width, window.height) / 3 * 2) modal: true focus: true title: "Settings" standardButtons: Dialog.Ok | Dialog.Cancel onAccepted: { settings.style = styleBox.displayText settingsDialog.close() } onRejected: { styleBox.currentIndex = styleBox.styleIndex settingsDialog.close() } contentItem: ColumnLayout { id: settingsColumn spacing: 20 RowLayout { spacing: 10 Label { text: "Style:" } ComboBox { id: styleBox property int styleIndex: -1 model: window.builtInStyles Component.onCompleted: { styleIndex = find(settings.style, Qt.MatchFixedString) if (styleIndex !== -1) currentIndex = styleIndex } Layout.fillWidth: true } } Label { text: "Restart required" color: "#e41e25" opacity: styleBox.currentIndex !== styleBox.styleIndex ? 1.0 : 0.0 horizontalAlignment: Label.AlignHCenter verticalAlignment: Label.AlignVCenter Layout.fillWidth: true Layout.fillHeight: true } } } Dialog { id: aboutDialog modal: true focus: true title: "About" x: (window.width - width) / 2 y: window.height / 6 width: Math.min(window.width, window.height) / 3 * 2 contentHeight: aboutColumn.height Column { id: aboutColumn spacing: 20 Label { width: aboutDialog.availableWidth text: "The Qt Quick Controls module delivers the next generation user interface controls based on Qt Quick." wrapMode: Label.Wrap font.pixelSize: 12 } Label { width: aboutDialog.availableWidth text: "In comparison to Qt Quick Controls 1, Qt Quick Controls " + "are an order of magnitude simpler, lighter, and faster." wrapMode: Label.Wrap font.pixelSize: 12 } } } }
中腹にある以下のコードで別ファイルのコードを呼び出している。
ListVIew{ model:ListModel{ ListElement { title: "Button"; source: "qrc:/pages/ButtonPage.qml"} }
各種コンポーネント
頻繁に使うことが予想されるコンポーネントについて説明する。 ここでは、以下について説明する。 1. Button 1. CheckBox 1. ComboBox 1. DelayButton 1. Slider 1. Switch 1. TextField
Button
コードは以下。
import QtQuick.Layouts import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "Button presents a push-button that can be pushed or clicked by the user. " + "Buttons are normally used to perform an action, or to answer a question." } ColumnLayout { spacing: 20 anchors.horizontalCenter: parent.horizontalCenter Button { text: "First First First" Layout.fillWidth: true } Button { id: button text: "Second" highlighted: true Layout.fillWidth: true } Button { text: "Third" enabled: false Layout.fillWidth: true } } } }
ここではColumnLayout
を用いてボタンを3つ並べているが、Buttonコンポーネント自体は以下のような記述で表示できる。
Button { id: button text: "Second" highlighted: true enabled: true Layout.fillWidth: true }
ButtonではなくRoundButtonとすれば丸みを帯びたボタンになる。
CheckBox
コードは以下。
import QtQuick import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "CheckBox presents an option button that can be toggled on or off. " + "Check boxes are typically used to select one or more options from a set of options." } Column { spacing: 20 anchors.horizontalCenter: parent.horizontalCenter CheckBox { text: "First" checked: true } CheckBox { text: "Second" } CheckBox { text: "Third" checked: true enabled: false } } } }
CheckBox自体は以下の記述で表示可能。
CheckBox {
text: "First"
checked: true
enabled: true
}
ComboBox
コードは以下。
import QtQuick import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "ComboBox is a combined button and popup list. It presents " + "a list of options to the user that occupies minimal screen space." } ComboBox { model: ["First", "Second", "Third"] anchors.horizontalCenter: parent.horizontalCenter } Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "ComboBox can be made \l editable. An editable combo box auto-" + "completes its text based on what is available in the model." } ComboBox { editable: true model: ListModel { id: model ListElement { text: "Banana" } ListElement { text: "Apple" } ListElement { text: "Coconut" } } onAccepted: { if (find(editText) === -1) model.append({text: editText}) } anchors.horizontalCenter: parent.horizontalCenter } } }
シンプルな記述方法は以下。
ComboBox { model: ["First", "Second", "Third"] }
DelayButton
Delayボタンは、長押しすることで反応するボタン。
コードは以下。
import QtQuick import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "DelayButton is a checkable button that incorporates a delay before the " + "button is activated. This delay prevents accidental presses." } DelayButton { text: "DelayButton" anchors.horizontalCenter: parent.horizontalCenter } } }
delayプロパティで長押しする時間を調整可能(単位はms)。
DelayButton { text: "DelayButton" delay: 10000 }
Slider
コードは以下。
import QtQuick import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "Slider is used to select a value by sliding a handle along a track." } Slider { id: slider value: 0.5 anchors.horizontalCenter: parent.horizontalCenter } Slider { orientation: Qt.Vertical value: 0.5 anchors.horizontalCenter: parent.horizontalCenter } } }
Sliderのデフォルト値や縦横の向きが設定可能である。
Slider { orientation: Qt.Vertical value: 0.5 from: 0.0 to: 1.0 }
Switch
コードは以下。
import QtQuick import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "Switch is an option button that can be dragged or toggled on or off. " + "Switches are typically used to select between two states." } Column { spacing: 20 anchors.horizontalCenter: parent.horizontalCenter Switch { text: "First" } Switch { text: "Second" checked: true } Switch { text: "Third" enabled: false } } } }
Switch部は下記。
Switch {
text: "Second"
enabled: true
}
TextField
コードは以下。
import QtQuick import QtQuick.Controls ScrollablePage { id: page Column { spacing: 40 width: parent.width Label { width: parent.width wrapMode: Label.Wrap horizontalAlignment: Qt.AlignHCenter text: "TextField is a single-line text editor." } TextField { id: field placeholderText: "TextField" anchors.horizontalCenter: parent.horizontalCenter } } }
TextField部は下記。
TextField { id: field placeholderText: "TextField" }
まとめ
今回は表示に関する内容のみにとどめた。実際には入力情報をもとに状態を変更する必要がある。 その方法については今後まとめて記載する予定。