Calqlatr
Une calculatrice conçue pour les appareils en mode paysage et portrait. Elle utilise Qt Quick des composants personnalisés et des mises en page réactives pour l'interface utilisateur, et JavaScript pour la logique de l'application.

Calqlatr fait la démonstration de diverses fonctionnalités QML et Qt Quick telles que l'affichage de composants personnalisés et l'utilisation de mises en page réactives. La logique d'application est mise en œuvre en JavaScript et l'interface utilisateur en QML.
Exécution de l'exemple
Pour exécuter l'exemple à partir de Qt CreatorOuvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter. Après avoir exécuté l'exemple, vous devriez pouvoir utiliser l'application comme une calculatrice standard. Faites passer le téléphone du mode portrait au mode paysage - ou, sur le bureau, redimensionnez la fenêtre principale - pour activer la mise en page réactive.
Utilisation de composants personnalisés
L'application Calqlatr utilise des types personnalisés. La plupart d'entre eux sont définis dans leur propre fichier .qml :
Main.qmlcontent/ApplicationState.qmlcontent/BackspaceButton.qmlcontent/CalculatorButton.qmlcontent/Display.qmlcontent/NumberPad.qml
Main.qml contient la fenêtre de premier niveau et l'élément racine. Elle utilise les autres types personnalisés du répertoire content. Par exemple, le type NumberPad (défini par content/NumberPad.qml) est utilisé dans Main.qml pour créer le pavé numérique de la calculatrice :
NumberPad { id: numberPad Layout.margins: root.margin isPortraitMode: root.isPortraitMode applicationState: state }
Lescomposants en ligne vous permettent de déclarer plusieurs composants à l'intérieur d'un fichier .qml. L'exemple utilise des composants en ligne dans NumberPad.qml pour définir deux composants, DigitButton et 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(); } }
Les deux composants sont de type CalculatorButton (comme défini dans CalculatorButton.qml), mais fournissent un gestionnaire personnalisé pour le signal "clicked" et modifient certaines propriétés par défaut. DigitButton et OperatorButton sont ensuite instanciés dans NumberPad.qml:
DigitButton { text: "e" dimmable: true implicitWidth: 48 } OperatorButton { text: "ln" }
Pour plus de détails sur la définition de composants QML personnalisés, consultez la section Définir des types d'objets à l'aide de documents QML.
Mises en page réactives
Dans cet exemple, les mises en page réactives organisent les différents composants de l'interface utilisateur pour les modes portrait et paysage. Vous pouvez le constater dans Main.qml, qui définit un ColumnLayout pour le mode portrait et un RowLayout pour le mode paysage :
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 } }
Le ColumnLayout représente la disposition en mode portrait pour l'application, et le RowLayout représente la disposition en mode paysage. Les propriétés visible gèrent la disposition utilisée à un moment donné. L'attribut id des composants NumberPad et Display est utilisé pour définir la propriété target des types LayoutItemProxy. Cela permet aux deux présentations d'utiliser les mêmes éléments de contenu. En outre, les propriétés peuvent être transférées de l'élément LayoutItemProxy à l'élément target lui-même. Par exemple, lorsque l'élément NumberPad est instancié, les deux présentations requièrent un élément Layout.alignment différent.
Une mise en page réactive est également utilisée dans NumberPad.qml lors de la définition de la mise en page portrait et paysage pour le site NumberPad lui-même :
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
En fonction de controller.isPortraitMode, le pavé numérique ne contient que les boutons d'une simple calculatrice (mainGrid), ou également des fonctionnalités plus avancées via scientificGrid.
Animation des couleurs du texte des boutons
Dans le fichier CalculatorButton.qml, les couleurs du texte des boutons du pavé numérique sont également animées.
...
color: button.getTextColor()
Behavior on color {
ColorAnimation {
duration: 120
easing.type: Easing.OutElastic
}
}
}Les changements de couleur sont animés en définissant une propriété Behavior sur color. Lorsqu'un bouton est défini sur dimmed = true, il apparaît plus sombre. Lorsqu'un bouton est pressé, il s'allume en vert. Pour modifier dynamiquement la propriété dimmed de tous les boutons du site NumberPad, le signal buttonPressed appelle la fonction updateDimmed() du site NumberPad.
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); } }
Effectuer des calculs
Les fichiers calculator.js et ApplicationState.qml définissent le moteur de la calculatrice. calculator.js contient l'état logique de la calculatrice, ainsi que les opérations permettant de modifier cet état. ApplicationState.qml expose cette API par le biais d'un type QML.
Jetons d'abord un coup d'œil à calculator.js:
let accumulator = 0 let pendingOperator = "" let lastButton = "" let digits = ""
accumulator, pendingOperator, lastButton, represent l'état logique de la calculatrice.
function isOperationDisabled(op, display) {
...
}isOperationDisabled() renvoie true si une opération op doit être désactivée, en fonction de l'état actuel du moteur et de l'affichage. Sinon, il renvoie false.
function digitPressed(op, display) {
...
}
function operatorPressed(op, display) {
...
}digitPressed() et operatorPressed() mettent à jour l'état du moteur et de l'affichage en fonction des boutons enfoncés.
ApplicationState.qml expose les fonctions JavaScript définies dans calculator.js derrière une API QML plus agréable :
import QtQml import "calculator.js" as CalcEngine
calculator.js est importé sous le nom 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 s'assure qu'une instance de Display.qml est toujours disponible dans ApplicationState sous le nom display. display est à son tour utilisé dans les fonctions operatorPressed(), digitPressed() et isButtonDisabled().
Fichiers sources
Test de l'interface graphique Squish
Cette application est livrée avec des tests Squish GUI destinés à Qt Test pour Android. Les tests sont réalisés avec Squish for Qt et sont écrits en Python. Ils se trouvent dans le répertoire de test de l'application.
Lorsque vous utilisez Squish pour Qt pour Android, assurez-vous d'utiliser le hook intégré à Qt et de transférer le port Squish avec adb. Si vous obtenez l'erreur "Cannot load library" lorsque dlopen échoue, désactivez QT_USE_TARGET_ANDROID_BUILD_DIR dans la configuration CMake, ou effacez-la de Projects->Build Settings->CMake->Current Configuration dans Qt Creator.
Si vous n'avez pas de licence Squish, vous pouvez obtenir une version d'essai gratuite.
Voir aussi Applications 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.