B站25号底片编程的Qt5/Qt6/QML教程,带你手把手搓一个网易云音乐前端的Qml基础知识部分
记录以供自己查询使用,零基础推荐以视频为主(直接看可能会有的摸不着头脑)
结合个人使用需要,25-28 图像处理相关、29 Blend、38-39 问题汇总 跳过
目录
4)RadioButton 单选按钮(继承自AbstractButton)
2)Column 列布局(类似 Row,无 layoutDirection)
5)ColumnLayout 列布局(同RowLayout)
十一、ListView 列表(视图、代理、模型)(些许抽象)(支持高亮滚动、添加、删除及动画)
一、基础概念
1)属性
1. F1可以查看当前类的属性
2. 拥有所有父类属性
3. 可以自定义属性:type 常见 int、real、double、string、list、color…
① default property type name: value
② readonly property type name: value // 只读属性,不可修改
③ required property type name
name: value // 定义后程序才可运行
2)函数
Component.onCompleted: { // 在渲染时
console.log("adfsads") // 输出内容,同 qDebug()、cout
aaa.aaaColor = "#333333" // 更改颜色
}
3)基础控件 Item
1. width,height // 略
z:堆叠层级,大的遮小的,相同时后者遮前者
visible:true / false // 是否可见
scale:1.5 // 放大到原来的1.5倍
rotation:50 // 旋转角度
opacity:0.5 // 透明度
2. x,y // Rectangle { …… Rectangle { …… } } 内部的偏移以外部偏移后的坐标为原点
3. clip:true // 限制子控件展示在本控件范围内
4)锚布局 Item及子类都支持
1. 给控件命名 id(例 Rectangle { id: aaa } Rectangle { id: bbb } )
2. anchors.left: aaa.right // bbb的左侧与aaa的右侧相连,此外还有 top、right、bottom
3. anchors.leftMargin: 10 // 左边距为10,可以为负
4. anchors.centerIn: aaa // bbb 永远在 aaa 的中心
5. anchors.verticalCenter: aaa.verticalCenter // bbb在aaa的垂直中心,同理有horizontalCenter
6. anchors.fill: aaa // bbb变形以填充aaa
二、简单控件
1)Text
Text {
id: text
width: 150 // 与wrepMode共同决定是否换行
text: "hello world"
font.family: "黑体"
font.pixlSize: 30
color: "blue"
wrepMode: Text.NoWrap // 不换行
// Text.WordWrap 单词间隙间换行
// Text.WrapAnywhere 超过宽度就换行
}
2)TextField
TextField { // 文本输入框
width: 300
height: 50
font.family: "黑体"
placeholderText: "请输入密码" // 输入文本前的提示
echoMode: TextInput.Password // 输入的字符以“·”显示
selectByMouse: true // 鼠标选中
maximumLength: 3 // 输入字符最大长度
horizontalAlignment: Text.AlignHCenter // H:输入的文本在水平方向居中
background: Rectangle { // 设置背景颜色
anchors.fill: parent // Rectangle填充母控件
border.color: "red" // 边框颜色
border.width: 1 // 边框宽度
color: "yellow" // Rectangle颜色
radius: 20 // 圆角,since Qt 6.7 有 topLeftRadius、topRightRadius、bottomLeftRadius、bottomRightRadius ← Qt5 可以由 clip 间接实现,
}
}
补充:horizontalAlignment: Text.AlignHCenter 可以改为
leftPadding: 100 // 输入文本的左边距,同理有rightPadding、topPadding、bottomPadding,高度、字符大小、上或下内边距同时限制可能导致密码字符只显示一半,如下图:

