信号和处理程序事件系统

应用程序和用户界面组件需要相互通信。例如,按钮需要知道用户是否点击了它。按钮可能会改变颜色,以显示其状态或执行某些逻辑。应用程序也需要知道用户是否点击了按钮。应用程序可能需要将点击事件转发给其他应用程序。

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 属性的存在已隐含地提供了该信号。

信号参数

信号可能有参数。要访问这些参数,应为处理程序分配一个函数。箭头函数和匿名函数都可以使用。

在以下示例中,请考虑带有 errorOccurred 信号的 Status 组件(有关如何将信号添加到 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 引擎的查找速度减慢。以这种方式注入参数已被弃用,如果参数被实际使用,将导致运行时警告。

使用连接类型

在某些情况下,可能需要在发出信号的对象之外访问信号。为此,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 信号。相反,一个带有completed 信号的Component 附加类型对象已被 QML 引擎自动附加Rectangle 对象。当创建 Rectangle 对象时,引擎会发出这个信号,从而触发Component.onCompleted 信号处理程序。

附加信号处理程序允许对象收到对每个对象都很重要的特定信号。举例来说,如果没有Component.onCompleted 附加信号处理器,对象就无法在不注册某些特殊对象发出的特殊信号的情况下收到此通知。附加信号处理机制使对象无需额外代码即可接收特定信号。

有关附加信号处理器的更多信息,请参阅附加属性和附加信号处理器

为自定义 QML 类型添加信号

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

定义新信号的语法是

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

信号是通过调用信号作为方法发出的。

例如,下面的代码定义在名为SquareButton.qml 的文件中。根Rectangle 对象有一个activated 信号,每当子TapHandler tapped 时,就会发出该信号。在这个特定示例中,激活的信号是以鼠标点击的 x 和 y 坐标发出的:

// 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 信号处理器连接到activated 信号:

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

有关为自定义 QML 类型编写信号的更多详情,请参阅信号属性(Signal Attributes)。

将信号连接到方法和信号

信号对象有一个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: 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)
    }
}

每当TapHandlertapped 信号发出时,send 信号也会自动发出。

output:
    MouseArea clicked
    Send clicked

注意: 只要信号发送者还活着,与函数对象的连接就会一直存在。这种行为类似于 C++ 中QObject::connect() 的 3 参数版本。

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 的项目被销毁后,连接才会被销毁。为了防止连接继续存在,可以在销毁矩形时显式断开连接。

© 2025 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.