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

Calqlatr illustre diverses fonctionnalités de QML et Qt Quick , telles que l’affichage de composants personnalisés et l’utilisation de mises en page adaptatives. La logique d’application est implémentée en JavaScript et l’interface utilisateur en QML.
Exécution de l'exemple
Vous pouvez exécuter l’exemple à partir de :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l’exemple à l’adresse Examples. Pour plus d’informations, consultez Qt Creator: Tutoriel : Compiler et exécuter.
- Qt Extension for Visual Studio Code
Exécutez la commande « Qt: Open Qt examples » à partir de Command Palette, puis sélectionnez l’exemple dans la liste. Pour plus d’informations, consultez Qt Extension for Visual Studio Code: Tutoriel : Compiler et exécuter.
Une fois l’exemple exécuté, vous devriez pouvoir utiliser l’application comme une calculatrice standard. Passez 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 adaptative.
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 distinct :
Main.qmlcontent/ApplicationState.qmlcontent/BackspaceButton.qmlcontent/CalculatorButton.qmlcontent/Display.qmlcontent/NumberPad.qml
Main.qml contient la fenêtre de niveau supérieur 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: appState
}Les composants 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();
}
}Ces deux composants sont de type « CalculatorButton » (tel que défini dans CalculatorButton.qml), mais fournissent un gestionnaire personnalisé pour le signal « clicked » et modifient certaines valeurs par défaut des propriétés. « DigitButton » et « OperatorButton » sont ensuite instanciés plus loin dans NumberPad.qml:
DigitButton {
text: "e"
dimmable: true
implicitWidth: 48
}
OperatorButton { text: "ln" }Consultez la section « Définition de types d’objets via des documents QML » pour plus de détails sur la définition de composants QML personnalisés.
Mises en page adaptatives
Dans cet exemple, des mises en page adaptatives 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 RowLayout pour le mode paysage :
ColumnLayout {
id: portraitMode
anchors.fill: parent
visible: root.isPortraitMode
LayoutItemProxy {
target: calcDisplay
Layout.minimumHeight: calcDisplay.minHeight
}
LayoutItemProxy {
target: numberPad
Layout.alignment: Qt.AlignHCenter
}
}
RowLayout {
id: landscapeMode
anchors.fill: parent
visible: !root.isPortraitMode
LayoutItemProxy {
target: calcDisplay
}
LayoutItemProxy {
target: numberPad
Layout.alignment: Qt.AlignVCenter
}
}Le fichier ColumnLayout représente la mise en page en mode portrait de l’application, tandis que le fichier RowLayout représente la mise en page en mode paysage. Les propriétés visible déterminent quelle mise en page est utilisée à un moment donné. L’attribut id des composants NumberPad et Display sert à définir la propriété target des types LayoutItemProxy. Cela permet aux deux mises en page d’utiliser les mêmes éléments de contenu. De plus, les propriétés peuvent être transmises au sein de l’élément LayoutItemProxy vers l’ target lui-même. Par exemple, lorsque l’ NumberPad est instancié, les deux mises en page nécessitent un Layout.alignment différent.
Une mise en page responsive est également utilisée dans NumberPad.qml lors de la définition des mises en page portrait et paysage pour le 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
}
}
} // RowLayoutSelon controller.isPortraitMode, le pavé numérique contient uniquement les touches d’une calculatrice simple (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 Behavior sur la propriété color. Lorsqu’un bouton est défini sur dimmed = true, il apparaît plus sombre. Lorsqu’on appuie sur un bouton, il s’allume en vert. Afin de modifier dynamiquement la propriété dimmed de tous les boutons du NumberPad, le signal buttonPressed appelle la fonction updateDimmed() de la classe 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. Le fichier calculator.js contient l’état logique de la calculatrice, ainsi que les opérations permettant de modifier cet état. Le fichier ApplicationState.qml expose cette API via un type QML.
Examinons tout d’abord le fichier 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, elle renvoie false.
function digitPressed(op, display) {
...
}
function operatorPressed(op, display) {
...
}digitPressed() Les fonctions ` 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 conviviale :
import QtQml
import "calculator.js" as CalcEnginecalculator.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 garantit qu’une instance de ` Display.qml ` est toujours disponible dans l’ ApplicationState sous le nom ` display`. ` display ` est à son tour utilisé dans les fonctions ` operatorPressed()`, ` digitPressed() ` et ` isButtonDisabled() `.
Fichiers source
Tests d’interface graphique avec Squish
Cette application est fournie avec des tests d’interface graphique Squish destinés à Qt pour Android. Ces tests sont réalisés avec Squish pour Qt, écrits en Python et se trouvent dans le répertoire de test de l’application.
Lorsque vous utilisez Squish pour Qt sur Android, veillez à utiliser le hook intégré à Qt et à rediriger le port Squish via adb. Si vous obtenez l'erreur « Impossible de charger la bibliothèque » et que la commande « dlopen » échoue, désactivez l'option « QT_USE_TARGET_ANDROID_BUILD_DIR » dans la configuration CMake, ou supprimez-la de la ligne « Projects->Build Settings->CMake->Current Configuration » dans le fichier « Qt Creator ».
Si vous ne disposez pas d’une licence Squish, vous pouvez bénéficier d’un essai gratuit.
Voir également 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.