本文由 简悦 SimpRead 转码, 原文地址 zhuanlan.zhihu.com
QWidget嵌入Qml文件
第一种 QQmlApplicationEngine
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
1
2
其中main.qml 可以以Window作为根元素,这个时候QML就完全拥有了控制权,可以直接设置窗体的标题、尺寸等信息;
第二种 QQuickView
QQuickView最常见的用法如下:
QQuickView view;
view.setResizeMode (QQuickView::SizeRootObjectToView);
view.setSource (QUrl("qrc:/main.qml"));
view.show ();
注意:其中qml文件不能以Window作为根元素,最佳选择是使用Item为根元素,否则会警告:
QQuickView does not support using windows as a root item.
If you wish to create your root window from QML, consider using QQmlApplicationEngine instead.
第三种 QQuickWidget
官网自带的说明例子.一般用来在QWidget界面上加载QML界面
QQuickWidget *view = new QQuickWidget;
view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
view->show();
QML 是一种基于 JavaScript 的声明式语言
QML 文档描述了一个对象树。QML 元素包含了其构造块、图形元素(矩形、图片等)和行为(例如动画、切换等)。这些 QML 元素按照一定的嵌套关系构成复杂的组件,供用户交互。
Qt Quick 就是使用 QML 构建的一套类库。
一个 QML 文档分为 import 和对象声明两部分。如果你要使用 Qt Quick,就需要 import QtQuick 2。QML 是一种声明语言,用于描述程序界面。QML 将用户界面分解成一块块小的元素,每一元素都由很多组件构成。QML 定义了用户界面元素的外观和行为;更复杂的逻辑则可以结合 JavaScript 脚本实现。这有点类似于 HTML 和 JavaScript 的关系,前者用来显示界面,后者用来定义行为。
-
// rectangle.qml
-
import QtQuick 2.0
-
// 根元素:Rectangle
-
Rectangle {
-
// 命名根元素
-
id: root // 声明属性:
: -
width: 120; height: 240
-
color: "#D8D8D8" // 颜色属性
-
// 声明一个嵌套元素(根元素的子元素)
-
Image {
-
id: rocket
-
x: (parent.width - width)/2; y: 40 // 使用 parent 引用父元素
-
source: 'assets/rocket.png'
-
}
-
// 根元素的另一个子元素
-
Text {
-
// 该元素未命名
-
y: rocket.y + rocket.height + 20 // 使用 id 引用元素
-
width: root.width // 使用 id 引用元素
-
horizontalAlignment: Text.AlignHCenter
-
text: 'Rocket'
-
}
-
}
-
Text {
-
// (1) 标识符
-
id: thisLabel
-
// (2) x、y 坐标
-
x: 24; y: 16
-
// (3) 绑定
-
height: 2 * width
-
// (4) 自定义属性
-
property int times: 24
-
// (5) 属性别名
-
property alias anotherTimes: times
-
// (6) 文本和值
-
text: "Greetings " + times
-
// (7) 字体属性组
-
font.family: "Ubuntu"
-
font.pixelSize: 24
-
// (8) 附加属性 KeyNavigation
-
KeyNavigation.tab: otherLabel
-
// (9) 属性值改变的信号处理回调
-
onHeightChanged: console.log('height:', height)
-
// 接收键盘事件需要设置 focus
-
focus: true
-
// 根据 focus 值改变颜色
-
color: focus?"red":"black"
-
}
-
Text {
-
id: label
-
x: 24; y: 24
-
// 自定义属性,表示空格按下的次数
-
property int spacePresses: 0
-
text: "Space pressed: " + spacePresses + " times"
-
// (1) 文本变化的响应函数
-
onTextChanged: console.log("text changed to:", text)
-
// 接收键盘事件,需要设置 focus 属性
-
focus: true
-
// (2) 调用 JavaScript 函数
-
Keys.onSpacePressed: {
-
increment()
-
}
-
// 按下 Esc 键清空文本
-
Keys.onEscapePressed: {
-
label.text = ''
-
}
-
// (3) 一个 JavaScript 函数
-
function increment() {
-
spacePresses = spacePresses + 1
-
}
-
}
qml属性
QML 基本元素可以分为可视元素和不可视元素两类。可视元素(例如前面提到过的Rectangle)具有几何坐标,会在屏幕上占据一块显示区域。不可视元素(例如Timer)通常提供一种功能,这些功能可以作用于可视元素。
本章我们将会集中介绍集中最基本的可视元素:Item、Rectangle、Text、Image和MouseArea。
Item是所有可视元素中最基本的一个。它是所有其它可视元素的父元素,可以说是所有其它可视元素都继承Item。Item本身没有任何绘制,它的作用是定义所有可视元素的通用属性:
前面我们说过,Item定义了所有可视元素都具有的属性。所以在下面的内容中,我们会再次详细介绍这些属性。
除了定义通用属性,Item另外一个重要作用是作为其它可视元素的容器。从这一点来说,Item非常类似于 HTML 中 div 标签的作用。
MouseArea。顾名思义,这个元素用于用户交互。这是一个不可见的矩形区域,用于捕获鼠标事件。
MouseArea是 QtQuick 的重要组成部分,它将可视化展示与用户输入控制解耦。通过这种技术,你可以显示一个较小的元素,但是它有一个很大的可交互区域,以便在界面显示与用户交互之间找到一个平衡(如果在移动设备上,较小的区域非常不容易被用户成功点击。苹果公司要求界面的交互部分最少要有 40 像素以上,才能够很容易被手指点中)。
自定义组件:文件名首字母大写(不然在使用中会报没有这个属性错误);在pro文件中声明。
qml定位器 :Row、Column、Grid和Flow
https://www.bookstack.cn/read/qt-study-road-2/845b0afc468e33c4.md
QML 提供了很多用于定位的元素。这些元素叫做定位器,都包含在 QtQuick 模块。这些定位器主要有 Row、Column、Grid和Flow等
Repeater非常像一个for循环,它能够遍历数据模型中的元素。
锚点(anchor)
https://www.bookstack.cn/read/qt-study-road-2/f15a21344cfbabd8.md
锚点允许我们灵活地设置两个元素的相对位置。它使两个元素之间形成一种类似于锚的关系,也就是两个元素之间形成一个固定点。锚点的行为类似于一种链接,它要比单纯地计算坐标改变更强。由于锚点描述的是相对位置,所以在使用锚点时,我们必须指定两个元素,声明其中一个元素相对于另外一个元素。锚点是Item元素的基本属性之一,因而适用于所有 QML 可视元素。
一个元素有 6 个主要的锚点的定位线,如下图所示:
这 6 个定位线分别是:top、bottom、left、right、horizontalCenter和verticalCenter。对于Text元素,还有一个baseline锚点。每一个锚点定位线都可以结合一个偏移的数值。其中,top、bottom、left和right称为外边框;horizontalCenter、verticalCenter和baseline称为偏移量。
输入元素
TextInput是单行的文本输入框,支持验证器、输入掩码和显示模式等。
KeyNavigation是一个附加属性。当用户点击了指定的按键时,属性指定的组件就会获得焦点。附加属性类似于普通属性,但是又有所区别。普通的属性隶属于这个类型;附加属性一般用于修饰或补充目标类型。比如这里的http://KeyNavigation.tab并不是TextInput的普通属性,仅仅是用来说明TextInput的一种特征。附加属性的一般语法是类型.属性名,以此为例,类型就是KeyNavigation,属性名就是tab。
- import QtQuick 2.0
- Rectangle {
- width: 200
- height: 80
- color: "linen"
- TextInput {
- id: input1
- x: 8; y: 8
- width: 96; height: 20
- focus: true
- text: "Text Input 1"
- KeyNavigation.tab: input2
- }
- TextInput {
- id: input2
- x: 8; y: 36
- width: 96; height: 20
- text: "Text Input 2"
- KeyNavigation.tab: input1
- }
- }
为了让外界可以直接设置TextInput的text属性,我们给这个属性声明了一个别名。同时,为了让外界可以访问到内部的textInput,我们将这个子组件也暴露出来。不过,从封装的角度而言,将实现细节暴露出去并不是一个好的设计,这要看暴露出来这个子组件的影响究竟有多大。
25. // LineEdit.qml
26.
27. import QtQuick 2.0
28.
29. Rectangle {
30. width: 96;
31. height: input.height + 8
32. color: "lightsteelblue"
33. border.color: "gray"
34.
35. property alias text: input.text
36. property alias input: input
37.
38. TextInput {
39. id: input
40. anchors.fill: parent
41. anchors.margins: 4
42. focus: true
43. }
44. }
FocusScope接收到焦点时,会将焦点转发给最后一个设置了focus:true的子对象
- FocusScope {
- width: 96;
- height: input.height + 8
- color: "lightsteelblue"
- border.color: "gray"
- property alias text: input.text
- property alias input: input
- TextInput {
- id: input
- anchors.fill: parent
- anchors.margins: 4
- focus: true
- }
- }
TextEdit是多行的文本编辑组件
附加属性Keys类似于键盘事件,允许我们相应特定的按键按下事件。例如,我们可以利用方向键控制举行的位置,如下代码所示:
- import QtQuick 2.0
- DarkSquare {
- width: 400; height: 200
- GreenSquare {
- id: square
- x: 8; y: 8
- }
- focus: true
- Keys.onLeftPressed: square.x -= 8
- Keys.onRightPressed: square.x += 8
- Keys.onUpPressed: square.y -= 8
- Keys.onDownPressed: square.y += 8
- Keys.onPressed: {
- switch(event.key) {
- case Qt.Key_Plus:
- square.scale += 0.2
- break;
- case Qt.Key_Minus:
- square.scale -= 0.2
- break;
- }
- }
- }
Qt Quick Controls。
顾名思义,这个模块提供了大量类似 Qt Widgets 模块那样可重用的组件。本章我们将介绍 Qt Quick Controls,你会发现这个模块与 Qt 组件非常类似。
应用程序窗口 | |
用于描述应用程序的基本窗口属性的组件 | |
ApplicationWindow | 对应QMainWindow,提供顶层应用程序窗口 |
MenuBar | 对应QMenuBar,提供窗口顶部横向的菜单栏 |
StatusBar | 对应QStatusBar,提供状态栏 |
ToolBar | 对应QToolBar,提供工具栏,可以添加ToolButton和其它组件 |
Action | 对应QAction,提供能够绑定到导航和视图的抽象的用户界面动作 |
导航和视图 | |
方便用户在一个布局中管理和显示其它组件 | |
ScrollView | 对应QScrollView,提供滚动视图 |
SplitView | 对应QSplitter,提供可拖动的分割视图布局 |
StackView | 对应QStackedWidget,提供基于栈的层叠布局 |
TabView | 对应QTabWidget,提供带有标签的基于栈的层叠布局 |
TableView | 对应QTableWidget,提供带有滚动条、样式和表头的表格 |
控件 | |
控件用于表现或接受用户输入 | |
BusyIndicator | 提供忙等示意组件 |
Button | 对应QPushButton,提供按钮组件 |
CheckBox | 对应QCheckBox,提供复选框 |
ComboBox | 对应QComboBox,提供下拉框 |
GroupBox | 对应QGroupBox,提供带有标题、边框的容器 |
Label | 对应QLabel,提供标签组件 |
ProgressBar | 对应QProgressBar,提供进度条组件 |
RadioButton | 对应QRadioButton,提供单选按钮 |
Slider | 对应QSlider,提供滑动组件 |
SpinBox | 对应QSpinBox,提供微调组件 |
Switch | 提供类似单选按钮的开关组件 |
TextArea | 对应QTextEdit,提供能够显示多行文本的富文本编辑框 |
TextField | 对应QTextLine,提供显示单行文本的纯文本编辑框 |
ToolButton | 对应QToolButton,提供在工具栏上显示的工具按钮 |
ExclusiveGroup | 提供互斥 |
菜单 | |
用于构建菜单的组件 | |
Menu | 对应QMenu,提供菜单、子菜单、弹出菜单等 |
MenuSeparator | 提供菜单分隔符 |
MenuItem | 提供添加到菜单栏或菜单的菜单项 |
StatusBar | 对应QStatusBar,提供状态栏 |
ToolBar | 对应QToolBar,提供工具栏,可以添加ToolButton和其它组件 |
Repeater
能够使用 JavaScript 数组作为Repeater的模型,而 JavaScript 数组能够以对象作为其元素类型,因而Repeater就可以处理复杂的数据项,比如带有属性的对象。这种情况其实更为常见。相比普通的 JavaScript 对象,更常用的是ListElement类型。类似普通 JavaScript 对象,每一个ListElement可以有任意属性。例如下面的代码示例中,每一个数据项都有一个名字和外观颜色。
- import QtQuick 2.2
- Column {
- spacing: 2
- Repeater {
- model: ListModel {
- ListElement
- ListElement
- ListElement
- ListElement
- ListElement
- ListElement
- ListElement
- ListElement
- }
- Rectangle {
- width: 100
- height: 20
- radius: 3
- color: "lightBlue"
- Text {
- anchors.centerIn: parent
- text: name
- }
- Rectangle {
- anchors.left: parent.left
- anchors.verticalCenter: parent.verticalCenter
- anchors.leftMargin: 2
- width: 16
- height: 16
- radius: 8
- border.color: "black"
- border.width: 1
- color: surfaceColor
- }
- }
- }
- }
运行结果如下图所示:
ListElement的每个属性都被Repeater绑定到实例化的显示项。正如上面代码中显示的那样,这意味着每一个用于显示数据的Rectangle作用域内都可以访问到ListElement的name和surfaceColor属性。
动态视图 ListView和GridView 即滚动列表
Repeater适用于少量的静态数据集。但是在实际应用中,数据模型往往是非常复杂的,并且数量巨大。这种情况下,Repeater并不十分适合。于是,QtQuick 提供了两个专门的视图元素:ListView和GridView。这两个元素都继承自Flickable,因此允许用户在一个很大的数据集中进行移动。同时,ListView和GridView能够复用创建的代理,这意味着,ListView和GridView不需要为每一个数据创建一个单独的代理。这种技术减少了大量代理的创建造成的内存问题。
视图代理
与 Qt model/view 架构类似,在自定义用户界面中,代理扮演着重要的角色。模型中的每一个数据项都要通过一个代理向用户展示,事实上,用户看到的可视部分就是代理。
每一个代理都可以访问一系列属性和附加属性。这些属性及附加属性中,有些来自于数据模型,有些则来自于视图。前者为代理提供了每一个数据项的数据信息;后者则是有关视图的状态信息。
代理中最常用到的是来自于视图的附加属性ListView.isCurrentItem和ListView.view。前者是一个布尔值,用于表示代理所代表的数据项是不是视图所展示的当前数据项;后者则是一个只读属性,表示该代理所属于的视图。通过访问视图的相关数据,我们就可以创建通用的可复用的代理,用于适配视图的大小和展示特性。下面的例子展示了每一个代理的宽度都绑定到视图的宽度,而代理的背景色则根据附加属性ListView.isCurrentItem的不同而有所不同。
- import QtQuick 2.0
- Rectangle {
- width: 120
- height: 300
- gradient: Gradient {
- GradientStop
- GradientStop
- }
- ListView {
- anchors.fill: parent
- anchors.margins: 20
- clip: true
- model: 100
- delegate: numberDelegate
- spacing: 5
- focus: true
- }
- Component {
- id: numberDelegate
- Rectangle {
- width: ListView.view.width
- height: 40
- color: ListView.isCurrentItem?"#157efb":"#53d769"
- border.color: Qt.lighter(color, 1.1)
- Text {
- anchors.centerIn: parent
- font.pixelSize: 10
- text: index
- }
- }
- }
- }
模型-视图高级技术
PathView
从 XML 加载模型
分组列表
https://www.bookstack.cn/read/qt-study-road-2/340ee79fddc2d44f.md
Canvas
https://www.bookstack.cn/read/qt-study-road-2/01ab6cd4ed7ab721.md
粒子系统
https://www.bookstack.cn/read/qt-study-road-2/de0c9d561eb0f5eb.md