QML 具有信号和处理程序机制。当发出信号时,会调用相应的信号处理程序。
一、使用信号处理程序接收信号
要在为特定对象发出特定信号时接收通知,对象定义应声明一个名为 on<Signal> 的信号处理程序,其中 <Signal> 是信号的名称,首字母大写。信号处理程序应包含在调用信号处理程序时要执行的 JavaScript 代码。
例如,来自 Qt Quick Controls 模块的 Button 类型有一个 clicked 信号,只要单击按钮就会发出该信号。在这种情况下,用于接收此信号的信号处理程序应为 onClicked:
import QtQuick
import QtQuick.Controls
Rectangle
{
id: rect
width: 250; height: 250
Button
{
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: "Change color!"
onClicked:
{
rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
}
}
}
1.1、属性更改信号处理程序
当 QML 属性的值发生变化时,会自动发出信号。 这种类型的信号是属性更改信号,这些信号的信号处理程序以 on<Property>Changed 的形式编写,其中 <Property> 是属性的名称,首字母大写。
例如,TapHandler 类型的 pressed 属性:
import QtQuick
Rectangle
{
id: rect
width: 100; height: 100
TapHandler
{
onPressedChanged: console.log("taphandler pressed?", pressed)
}
}
1.2、信号参数
信号可能有参数。要访问这些,应该为处理程序分配一个函数。可以使用箭头函数和匿名函数。
示例:
// Status.qml
import QtQuick
Item
{
id: myitem
signal errorOccurred(message: string, line: int, column: int)
}
Status
{
onErrorOccurred: (mgs, line, col) => console.log(`${line}:${col}: ${msg}`)
}
注意:函数中形式参数的名称不必与信号中的名称匹配。
如果不需要处理所有参数,则可以省略尾随参数:
Status
{
onErrorOccurred: function (message) { console.log(message) }
}
不能遗漏前导参数,但是可以使用一些占位符名称来向读者表明它们并不重要:
Status
{
onErrorOccurred: (_, _, col) => console.log(`Error happened at column ${col}`)
}
可以使用纯代码块代替函数,但不鼓励使用。在这种情况下,所有信号参数都被注入到块的作用域中。但是,这会使代码难以阅读,因为不清楚参数的来源,并导致 QML 引擎中的查找速度变慢。 不推荐以这种方式注入参数,如果实际使用该参数会导致运行时警告。
1.3、使用连接类型
在某些情况下,可能需要访问发出它的对象之外的信号。为了这些目的,QtQuick 模块提供了 Connections 类型来连接到任意对象的信号。Connections 对象可以接收来自其指定目标的任何信号。
示例:
import QtQuick
import QtQuick.Controls
Rectangle
{
id: rect
width: 250; height: 250
Button
{
id: button
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: "Change color!"
}
Connections
{
target: button
function onClicked()
{
rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
}
}
}
1.4、附加的信号处理程序
附加信号处理程序从附加类型而不是在其中声明处理程序的对象接收信号。
例如,Component.onCompleted 是一个附加的信号处理程序。它通常用于在其创建过程完成时执行一些 JavaScript 代码。下面是一个例子:
import QtQuick
Rectangle
{
width: 200; height: 200
color: Qt.rgba(Qt.random(), Qt.random(), Qt.random(), 1)
Component.onCompleted:
{
console.log("The rectangle's color is", color)
}
}
QML 引擎自动将具有完成信号的 Component 附加类型的对象附加到 Rectangle 对象。创建 Rectangle 对象时,引擎会发出此信号,从而触发 Component.onCompleted 信号处理程序。
二、向自定义 QML 类型添加信号
可以通过 signal 关键字将信号添加到自定义 QML 类型。
定义新信号的语法是:
signal <name>[([<type> <parameter name>[, ...]])]
2.1、通过将信号作为方法调用来发出信号。
示例:
// SquareButton.qml
import QtQuick
Rectangle
{
id: root
signal activated(real xPosition, real yPosition) //定义信号
property point mouseXY
property int side: 100
width: side; height: side
TapHandler
{
id: handler
onTapped: root.activated(root.mouseXY.x, root.mouseXY.y) //发出信号
onPressedChanged: root.mouseXY = handler.point.position
}
}
现在 SquareButton 的任何对象都可以使用 onActivated 信号处理程序连接到激活的信号:
// myapplication.qml
SquareButton
{
onActivated: (xPosition, yPosition)=> console.log("Activated at " + xPosition + "," + yPosition)
}
三、将信号连接到方法和信号
信号对象有一个 connect() 方法来将信号连接到一个方法或另一个信号。当信号连接到方法时,只要发出信号,就会自动调用该方法。 这种机制使信号能够由方法而不是信号处理程序接收。
下面,使用 connect() 方法将 messageReceived 信号连接到三个方法:
import QtQuick
Rectangle
{
id: relay
signal messageReceived(string person, string notice)
Component.onCompleted:
{
relay.messageReceived.connect(sendToPost)
relay.messageReceived.connect(sendToTelegraph)
relay.messageReceived.connect(sendToEmail)
relay.messageReceived("Tom", "Happy Birthday")
}
function sendToPost(person, notice)
{
console.log("Sending to post: " + person + ", " + notice)
}
function sendToTelegraph(person, notice)
{
console.log("Sending to telegraph: " + person + ", " + notice)
}
function sendToEmail(person, notice)
{
console.log("Sending to email: " + person + ", " + notice)
}
}
使用 connect() 允许多个方法接收信号。此外,connect() 在将信号连接到动态创建的对象时很有用。
有一个对应的 disconnect() 方法用于移除连接的信号:
Rectangle
{
id: relay
//...
function removeTelegraphSignal()
{
relay.messageReceived.disconnect(sendToTelegraph)
}
}
3.1、信号到信号连接
通过将信号连接到其他信号,connect() 方法可以形成不同的信号链。
import QtQuick
Rectangle
{
id: forwarder
width: 100; height: 100
signal send()
onSend: console.log("Send clicked")
TapHandler
{
id: mousearea
anchors.fill: parent
onTapped: console.log("Mouse clicked")
}
Component.onCompleted:
{
mousearea.tapped.connect(send) // 信号连接到信号
}
}
每当 TapHandler 的 tapped 信号发出时,send() 信号也将自动发出。输出:
MouseArea clicked
Send clicked