3)TextArea
TextArea { // 输入大文本并进行文本预览的控件,继承自 TextEdit
id: ttt
anchors.fill: parent
font.underline: true //下划线
wrapMode、font、selectByMouse 等同上
background: Rectangle { 同上 }
}
4)Image
Image {
// 支持锚布局
sourece: "/img/img/pic.png"
}
补充:往项目中添加资源文件
项目根目录右键 → 添加新文件 → Qt - Qt Reasource File → 添加前缀 → 添加文件(选中图片)
目录中选择资源文件右键 → copy path( 删除 “ :” )→ source: "/img/img/pic.png"
三、事件系统
1)鼠标事件
Rectangle {
……
MouseArea {
anchors.fill: parent // 可以检测到鼠标事件的区域
hoverEnabled: true // 是否支持鼠标移入移出效果
onClicked: { // F1 查看 Signals 即为可以触发的事件,on+"事件(首字母大写)"
aaa.color = "yellow" // aaa 变为黄色
}
onEntered: {
aaa.color = "red"
}
onExited: {
aaa.color = "cyan"
}
onWheel: { // 鼠标滚轮事件
if ( wheel.angleDelta.y>0 ) {
console.log("滚轮往前滚了")
}
else {
console.log("滚轮往后滚了")
}
}
}
2)键盘事件(代码接上)
focus: true // 聚焦键盘事件
Keys.onPressed: {
if ( event.key == Qt.Key_A ) {
console.log ( "AAAA" )
}
}
}
补充:关于等号,“ === ” 比 “ == ” 判断强度更强
var a = 1
var b = "1"
console.log ( a, b, a == b, a === b ) // 结果为 1,1,true,false
3)拖拽事件 (鼠标事件相关)
DropArea { // 大区域,被拖进的区域,不可见的
width: 300
height: 300
Rectangle { // 因可见而被用于设置背景颜色,① 默认为白色
id: ppp
anchors.fill: parent
}
onEntered: {
ppp.color = "pink" // ② 有东西被拖进后变为粉色
}
}
Rectangle { // 小区域,被拖的
id: aaa
width: 100
height: 100
color: "cyan" // 区分颜色
Drag.active: area.drag.active // 鼠标区域 area 激活拖动
MouseArea {
id: area
anchors.fill: parent
drag.target: aaa // 鼠标拖动时,移动的是id为aaa的Rectangle
drag.axis: XAndYAxis // XAxis 只能水平移动、YAxis 只能垂直移动
onReleased: ppp.color = "white" // ③ 释放时被拖的区域变(恢复)为白色
}
}
四、带事件的控件
1)Button(继承自AbstractButton)
Button {
width:
height:
text: "text"
anchors.centerIn: parent
icon.source: "……" // 图标在文字前,同TextBesideIcon,还有IconOnly、TextOnly
display: AbstractButton.TextUnderIcon // 文字在图标下
onClicked: console.log ( "按钮被按下+松开" ) // 鼠标释放在按钮点击范围内完成
onCanceled: console.log ( "按钮被取消" ) // 鼠标释放在按钮外完成
onPressed: console.log ( "按钮被按下" )
onPressAndHold: console.log ( "按钮被长按" )
}
2)DelayButton 延时按钮(继承自Button)
DelayButton { // 长按触发某事件,有进度条
delay: int // 延迟时间
onProgressChanged: {
console.log ( " 当前进度 ", progress ) // 打印长按延时按钮的进度,0 → 1
}
onActivated: { // 槽函数(Activated 是信号 signal ),类似 onClicked
console.log ( " time out " )
}
}
3)Switch(继承自AbstractButton)
Switch {
onPositionChanged : {
console.log ( "postion: ", position ) //观察属性 position 随按钮位置的变化,type是real
}
onVisualPositionChanged : {
console.log ( "visualpostion: ", position ) //观察属性 visualposition,同position
}
onCheckedChanged : {
console.log ( "checked: ", checked ) // bool类型,判断开关状态
}
}
4)RadioButton 单选按钮(继承自AbstractButton)
RadioButton{ // 不设置ButtonGroup、在同一个母控件下时,默认多个只能选中一个
onCheckedChanged : {
checked? console.log ( "被选中了" ) : console.log ( "被取消选中了" )
}
}
分组方式 1 —— 用母控件分类
Item{
RadioButton{
}
RadioButton{
}
}
Item{
RadioButton{
}
RadioButton{
}
RadioButton{
}
}
效果:按钮1、2互斥,按钮3、4、5互斥
分组方式 2 —— ButtonGroup
ButtonGroup {
id: groupA
exclusive: false // 组内按钮不互斥
}
ButtonGroup {
id: groupB
exclusive: true // 互斥
}
RadioButton{
y: 0 // 避免重叠
ButtonGroup.group: groupA // 分组
}
RadioButton{
y: 40
ButtonGroup.group: groupA
}
RadioButton{
y: 80
ButtonGroup.group: groupA
}
RadioButton{
y: 120
ButtonGroup.group: groupB // 分组
}
RadioButton{
y: 160
ButtonGroup.group: groupB
}
效果:按钮1、2、3不互斥,按钮4、5互斥
五、弹出型窗口控件
1)通用型 Popup
Button {
onClicked: { // 弹出动作需要事件触发,故设置按钮用于弹出窗口
pop.open() // open()、close() 为 Popup 的方法 Methods,默认点空白区域能关闭
}
}
Popup {
id: pop
modal: true // 模态对话框 —— 当前对话框未关闭无法点击母控件
closePolicy: Popup.NoAutoClose // 无法自动关闭
anchors.centerIn: parent // 因为没有边,所以锚布局只能使用这个
background: Rectangle{ // 属性 —— 背景,区别于信号Signal 、方法 Method
anchors.fill: parent
color: "red"
}
onClosed: { // Closed 信号用于槽函数 onClosed,与 Methods(用于对象本身)区分
console.log ( "closed。" )
}
onOpened:{
console.log ( "opened。" )
}
onAboutToHide : { // 在关闭之前,同理还有signal:aboutToShow()在打开之前
consold.log ( "hide..." )
}
}
补充:其它枚举类型
还有Popup.CloseOnEscape(默认)
Popup.CloseOnPressOutside(默认)
Popup.CloseOnPressOutsideParent(母窗口之外)、
Popup.CloseOnReleaseOutside(松开后)
Popup.CloseOnReleaseOutsideParent
补充:其他属性

