Auf dieser Seite

Calqlatr

Ein Taschenrechner, der für Geräte im Quer- und Hochformat entwickelt wurde. Er nutzt benutzerdefinierte Komponenten von „ Qt Quick “ und responsive Layouts für die Benutzeroberfläche sowie JavaScript für die Anwendungslogik.

Die Benutzeroberfläche des Taschenrechner-Beispiels

Calqlatr demonstriert verschiedene QML- und Qt Quick Funktionen, wie beispielsweise die Darstellung benutzerdefinierter Komponenten und die Verwendung responsiver Layouts. Die Anwendungslogik ist in JavaScript implementiert, die Benutzeroberfläche in QML.

Ausführen des Beispiels

Sie können das Beispiel wie folgt ausführen:

Nach dem Ausführen des Beispiels sollten Sie die Anwendung wie einen normalen Taschenrechner verwenden können. Wechseln Sie auf dem Smartphone vom Hoch- in den Querformatmodus – oder passen Sie auf dem Desktop die Größe des Hauptfensters an –, um das responsive Layout zu aktivieren.

Verwendung benutzerdefinierter Komponenten

Die Calqlatr -Anwendung verwendet benutzerdefinierte Typen. Die meisten davon sind in einer eigenen, separaten .qml-Datei definiert:

  • Main.qml
  • content/ApplicationState.qml
  • content/BackspaceButton.qml
  • content/CalculatorButton.qml
  • content/Display.qml
  • content/NumberPad.qml

Main.qml enthält das oberste Fenster und das Stammelement. Es nutzt die anderen benutzerdefinierten Typen im Verzeichnis „ content “. Beispielsweise wird der Typ „ NumberPad “ (definiert in „ content/NumberPad.qml “) in „ Main.qml “ verwendet, um den Ziffernblock des Taschenrechners zu erstellen:

        NumberPad {
            id: numberPad
            Layout.margins: root.margin

            isPortraitMode: root.isPortraitMode
            applicationState: appState
        }

Inline-Komponenten ermöglichen es Ihnen, mehrere Komponenten innerhalb einer „ .qml “-Datei zu deklarieren. Das Beispiel verwendet Inline-Komponenten in „ NumberPad.qml “, um zwei Komponenten zu definieren: „ DigitButton “ und „ 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();
        }
    }

Beide Komponenten sind vom Typ „ CalculatorButton “ (wie in „ CalculatorButton.qml “ definiert), stellen jedoch einen benutzerdefinierten Handler für das Signal „clicked“ bereit und passen einige Standardwerte der Eigenschaften an. „ DigitButton “ und „ OperatorButton “ werden dann später in „ NumberPad.qml “ instanziiert:

                DigitButton {
                    text: "e"
                    dimmable: true
                    implicitWidth: 48
                }
                OperatorButton { text: "ln" }

Weitere Informationen zum Definieren benutzerdefinierter QML-Komponenten finden Sie unter „Defining Object Types through QML Documents “.

Responsive Layouts

In diesem Beispiel ordnen responsive Layouts die verschiedenen UI-Komponenten sowohl für den Hoch- als auch für den Querformatmodus an. Dies wird in „ Main.qml “ deutlich, wo „ ColumnLayout “ für den Hochformatmodus und „ RowLayout “ für den Querformatmodus definiert sind:

        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
            }
        }

Die Datei „ ColumnLayout “ stellt das Hochformat-Layout der Anwendung dar, während die Datei „ RowLayout “ das Querformat-Layout enthält. Die Eigenschaften von „ visible “ legen fest, welches Layout zu einem bestimmten Zeitpunkt verwendet wird. Das „id“-Attribut der Komponenten „ NumberPad “ und „ Display “ wird verwendet, um die Eigenschaft „ target “ der Typen „ LayoutItemProxy “ festzulegen. Dadurch können beide Layouts dieselben Inhaltselemente verwenden. Darüber hinaus können Eigenschaften innerhalb des Elements „ LayoutItemProxy “ an das „ target “ selbst weitergeleitet werden. Wenn beispielsweise das „ NumberPad “ instanziiert wird, benötigen beide Layouts ein unterschiedliches „ Layout.alignment “.

