计算器
一款专为横向和纵向设备设计的计算器。它的用户界面使用Qt Quick 自定义组件和响应式布局,应用程序逻辑使用 JavaScript。
Calqlatr演示了各种 QML 和 Qt Quick功能,如显示自定义组件和使用响应式布局。应用逻辑用 JavaScript 实现,用户界面用 QML 实现。
运行示例
要从 Qt Creator,打开Welcome 模式,并从Examples 中选择示例。更多信息,请参阅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 }
此外,NumberPad
上的不同按钮使用CalculatorButton
类型。CalculatorButton.qml
定义了按钮的基本属性,您可以在NumberPad.qml
中修改每个实例的属性。对于数字和运算符按钮,还添加了一些额外属性,如text
、width
和dimmable
属性。只要计算器引擎不接受按钮输入,就可以使用dimmable
可视化禁用(变暗)按钮。
DigitButton { text: "e" dimmable: true implicitWidth: 48 }
content
目录中还有一个名为BackSpaceButton.qml
的文件,这是CalculatorButton
的一个特例,我们希望在按钮上呈现图像而不是文本。该按钮与OperatorButton
相同,但包括icon
而不是text
。
响应式布局
在本示例中,响应式布局为纵向和横向模式安排了不同的用户界面组件。您还可以在这两种模式之间切换。在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
属性用于处理在给定时间内使用哪种布局。NumberPad
和Display
组件的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
属性分别设置为scientificGrid
和mainGrid
,前者是Grid
类型,包含所有科学按钮,后者是Grid
类型,包含所有标准按钮。
动画按钮文本颜色
在CalculatorButton.qml
中,数字键盘按钮的文本颜色也是动画的。
... color: 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 = 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) 运算符可重置计算器引擎。
源文件
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 应用程序。
© 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.