Calqlatr

一个针对横屏和竖屏设备设计的 Qt Quick 应用程序,它使用自定义组件、响应式布局和 JavaScript 以实现应用程序逻辑。

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

运行示例

要从 Qt Creator 运行示例,请打开 欢迎 模式并从 示例 中选择示例。有关更多信息,请访问 构建和运行示例

显示自定义组件

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

  • BackspaceButton.qml
  • CalculatorButton.qml
  • Display.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
    }

此外,使用 CalculatorButton 类型为 NumberPad 上的不同按钮。在 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

响应式布局

在此示例中,响应式布局为竖屏和横屏模式排列不同的 UI 组件。它还允许您在这两种模式之间切换。您可以在 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 为所有自声明的属性创建信号处理程序,在这个例子中,是 onChanged 处理程序,其中 isPortraitMode 属性。

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

        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 属性设置为 scientificGrid,一个包含所有科学按钮的 Grid 类型,以及 mainGrid,另一个包含所有标准按钮的 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 文件定义了计算器的引擎。它包含用于存储计算器状态的变量,以及当用户按下数字和运算按钮时调用的函数。要使用引擎,请使用别名 CalcEngineMain.qml 文件中导入 calculator.js。

import "content/calculator.js" as CalcEngine

默认情况下,从 QML 导入 JavaScript 文件会为其创建一个新的实例,而它所包含的状态在实例之间是唯一的。使用 .pragma library 允许脚本的所有用户共享状态。

.pragma library

当用户按下数字时,数字文本会出现在显示器上。当用户按下运算符时,会执行相应的计算,并可以使用等号 (=) 运算符显示结果。所有清除 (AC) 运算符重置计算器引擎。

文件列表

代码示例 @ code.qt.io

另请参阅QML 应用程序

© 2024 The Qt Company Ltd. 本文档中的贡献是各自所有者的版权。本提供的文档根据 Free Software Foundation 发布的 GNU 自由文献许可证版本 1.3 的条款进行许可。Qt 和相应的标志是芬兰和/或其他国家的 The Qt Company Ltd. 的商标。所有其他商标均为其各自所有者的财产。