Ein responsives Layout wird auch in NumberPad.qml verwendet, wenn das Hoch- und Querformat-Layout für das NumberPad selbst definiert wird:

        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

Je nach controller.isPortraitMode enthält der Ziffernblock entweder nur die Tasten eines einfachen Taschenrechners (mainGrid) oder zusätzlich einige erweiterte Funktionen über den scientificGrid.

Animieren der Textfarben der Tasten

In der Datei „ CalculatorButton.qml “ werden auch die Textfarben der Tasten des Ziffernblocks animiert.

        ...
        color: button.getTextColor()
        Behavior on color {
            ColorAnimation {
                duration: 120
                easing.type: Easing.OutElastic
            }
        }
    }

Die Farbwechsel werden durch die Definition eines „ Behavior “ für die Eigenschaft „ color “ animiert. Wenn eine Taste auf „ dimmed = true “ gesetzt ist, erscheint sie dunkler. Wird eine Taste gedrückt, leuchtet sie grün auf. Um die Eigenschaft „ dimmed “ aller Tasten des „ NumberPad “ dynamisch zu ändern, ruft das Signal „ buttonPressed “ die Funktion „ updateDimmed() “ von „ NumberPad “ auf.

    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);
        }
    }

Durchführung von Berechnungen

Die Dateien „ calculator.js “ und „ ApplicationState.qml “ definieren die Engine des Taschenrechners. „ calculator.js “ enthält den logischen Zustand des Taschenrechners sowie Operationen zur Änderung dieses Zustands. „ ApplicationState.qml “ stellt diese API über einen QML-Typ bereit.

Schauen wir uns zunächst einmal „ calculator.js “ an:

let accumulator = 0
let pendingOperator = ""
let lastButton = ""
let digits = ""

accumulator, pendingOperator, lastButton, represent – der logische Zustand des Taschenrechners.

function isOperationDisabled(op, display) {
    ...
}

isOperationDisabled() Gibt „ true “ zurück, wenn eine Operation „ op “ basierend auf dem aktuellen Zustand der Engine und der Anzeige deaktiviert werden sollte. Andernfalls wird „ false “ zurückgegeben.

function digitPressed(op, display) {
    ...
}

function operatorPressed(op, display) {
    ...
}

digitPressed() Die Funktionen „ operatorPressed() “ aktualisieren den Engine- und Anzeigestatus basierend auf den gedrückten Tasten.

ApplicationState.qml stellt die in ` calculator.js ` definierten JavaScript-Funktionen hinter einer übersichtlicheren QML-API bereit:

import QtQml
import "calculator.js" as CalcEngine

calculator.js wird unter dem Namen „ CalcEngine “ importiert.

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 stellt sicher, dass eine Instanz von ` Display.qml ` im ` ApplicationState ` stets unter dem Namen ` display` verfügbar ist. ` display ` wird wiederum in den Funktionen ` operatorPressed()`, ` digitPressed() ` und ` isButtonDisabled() ` verwendet.

Quelldateien

Squish-GUI-Tests

Diese Anwendung enthält Squish-GUI-Tests, die auf Qt für Android ausgerichtet sind. Die Tests wurden mit Squish für Qt erstellt, sind in Python geschrieben und befinden sich im Testverzeichnis der Anwendung.

Wenn Sie Squish für Qt für Android verwenden, stellen Sie sicher, dass Sie den in Qt integrierten Hook nutzen und den Squish-Port mit adb weiterleiten. Falls Sie die Fehlermeldung „Cannot load library“ erhalten und „ dlopen “ fehlschlägt, deaktivieren Sie „ QT_USE_TARGET_ANDROID_BUILD_DIR “ in der CMake-Konfiguration oder entfernen Sie den Eintrag aus „ Projects->Build Settings->CMake->Current Configuration “ unter „ Qt Creator “.

Falls du keine Squish-Lizenz hast, kannst du eine kostenlose Testversion erhalten.

Beispielprojekt auf code.qt.io

Siehe auch QML-Anwendungen.

© 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.