Qt Quick でプログラミングを始めよう
アラームアプリケーションに基づいた Qt Quick のチュートリアルです。
このチュートリアルでは、Qt Quick と Qt Quick Controls の入門編として、簡単なアラームアプリケーションを開発する方法を説明します。
このアプリケーションは、Android携帯によくあるアラームアプリケーションに似ています。アラームを入力、編集、削除することができます。アラームは指定した日付に作動させることができ、それ以降の日に繰り返し作動させるように設定できます。
メイン画面には保存されたアラームのリストが表示されます:
詳細画面では、既存のアラームを編集または削除できます:
ダイアログ画面は新規アラームの追加に使用します。メイン画面の下部にある "+"RoundButton をクリックするとポップアップ表示されます:
ソースファイルは qtdoc リポジトリにあります。Qt プロジェクトから Qt ソースを取得するか、Qt の一部としてインストールすることができます。このアプリケーションは Qt Creator の Welcome モードのサンプルリストにもあります。
Alarms プロジェクトの作成
このセクションでは、Qt Creatorでプロジェクトを作成する方法を説明します。Qt Creatorが自動的に生成するファイルと、プログラマがQt Creatorや他のエディタで作成する2つのファイルについて説明します。後者の2つのファイルは、このチュートリアルのソースコードに含まれています。
注意: Qt Creator の UI テキストと生成されるファイルの内容は、使用する Qt Creator のバージョンによって異なります。
Qt Creatorについて
Qt Creator で新しいプロジェクトをセットアップするには、プロジェクト作成プロセスをステップバイステップでガイドするウィザードが役立ちます。ウィザードは、特定のタイプのプロジェクトに必要な設定を入力するよう促し、プロジェクトを作成します。
Alarmsプロジェクトを作成するには、File >New Project >Application (Qt) >Qt Quick Application >Choose を選択します。Name フィールドにalarmsと入力し、ウィザードの指示に従います。
Qt Quick アプリケーション・ウィザードは、以下のソース・ファイルを含むプロジェク トを作成します:
ソース・ファイル | 目的 |
---|---|
CMakeLists.txt | プロジェクト・ファイル |
main.cpp | アプリケーションのメインC++コードファイル。 |
main.qml | アプリケーションのメインQMLコードファイル。このファイルでカスタムQMLタイプ(AlarmDialog ,AlarmModel ,AlarmDelegate ,TumblerDelegate )をインスタンス化します。 |
ウィザードは以下のmain.cppファイルにコードを生成します。このコードブロックでは、High DPI スケーリングを有効にし、app
とengine
を宣言します。その後、エンジンはメインの QML ファイルをロードします。
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
追加のソース ファイル
ソース ファイル | 目的 |
---|---|
qtquickcontrols2.conf | Dark テーマでMaterial スタイルを選択します。 |
AlarmDialog.qml | 新しいアラームを追加するためのダイアログを定義します。 |
AlarmDelegate.qml | アプリのメイン画面のレイアウトを定義します。 |
AlarmModel.qml | アラームデータの保存に使用するListModel を定義します。 |
TumblerDelegate.qml | タンブラーのグラフィカルレイアウトを定義します。 |
qml.qrc | リソースファイル。main.cppとプロジェクトファイル以外のソースファイル名が含まれています。 |
qtquickcontrols2.conf
次のスニペットは、Material
スタイルでDark
テーマを設定する方法を示しています:
[Controls] Style=Material [Material] Theme=Dark Accent=Red
Main.qml
mainWindow
このアプリのルートアイテムは、ApplicationWindow QMLタイプです。
ApplicationWindow { id: window width: 400 height: 500 visible: true
ListView alarmListView
はalarmModel
のデータとalarmDelegate
で定義されたレイアウトを結合します。
ListView { id: alarmListView anchors.fill: parent model: AlarmModel {} delegate: AlarmDelegate {} }
新しいアラームはRoundButton addAlarmButton
をクリックして追加できます。クリックするとDialog 画面alarmDialog
が開きます。
RoundButton { id: addAlarmButton text: "+" anchors.bottom: alarmListView.bottom anchors.bottomMargin: 8 anchors.horizontalCenter: parent.horizontalCenter onClicked: alarmDialog.open() } AlarmDialog { id: alarmDialog x: Math.round((parent.width - width) / 2) y: Math.round((parent.height - height) / 2) alarmModel: alarmListView.model }
AlarmDialog.qml
このダイアログ画面には、RowLayout に時、分を表すTumbler がそれぞれあり、RowLayout に日、月、年を表すタンブラーがそれぞれあります。
contentItem: RowLayout { RowLayout { id: rowTumbler Tumbler { id: hoursTumbler model: 24 delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } Tumbler { id: minutesTumbler model: 60 delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } } RowLayout { id: datePicker Layout.leftMargin: 20 property alias dayTumbler: dayTumbler property alias monthTumbler: monthTumbler property alias yearTumbler: yearTumbler readonly property var days: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] Tumbler { id: dayTumbler function updateModel() { // Populate the model with days of the month. For example: [0, ..., 30] var previousIndex = dayTumbler.currentIndex var array = [] var newDays = datePicker.days[monthTumbler.currentIndex] for (var i = 1; i <= newDays; ++i) array.push(i) dayTumbler.model = array dayTumbler.currentIndex = Math.min(newDays - 1, previousIndex) } Component.onCompleted: updateModel() delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } Tumbler { id: monthTumbler onCurrentIndexChanged: dayTumbler.updateModel() model: 12 delegate: TumblerDelegate { text: alarmDialog.locale.standaloneMonthName(modelData, Locale.ShortFormat) } } Tumbler { id: yearTumbler // This array is populated with the next three years. For example: [2018, 2019, 2020] readonly property var years: (function() { var currentYear = new Date().getFullYear() return [0, 1, 2].map(function(value) { return value + currentYear; }) })() model: years delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } } } }
ダイアログでOKをクリックすると、入力されたデータがalarmModel
に追加されます:
onAccepted: { alarmModel.append({ "hour": hoursTumbler.currentIndex, "minute": minutesTumbler.currentIndex, "day": dayTumbler.currentIndex + 1, "month": monthTumbler.currentIndex + 1, "year": yearTumbler.years[yearTumbler.currentIndex], "activated": true, "label": "", "repeat": false, "daysToRepeat": [ { "dayOfWeek": 0, "repeat": false }, { "dayOfWeek": 1, "repeat": false }, { "dayOfWeek": 2, "repeat": false }, { "dayOfWeek": 3, "repeat": false }, { "dayOfWeek": 4, "repeat": false }, { "dayOfWeek": 5, "repeat": false }, { "dayOfWeek": 6, "repeat": false } ], }) } onRejected: alarmDialog.close()
AlarmDelegate.qml
メイン画面の各アラームはItemDelegate である。ItemDelegate root
にはメイン画面と詳細画面のすべてのフィールドが含まれる。詳細画面のフィールドは、アラームがクリックされた後、つまりroot.checked
がtrue
の場合にのみ表示されます。
ItemDelegate { id: root width: parent.width checkable: true required property int index required property int hour required property int minute required property int day required property int month required property int year required property bool activated required property string label required property bool repeat required property list<var> daysToRepeat onClicked: ListView.view.currentIndex = index contentItem: ColumnLayout { spacing: 0 RowLayout { ColumnLayout { id: dateColumn readonly property date alarmDate: new Date( root.year, root.month - 1, root.day, root.hour, root.minute) Label { id: timeLabel font.pixelSize: (Qt.application as Application).font.pixelSize * 2 text: dateColumn.alarmDate.toLocaleTimeString(root.locale, Locale.ShortFormat) } RowLayout { Label { id: dateLabel text: dateColumn.alarmDate.toLocaleDateString(root.locale, Locale.ShortFormat) } Label { id: alarmAbout text: "⸱ " + root.label visible: root.label.length > 0 && !root.checked } } } Item { Layout.fillWidth: true } Switch { checked: root.activated Layout.alignment: Qt.AlignTop onClicked: root.activated = checked } } CheckBox { id: alarmRepeat text: qsTr("Repeat") checked: root.repeat visible: root.checked onToggled: root.repeat = checked } Flow { visible: root.checked && root.repeat Layout.fillWidth: true Repeater { id: dayRepeater model: root.daysToRepeat delegate: RoundButton { required property int dayOfWeek required property bool repeat text: Qt.locale().dayName(dayOfWeek, Locale.NarrowFormat) flat: true checked: repeat checkable: true Material.background: checked ? Material.accent : "transparent" onToggled: repeat = checked } } } TextField { id: alarmDescriptionTextField placeholderText: qsTr("Enter description here") cursorVisible: true visible: root.checked text: root.label onTextEdited: root.label = text } Button { id: deleteAlarmButton text: qsTr("Delete") visible: root.checked onClicked: root.ListView.view.model.remove(root.ListView.view.currentIndex, 1) } } }
AlarmModel.qml
この QML ファイルには、アラームデータを管理するListModel alarmModel
の定義が含まれています。
この QML ファイルには、アラームの例を含む 5 つのListElements が作成されています。
import QtQuick // Populate the model with some sample data. ListModel { id: alarmModel ListElement { hour: 6 minute: 0 day: 2 month: 8 year: 2018 activated: true label: "Wake up" repeat: true daysToRepeat: [ ListElement { dayOfWeek: 0; repeat: false }, ListElement { dayOfWeek: 1; repeat: false }, ListElement { dayOfWeek: 2; repeat: false }, ListElement { dayOfWeek: 3; repeat: false }, ListElement { dayOfWeek: 4; repeat: false }, ListElement { dayOfWeek: 5; repeat: false }, ListElement { dayOfWeek: 6; repeat: false } ] } ListElement { hour: 6 minute: 0 day: 3 month: 8 year: 2018 activated: true label: "Wake up" repeat: true daysToRepeat: [ ListElement { dayOfWeek: 0; repeat: true }, ListElement { dayOfWeek: 1; repeat: true }, ListElement { dayOfWeek: 2; repeat: true }, ListElement { dayOfWeek: 3; repeat: true }, ListElement { dayOfWeek: 4; repeat: true }, ListElement { dayOfWeek: 5; repeat: false }, ListElement { dayOfWeek: 6; repeat: false } ] } ListElement { hour: 7 minute: 0 day: 3 month: 8 year: 2018 activated: false label: "Exercise" repeat: true daysToRepeat: [ ListElement { dayOfWeek: 0; repeat: true }, ListElement { dayOfWeek: 1; repeat: true }, ListElement { dayOfWeek: 2; repeat: true }, ListElement { dayOfWeek: 3; repeat: true }, ListElement { dayOfWeek: 4; repeat: true }, ListElement { dayOfWeek: 5; repeat: true }, ListElement { dayOfWeek: 6; repeat: true } ] } ListElement { hour: 5 minute: 15 day: 1 month: 9 year: 2018 activated: true label: "" repeat: false daysToRepeat: [ ListElement { dayOfWeek: 0; repeat: false }, ListElement { dayOfWeek: 1; repeat: false }, ListElement { dayOfWeek: 2; repeat: false }, ListElement { dayOfWeek: 3; repeat: false }, ListElement { dayOfWeek: 4; repeat: false }, ListElement { dayOfWeek: 5; repeat: false }, ListElement { dayOfWeek: 6; repeat: false } ] } ListElement { hour: 5 minute: 45 day: 3 month: 9 year: 2018 activated: false label: "" repeat: false daysToRepeat: [ ListElement { dayOfWeek: 0; repeat: false }, ListElement { dayOfWeek: 1; repeat: false }, ListElement { dayOfWeek: 2; repeat: false }, ListElement { dayOfWeek: 3; repeat: false }, ListElement { dayOfWeek: 4; repeat: false }, ListElement { dayOfWeek: 5; repeat: false }, ListElement { dayOfWeek: 6; repeat: false } ] } }
TumblerDelegate.qml
TumblerDelegate はタンブラーのグラフィカルプロパティを定義します。
import QtQuick import QtQuick.Controls import QtQuick.Controls.Material Text { required property int modelData text: modelData color: Tumbler.tumbler.Material.foreground font: Tumbler.tumbler.font opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2) horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter }
新しいアラームの入力
起動画面の下部に、アラームを追加するためのボタンがあります。これをクリックして新規アラーム追加ダイアログを開きます。
RoundButton { id: addAlarmButton text: "+" anchors.bottom: alarmListView.bottom anchors.bottomMargin: 8 anchors.horizontalCenter: parent.horizontalCenter onClicked: alarmDialog.open() }
新規アラームのダイアログ:
すべてのフィールドはTumbler QML タイプを使用して入力されます。OK
を押すと、タンブラーで選択された値がalarmModel
に書き込まれます。
contentItem: RowLayout { RowLayout { id: rowTumbler Tumbler { id: hoursTumbler model: 24 delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } Tumbler { id: minutesTumbler model: 60 delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } } RowLayout { id: datePicker Layout.leftMargin: 20 property alias dayTumbler: dayTumbler property alias monthTumbler: monthTumbler property alias yearTumbler: yearTumbler readonly property var days: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] Tumbler { id: dayTumbler function updateModel() { // Populate the model with days of the month. For example: [0, ..., 30] var previousIndex = dayTumbler.currentIndex var array = [] var newDays = datePicker.days[monthTumbler.currentIndex] for (var i = 1; i <= newDays; ++i) array.push(i) dayTumbler.model = array dayTumbler.currentIndex = Math.min(newDays - 1, previousIndex) } Component.onCompleted: updateModel() delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } Tumbler { id: monthTumbler onCurrentIndexChanged: dayTumbler.updateModel() model: 12 delegate: TumblerDelegate { text: alarmDialog.locale.standaloneMonthName(modelData, Locale.ShortFormat) } } Tumbler { id: yearTumbler // This array is populated with the next three years. For example: [2018, 2019, 2020] readonly property var years: (function() { var currentYear = new Date().getFullYear() return [0, 1, 2].map(function(value) { return value + currentYear; }) })() model: years delegate: TumblerDelegate { text: alarmDialog.formatNumber(modelData) } } } } }
アラームの編集
特定のアラームをクリックすると、詳細画面でそのアラームを編集できます。
アラームをクリックすると、root.checked
がtrue
に設定され、詳細画面のフィールドが表示されます。
visible: root.checked
アラームを他の日にも作動させたい場合は、alarmRepeat
をチェックします。リピーターは曜日ごとにチェック可能なRoundButton を表示します。
Flow { visible: root.checked && root.repeat Layout.fillWidth: true Repeater { id: dayRepeater model: root.daysToRepeat delegate: RoundButton { required property int dayOfWeek required property bool repeat text: Qt.locale().dayName(dayOfWeek, Locale.NarrowFormat) flat: true checked: repeat checkable: true Material.background: checked ? Material.accent : "transparent" onToggled: repeat = checked } } }
アラームの説明を変更すると、メイン画面に反映されます。
TextField { id: alarmDescriptionTextField placeholderText: qsTr("Enter description here") cursorVisible: true visible: root.checked text: root.label onTextEdited: root.label = text }
アラームの削除
詳細画面(上記参照)にはアラームを削除するためのボタンがあります。onClicked
が発信されると、現在のListElement がalarmModel
から削除されます。
Button { id: deleteAlarmButton text: qsTr("Delete") visible: root.checked onClicked: root.ListView.view.model.remove(root.ListView.view.currentIndex, 1) }
概要
このアプリには、アラームにサウンドやバイブレーションを追加するコードはありません。これらの機能を追加するのは、面白いコーディング・プロジェクトになるかもしれない。データの保存は、JSONフォーマットで素早く簡単にできるだろう。
Qt の JSON サポートも参照してください 。
©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。