2)Dialog
Dialog {
flags: // 控制是否显示边框上方的标题栏 → 是否为无边框窗口
modality: NonModal // 非模态,未关闭时可以操作母窗口
title: string // 未设置时沿用母窗口标题
standardButtons: Dialog.Ok | Dialog.Cancel // 默认有Ok,没有cancel,右上关闭“x”close
onAccepted : {
console.log ( "ok…" ) // 因为默认有OK所以可直接用,onRejected需先启用cancel按钮
}
}
补充:其它枚举类型
Qt.WindowModal 未关闭时可以操作与母子窗口没有关系的窗口,例如 cpp 文件中的 widget 与 qml 文件中的 Dialog 没有联系,同一 Item(母)控件下的 Window 无法在 Dialog 关闭前进行操作
Qt.ApplicationModal 未关闭时无法操作其他任何窗口
补充的补充:新建一个widget
1.pro 文件中第一行加入 QT += widgets
2.main.cpp:
① #include <QWidget>
② main中加入以下代码
Qwidget subWidget ;
subWidget.setWindowRitle( "Sub Window" ) ;
subWidget.show() ;
3)FileDialog(继承自Dialog)
Platform.FileDialog { // Qt5 需 import Qt.labs.platform 1.1 as Platform 多模块同控件指定版本
id: pop // 用于弹出窗口
acceptLabel: "打开" // 按钮重命名,还有rejectLabel
fileMode: OpenFiles // 选中多个文件,默认OpenFile,还有SaveFile
onAccepted: {
console.log (currentFile) // 用低版本的FileDialog
}
}
4)FolderDialog(继承自Dialog)
Platform.FolderDialog { // 选择文件夹
id:pop // 用于弹出窗口
onAccepted: {
console.log (currentFolder)
}
}
since Qt6.3:import QtQuick.Dialogs since
Qt5:import Qt.labs.platform 1.1 as Platform
5)ColorDialog 颜色选择对话框
ColorDialog { //有默认宽高,继承自系统
id:pop
onAccepted: {
console.log (currenrColor)
}
}
6)FontDialog 字体
FontDialog {
id:pop
onAccepted: {
console.log (currenrFont.bold, currenrFont.family) // 复合属性Font.xxx
}
}
7)MessageDialog 弹出一个消息
Platform.MessageDialog {
id:pop
buttons: MessageDialog.Ok | MessageDialog.Cancel
text: "这是一个消息对话框" //默认有Ok、关闭
}
补充:使用时 pro文件 += widgets,main.cpp:QGuiApplocation → QApplication
六、图形效果&动画系统
1)state 状态(每个 Item 都有)
Rectangle {
id: btn
……
states: {
State {
name: "normal"
PropertyChanges {
target: btn
color: "red"
}
PropertyChanges {
target: txt
text: "放进来了"
}
}, // 不要忽略逗号
State {
name: "hovered"
PropertyChanges {
target: btn
color: "yellow"
}
PropertyChanges {
target: txt
text: "移出去了"
}
}
}
……
2)Transaction 过渡(代码接上)
transitions: {
Transition {
from: "normal"
to: "hovered"
ColorAnimation {
target: btn
duration: 2000 // 2s
}
}, // 不要忽略逗号
Transition {
from: "hovered"
to: "normal"
ColorAnimation {
target: btn
duration: 2000
}
}
}
Text {
id:txt
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onRntered: {
btn.state = "hovered"
}
onExited: {
btn.state = "normal"
}
}
}
3)PropertyAnimation 属性动画
1. 方法一:用 Button 触发动画
Button {
onClicked: ani1.restart() // ani1.start()
}
PropertyAnimation {
id: ani1
target: btn // 上方1) 2) 中的Rectangle
property: "width" // 发生动画的属性
from: 100
to: 400
easing.type: Easing.OutQuart //枚举类型,不同曲线变速动画效果
}
2. 方法二:直接在目标中使用,程序启动时执行
Rectangle {
……
PropertyAnimation on width { to: 400; duration: 1000 } //按钮矩形宽度由 100 拉伸 400
}
3. 方法三:把属性 PropertyAnimation 写到过渡动画 Transition 中
Rectangle {
id: btn
width: 100
……
transitions: {
Transition {
from: "normal" // states复用1) 中定义
to: "hovered"
ColorAnimation { // 可以直接由 PropertyAnimation 实现,property 改为 "color"
target: bth
duration: 300
}
PropertyAnimation { // 对比方法一少了id、from,easing.type改duration
target: btn
property: "width" // 发生动画的属性
to: 300
duration: 300
}
}
Transition {
from: "hovered"
to: "normal"
ColorAnimation {
target: bth
duration: 300
}
PropertyAnimation {
target: btn
property: "width"
to: 100
duration: 300
}
}
}
}
效果:鼠标移入时,按钮由宽度由100变为300、颜色由红变黄,鼠标移出时恢复
补充:没有id是因为Rectangle有id了,不能重复;少了from,是因为width已经有了;改为duration是为了与颜色过渡动画实现视觉同步
4)Behavior 预设行为动画(写在作业控件中)
Rectangle {
id: btn
width: 100
……
Behavior on width { // 只有宽度的数值随事件的变化而变化时使用
NumberAnimation { duration: 1000 } // 数值动画
}
MouseArea {
……
onClicked: { // 事件1
parent.width = 400 // 宽度数值变化时自动使用已经预设的行为动画
}
onDoubleClicked: { // 事件2
parent.width = 700 // 宽度数值变化
}
}
}
6)组合动画
ParallelAnimation { // ani1 与 ani2 一起封装为并行动画
id: pAni
PropertyAnimation {
id: ani1
……
onStopped: {
ani2.restart
}
}
PropertyAnimation {
id: ani2
……
}
}
Rectangle {
id: btn
……
MouseArea {
……
onClicked: {
pAni.start () // 执行并行动画
}
}
}
串行动画 SequentialAnimation 同理
七、图像处理相关(略)
25-28 节跳过
八、图像的混合(高级图像处理)
1)ColorOverlay 颜色覆盖
Item {
id: item
width: 400
height: 400
anchors.centerIn: parent
}
Image {
id: img1
scale: 0.1
source: "/img/img/apple_green.png"
}
ColorOverlay { // 常用来动态修改ui的图标颜色
anchors.fill: item // 若img1,覆盖对象则为图片,而需覆盖的是item控件内可见的某对象
source: img1 // 进行图形混合的对象
color: "purple" // 透明度略
}
2)多图片混合 Blend 略
九、投影和发光(模糊略)
1)DropShadow 投影
Rectangle {
id: rect
……
}
DropShadow {
anchors.fill: rect // 大小
source: rect // 产生阴影的对象
horizontalOffset: 10 // 水平偏移
verticalOffset
radius: 10 // 模糊半径
samples: radius * 2 // 采样结果,通常设为radius * 2
transparentBorder: true // 边界过渡
}
设模糊半径 radius 效果

