本页内容

信号与处理程序事件系统

应用程序和用户界面组件需要相互通信。例如,一个按钮需要知道用户是否点击了它。该按钮可能会改变颜色以指示其状态,或执行某些逻辑操作。同样,应用程序也需要知道用户是否点击了该按钮。应用程序可能需要将此点击事件传递给其他应用程序。

QML 提供了一种信号与处理程序机制,其中信号即为事件,而通过信号处理程序来响应该信号。当信号被发出时,相应的信号处理程序会被调用。在处理程序中放置脚本或其他操作等逻辑,可使组件对事件做出响应。

使用信号处理程序接收信号

若要在特定对象发出特定信号时接收通知,该对象的定义应声明一个名为on<Signal> 的信号处理程序,其中<Signal>是信号的名称,首字母大写。该信号处理程序应包含在被调用时需执行的 JavaScript 代码。

Button 例如,来自 Qt Quick Controls 模块中的clicked 类型具有一个 信号,该信号在按钮被点击时会发出。在这种情况下,用于接收此信号的信号处理程序应为onClicked 。在下面的示例中,每当按钮被点击时,onClicked 处理程序就会被调用,从而为父对象Rectangle 应用一种随机颜色:

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);
        }
    }
}

注意:尽管 信号处理程序看起来有点像 JavaScript 函数,但你不 应该直接调用它们。如果你需要在信号处理程序和其他功能之间共享代码,应将其重构为一个单独的函数。否则,如果你希望调用信号处理程序,请始终发出该信号。同一个信号可以有多个处理程序,且位于不同的作用域中。

属性变更信号处理程序

当 QML 属性的值发生变化时,系统会自动发出一个信号。此类信号称为属性变化信号,其处理程序的写法为on<Property>Changed,其中<Property>是属性的名称,首字母大写。

例如,MouseArea 类型具有一个名为pressed 的属性。若要在该属性发生变化时接收通知,请编写一个名为onPressedChanged 的信号处理程序:

import QtQuick

Rectangle {
    id: rect
    width: 100; height: 100

    TapHandler {
        onPressedChanged: console.log("taphandler pressed?", pressed)
    }
}

尽管TapHandler 的文档中并未记录名为onPressedChanged 的信号处理程序,但鉴于pressed 属性确实存在,该信号被隐式地提供了。

信号参数

信号可能带有参数。要访问这些参数,应为处理程序赋值一个函数。箭头函数和匿名函数均可使用。

在以下示例中,假设有一个名为 Status 的组件,它具有一个名为 errorOccurred 的信号(有关如何向QML 组件添加信号的更多信息,请参阅“向自定义 QML 类型添加信号”)。

// 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: message => console.log(message)
}

虽然无法省略您感兴趣的开头参数,但可以使用一些占位符名称来向读者表明这些参数并不重要:

Status {
    onErrorOccurred: (_, _, col) => console.log(`Error happened at column ${col}`)
}

注意:虽然可以使用 普通代码块代替 函数,但这种做法虽可行,却不被推荐。 在这种情况下,所有信号参数都会被注入到代码块的作用域中。但是,这可能会导致代码难以阅读,因为无法明确参数的来源,并且会导致 QML 引擎中的查找速度变慢。以这种方式注入参数已被弃用,如果参数被实际使用,将会引发运行时警告。

使用 `arguments` 特殊对象

在 JavaScript 中,您可以引用 `arguments ` 特殊对象,当该对象可用时,它允许将传递给非箭头函数的参数值作为数组样式对象进行访问。

通常在函数体中,或被赋值给信号处理程序的代码块中,该对象是可用的。

当将代码块或匿名函数赋值给信号处理程序时,特殊的arguments 对象将提供由该信号传递过来的参数。

例如,以下两种情况都会输出[object Arguments] world undefined :

import QtQml

QtObject {
    id: root

    signal hello(message: string)

    onHello: { console.log(arguments, arguments[0], arguments[1]) }

    Component.onCompleted: root.hello("world")
}
import QtQml

QtObject {
    id: root

    signal hello(message: string)

    onHello: function () { console.log(arguments, arguments[0], arguments[1]) }

    Component.onCompleted: root.hello("world")
}

当将箭头函数赋值给信号处理程序时,行为会有所不同。此时,虽然仍然可以访问arguments 这个特殊对象,但它将是一个空的数组样式对象。

例如,以下代码将输出[object Arguments] undefined undefined :

import QtQml

QtObject {
    id: root

    signal hello(message: string)

    onHello: () => { console.log(arguments, arguments[0], arguments[1]) }

    Component.onCompleted: root.hello("world")
}

这种行为差异源于arguments 特殊对象与箭头函数的交互方式,但这与绑定的一般行为是一致的。

根据规范,箭头函数不携带自己的 `arguments ` 特殊对象。由于箭头函数仍会从其外围上下文中“借用”变量,因此如果可用,它可以“借用” `arguments ` 特殊对象。

绑定在求值时提供其自身的作用域。特别是,底层箭头函数的获取是在绑定求值所提供的作用域中进行的。

在绑定作用域内未提供任何参数,因此当箭头函数检索时,将有一个空的arguments 特殊对象可用并被其借用。

由于非箭头函数会在其自身作用域中提供arguments 特殊对象,因此它可以引用提供给底层函数本身的参数,这些参数即由信号传递的转发参数。

通常应避免使用arguments 特殊对象,而应优先使用命名参数,因为命名参数更明确,且无论使用箭头函数还是非箭头函数,其行为都保持一致。

使用 Connections 类型

在某些情况下,可能需要访问其外部对象发出的信号。为此,QtQuick 模块提供了Connections 类型,用于连接任意对象的信号。一个Connections 对象可以接收其指定的target 发出的任何信号。

