Empezando a programar con Qt Quick: Una aplicación de alarmas
Un tutorial para Qt Quick basado en una aplicación de alarmas.
Este tutorial muestra cómo desarrollar una sencilla aplicación de alarmas como introducción a Qt Quick y Qt Quick Controls.
En este tutorial puedes introducir, editar o borrar alarmas. Una alarma puede dispararse en una fecha determinada, y puedes configurarla para que se repita en una serie de días posteriores. Esta aplicación es similar a la aplicación de alarma que suele encontrarse en un teléfono Android.
Ejecutar el ejemplo
Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.
Creación del proyecto de alarmas
Esta sección muestra cómo crear el proyecto en Qt Creator. Discute los archivos generados automáticamente por Qt Creator, y los dos archivos que el programador tiene que crear en Qt Creator o algún otro editor. Estos dos últimos archivos se incluyen con el código fuente de este tutorial.
Nota: El texto de la interfaz de usuario en Qt Creator y el contenido de los archivos generados dependen de la versión de Qt Creator que utilice.
Qt Creator
La configuración de un nuevo proyecto en Qt Creator está asistida por un asistente que le guía paso a paso a través del proceso de creación del proyecto. El asistente le pedirá que introduzca la configuración necesaria para ese tipo concreto de proyecto y creará el proyecto por usted.
Para crear el proyecto Alarmas, seleccione File > New Project > Application (Qt) > Qt Quick Application > Choose. Escriba alarmas en el campo Name y siga las instrucciones del asistente. Utilice Qt Quick Application (compact) si desea utilizar otros sistemas de compilación distintos de CMake o versiones de Qt inferiores a la 6.


El asistente de la aplicación Qt Quick crea un proyecto que contiene los siguientes archivos fuente:
| Archivo fuente | Propósito |
|---|---|
| CMakeLists.txt | El archivo del proyecto |
| main.cpp | El archivo de código C++ principal de la aplicación. |
| main.qml | El archivo de código QML principal de la aplicación. En este archivo instanciaremos nuestros tipos QML personalizados (AlarmDialog, AlarmModel, AlarmDelegate, y TumblerDelegate). |
El asistente genera el código en el siguiente archivo main.cpp. Este bloque de código habilita el escalado High DPI y declara app y engine. A continuación, el motor carga nuestro archivo QML principal.
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
Archivos fuente adicionales
| Archivo fuente | Propósito |
|---|---|
qtquickcontrols2.conf | Selecciona el estilo Material con el tema Dark. |
AlarmDialog.qml | Define el diálogo para añadir nuevas alarmas. |
AlarmDelegate.qml | Define el diseño de la pantalla principal de la aplicación. |
AlarmModel.qml | Define el ListModel utilizado para almacenar los datos de las alarmas. |
TumblerDelegate.qml | Define el diseño gráfico de los Tumblers. |
qml.qrc | El archivo de recursos, que contiene los nombres de los archivos fuente, excepto main.cpp y el archivo de proyecto. |
qtquickcontrols2.conf
El siguiente fragmento muestra cómo configurar el tema Dark en el estilo Material:
[Controls] Style=Material [Material] Theme=Dark Accent=Red
Main.qml
mainWindow, un tipo QML ApplicationWindow, es el elemento raíz de esta aplicación.
ApplicationWindow { id: window width: 400 height: 500 visible: true
El ListView alarmListView combina los datos de alarmModel con el diseño definido en alarmDelegate.
ListView { id: alarmListView anchors.fill: parent model: AlarmModel {} delegate: AlarmDelegate {} }
Se pueden añadir nuevas alarmas haciendo clic en RoundButton addAlarmButton . Al hacer clic se abre una pantalla 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
Esta pantalla de diálogo tiene un RowLayout con un Tumbler cada uno para horas y minutos, y otro RowLayout con un Tumbler cada uno para día, mes y año.
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) } } } } }
Si pulsa OK en el diálogo, los datos introducidos se añadirán a 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
Cada alarma de la pantalla principal es un ItemDelegate. El ItemDelegate root contiene todos los campos de la pantalla principal y de la pantalla detallada. Los campos de la pantalla detallada sólo son visibles después de hacer clic en una alarma, es decir, cuando root.checked es 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
Este archivo QML contiene la definición de alarmModel, el ListModel que gestiona los datos de las alarmas.
Crea cinco ListElements con ejemplos de alarmas.
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 define las propiedades gráficas de los Tumblers.
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 }
Introducción de nuevas alarmas
En la parte inferior de la pantalla de inicio, puede ver un Botón para añadir alarmas. Púlselo para abrir el diálogo Añadir nueva alarma.
RoundButton { id: addAlarmButton text: "+" anchors.bottom: alarmListView.bottom anchors.bottomMargin: 8 anchors.horizontalCenter: parent.horizontalCenter onClicked: alarmDialog.open() }
El diálogo para nuevas alarmas:

Todos los campos se introducen utilizando los tipos QML de Tumbler. Si pulsa OK, los valores seleccionados en los Tumblers se escriben en 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) } } } } }
Edición de alarmas
Si hace clic en una alarma concreta, puede editarla en la pantalla de detalles.

Al hacer clic en una alarma, se activa root.checked en true, lo que hace visibles los campos de la pantalla detallada.
visible: root.checkedSi desea que la alarma se active también otros días, marque alarmRepeat. El repetidor mostrará un RoundButton comprobable para cada día de la semana.
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 } } }
Si modifica la descripción de la alarma, se reflejará posteriormente en la pantalla principal.
TextField { id: alarmDescriptionTextField placeholderText: qsTr("Enter description here") cursorVisible: true visible: root.checked text: root.label onTextEdited: root.label = text }
Borrar alarmas
La pantalla detallada (véase más arriba) dispone de un botón para borrar alarmas. Cuando se emite onClicked, la actual ListElement se borra de alarmModel.
Button { id: deleteAlarmButton text: qsTr("Delete") visible: root.checked onClicked: root.ListView.view.model.remove(root.ListView.view.currentIndex, 1) }
Pasos siguientes
La aplicación no tiene código para añadir sonido o vibración a la alarma, ni almacena las alarmas en ningún formato o base de datos. Ponte a prueba añadiendo estas funciones al proyecto. El almacenamiento de los datos podría hacerse en formato JSON.
Archivos fuente
Ver también Soporte JSON en Qt, Todos los ejemplos de Qt, y Qt Quick Ejemplos y Tutoriales.
© 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.