设模糊半径 radius & 采样结果 samples 效果

设模糊半径 radius & 采样结果 samples & 边界过渡transparentBorder 效果

2)InnerShadow 内阴影
属性同投影(外阴影)

3)Glow 外边框发光
Glow {
anchors.fill: rect
source: rect
color: "green"
radius: 10 // 发光往外延伸的半径
samples: radius * 2 // 固定计算公式
}
设模糊半径 radius 效果

设模糊半径 radius & 采样结果 samples 效果

4)RectanglarGlow 内部发光
RectanglarGlow {
anchors.fill: rect
color: "red"
glowRadius: 10 // 发光半径,距边框 10 像素时渐变消失
cornerRadius: Math.min(rect.width, rect.height)/2 + glowRadius // 发光圆角,常用公式
}
设发光半径 glowRadius 效果

设发光半径 glowRadius & 圆角半径 cornerRadius 效果

十、布局系统
1)Row 行布局
Row {
spacing: 10
anchors.centerIn: parent
layoutDirection: Qt.RightToLeft // 从右至左,默认为从左至右
topPadding: 300 // 向下偏移 300 像素
Rectangle {
id: rect1
width: 100
height: 100
color: " red "
}
Rectangle {
id: rect2
width: 100
height: 100
color: " green "
}
Rectangle {
id: rect3
width: 100
height: 100
color: " pink "
}
}
2)Column 列布局(类似 Row,无 layoutDirection)
3)Grid 网格布局
Grid {
spacing: 10
columns: 2 // 列数,一列满两个后自动下一列
flow: Grid.TopToButtom // 默认先从左往右
anchors.centerIn: parent
Rectangle {
id: rect1
width: 100
height: 100
color: " red "
}
Rectangle {
id: rect2
width: 100
height: 100
color: " green "
}
Rectangle {
id: rect3
width: 100
height: 100
color: " pink "
}
}
4)RowLayout 行布局
与 Row 区别:自动拉伸,随窗口大小变化,即固定与初始窗口大小的比例
RowLayout {
spacing: 10
anchors.fill: parent
Rectangle {
id: rect1
width: 100
height: 100
color: " red "
Layout.minimumWidth: 50 // 限制最大最小宽高
Layout.minimumHeight: 50
Layout.fillHeight: true // 自动填充母控件
Layout.fillWidth: true
}
Rectangle {
id: rect2
width: 100
height: 100
color: " green "
……
}
Rectangle {
id: rect3
width: 100
height: 100
color: " pink "
……
}
}
5)ColumnLayout 列布局(同RowLayout)
6)GridLayout 网格布局
GridLayout {
rowSpacing: 10
columnSpacing: 20
columns: 2 // 先把网格布局的区域根据columns和Rectangle个数分为2行2列
anchors.fill: parent
Rectangle {
id: rect1
width: 100
height: 100
color: "red"
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop // 枚举类型
}
Rectangle { …… }
Rectangle { …… }
}
十一、ListView 列表(视图、代理、模型)(些许抽象)(支持高亮滚动、添加、删除及动画)
必备模块(Qt5):
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Dialogs 1.3
import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.15
import QtQml.Models 2.15
代理 delegate:每一条数据应该显示的形式
模型 model:数据内容
1)列表视图
ListModel { // 列表模型
id: listModel
ListElement { // 列表元素 “ 属性名:属性值 ”
name: " 张三 "
age: 14
}
}
ListView { //继承自 Flickable 可弹动控件
id: listView
anchors.fill: parent
// model: [ "A", "B", "C" ]
model: listModel
spacing: 10 // 间距
cacheBuffer: int // 提前缓存好还未显示的数据
delegate: Item { // 为避免干扰 Rectangle 中样式,由 Rectangle 改为不可见的 Item
width: listView.width // 通常与母控件等宽,也可使用parent.width
height: 50
// 形式一:直接填Text,生成与母控件等宽的列表项
// 使用 Rectangle 后需要在其中分别定义样式
// color: "red"
// border.color: "yellow"
// border.width: 1
// Text: {
// anchors.centerIn: parent
// font.pixelSize: 30
// // text: modelData // 对应 model 字符串数组形式时的文本内容
// text: name // 使用 ListModel 时直接填属性名,还能为 "name + age"
// }
// 形式二: 类似窗口中的描述,如直接使用两个 Rectangle 分开显示两个属性
Rectangle {
id: leftRect
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: parent.width/2 - 5 // -5 是因为间距为10
color: "red"
border.color: "yellow"
border.width: 1
Text: { // Rectangle 中的 Text
anchors.centerIn: parent
font.pixelSize: 30
text: name
}
}
Rectangle {
id: rightRect
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: parent.width/2 - 5
color: "red"
border.color: "yellow"
border.width: 1
Text: {
anchors.centerIn: parent
font.pixelSize: 30
text: age
}
}
}
}
2)高亮
ListView {
……
onCountChanger: {
decrementCurrentIndex() // 确保高亮位置不变,不随数据添加而移动
// 可以直接用 MouseArea 中的 listView.currentIndex 加一、减一
}
highlight: { // 高亮类别的代理,同表头,删除文本内容,更换color。需先设MouseArea
……
z: 2 // 未设置时 highlight 会被 delegate 覆盖
function setText (name,age) { // 定义方法供 delegate 中的 MouseArea 调用
//未设置时 highlight 效果只有高亮没有文本
nameHighlightText.text = name
ageHighLightText.text = age
}
Rectangle {
id: nameHighlightText
……
}
Rectangle {
id: ageHighlightText
……
}
}
delegate {
……
MouseArea { // 因为一开始数据展示在delegate上,所以要在 delegate中设置
anchors.fill: parent
hoverEnabled: true
onEntered: {
listView.currentIndex = index // 内置属性,当前 delegate 是视图中第几条数据
// 设置后可以用 currentItem 调用当前 delegate的Item
listView.highlightItem.setText ( name, age ) // 调用 highlight中的函数
}
}
}
}
3)表头表尾
ListView {
……
footerPositioning: listView.OverlayFooter // 固定在窗口底部,避免在列表底部被隐藏
headerPositioning: listView.OverlayHeader
header: Item { …… } // text 使用字符串如 “姓名”、“年龄” 而非 ListModel 的属性
// 删除 id 因为无用且重复
// 更改 color,其它同 delegate,本质是增加一个格式相同的表头
footer: Rectangle { // 本质形式与 header、delegate 通用
width: listView.width
height: 50
color: "pink"
text: "表尾"
z: 3 // 避免固定后被滚动的数据内容遮盖
}
delegate: Item { …… }
}

