Qt Quick:附加属性、函数与信号详解
- [Qt Quick 学习笔记:附加属性、函数与信号详解](#Qt Quick 学习笔记:附加属性、函数与信号详解)
- [二、附加属性(Attached Properties)](#二、附加属性(Attached Properties))
-
- [2.1 什么是附加属性](#2.1 什么是附加属性)
- [2.2 学习附加属性时要注意什么](#2.2 学习附加属性时要注意什么)
- [2.3 附加属性的基本语法](#2.3 附加属性的基本语法)
- [2.4 附加信号处理器的写法](#2.4 附加信号处理器的写法)
- [2.5 附加属性示例:ListView 当前项高亮](#2.5 附加属性示例:ListView 当前项高亮)
- [2.6 附加信号处理器示例:Component.onCompleted](#2.6 附加信号处理器示例:Component.onCompleted)
- [2.7 附加属性的使用注意事项](#2.7 附加属性的使用注意事项)
- 三、函数(Function)
-
- [3.1 什么是函数](#3.1 什么是函数)
- [3.2 为什么要使用函数](#3.2 为什么要使用函数)
- [3.3 函数的基本语法](#3.3 函数的基本语法)
- [3.4 函数的返回值](#3.4 函数的返回值)
- [3.5 函数示例:按钮控制文本加减](#3.5 函数示例:按钮控制文本加减)
- [3.6 多参数函数示例](#3.6 多参数函数示例)
- [3.7 函数中可以定义变量](#3.7 函数中可以定义变量)
- [3.8 函数可以嵌套调用](#3.8 函数可以嵌套调用)
- [3.9 使用函数的建议](#3.9 使用函数的建议)
-
- 1)界面逻辑可以放在函数里
- [2)复杂逻辑不要都写在 QML 里](#2)复杂逻辑不要都写在 QML 里)
- 四、信号(Signal)
-
- [4.1 什么是信号](#4.1 什么是信号)
- [4.2 信号处理器是什么](#4.2 信号处理器是什么)
- [4.3 信号处理器的命名规则](#4.3 信号处理器的命名规则)
- [4.4 使用内置信号的示例](#4.4 使用内置信号的示例)
- 五、自定义信号
-
- [5.1 为什么要自定义信号](#5.1 为什么要自定义信号)
- [5.2 自定义信号的基本语法](#5.2 自定义信号的基本语法)
- [5.3 自定义信号示例](#5.3 自定义信号示例)
- [5.4 信号命名规则](#5.4 信号命名规则)
- 六、属性值改变信号
-
- [6.1 属性变化时会自动发出信号](#6.1 属性变化时会自动发出信号)
- [6.2 示例:监听文本变化](#6.2 示例:监听文本变化)
- [6.3 常见属性变化处理器](#6.3 常见属性变化处理器)
- [七、Connections 的使用](#七、Connections 的使用)
-
- [7.1 什么是 Connections](#7.1 什么是 Connections)
- [7.2 Connections 的基本写法](#7.2 Connections 的基本写法)
- [7.3 Connections 示例](#7.3 Connections 示例)
- [7.4 新旧写法说明](#7.4 新旧写法说明)
-
- [Qt6 推荐写法](#Qt6 推荐写法)
- 旧写法
- [八、connect() 函数](#八、connect() 函数)
-
- [8.1 connect() 是什么](#8.1 connect() 是什么)
- [8.2 connect() 的基本示例](#8.2 connect() 的基本示例)
- [8.3 一个信号可以连接多个函数](#8.3 一个信号可以连接多个函数)
- [九、综合示例:函数、信号与 Connections 一起使用](#九、综合示例:函数、信号与 Connections 一起使用)
- 十、常见错误整理
-
- [10.1 把附加属性当普通属性乱用](#10.1 把附加属性当普通属性乱用)
- [10.2 信号处理器名字写错](#10.2 信号处理器名字写错)
- [10.3 定义了函数却没调用](#10.3 定义了函数却没调用)
- [10.4 自定义信号定义了但没有发射](#10.4 自定义信号定义了但没有发射)
- [10.5 混淆 `Connections` 和对象内部的 `onXxx`](#10.5 混淆
Connections和对象内部的onXxx)
- 十一、核心知识对比表
- 十二、快速复习口诀
- 十三、复习问题
- 十四、本节总结
Qt Quick 学习笔记:附加属性、函数与信号详解
本文整理 Qt Quick 开发教程中关于 附加属性(Attached Properties) 、函数(Function) 和 信号(Signal) 的核心知识点。
这几个内容在 QML 中非常常见,尤其是在界面交互、组件通信和事件处理时,会频繁使用。
一、本节内容概览
本文主要包括以下内容:
- 什么是附加属性
- 附加属性的语法与使用方式
- 附加信号处理器
- QML 中的函数定义与调用
- 函数参数、返回值与使用建议
- 什么是信号
- 信号处理器的写法
- 自定义信号
- 属性值改变信号
Connections的使用connect()函数的作用
二、附加属性(Attached Properties)
2.1 什么是附加属性
附加属性本质上也是一种属性,只不过它不是普通对象自身直接声明出来的属性,而是:
某些特定类型,为对象额外提供的一组属性或信号处理能力。
可以简单理解成:
text
对象除了自己的普通属性之外,还能"额外拿到"的属性或信号处理器。
例如在 ListView 的委托中,经常会用到:
qml
ListView.isCurrentItem
这里的 isCurrentItem 就是 ListView 提供的附加属性。
2.2 学习附加属性时要注意什么
学习附加属性时,不需要过度纠结"它和普通属性到底有什么本质区别"。
更重要的是记住两点:
- 它的写法是什么;
- 它通常在什么场景下使用。
一句话理解:
会用,比死扣概念更重要。
2.3 附加属性的基本语法
附加属性的写法通常是:
qml
附加类型.属性名
也就是:
qml
AttachingType.propertyName
例如:
qml
ListView.isCurrentItem
其中:
ListView:附加类型isCurrentItem:附加属性
2.4 附加信号处理器的写法
附加类型不仅可以提供附加属性,有时还会提供附加信号处理器。
其写法一般为:
qml
附加类型.on信号名
例如:
qml
Component.onCompleted
这里:
Component是附加类型onCompleted是附加信号处理器
2.5 附加属性示例:ListView 当前项高亮
在 ListView 中,委托项可以通过 ListView.isCurrentItem 判断自己是否是当前选中项。
示例:
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
ListView {
anchors.fill: parent
model: ["Qt", "QML", "C++", "JavaScript"]
currentIndex: 0
delegate: Rectangle {
width: parent.width
height: 50
color: ListView.isCurrentItem ? "lightgreen" : "lightgray"
Text {
anchors.centerIn: parent
text: modelData
}
MouseArea {
anchors.fill: parent
onClicked: {
ListView.view.currentIndex = index
}
}
}
}
}
代码说明
这里最核心的一句是:
qml
color: ListView.isCurrentItem ? "lightgreen" : "lightgray"
表示:
- 如果当前委托项是选中项,则显示绿色;
- 否则显示灰色。
2.6 附加信号处理器示例:Component.onCompleted
Component.onCompleted 会在组件创建完成后执行。
示例:
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
ListModel {
id: nameModel
Component.onCompleted: {
append({ name: "Tom" })
append({ name: "Jack" })
append({ name: "Lucy" })
}
}
ListView {
anchors.fill: parent
model: nameModel
delegate: Text {
text: name
font.pixelSize: 20
}
}
}
代码说明
qml
Component.onCompleted
表示:
当
ListModel构建完成后,自动执行里面的代码。
因此这里会在模型创建完成后自动追加数据。
2.7 附加属性的使用注意事项
1)通常依赖特定场景
附加属性不是所有地方都能随便使用,它一般依赖特定对象或特定上下文。
例如:
ListView.isCurrentItem一般用于ListView的委托内部;Component.onCompleted用于对象创建完成后的处理。
2)委托中经常见到附加属性
像 ListView、GridView、PathView 等视图组件中,附加属性在代理对象里非常常见。
3)不用把它想得太复杂
开发时你可以把它看成"特殊场景下可直接使用的一类额外属性"。
三、函数(Function)
3.1 什么是函数
QML 中的函数,本质上就是:
text
函数 = 方法
它和 Java、C、C++、JavaScript 中的函数本质类似,都是用来:
- 封装逻辑
- 处理运算
- 响应事件
- 提高代码可读性
- 避免把大量逻辑写在一行里
3.2 为什么要使用函数
如果所有逻辑都直接写进信号处理器里,代码会越来越乱。
例如这种写法虽然能运行,但可读性一般:
qml
onClicked: {
label.text = Number(label.text) + 1
}
如果逻辑复杂一点,建议单独封装成函数:
qml
onClicked: {
increaseValue()
}
这样结构会更清晰。
3.3 函数的基本语法
QML 中定义函数的写法如下:
qml
function 函数名(参数列表) {
函数体
}
例如:
qml
function sayHello() {
console.log("Hello QML")
}
带参数的例子:
qml
function add(a, b) {
return a + b
}
3.4 函数的返回值
函数可以有返回值,也可以没有返回值。
例如:
qml
function add(a, b) {
return a + b
}
调用:
qml
var result = add(3, 5)
console.log(result)
输出:
text
8
如果函数没有 return,则一般不返回有效结果。
3.5 函数示例:按钮控制文本加减
下面是一个常见示例,通过函数控制文本值加减。
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
Column {
anchors.centerIn: parent
spacing: 12
Label {
id: valueLabel
text: "0"
font.pixelSize: 28
}
Row {
spacing: 10
Button {
text: "加"
onClicked: {
increaseValue()
}
}
Button {
text: "减"
onClicked: {
decreaseValue()
}
}
}
}
function increaseValue() {
valueLabel.text = Number(valueLabel.text) + 1
}
function decreaseValue() {
valueLabel.text = Number(valueLabel.text) - 1
}
}
代码说明
这里定义了两个函数:
qml
function increaseValue() { ... }
function decreaseValue() { ... }
按钮点击后分别调用不同函数,实现数值变化。
3.6 多参数函数示例
函数也可以有多个参数。
qml
function addNumbers(a, b, c) {
return a + b + c
}
调用:
qml
var result = addNumbers(10, 20, 30)
console.log("结果:", result)
输出:
text
结果: 60
3.7 函数中可以定义变量
函数内部可以定义局部变量:
qml
function calculateArea(width, height) {
var area = width * height
return area
}
调用:
qml
console.log(calculateArea(10, 20))
输出:
text
200
3.8 函数可以嵌套调用
例如:
qml
function add(a, b) {
return a + b
}
function calculate() {
var result = add(10, 20)
console.log(result)
}
这里 calculate() 调用了 add()。
3.9 使用函数的建议
1)界面逻辑可以放在函数里
例如:
- 数值加减
- UI 状态切换
- 控件显示隐藏
- 文本更新
- 简单数据处理
2)复杂逻辑不要都写在 QML 里
QML 更适合做:
- 界面描述
- 轻量交互
- UI 层逻辑
对于复杂业务逻辑,通常更适合放到 C++ 层处理。
一句话:
QML 负责界面,复杂业务逻辑尽量交给 C++。
四、信号(Signal)
4.1 什么是信号
信号可以理解为:
对象在某个事件发生时发出的通知。
也可以把它看成:
- event(事件)
- 消息通知
- 某种"触发器"
例如按钮被点击时,就会发出点击相关的信号。
4.2 信号处理器是什么
信号处理器就是:
当信号发出后,用来接收并处理这个信号的代码块。
最常见的写法就是:
qml
onClicked: {
console.log("按钮被点击了")
}
其中:
clicked是信号onClicked是信号处理器
4.3 信号处理器的命名规则
信号处理器的命名规则非常固定:
text
on + 信号名首字母大写
例如:
| 信号 | 处理器 |
|---|---|
clicked |
onClicked |
pressed |
onPressed |
released |
onReleased |
textChanged |
onTextChanged |
4.4 使用内置信号的示例
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
Label {
id: infoLabel
anchors.centerIn: parent
text: "点击区域后更新文本"
}
MouseArea {
anchors.fill: parent
onClicked: {
infoLabel.text = "你点击了界面"
console.log("MouseArea 被点击")
}
}
}
五、自定义信号
5.1 为什么要自定义信号
除了系统自带信号之外,我们还可以自己定义信号,用于:
- 自定义组件通信
- 主动通知外部对象
- 传递参数
- 解耦逻辑
5.2 自定义信号的基本语法
定义信号的语法:
qml
signal 信号名
带参数的写法:
qml
signal 信号名(参数列表)
例如:
qml
signal positionChanged(int x, int y)
5.3 自定义信号示例
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
signal positionChanged(int x, int y)
MouseArea {
anchors.fill: parent
onPressed: function(mouse) {
positionChanged(mouse.x, mouse.y)
}
}
onPositionChanged: function(x, y) {
console.log("当前点击坐标:", x, y)
}
}
代码说明
这里定义了一个自定义信号:
qml
signal positionChanged(int x, int y)
当按下鼠标时触发:
qml
positionChanged(mouse.x, mouse.y)
然后通过:
qml
onPositionChanged: function(x, y) { ... }
接收这个信号。
5.4 信号命名规则
信号命名一般建议:
- 使用小驼峰命名
- 同一作用域内不能重名
- 名字尽量表达实际含义
例如:
qml
signal loginSuccess()
signal valueChanged(int value)
signal positionChanged(int x, int y)
六、属性值改变信号
6.1 属性变化时会自动发出信号
QML 中很多属性在值发生变化时,都会自动产生对应的变化信号。
例如:
qml
text
对应的变化信号处理器就是:
qml
onTextChanged
6.2 示例:监听文本变化
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
TextField {
id: inputField
anchors.centerIn: parent
placeholderText: "请输入内容"
onTextChanged: {
console.log("当前文本:", text)
}
}
}
只要输入框内容变化,就会自动执行:
qml
onTextChanged
6.3 常见属性变化处理器
| 属性 | 处理器 |
|---|---|
text |
onTextChanged |
width |
onWidthChanged |
height |
onHeightChanged |
x |
onXChanged |
y |
onYChanged |
visible |
onVisibleChanged |
例如:
qml
Rectangle {
width: 100
onWidthChanged: {
console.log("宽度变化:", width)
}
}
七、Connections 的使用
7.1 什么是 Connections
Connections 用于:
连接外部对象的信号,并进行统一处理。
它特别适合以下情况:
- 想在当前对象中监听另一个对象的信号;
- 不方便直接在对象内部写
onXxx; - 一个对象需要集中处理外部多个信号;
- QML 和 C++ 对象之间通信。
7.2 Connections 的基本写法
qml
Connections {
target: 目标对象
function on信号名() {
// 处理逻辑
}
}
例如:
qml
Connections {
target: myButton
function onClicked() {
console.log("按钮被点击了")
}
}
7.3 Connections 示例
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
Button {
id: myButton
text: "点击我"
anchors.centerIn: parent
}
Connections {
target: myButton
function onClicked() {
console.log("通过 Connections 接收到点击信号")
}
}
}
7.4 新旧写法说明
Qt6 推荐写法
qml
Connections {
target: myButton
function onClicked() {
console.log("clicked")
}
}
旧写法
qml
Connections {
target: myButton
onClicked: {
console.log("clicked")
}
}
在新版本 Qt 中,通常更推荐使用 function 写法。
八、connect() 函数
8.1 connect() 是什么
connect() 用于:
在运行时,把某个信号和某个函数动态连接起来。
它的特点是更加灵活,尤其适合:
- 动态创建对象
- 动态绑定信号
- 一个信号连接多个函数
- 更灵活地控制信号与槽关系
8.2 connect() 的基本示例
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 300
visible: true
signal testSignal()
Component.onCompleted: {
testSignal.connect(handleSignal)
testSignal.connect(printAnotherMessage)
testSignal()
}
function handleSignal() {
console.log("handleSignal 被调用")
}
function printAnotherMessage() {
console.log("另一个处理函数也被调用")
}
}
代码说明
这里做了三件事:
- 定义信号:
qml
signal testSignal()
- 动态连接函数:
qml
testSignal.connect(handleSignal)
testSignal.connect(printAnotherMessage)
- 发射信号:
qml
testSignal()
结果是两个函数都会被调用。
8.3 一个信号可以连接多个函数
这是 connect() 很灵活的地方。
例如:
qml
mySignal.connect(funcA)
mySignal.connect(funcB)
mySignal.connect(funcC)
当 mySignal() 发出时:
funcAfuncBfuncC
都会依次执行。
九、综合示例:函数、信号与 Connections 一起使用
下面给出一个综合示例,把本节内容串起来。
qml
import QtQuick
import QtQuick.Controls
ApplicationWindow {
id: root
width: 500
height: 360
visible: true
title: "函数、信号与 Connections 示例"
signal countChangedSignal(int value)
property int count: 0
Column {
anchors.centerIn: parent
spacing: 16
Label {
id: countLabel
text: "当前值:" + count
font.pixelSize: 24
}
Row {
spacing: 10
Button {
id: addButton
text: "加一"
onClicked: {
increaseCount()
}
}
Button {
id: subButton
text: "减一"
onClicked: {
decreaseCount()
}
}
}
}
function increaseCount() {
count += 1
countChangedSignal(count)
}
function decreaseCount() {
count -= 1
countChangedSignal(count)
}
onCountChangedSignal: function(value) {
console.log("自定义信号收到的值:", value)
}
Connections {
target: addButton
function onClicked() {
console.log("Connections:点击了加一按钮")
}
}
onCountChanged: {
countLabel.text = "当前值:" + count
console.log("属性 count 变化为:", count)
}
Component.onCompleted: {
console.log("窗口创建完成")
}
}
十、常见错误整理
10.1 把附加属性当普通属性乱用
错误理解:
qml
Rectangle {
color: ListView.isCurrentItem ? "red" : "blue"
}
如果当前对象不是 ListView 的委托,这样写可能没有意义。
10.2 信号处理器名字写错
错误:
qml
onclicked: {
}
正确:
qml
onClicked: {
}
注意:
on开头- 信号名首字母大写
10.3 定义了函数却没调用
例如:
qml
function increaseValue() {
console.log("增加")
}
如果没有地方调用它,它就不会执行。
10.4 自定义信号定义了但没有发射
例如:
qml
signal loginSuccess()
如果你从来没有调用:
qml
loginSuccess()
那么信号处理器也不会执行。
10.5 混淆 Connections 和对象内部的 onXxx
如果只是处理对象自己的信号,通常直接写:
qml
Button {
onClicked: {
console.log("clicked")
}
}
如果要在外部监听它的信号,再用:
qml
Connections {
target: someButton
}
十一、核心知识对比表
| 知识点 | 语法示例 | 作用 |
|---|---|---|
| 附加属性 | ListView.isCurrentItem |
获取附加到对象上的额外属性 |
| 附加信号处理器 | Component.onCompleted |
在特定时机执行代码 |
| 函数 | function add(a,b){} |
封装逻辑、提高可读性 |
| 内置信号处理器 | onClicked |
处理对象发出的内置信号 |
| 自定义信号 | signal positionChanged(int x, int y) |
自定义事件通知 |
| 属性变化处理器 | onTextChanged |
监听属性值变化 |
| Connections | Connections { target: xxx } |
监听外部对象的信号 |
| connect() | mySignal.connect(func) |
动态绑定信号与函数 |
十二、快速复习口诀
text
附加属性:特殊场景下可直接使用的额外属性。
函数:用来封装逻辑,减少代码混乱。
信号:对象发生事件时发出的通知。
信号处理器:用 onXxx 来接收和处理信号。
Connections:在外部监听对象信号。
connect():动态把信号和函数关联起来。
十三、复习问题
学完本节后,可以尝试回答下面这些问题:
- 什么是附加属性?
ListView.isCurrentItem通常用在什么场景?Component.onCompleted的作用是什么?- QML 中函数的基本写法是什么?
- 为什么复杂逻辑不建议全部写在 QML 里?
- 什么是信号?
onClicked属于什么?- 如何自定义一个带参数的信号?
onTextChanged是哪类信号处理器?Connections适合在什么情况下使用?- Qt6 中
Connections推荐使用哪种写法? connect()和普通onXxx写法有什么区别?
十四、本节总结
这一节可以归纳为三句话:
附加属性
text
附加属性是在特定场景下,类型额外提供给对象使用的属性或信号处理能力。
函数
text
函数用于封装逻辑,让界面代码更清晰、更好维护。
信号
text
信号用于通知事件发生,信号处理器负责接收并处理这些通知。
一句话总结:
在 QML 中,附加属性解决特殊场景下的属性访问问题,函数负责封装逻辑,信号负责驱动交互和通信。