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
中为每个实例修改这些属性。对于数字和运算符按钮,还添加了一些额外的属性,例如 text
、width
和 dimmable
属性。使用 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
属性决定在某一时刻使用哪个布局。 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
处理程序,其中
是 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
信号调用 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
在 Main.qml
文件中导入 calculator.js。
import "content/calculator.js" as CalcEngine
默认情况下,从 QML 导入 JavaScript 文件会为其创建一个新的实例,而它所包含的状态在实例之间是唯一的。使用 .pragma library
允许脚本的所有用户共享状态。
.pragma library
当用户按下数字时,数字文本会出现在显示器上。当用户按下运算符时,会执行相应的计算,并可以使用等号 (=) 运算符显示结果。所有清除 (AC) 运算符重置计算器引擎。
文件列表
另请参阅QML 应用程序。
© 2024 The Qt Company Ltd. 本文档中的贡献是各自所有者的版权。本提供的文档根据 Free Software Foundation 发布的 GNU 自由文献许可证版本 1.3 的条款进行许可。Qt 和相应的标志是芬兰和/或其他国家的 The Qt Company Ltd. 的商标。所有其他商标均为其各自所有者的财产。