Calqlatr

这是一个Qt Quick 应用程序,专为横向和纵向设备设计,使用自定义组件、响应式布局和 JavaScript 作为应用程序逻辑。

Calqlatr演示了各种 QML 和 Qt Quick功能,如显示自定义组件和使用响应式布局。应用逻辑用 JavaScript 实现,用户界面用 QML 实现。

运行示例

要从 Qt Creator,打开Welcome 模式,并从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行

显示自定义组件

Calqlatr应用程序中使用自定义类型。这些类型定义在各自独立的 .qml 文件中:

  • BackspaceButton.qml
  • 计算器按钮
  • 显示.qml
  • NumberPad.qml

要在Main.qml 中使用这些自定义类型,请为类型所在的content 文件夹添加导入语句:

import "content"

例如,Main.qml 中使用NumberPad 类型创建计算器的数字键盘。它嵌套在Item 类型中,该类型是Qt Quick 中所有可视化项目的基础类型:

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

自定义组件是可以在任何 QML 文件中定义的 QML 类型,它们的行为与在自己的 .qml 文件中定义的组件相同,如NumberPad.qml 。在NumberPad.qml 中定义了DigitButton 组件和OperatorButton 组件。可以在这些组件中添加新属性或修改现有属性。在这里,onReleased 处理程序将被覆盖。

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

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

此外,NumberPad 上的不同按钮使用CalculatorButton 类型。CalculatorButton.qml 定义了按钮的基本属性,您可以在NumberPad.qml 中修改每个实例的属性。对于数字和运算符按钮,还添加了一些额外属性,如textwidthdimmable 属性。只要计算器引擎不接受按钮输入,就可以使用dimmable 可视化禁用(变暗)按钮。

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

content 目录中还有一个名为BackSpaceButton.qml 的文件,这是CalculatorButton 的一个特例,我们希望在按钮上呈现图像而不是文本。该按钮与OperatorButton 相同,但包括icon 而不是text

    icon.source: getIcon()
    icon.width: 38
    icon.height: 38
    icon.color: getIconColor()
    // include this text property as the calculator engine
    // differentiates buttons through text. The text is never drawn.
    text: "bs"

    property bool dimmable: true
    property bool dimmed: false
    readonly property color backgroundColor: "#222222"
    readonly property color borderColor: "#A9A9A9"
    readonly property color backspaceRedColor: "#DE2C2C"
    readonly property int buttonRadius: 8

    function getBackgroundColor() {
        if (button.dimmable && button.dimmed)
            return backgroundColor
        if (button.pressed)
            return backspaceRedColor
        return backgroundColor

响应式布局

在本示例中,响应式布局为纵向和横向模式安排了不同的用户界面组件。您还可以在这两种模式之间切换。在Main.qml 中可以看到这一点,它为纵向模式定义了ColumnLayout ,为横向模式定义了RowLayout

        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 代表应用程序的纵向布局,而RowLayout 代表横向布局。visible 属性用于处理在特定时间使用哪种布局。NumberPadDisplay 组件的id 属性用于设置LayoutItemProxy 类型的target 属性。这样,两种布局就可以使用相同的内容项。此外,还可将LayoutItemProxy 项目内的属性转发到target 本身。例如,当NumberPad 实例化时,两种布局都需要不同的Layout.alignment

isPortraitMode 属性的信号处理器中,通过设置它们的可见性,可以在两种布局之间切换:

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

之所以能做到这一点,是因为 QML 为所有自声明的属性创建了信号处理程序,在本例中就是on<Property>Changed 处理程序,其中 <property> 是isPortraitMode 属性。

NumberPad.qml 中定义NumberPad 本身的纵向和横向布局时,也使用了响应式布局。

        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

在这种情况下,会创建两个LayoutItemProxy 项目。它们的target 属性分别设置为scientificGridmainGrid ,前者是Grid 类型,包含所有科学按钮,后者是Grid 类型,包含所有标准按钮。

CalculatorButton.qml 中,数字键盘按钮的文本颜色也是动画的。

        ...
        color: getBackgroundColor()
        border.color: getBorderColor()
    }

    contentItem: Text {
        text: button.text
        font.pixelSize: button.fontSize
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        color: getTextColor()
        Behavior on color {
            ColorAnimation {
                duration: 120
                easing.type: Easing.OutElastic
            }

通过在color 属性上定义Behavior ,可以实现颜色的动画变化。当按钮设置为dimmed = true 时,按钮的颜色会变深。按下按钮时,按钮会亮起绿灯。为了动态更改NumberPad 上所有按钮的dimmed 属性,buttonPressed 信号调用NumberPadupdateDimmed() 函数。

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

执行计算

calculator.js 文件定义了计算器的引擎。它包含用于存储计算器状态的变量,以及用户按下数字和运算符按钮时调用的函数。要使用该引擎,请使用别名CalcEngine 将 calculator.js 导入Main.qml 文件:

import "content/calculator.js" as CalcEngine

默认情况下,从 QML 导入 JavaScript 文件会创建一个新实例,其中包含的任何状态都是该实例独有的。使用.pragma library 可以让脚本的所有用户共享状态。

.pragma library

当用户按下一个数字时,该数字的文本就会出现在显示屏上。按下运算符时,将执行相应的计算,并使用等式 (=) 运算符显示计算结果。全清 (AC) 运算符可重置计算器引擎。

文件列表

示例项目 @ code.qt.io

另请参阅 QML 应用程序

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