计算器
专为横向和纵向设备设计的计算器。它的用户界面使用Qt Quick 自定义组件和响应式布局,应用程序逻辑使用 JavaScript。

Calqlatr演示了各种 QML 和 Qt Quick功能,如显示自定义组件和使用响应式布局。应用逻辑用 JavaScript 实现,用户界面用 QML 实现。
运行示例
要从 Qt Creator,打开Welcome 模式,并从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行。运行示例后,您就可以将应用程序作为标准计算器使用了。将手机从纵向模式改为横向模式,或在桌面上调整主窗口大小,以激活响应式布局。
使用自定义组件
Calqlatr应用程序使用自定义类型。其中大部分都定义在各自独立的 .qml 文件中:
Main.qmlcontent/ApplicationState.qmlcontent/BackspaceButton.qmlcontent/CalculatorButton.qmlcontent/Display.qmlcontent/NumberPad.qml
Main.qml Calqlatr 应用程序包含顶级窗口和根项目。它使用content 目录中的其他自定义类型。例如,Main.qml 中使用了NumberPad 类型(由content/NumberPad.qml 定义)来创建计算器的数字键盘:
NumberPad { id: numberPad Layout.margins: root.margin isPortraitMode: root.isPortraitMode applicationState: state }
内联组件允许您在.qml 文件中声明多个组件。示例在NumberPad.qml 中使用内联组件定义了两个组件DigitButton 和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(); } }
这两个组件的类型都是CalculatorButton (如CalculatorButton.qml 中所定义),但为点击信号提供了一个自定义处理程序,并调整了一些属性默认值。DigitButton 和OperatorButton 随后在NumberPad.qml 中实例化:
DigitButton { text: "e" dimmable: true implicitWidth: 48 } OperatorButton { text: "ln" }
有关定义自定义QML组件的更多详情,请查阅《通过 QML 文档定义对象类型》(Defining Object Types throughQMLDocuments)。
响应式布局
在本例中,响应式布局为纵向和横向模式安排了不同的用户界面组件。你可以在Main.qml 中看到这一点,它为纵向模式定义了ColumnLayout ,为横向模式定义了RowLayout :
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 表示应用程序的纵向布局,而RowLayout 表示横向布局。visible 属性用于处理在特定时间使用哪种布局。NumberPad 和Display 组件的id属性用于设置LayoutItemProxy 类型的target 属性。这样,两种布局就可以使用相同的内容项。此外,还可将LayoutItemProxy 项目内的属性转发到target 本身。例如,当NumberPad 实例化时,两种布局都需要不同的Layout.alignment 。
在NumberPad.qml 中定义NumberPad 本身的纵向和横向布局时,也会使用响应式布局:
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
根据controller.isPortraitMode ,数字键盘只包含一个简单计算器的按钮 (mainGrid),或通过scientificGrid 包含一些更高级的功能。
动画按钮文本颜色
在CalculatorButton.qml 文件中,数字键盘按钮的文本颜色也是动态的。
...
color: button.getTextColor()
Behavior on color {
ColorAnimation {
duration: 120
easing.type: Easing.OutElastic
}
}
}通过在color 属性上定义Behavior ,可以实现颜色的动画变化。当按钮设置为dimmed = true 时,按钮的颜色会变深。按下按钮时,按钮会亮起绿灯。为了动态更改NumberPad 上所有按钮的dimmed 属性,buttonPressed 信号调用NumberPad 的updateDimmed() 函数。
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); } }
执行计算
calculator.js 和ApplicationState.qml 文件定义了计算器的引擎。calculator.js 包含计算器的逻辑状态以及改变状态的操作。ApplicationState.qml 通过 QML 类型公开了这一 API。
让我们先看看calculator.js :
let accumulator = 0 let pendingOperator = "" let lastButton = "" let digits = ""
accumulator,pendingOperator,lastButton,represent 计算器的逻辑状态。
function isOperationDisabled(op, display) {
...
}isOperationDisabled() 根据当前引擎和显示状态,如果操作op 应被禁用,则返回true 。否则,返回false 。
function digitPressed(op, display) {
...
}
function operatorPressed(op, display) {
...
}digitPressed() 和operatorPressed() 函数根据按下的按钮更新引擎和显示状态。
ApplicationState.qml 将calculator.js 中定义的 JavaScript 函数公开在一个更漂亮的 QML API 后面:
import QtQml import "calculator.js" as CalcEngine
calculator.js 以CalcEngine 名称导入。
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 确保Display.qml 的实例在ApplicationState 中始终可用,其名称为display 。display 又用于operatorPressed() 、digitPressed() 和isButtonDisabled() 函数。
源文件
Squish GUI 测试
本应用程序带有针对 Qt for Android 的Squish GUI 测试。这些测试使用Squish for Qt制作,用 Python 编写,可在应用程序测试目录下找到。
使用 Squish for Qt for Android 时,请确保使用Qt 内置钩子并用 adb转发 Squish 端口。如果在dlopen 失败时出现 "Cannot load library"(无法加载库)错误,请禁用 CMake 配置中的QT_USE_TARGET_ANDROID_BUILD_DIR ,或在Qt Creator 中清除Projects->Build Settings->CMake->Current Configuration 中的 。
如果没有 Squish 许可证,可以免费试用。
另请参阅 QML 应用程序。
© 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.