Auf dieser Seite

Kalqlatr

Ein Taschenrechner, der für Geräte im Hoch- und Querformat entwickelt wurde. Er verwendet Qt Quick benutzerdefinierte Komponenten und responsive Layouts für die Benutzeroberfläche und JavaScript für die Anwendungslogik.

Die Beispiel-UI des Rechners

Calqlatr demonstriert verschiedene QML- und Qt Quick Funktionen, wie z. B. die Anzeige von benutzerdefinierten Komponenten und die Verwendung von responsiven Layouts. Die Anwendungslogik ist in JavaScript und die Benutzeroberfläche in QML implementiert.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorzu starten, öffnen Sie den Modus Welcome und wählen Sie das Beispiel aus Examples. Weitere Informationen finden Sie unter Qt Creator: Tutorial: Erstellen und Ausführen. Nachdem Sie das Beispiel ausgeführt haben, sollten Sie in der Lage sein, die Anwendung wie einen Standardtaschenrechner zu verwenden. Wechseln Sie auf dem Handy vom Hoch- in den Querformatmodus - oder ändern Sie auf dem Desktop die Größe des Hauptfensters - um das responsive Layout zu aktivieren.

Verwendung benutzerdefinierter Komponenten

Die Calqlatr-Anwendung verwendet benutzerdefinierte Typen. Die meisten von ihnen sind in einer separaten .qml-Datei definiert:

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

Main.qml Die Datei "Calqlatr" enthält das Top-Level-Fenster und das Root-Element. Sie nutzt die anderen benutzerdefinierten Typen im Verzeichnis content. Zum Beispiel wird der Typ NumberPad (definiert durch content/NumberPad.qml) in Main.qml verwendet, um den Nummernblock des Taschenrechners zu erstellen:

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

            isPortraitMode: root.isPortraitMode
            state: state
        }

MitInline-Komponenten können Sie mehrere Komponenten in einer .qml Datei deklarieren. Das Beispiel verwendet Inline-Komponenten in NumberPad.qml, um zwei Komponenten zu definieren, DigitButton und OperatorButton:

    component DigitButton: CalculatorButton {
        onClicked: {
            controller.state.digitPressed(text);
            controller.updateDimmed();
        }
    }

    component OperatorButton: CalculatorButton {
        dimmable: true
        implicitWidth: 48
        textColor: controller.qtGreenColor

        onClicked: {
            controller.state.operatorPressed(text);
            controller.updateDimmed();
        }
    }

Beide Komponenten sind vom Typ CalculatorButton (wie in CalculatorButton.qml definiert), bieten aber einen benutzerdefinierten Handler für das Klick-Signal und ändern einige Eigenschaftsvorgaben. DigitButton und OperatorButton werden dann später in NumberPad.qml instanziiert:

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

Weitere Einzelheiten zur Definition benutzerdefinierter QML-Komponenten finden Sie unter Definieren von Objekttypen durch QML-Dokumente.

Reaktionsfähige Layouts

In diesem Beispiel ordnen responsive Layouts die verschiedenen UI-Komponenten sowohl für den Hoch- als auch für den Querformatmodus an. Sie können dies an Main.qml erkennen, das ein ColumnLayout für den Hochformatmodus und ein RowLayout für das Querformat definiert:

        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 steht für das Hochformat-Layout der Anwendung und RowLayout für das Querformat-Layout. Die visible Eigenschaften regeln, welches Layout zu einem bestimmten Zeitpunkt verwendet wird. Das Attribut id der Komponenten NumberPad und Display wird verwendet, um die Eigenschaft target der Typen LayoutItemProxy zu setzen. Auf diese Weise können beide Layouts die gleichen Inhaltselemente verwenden. Darüber hinaus können die Eigenschaften innerhalb des Elements LayoutItemProxy an das Element target selbst weitergeleitet werden. Wenn zum Beispiel NumberPad instanziiert wird, benötigen beide Layouts ein anderes Layout.alignment.

Ein responsives Layout wird auch in NumberPad.qml verwendet, wenn das Hoch- und Querformat für die 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.state.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 nur die Schaltflächen eines einfachen Taschenrechners (mainGrid), oder auch einige weitergehende Funktionen über die scientificGrid.

Animierte Textfarben für Schaltflächen

In der Datei CalculatorButton.qml werden auch die Textfarben der Schaltflächen des Ziffernblocks animiert.

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

Die Farbänderungen werden animiert, indem eine Behavior für die Eigenschaft color definiert wird. Wenn eine Taste auf dimmed = true gesetzt ist, erscheint die Taste dunkler. Wenn eine Schaltfläche gedrückt wird, leuchtet sie grün. Um die Eigenschaft dimmed für alle Schaltflächen auf 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 = state.isButtonDisabled(mainGrid.children[i].text);
        }
        for (let j = 0; j < scientificGrid.children.length; j++) {
            scientificGrid.children[j].dimmed = state.isButtonDisabled(
                        scientificGrid.children[j].text);
        }
    }

Ausführen 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 des Zustands. ApplicationState.qml stellt diese API durch einen QML-Typ zur Verfügung.

Werfen wir zunächst einen Blick auf calculator.js:

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

accumulator, pendingOperator, lastButton, represent den logischen Zustand des Taschenrechners.

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

isOperationDisabled() gibt true zurück, wenn eine Operation op auf der Grundlage des aktuellen Motor- und Anzeigestatus deaktiviert werden sollte. Andernfalls gibt sie false zurück.

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

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

digitPressed() Die Funktionen operatorPressed() und aktualisieren den Motor- und Anzeigestatus auf der Grundlage der gedrückten Tasten.

ApplicationState.qml stellt die in calculator.js definierten JavaScript-Funktionen hinter einer schöneren QML-API dar:

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

Quelldateien

Squish GUI-Tests

Diese Anwendung wird mit Squish GUI Tests geliefert, die für Qt for Android gedacht 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 eingebauten Qt-Hook verwenden und den Squish-Port mit adb weiterleiten. Wenn Sie die Fehlermeldung "Cannot load library" erhalten und dlopen nicht funktioniert, deaktivieren Sie QT_USE_TARGET_ANDROID_BUILD_DIR in der CMake-Konfiguration oder löschen Sie es aus Projects->Build Settings->CMake->Current Configuration in Qt Creator.

Wenn Sie keine Squish-Lizenz haben, können Sie eine kostenlose Testversion erhalten.

Beispielprojekt @ code.qt.io

Siehe auch QML-Anwendungen.

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