Qt Quick プログラミング入門:アラームアプリケーション
アラームアプリケーションを題材としたQt Quick のチュートリアル。
このチュートリアルでは、Qt Quick およびQt Quick コントロール入門として、簡単なアラームアプリケーションの開発方法を解説します。
このチュートリアルでは、アラームの登録、編集、削除を行うことができます。アラームは指定した日付に発動させることができ、連続する数日間にわたって繰り返し発動するように設定することも可能です。このアプリケーションは、Androidスマートフォンに通常搭載されているアラームアプリと類似しています。
例の動作確認
サンプルは以下の手順で実行できます。
- Qt Creator
Welcome モードを開き、Examples からこのサンプルを選択します。詳細については、Qt Creator の「チュートリアル:ビルドと実行」を参照してください。
- Qt Extension for Visual Studio Code
Command Palette から `Qt: Open Qt examples ` コマンドを実行し、リストからこのサンプルを選択します。詳細については、Qt Extension for Visual Studio Code の「チュートリアル:ビルドと実行」を参照してください。
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」と入力し、ウィザードの指示に従ってください。CMake 以外のビルドシステムを使用する場合、または Qt バージョン 6 より前のバージョンを使用する場合は、「Qt Quick Application (compact)」を選択してください。


「Qt Quick 」アプリケーションのウィザードを実行すると、以下のソースファイルを含むプロジェクトが作成されます:
| ソースファイル | 目的 |
|---|---|
| CMakeLists.txt | プロジェクトファイル |
| main.cpp | アプリケーションのメイン C++ コードファイル。 |
| Main.qml | アプリケーションのメインの QML コードファイルです。このファイル内で、カスタム QML タイプ(AlarmDialog 、AlarmModel 、AlarmDelegate 、およびTumblerDelegate )をインスタンス化します。 |
ウィザードにより、以下の main.cpp ファイルにコードが生成されます。このコードブロックは、高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 | 「Material 」スタイルと「Dark 」テーマを選択します。 |
AlarmDialog.qml | 新しいアラームを追加するためのダイアログを定義します。 |
AlarmDelegate.qml | アプリのメイン画面のレイアウトを定義します。 |
AlarmModel.qml | アラームデータの保存に使用するListModel を定義します。 |
TumblerDelegate.qml | タンブラーのグラフィックレイアウトを定義します。 |
qml.qrc | main.cpp およびプロジェクトファイルを除く、ソースファイルの名前が記載されたリソースファイル。 |
qtquickcontrols2.conf
以下のスニペットは、Material スタイルでDark のテーマを設定する方法を示しています。
[Controls]
Style=Material
[Material]
Theme=Dark
Accent=RedMain.qml
mainWindow ApplicationWindow のQML型である` `は、このアプリのルートアイテムです。
ApplicationWindow {
id: window
width: 400
height: 500
visible: trueListView の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
このダイアログ画面には、時間と分それぞれに「Tumbler 」が設定された「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
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 (let 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 の定義が含まれています。
このファイルでは、サンプルアラームを含む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
required property int index
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
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 (let 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 サポート、すべての Qt サンプル、およびQt Quick のサンプルとチュートリアル。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.