本教程介绍Qt 的Quick UI的语言QML。该教程并不涵盖一切;重点讲解一些关键原则,并根据需要介绍其特点。
通过本教程的各个步骤,我们将学习QML基本类型(Basic Types),我们将创建具有属性(properties)和信号(signals)的QML组件,我们将在状态(states)和转换(transitions)的帮助下创建一个简单的动画。
从一个最小的“Hello world”程序开始,夹杂着介绍一些新概念。
第一个程序是一个非常简单的“Hello world”示例,它使用了一些基本的QML概念。下图是这个程序的截图。
根项目是Item的子类Rectangle,自身不带显示窗口,需要使用QQuickView加载qml来显示才行,所以需要修改加载的方式。
import QtQuick 2.0
Rectangle {
id: page
width: 320; height: 480
color: "lightgray"
Text {
id: helloText
text: "Hello world!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
}
}
#include
#include
#include
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
#if 0
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
#else
QQuickView view ;
view.setSource(QUrl("qrc:/Helloworld.qml"));
view.show();
return app.exec();
#endif
}
到此即可运行。
qml项目打包命令:
windeployqt xxxx.exe --qmldir qmlPath
注意,qmlPath 指的是你的项目中 qml 文件所在的文件路径!
::@echo off
echo Setting up environment for Qt usage...
set QtKit=C:\Qt\5.15.2\msvc2019_64
set PATH=%QtKit%\bin;%PATH%
echo %QtKit%
::cd /D C:\Qt\5.15.2\msvc2019_64
echo Remember to call vcvarsall.bat to complete environment setup!
start windeployqt ch1_1_1_Helloworld.exe --qmldir E:\Project\QML\SimpleQMLTutorial\ ch1_1_1_Helloworld
cmd /k
首先,我们需要导入本例所需的类型。大多数QML文件将导入内置的QML类型(如矩形,图像,…)
import QtQuick 2.0
Rectangle {
id: page
width: 320; height: 480
color: "lightgray"
声明一个Rectangle类型的根对象。它是用来在QML创建应用程序的基本构建块之一。我们给它一个id以便以后可以引用它,称之为“page”。我们还设置了宽度width、高度height和颜色color属性。矩形类型还包含许多其他属性(如x和y),不设置则会保留其默认值。
Text {
id: helloText
text: "Hello world!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
}
添加一个文本Text类型作为根矩形类型的子类型:
本节添加一个颜色选择器用来改变文本的颜色。
新建Cell.qml ,注意文件名大小写
import QtQuick 2.0
Item {
id: container
property alias cellColor: rectangle.color
signal clicked(cellColor: color)
width: 40; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
}
}
组件的根类型是一个id为container的Item。
Item是QML中最基础的视觉类型,通常用作其他类型的容器。
property alias cellColor: rectangle.color
给container声明一个cellColor属性。
属性可以从组件外部访问,在组件可以用不同的颜色实例化单元格。这个属性的值是一个现有属性的别名alias --矩形的颜色(参见属性绑定)。
给container定义一个信号:
signal clicked( cellColor : color )
组件也可以有一个信号,我们调用click时发射一个类型为color的cellColor参数。稍后,使用这个信号来更改主QML文件中的文本的颜色。
Cell组件的颜色显示是用一个矩形来实现的。
anchors.fill属性是设置可视类型大小的方便方法,该矩形的大小将与其父对象container相同。
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
为了在单击单元格时改变文本的颜色,我们创建了一个MouseArea类型,其大小与其父单元格container相同。
MouseArea定义了一个称为clicked的信号。当这个信号被触发时,将会发出自定义的点击信号,并将颜色作为参数进行传递。
MouseArea {
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
}
在main.qml文件中,使用Cell组件来创建颜色选择器:
import QtQuick 2.0
Rectangle {
id: page
width: 320; height: 480
color: "lightgray"
Text {
id: helloText
text: "Hello world!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
}
Grid {
id: colorPicker
x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4
rows: 2; columns: 3; spacing: 3
Cell { cellColor: "red"; onClicked: helloText.color = cellColor }
Cell { cellColor: "green"; onClicked: helloText.color = cellColor }
Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "yellow"; onClicked: helloText.color = cellColor }
Cell { cellColor: "steelblue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "black"; onClicked: helloText.color = cellColor }
}
}
在网格Grid中放置6个不同颜色的单元格来创建颜色选择器。
当单元格的点击信号被触发时,我们希望将文本的颜色设置为作为参数传递的cellColor。我们可以通过名为“onSignalName”的属性对组件的任何信号做出反应(参见信号属性)。
在main.cpp文件中view.setSource(QUrl("qrc:/main.qml"));
要使用url的链接,qt才会找到Cell.qml ,否则会报Cell 不是类型的错误。
在本节中,我们通过引入状态和转换使这个例子更加动态。
我们希望我们的文本移动到屏幕底部,旋转并在点击时变成红色。
在上述代码中,增加代码
Text {
...
MouseArea {
id: mouseArea;
anchors.fill: parent
}
...
}
MouseArea是一个不可见项,通常与可见项一起使用,以便为该项提供鼠标处理。通过有效地充当代理,鼠标处理的逻辑可以包含在MouseArea项中。
Text添加MouseArea项后,则具备了鼠标事件,可以被点击。
Text {
...
states: State {
name: "down";
when: mouseArea.pressed === true
PropertyChanges {
target: helloText;
y: 160;
rotation: 180;
color: "red"
}
}
...
}
首先,我们为文本类型创建一个新的“down”状态。
当鼠标区域被按下时,该状态将被激活,当鼠标区域被释放时,该状态将被取消激活。
“down”状态包括一组从起始状态变化后的属性值。具体来说,我们将文本的y属性设置为160,旋转设置为180,颜色设置为红色。
因为我们不希望文本立即出现在底部,而是要平稳地移动,所以我们在两个状态之间添加了一个过渡。并定义转换将在其之间运行的状态。在本例中,我们希望从默认状态转换到下状态。
Text {
...
transitions: Transition {
from: ""; to: "down"; reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y,rotation";
duration: 500;
easing.type: Easing.InOutQuad
}
ColorAnimation { duration: 500 }
}
}
...
}
ParallelAnimation类型确保两种类型的动画(数量NumberAnimation和颜色ColorAnimation)同时开始。我们也可以使用SequentialAnimation来逐个运行它们。