4)添加、删除及对应动画
Column {
spacing: 10
anchors.right: parent.right
Button {
text: "添加一条数据"
onClicked: {
listModel.append ( { name: "王二" + listView.count , age:14 } )
// 尾部插入,属性: 属性值
// listModel.insert ( 0, { name: "王二" + listView.count , age:14 } )
// 指定插入位置 → 头插法
}
}
Button {
text: "删除一条数据"
onClicked: listModel.remove (listModel.count - 4) // 删除倒数第四条数据
}
}
ListView {
id: rect1
anchors.fill: parent
anchors.rightMargin: 200 //空出右侧放添加和删除的按钮
model: listModel
……
add: Transition { // 在最下方添加数据时,被添加数据从初始位置移动到目标位置的动画
NumberAnimation { properties: "y"; duration: 2000 }
}
addDisplaced: Transition { // 因新数据加入而移动的旧数据的位置变化的过渡动画
NumberAnimation { properties: "y"; duration: 300 }
}
remove: Transition { // 被删除数据渐渐消失
NumberAnimation { properties: "opacity"; from: 1; to: 0; duration: 300 }
}
removeDisplaced: Transition { // 某条数据被删后,其他数据向上滑动的动画
NumberAnimation { properties: "y"; duration: 300 }
}
……
}
5)Section 分组
ListView {
……
// 动画结束
section {
properity: "name"
labelPosition: ViewSection.CurrentLabelAtStart // 只在最开始显示当前分组的标签
criteria: ViewSection.FirstCharacter // 显示第一个字符
delegate: Rectangle { // 分组标签的形式
height: 50
width: 50
radius: 25 // 分组标签的形式:球
Text {
anchors.centerIn: parent
text: section // 内置属性
}
}
}
……
// 高亮、表头、表尾、代理部分代码
}
6)一些其它方法
int indexAt (real x, real y) // 根据坐标返回当前数据的索引
Item itemAt (real x, real y) // 根据坐标返回当前 item 的内容
Item itemAtIndex (int index) // 根据索引返回当前 item 的内容
// 获得Item可以直接调用function(),例如:
ListView {
……
delegate: Item {
……
function dy () { console.log ("打印") }
……
}
MouseArea {
……
onClicked: {
ListView.itemAtIndex(0).dy()
}
}
}
positionViewAtBeginning () // 每次数据更新后,自动定位到开头。如头插法每次查看最新数据
positionViewAtEnd () // 数据更新后自动定位到列表尾部
positionViewAtAtIndex () // 数据更新后自动定位到指定索引对应的数据的位置
Column {
……
Button {
text: "添加一条数据"
onClicked: {
listModel.append( { name: "王二" + listView.count, age:14 } )
listView.positionViewAtEnd ()
}
}
Button { …… }
}
十二、自定义控件
1)同级目录
1. 创建控件

