프로그래밍 시작하기 Qt Quick
알람 애플리케이션을 기반으로 한 Qt Quick 자습서입니다.
이 튜토리얼에서는 Qt Quick 및 Qt Quick Controls 에 대한 소개로 간단한 알람 애플리케이션을 개발하는 방법을 보여줍니다.
이 애플리케이션은 일반적으로 안드로이드 휴대폰에서 볼 수 있는 알람 애플리케이션과 유사합니다. 이 애플리케이션의 기능을 사용하면 알람을 입력, 편집 또는 삭제할 수 있습니다. 알람은 지정된 날짜에 트리거될 수 있으며, 이후 일련의 날짜에 반복되도록 설정할 수 있습니다.
메인 화면에는 저장된 알람 목록이 표시됩니다:
상세 화면에서는 기존 알람을 편집하거나 삭제할 수 있습니다:
대화 화면은 새 알람을 추가하는 데 사용됩니다. 메인 화면 하단의 "+" RoundButton 를 클릭하면 대화상자가 나타납니다:
소스 파일은 qtdoc 리포지토리에 있습니다. Qt 프로젝트에서 Qt 소스를 가져오거나 Qt의 일부로 설치할 수 있습니다. 이 애플리케이션은 Qt Creator 의 시작 모드의 예제 목록에서도 사용할 수 있습니다.
알람 프로젝트 생성하기
이 섹션에서는 Qt Creator 에서 프로젝트를 생성하는 방법을 보여줍니다. Qt Creator 에서 자동으로 생성되는 파일과 프로그래머가 Qt Creator 또는 다른 편집기에서 생성해야 하는 두 개의 파일에 대해 설명합니다. 후자의 두 파일은 이 튜토리얼의 소스 코드에 포함되어 있습니다.
참고: Qt Creator 의 UI 텍스트와 생성된 파일의 내용은 사용하는 Qt Creator 버전에 따라 다릅니다.
Qt Creator
Qt Creator 에서 새 프로젝트를 설정하면 프로젝트 생성 과정을 단계별로 안내하는 마법사의 도움을 받을 수 있습니다. 마법사는 특정 유형의 프로젝트에 필요한 설정을 입력하라는 메시지를 표시하고 프로젝트를 만들어 줍니다.
알람 프로젝트를 만들려면 File > New Project > Application (Qt) > Qt Quick Application > Choose 을 선택합니다. Name 필드에 알람을 입력하고 마법사의 안내를 따릅니다.
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) } } } } }
대화 상자에서 확인을 클릭하면 입력한 데이터가 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 파일에는 alarmModel
, 즉 알람 데이터를 관리하는 ListModel 의 정의가 포함되어 있습니다.
예제 알람으로 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
텀블러 델리게이트는 텀블러의 그래픽 속성을 정의합니다.
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 지원도참조하세요 .
© 2025 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.