Calqlatr

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 als Standard-Taschenrechner zu verwenden.

Anzeige von benutzerdefinierten Komponenten

In der Calqlatr-Anwendung werden benutzerdefinierte Typen verwendet. Diese sind in einer eigenen, separaten .qml-Datei definiert:

  • BackspaceButton.qml
  • CalculatorButton.qml
  • Display.qml
  • NumberPad.qml

Um diese benutzerdefinierten Typen in Main.qml zu verwenden, fügen Sie eine Importanweisung für den Ordner content hinzu, in dem sich die Typen befinden:

import "content"

Der Typ NumberPad wird beispielsweise in Main.qml verwendet, um den Nummernblock des Taschenrechners zu erstellen. Dieser ist in einem Typ Item verschachtelt, dem Basistyp für alle visuellen Elemente in Qt Quick:

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

Benutzerdefinierte Komponenten sind QML-Typen, die in jeder QML-Datei definiert werden können. Sie verhalten sich genauso wie die Komponenten, die in ihrer eigenen .qml-Datei definiert sind, wie NumberPad.qml. In NumberPad.qml sind die Komponente DigitButton und die Komponente OperatorButton definiert. In diesen Komponenten können neue Eigenschaften hinzugefügt oder bestehende Eigenschaften geändert werden. Hier wird der onReleased Handler für die beiden benutzerdefinierten Komponenten überschrieben.

    component DigitButton: CalculatorButton {
        onReleased: {
            root.digitPressed(text);
            updateDimmed();
        }
    }

    component OperatorButton: CalculatorButton {
        onReleased: {
            root.operatorPressed(text);
            updateDimmed();
        }
        textColor: controller.qtGreenColor
        implicitWidth: 48
        dimmable: true
    }

Verwenden Sie außerdem den Typ CalculatorButton für die verschiedenen Schaltflächen auf NumberPad. CalculatorButton.qml definiert grundlegende Eigenschaften einer Schaltfläche, die Sie für jede Instanz in NumberPad.qml ändern. Für die Ziffern- und Operator-Schaltflächen werden einige zusätzliche Eigenschaften hinzugefügt, z. B. die Eigenschaften text, width und dimmable. Verwenden Sie dimmable, um Schaltflächen visuell zu deaktivieren (abgeblendet), wenn der Taschenrechner keine Eingaben von dieser Schaltfläche akzeptiert.

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

Es gibt eine weitere Datei im Verzeichnis content mit dem Namen BackSpaceButton.qml. Dies ist ein Sonderfall von CalculatorButton, bei dem wir ein Bild auf die Schaltfläche rendern möchten, anstatt Text zu verwenden. Diese Schaltfläche ist die gleiche wie eine OperatorButton, enthält aber eine icon anstelle von text.

Reaktionsfähige Layouts

In diesem Beispiel ordnen responsive Layouts die verschiedenen Komponenten der Benutzeroberfläche sowohl im Hoch- als auch im Querformat an. Außerdem können Sie zwischen diesen beiden Modi umschalten. 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: true

            LayoutItemProxy {
                target: display
                Layout.minimumHeight: display.minHeight
            }
            LayoutItemProxy {
                target: numberPad
                Layout.alignment: Qt.AlignHCenter
            }
        }

        RowLayout {
            id: landscapeMode
            anchors.fill: parent
            visible: false

            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 Eigenschaft visible steuert, welches Layout zu einem bestimmten Zeitpunkt verwendet wird. Die Eigenschaft 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.

Das Umschalten zwischen den beiden Layouts erfolgt im Signalhandler für die isPortraitMode -Eigenschaft durch das Setzen ihrer Sichtbarkeit:

        onIsPortraitModeChanged: {
            if (isPortraitMode) {
                portraitMode.visible = true;
                landscapeMode.visible = false;
            } else {
                portraitMode.visible = false;
                landscapeMode.visible = true;
            }
        }

Dies ist möglich, weil QML Signalhandler für alle selbst deklarierten Eigenschaften erstellt, in diesem Fall den on<Property>Changed handler, wobei <property> die Eigenschaft isPortraitMode ist.

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: !isPortraitMode

                OperatorButton { text: "x²" }
                OperatorButton { text: "⅟x" }
                OperatorButton { text: "√" }
                OperatorButton { text: "x³" }
                OperatorButton { text: "sin" }
                OperatorButton { text: "|x|" }
                OperatorButton { text: "log" }
                OperatorButton { text: "cos" }
                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 {}
                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

In diesem Fall werden zwei LayoutItemProxy Elemente erstellt. Ihre target Eigenschaften werden auf scientificGrid gesetzt, ein Grid Typ, der alle wissenschaftlichen Schaltflächen enthält, und mainGrid, ein anderer Grid Typ, der alle Standardschaltflächen enthält.

Animieren von Schaltflächentextfarben

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

        ...
        color: 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 Schaltfläche auf dimmed = true gesetzt ist, erscheint die Schaltfläche 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 = root.isButtonDisabled(mainGrid.children[i].text);
        }
        for (let j = 0; j < scientificGrid.children.length; j++) {
            scientificGrid.children[j].dimmed = root.isButtonDisabled(
                        scientificGrid.children[j].text);
        }
    }

Ausführen von Berechnungen

Die Datei calculator.js definiert die Engine des Taschenrechners. Sie enthält Variablen, um den Zustand des Rechners zu speichern, und Funktionen, die aufgerufen werden, wenn der Benutzer die Ziffern- und Operator-Tasten drückt. Um die Engine zu verwenden, importieren Sie calculator.js in die Datei Main.qml unter Verwendung des Alias CalcEngine:

import "content/calculator.js" as CalcEngine

Standardmäßig wird beim Importieren einer JavaScript-Datei aus QML eine neue Instanz der Datei erstellt, und jeder darin enthaltene Status ist nur für diese Instanz gültig. Durch die Verwendung von .pragma library kann der Status von allen Benutzern des Skripts gemeinsam genutzt werden.

.pragma library

Wenn der Benutzer eine Ziffer drückt, wird der Text der Ziffer auf dem Display angezeigt. Wenn sie einen Operator drücken, wird die entsprechende Berechnung durchgeführt, und das Ergebnis kann mit dem Gleichheitsoperator (=) angezeigt werden. Der Alles-Löschen-Operator (AC) setzt den Taschenrechner zurück.

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.