![]()

首字母大写

直接把 main.qml 中 Window 的控件定义放到新建控件文件中
2. 使用控件
同级目录直接调用:MyButton { }
构建 - 执行qmake - 运行

3. 灵活使用
background 的宽高与 Button 的绑定,调用组件时定义的宽高会覆盖组件文件里原本的定义


2)控件目录下
1. 创建控件
根目录中创建文件夹 controls,在其中创建 qml 文件 MyRect.qml(新建 txt 文件后改后缀)

添加现有文件,选中刚创建的qml文件

MyRect.qml(把前文 MyButton 中的 background 的 Rectangle 提出来)

2. 使用控件
main.qml 中 import "./controls" 相对路径,然后调用 MyRect { }
构建 - 执行qmake - 运行
十三、C++ 访问控件
0)main.qml
Window { // 将用到的定义
……
signal sig() // 定义信号
onSig: {
console.log("sig is triggered")
}
function test Arg (arg) { // 定义函数
console.log ("the arg is:", arg)
return "recieved"
}
Rectangle {
objectName: "rect" // 不是id
width: 300
height: 300
color: "red"
anchors.centerIn: parent
}
}
补:id 与 objectName 区别
id 通常用于在QML文件中引用对象。
objectName 主要用于在Qt应用程序的C++代码中引用QML对象。
main.cpp // 1) 2) 3)中代码连续
1)通过C++修改QML文件中的属性
#include <QQmlProperty> // 修改qml属性时需要
#include <QGuiApplication>
#include <QmlApplicationEngine>
int main(int argc, char *argv[ ] )
{
……
engine.load(url)
Qobject* obj = engine.rootObjects().first(); // 只有一个 root 即 Window
Qobject* rect = obj -> findChild<QObject*>("rect"); // 在Window中找objectName为rect的控件
QmlProperty(obj, "title").write("hello window"); // 把 Window 的 title 改为 "hello window"
QmlProperty(rect, "color").write("yellow"); // 把 rect 的 color 属性改为 "yellow"
2)使用QML中的信号
QmetaObject::invokeMethod(obj,"sig") // 发出信号sig,QML 中的 onSig() 被触发
3)使用QML中的方法
Qvariant ret; // 接收返回值
QMetaObject::invokeMethod(obj,"testArg", Q_RETURN_ARG(QVARIANT,ret), Q_ARG(QVariant,"import_argc") );
// 调用QML中 Window 定义的方法 testArg,返回类型为 QVARIANT 的参数 ret,传入类型为 QVARIANT (弱类型)的参数 import_argc
return app.exec( );
}
4)C++中使用信号与槽
1. 创建新的 class(包括头文件)