例如,前例中的onClicked 处理程序本可以由根Rectangle 接收,方法是将onClicked 处理程序放入一个Connections 对象中,并将该对象的target 设置为button :

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);
        }
    }
}

附加的信号处理程序

附加的信号处理程序接收来自附加类型的信号,而不是接收来自声明该处理程序的对象发出的信号。

例如,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)
    }
}

onCompleted 处理程序并非响应来自Rectangle 类型的completed 信号。而是,QML 引擎已自动将一个带有completed 信号的Component 附加类型对象附加到了 Rectangle 对象上。当 Rectangle 对象被创建时,引擎会发出该信号,从而触发Component.onCompleted 信号处理程序。

已附加的信号处理程序允许对象接收对其自身具有重要意义的特定信号。例如,如果没有Component.onCompleted 这个已附加的信号处理程序,对象就无法接收该通知,除非它注册了来自某个特定对象的特殊信号。已附加的信号处理程序机制使对象无需额外代码即可接收特定信号。

有关附加信号处理程序的更多信息,请参阅“附加属性与附加信号处理程序”。

向自定义 QML 类型添加信号

可以通过signal 关键字将信号添加到自定义QML类型中。

定义新信号的首选语法是:

signal <name>[([<parameter name> : <type>[, ...]])]

此外还有一种旧语法,即类型位于名称之前:

signal <name>[([<type> <parameter name>[, ...]])]

您应该使用新语法。

通过将信号作为方法调用,即可触发该信号。

例如,以下代码定义在名为SquareButton.qml 的文件中。根对象Rectangle 具有一个activated 信号,每当子对象TapHandler 处于tapped 状态时,该信号就会被触发。在这个具体示例中,被触发的信号会带上鼠标点击的x和y坐标:

// SquareButton.qml
import QtQuick

Rectangle {
    id: root

    signal activated(xPosition: real, yPosition: real)
    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 信号处理程序连接到activated 信号:

// myapplication.qml
SquareButton {
    onActivated: (xPosition, yPosition) => console.log(`Activated at {xPosition}, ${yPosition}`)
}

有关为自定义 QML 类型编写信号的更多详细信息,请参阅《信号属性》。

将信号连接到方法和信号

信号对象具有一个connect() 方法,用于将信号连接到方法或另一个信号。当信号连接到方法时,每当该信号被发射,该方法就会自动被调用。这种机制使得信号可以由方法而非信号处理程序来接收。

下面,通过 `connect() ` 方法将 `messageReceived ` 信号连接到三个方法:

import QtQuick

Rectangle {
    id: relay

    signal messageReceived(person: string, notice: string)

    Component.onCompleted: {
        relay.messageReceived.connect(sendToPost)
        relay.messageReceived.connect(sendToTelegraph)
        relay.messageReceived.connect(sendToEmail)
        relay.messageReceived("Tom", "Happy Birthday")
    }

    function sendToPost(person: string, notice: string) {
        console.log(`Sending to post: ${person}, ${notice}`)
    }
    function sendToTelegraph(person: string, notice: string) {
        console.log(`Sending to telegraph: ${person}, ${notice}`)
    }
    function sendToEmail(person: string, notice: string) {
        console.log(`Sending to email: ${person}, ${notice}`)
    }
}

在许多情况下,通过信号处理程序接收信号就足够了,无需使用 connect() 函数。但是,如前所述,使用connect 方法允许一个信号被多个方法接收,而这在信号处理程序中是不可能实现的,因为信号处理程序必须具有唯一的名称。此外,connect 方法在将信号连接到动态创建的对象时非常有用。

还有一个对应的disconnect() 方法用于移除已连接的信号:

Rectangle {
    id: relay
    //...

    function removeTelegraphSignal() {
        relay.messageReceived.disconnect(sendToTelegraph)
    }
}

信号与信号的连接

通过将信号与其他信号连接,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 信号也会自动被触发。

output:
    MouseArea clicked
    Send clicked

注意: 只要信号的发送方仍处于活动状态,与函数对象的连接 就会保持有效。这种行为与 C++ 中QObject::connect() 的三参数版本类似。

Window {
    visible: true
    width: 400
    height: 400

    Item {
        id: item
        property color globalColor: "red"

        Button {
            text: "Change global color"
            onPressed: {
                item.globalColor = item.globalColor === Qt.color("red") ? "green" : "red"
            }
        }

        Button {
            x: 150
            text: "Clear rectangles"
            onPressed: repeater.model = 0
        }

        Repeater {
            id: repeater
            model: 5
            Rectangle {
                id: rect
                color: "red"
                width: 50
                height: 50
                x: (width + 2) * index + 2
                y: 100
                Component.onCompleted: {
                    if (index % 2 === 0) {
                        item.globalColorChanged.connect(() => {
                            color = item.globalColor
                        })
                    }
                }
            }
        }
    }
}

在上文的这个人为构造的示例中,目标是让每个偶数编号的矩形翻转颜色,以匹配某个全局颜色。为实现这一目标,针对每个偶数编号的矩形,都会在 `globalColorChanged` 信号与一个用于设置该矩形颜色的函数之间建立连接。只要这些矩形仍处于活动状态,该机制便能按预期工作。 然而,一旦按下“清除”按钮,矩形便消失了,但处理该信号的函数在每次信号发射时仍会被调用。这可以从更改全局颜色时,该函数试图在后台运行而抛出的错误消息中看出。

在当前的实现中,只有当存储 globalColor 的对象被销毁时,这些连接才会被断开。为了防止连接长期存在,可以在矩形被销毁时显式地断开这些连接。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.