Calqlatr
Una calculadora diseñada para dispositivos apaisados y verticales. Utiliza componentes personalizados de Qt Quick y diseños adaptables para la interfaz de usuario, y JavaScript para la lógica de la aplicación.

Calqlatr muestra varias funciones de QML y Qt Quick como la visualización de componentes personalizados y el uso de diseños adaptables. La lógica de la aplicación se implementa en JavaScript y la interfaz de usuario en QML.
Ejecución del 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. Después de ejecutar el ejemplo, deberías poder utilizar la aplicación como una calculadora estándar. Cambia el teléfono de modo vertical a horizontal -o, en el escritorio, cambia el tamaño de la ventana principal- para activar el diseño responsivo.
Uso de componentes personalizados
La aplicación Calqlatr utiliza tipos personalizados. La mayoría de ellos se definen en su propio archivo .qml:
Main.qmlcontent/ApplicationState.qmlcontent/BackspaceButton.qmlcontent/CalculatorButton.qmlcontent/Display.qmlcontent/NumberPad.qml
Main.qml contiene la ventana de nivel superior y el elemento raíz. Hace uso de otros tipos personalizados en el directorio content. Por ejemplo, el tipo NumberPad (definido por content/NumberPad.qml) se utiliza en Main.qml para crear el teclado numérico de la calculadora:
NumberPad { id: numberPad Layout.margins: root.margin isPortraitMode: root.isPortraitMode applicationState: state }
Los componentesen línea le permiten declarar múltiples componentes dentro de un archivo .qml. El ejemplo utiliza componentes en línea en NumberPad.qml para definir dos componentes, DigitButton y OperatorButton:
component DigitButton: CalculatorButton { onClicked: { controller.applicationState.digitPressed(text); controller.updateDimmed(); } } component OperatorButton: CalculatorButton { dimmable: true implicitWidth: 48 textColor: controller.qtGreenColor onClicked: { controller.applicationState.operatorPressed(text); controller.updateDimmed(); } }
Ambos componentes son del tipo CalculatorButton (tal y como se definen en CalculatorButton.qml), pero proporcionan un controlador personalizado para la señal de clic y modifican algunas propiedades predeterminadas. DigitButton y OperatorButton se instancian posteriormente en NumberPad.qml:
DigitButton { text: "e" dimmable: true implicitWidth: 48 } OperatorButton { text: "ln" }
Para más información sobre la definición de componentes QML personalizados, consulte Definición de tipos de objeto mediante documentos QML.
Diseños adaptables
En este ejemplo, los diseños adaptables organizan los diferentes componentes de la interfaz de usuario para los modos vertical y horizontal. Esto se puede observar en Main.qml, que define un ColumnLayout para el modo vertical y un RowLayout para el horizontal:
ColumnLayout { id: portraitMode anchors.fill: parent visible: root.isPortraitMode LayoutItemProxy { target: display Layout.minimumHeight: display.minHeight } LayoutItemProxy { target: numberPad Layout.alignment: Qt.AlignHCenter } } RowLayout { id: landscapeMode anchors.fill: parent visible: !root.isPortraitMode LayoutItemProxy { target: display } LayoutItemProxy { target: numberPad Layout.alignment: Qt.AlignVCenter } }
ColumnLayout representa el diseño vertical de la aplicación y RowLayout representa el diseño horizontal. Las propiedades visible gestionan qué diseño se utiliza en cada momento. El atributo id de los componentes NumberPad y Display se utiliza para establecer la propiedad target de los tipos LayoutItemProxy. Esto permite que ambas maquetaciones utilicen los mismos elementos de contenido. Además, las propiedades pueden reenviarse dentro del elemento LayoutItemProxy al propio target. Por ejemplo, cuando se instancie NumberPad, ambos diseños necesitarán un Layout.alignment diferente.
En NumberPad.qml también se utiliza un diseño adaptable cuando se define el diseño vertical y horizontal para el propio NumberPad:
RowLayout { spacing: controller.spacing GridLayout { id: scientificGrid columns: 3 columnSpacing: controller.spacing rowSpacing: controller.spacing visible: !controller.isPortraitMode OperatorButton { text: "x²" Accessible.name: "x squared" } OperatorButton { text: "⅟x" Accessible.name: "one over x" } OperatorButton { text: "√" } OperatorButton { text: "x³" Accessible.name: "x cubed" } OperatorButton { text: "sin" Accessible.name: "sine" } OperatorButton { text: "|x|" Accessible.name: "absolute value" } OperatorButton { text: "log" } OperatorButton { text: "cos" Accessible.name: "cosine" } DigitButton { text: "e" dimmable: true implicitWidth: 48 } OperatorButton { text: "ln" } OperatorButton { text: "tan" } DigitButton { text: "π" dimmable: true implicitWidth: 48 } } GridLayout { id: mainGrid columns: 5 columnSpacing: controller.spacing rowSpacing: controller.spacing BackspaceButton { onClicked: { controller.applicationState.operatorPressed(this.text); controller.updateDimmed(); } } DigitButton { text: "7" } DigitButton { text: "8" } DigitButton { text: "9" } OperatorButton { text: "÷" implicitWidth: 38 } OperatorButton { text: "AC" textColor: controller.backspaceRedColor accentColor: controller.backspaceRedColor } DigitButton { text: "4" } DigitButton { text: "5" } DigitButton { text: "6" } OperatorButton { text: "×" implicitWidth: 38 } OperatorButton { text: "=" implicitHeight: 81 Layout.rowSpan: 2 } DigitButton { text: "1" } DigitButton { text: "2" } DigitButton { text: "3" } OperatorButton { text: "−" implicitWidth: 38 } OperatorButton { text: "±" implicitWidth: 38 } DigitButton { text: "0" } DigitButton { text: "." dimmable: true } OperatorButton { text: "+" implicitWidth: 38 } } } // RowLayout
Dependiendo de controller.isPortraitMode, el teclado numérico contiene sólo los botones de una calculadora simple (mainGrid), o también algunas funcionalidades más avanzadas a través de scientificGrid.
Animación de los colores del texto de los botones
En el archivo CalculatorButton.qml, también se animan los colores del texto de los botones del teclado numérico.
...
color: button.getTextColor()
Behavior on color {
ColorAnimation {
duration: 120
easing.type: Easing.OutElastic
}
}
}Los cambios de color se animan definiendo un Behavior en la propiedad color. Cuando un botón está definido en dimmed = true, el botón aparece más oscuro. Cuando se pulsa un botón, se ilumina en verde. Para cambiar dinámicamente la propiedad dimmed de todos los botones de NumberPad, la señal buttonPressed llama a la función NumberPad's updateDimmed().
function updateDimmed() { for (let i = 0; i < mainGrid.children.length; i++) { mainGrid.children[i].dimmed = applicationState.isButtonDisabled(mainGrid.children[i].text); } for (let j = 0; j < scientificGrid.children.length; j++) { scientificGrid.children[j].dimmed = applicationState.isButtonDisabled( scientificGrid.children[j].text); } }
Realizar cálculos
Los archivos calculator.js y ApplicationState.qml definen el motor de la calculadora. calculator.js contiene el estado lógico de la calculadora, así como las operaciones para cambiar el estado. ApplicationState.qml expone esta API a través de un tipo QML.
Veamos primero calculator.js:
let accumulator = 0 let pendingOperator = "" let lastButton = "" let digits = ""
accumulator, pendingOperator, lastButton, represent el estado lógico de la calculadora.
function isOperationDisabled(op, display) {
...
}isOperationDisabled() devuelve true si una operación op debe ser desactivada, basándose en el estado actual del motor y de la pantalla. En caso contrario, devuelve false.
function digitPressed(op, display) {
...
}
function operatorPressed(op, display) {
...
}digitPressed() y operatorPressed() actualizan el estado del motor y de la pantalla en función de los botones pulsados.
ApplicationState.qml está exponiendo las funciones JavaScript definidas en calculator.js detrás de una API QML más agradable:
import QtQml import "calculator.js" as CalcEngine
calculator.js se importa con el nombre CalcEngine.
QtObject { required property Display display function operatorPressed(operator) { CalcEngine.operatorPressed(operator, display); } function digitPressed(digit) { CalcEngine.digitPressed(digit, display); } function isButtonDisabled(op) { return CalcEngine.isOperationDisabled(op, display); } }
required property se asegura de que una instancia de Display.qml esté siempre disponible en ApplicationState bajo el nombre display. display se utiliza a su vez en las funciones operatorPressed(), digitPressed() y isButtonDisabled().
Archivos fuente
Pruebas de Squish GUI
Esta aplicación viene con tests GUI de Squish orientados a Qt para Android. Las pruebas se realizan con Squish para Qt y están escritas en Python y se pueden encontrar en el directorio de pruebas de la aplicación.
Cuando utilice Squish para Qt para Android, asegúrese de utilizar el hook incorporado de Qt y reenviar el puerto de Squish con adb. Si recibes el error "Cannot load library" con dlopen fallando, desactiva QT_USE_TARGET_ANDROID_BUILD_DIR de la configuración CMake, o bórralo de Projects->Build Settings->CMake->Current Configuration en Qt Creator.
Si no tienes una licencia de Squish, puedes obtener una prueba gratuita.
Ver también Aplicaciones QML.
© 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.