2. testqmlsignal.h
class TestQmlSignal : public QObject
{
……
public slots:
void print (); // 声明当前类的函数
};

3. testqmlsignal.cpp // 类名大写,文件名小写。定义当前类里的某函数
#include "testqmlsignal.h"
#include "qdebug.h"
TestQmlSignal::TestQmlSignal(QObject *parent)
: QObject {parent}
{}
void TestQmlSignal::print()
{
qdebug() << "hello c++";
}
4. main.cpp // 将信号与槽绑定
#include <testqmlsignal.h>
int main(int argc, char *argv[ ] )
{
……
engine.load(url)
TextQmlSignal* test = new TestQmlSignal // 创建C++类文件时定义的类名
QObject::connect (obj , SIGNAL(sig()) , test , SLOT(print()) );
// QML文件中obj(Window)发出信号sig()时,触发test中的print()槽函数
return app.exec( );
}
十四、QML访问C++
1)访问单一属性
main.cpp
#include <……>
#include <QQmlContext>
int main(int argc,char *argv[ ])
{
……
engine.load(url);
engine.rootContext() -> setContextProperty("globalProperty","assvfbdrb");
// 注册一个值为assvfbdrb的属性globalProperty
return app.exec();
}
main.qml
Window{
……
rectangle {
……
Text {
id: name
text: globalProperty // 代码不会自动补全
}
}
}
2)访问C++的类
1. 在头文件中定义类的属性

2. 鼠标移动到属性名称上,Alt+Enter
自动在对应cpp文件中生成get、set方法
自动在头文件中生成对应public函数、signals


自动在头文件中属性定义的下方添加:
Q_PROPERTY(int pro1 READ getPro1 WRITE setPro1 NOTIFY pro1Changed FINAL)
// 用 getPro1() 来读int类型的属性pro1,用 setPro1() 来写,用 pro1Changed 来通知变化

手动在头文件的 public 函数前添加 Q_INVOKABLE

3. 把C++类注册为模块的某个控件,在 engine.load(url) 渲染之前完成注册
main.cpp
qmlRegisterType<TestQmlSignal>("TestQmlSignal", 1, 0, "TestQmlSignal");
// <类型>("模块名",主版本号,副版本号,"控件名(一模块可以有多个控件)")
4. qml中使用
main.qml
import TestQmlSignal 1.0
Window {
……
TestQmlSignal {
id: test
}
Rectangle {
……
Text {
id: name
text: test.pro1
Component.onCompleted: {
// test.pro1 = 56 // 方法一:直接赋值
test.setPro1(66) // 方法二:使用函数
}
}
}
}
十五、WorkerScript 多线程异步处理数据
0)未使用 WorkerScript 方案
main.qml
Window {
……
Timer {
interval: 1 // 每1ms执行一次代码
running: true // 运行中
repeat: true // 重复运转
onTriggered: {
for (let i = 0; i < listModel.count; i++) {
let data = listModel.get (i) // 获取某一条数据
listModel.set( i, {val1: data.val1 + 0.001, val2: data.val2 + 0.001, val3: data.val3 + 0.001, val4: data.val4 + 0.001, val5: data.val5 + 0.001})
}
}
}
ListModel { // 在一开始就往 ListView 中添加一万条数据
id: listModel
Component.onCompleted: {
for( let i = 0; i<10000; i++) {
listModel.append( {vall:0.001, val2:0.002, val3:0.003, val4:0.004, val5:0.005})
}
}
}
……
}
1)搭建异步框架
Window {
……
WorkerScript {
id: worker
source: "qrc:/worker.js" // 创建新js文件后复制的url路径
}
……
}








2)主线程往子线程传参
worker.js
WorkerScript.onMessage = function func(msg) { // 重写函数 WorkerScript.onMessage
// let listModel = msg
let listModel = msg.model // 对应onTriggered中传入参数的变化
for (let i = 0; i < listModel.count; i++) { // 原本 main.qml-Timer-onTriggered 中的内容
let data = listModel.get (i) // 获取某一条数据
listModel.set( i, {val1: data.val1 + 0.001, val2: data.val2 + 0.001, val3: data.val3 + 0.001, val4: data.val4 + 0.001, val5: data.val5 + 0.001})
listModel.syne() // 多线程中有效,将异步处理的数据同步更新到界面中
}
main.qml
Window{
……
Timer {
……
onTriggered: {
// worker.sendMessage(listModel)
worker.sendMessage({model:listModel, val:111}) // 还可以传送对象
}
}
……
}
3)子线程往主线程传参
worker.js
WorkerScript.onMessage = function func(msg) { // 重写函数 WorkerScript.onMessage
WorkerScript.sendMessage("safvbawrhetjs")
listModel.syne() // 多线程中有效,将异步处理的数据同步更新到界面中
}
main.qml
Window {
……
WorkerScript {
id: worker
source: "qrc:/worker.js"
onMessage: {
console.log( messageObject, typeof(messageObject) )
// 打印结果:safvbawrhetjs string
}
}
……
}


1万+

被折叠的 条评论
为什么被折叠?



