Signal and Handler Event System

应用程序和界面的各个组件需要彼此通信. 例如, 一个按钮在用户点击时, 可以改变颜色, 标识状态及执行相应逻辑. 应用程序可能需要将按钮点击事件发送到其他程序.

QML有信号和处理机制, 其中信号是事件, 通过处理程序响应. 信号发出时, 调用相应的处理程序, 处理程序运行脚本或其他逻辑.

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

若在对象中处理特定信号, 对象应该声明一个 on<Signal>, <Signal> 是信号名称, 首字母大写. 信号处理程序包含要执行的JavaScript代码.

例如, QtQuick模块的MouseArea 类型有一个 clicked 信号, 点击按钮时会发出这个信号. 在这种情况下, 接收信号的处理程序应该是 onClicked. 如下所示, 单击按钮时, 调用 onClicked 处理程序, 将 Rectangle的颜色变为随机颜色:


  import QtQuick 2.0

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

      MouseArea {
          anchors.fill: parent
          onClicked: {
              rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
          }
      }
  }

Looking at the MouseArea documentation, you can see the clicked signal is emitted with a parameter named mouse which is a MouseEvent object that contains further details about the mouse click event. This name can be referred to in our onClicked handler to access this parameter. For example, the MouseEvent type has x and y coordinates that allows us to print out the exact location where the mouse was clicked:


  import QtQuick 2.0

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

      MouseArea {
          anchors.fill: parent
          onClicked: {
              rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);

              // access 'mouse' parameter
              console.log("Clicked mouse at", mouse.x, mouse.y)
          }
      }
  }

属性改变信号处理

QML属性改变时, 自动发送信号. 信号类型是一个属性改变信号. 这类信号的格式是 on<Property>Changed, <Property> 是属性名称, 首字母大写.

例如, MouseArea 类型有一个 pressed 属性. 如果想接收这个属性的改变信号, 处理程序的名称是 onPressedChanged:


  import QtQuick 2.0

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

      MouseArea {
          anchors.fill: parent
          onPressedChanged: {
              console.log("Mouse area is pressed?", pressed)
          }
      }
  }

尽管 MouseArea 文档没有显示说明有信号处理程序 onPressedChanged, 但是 pressed 属性隐式提供说明.

使用Connections类型

某些情况下, 你可能需要访问发出信号的对象的外部信号. 为此, QtQuick 模块提供 Connections 类型, 连接任意对象的信号. Connections 对象可以接收来自其指定 target的任何信号.

例如, 通过将 onClicked 处理程序放置在targetMouseAreaConnections对象中, 可以由根 Rectangle 接收前面示例中的onClicked处理程序:


  import QtQuick 2.0

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

      MouseArea {
          id: mouseArea
          anchors.fill: parent
      }

      Connections {
          target: mouseArea
          onClicked: {
              rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
          }
      }
  }

附加信号处理

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

如下所示, Component.onCompleted 是一个附加的信号处理程序. 它通常用于创建过程完成时执行一些JavaScript代码:


  import QtQuick 2.0

  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引擎已将具有Comonent信号的组件附加类型的对象自动附加到Rectangle对象, 引擎在创建完Rectangle对象后发出此信号, 从而触发Component.onCompleted信号处理程序.

附加的信号处理程序允许向对象通知对每个单独对象都重要的特定信号. 例如, 如果没有Component.onCompleted附加的信号处理程序, 则对象在没有注册来自某个特殊对象的某个特殊信号的情况下无法接收此通知. 附加的信号处理程序机制使对象能够在没有额外代码的情况下接收特定信号.

有关附加信号处理程序的详细信息, 参见 Attached properties and attached signal handlers.

为自定义QML类型添加信号

使用signal关键字为自定义QML类型添加信号.

定义新信号的语法是:

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

发送信号的方式是如方法一样直接调用.

如下, 定义在SquareButton.qml文件的代码. 子对象MouseArea被点击时, 发出根对象Rectangle的activated信号. 这个示例中, activated信号发出时, 带有鼠标点的x和y坐标:


  // SquareButton.qml
  Rectangle {
      id: root

      signal activated(real xPosition, real yPosition)

      property int side: 100
      width: side; height: side

      MouseArea {
          anchors.fill: parent
          onPressed: root.activated(mouse.x, mouse.y)
      }
  }

现在, SquareButton的任何对象都可以使用onActivated处理程序连接activated信号:


  // myapplication.qml
  SquareButton {
      onActivated: console.log("Activated at " + xPosition + "," + yPosition)
  }

有关自定义QML类型信号的更多信息, 参见 Signal Attributes.

连接信号到信号或方法

信号对象有一个connect()方法, 可以将一个信号连接到另一个信号或方法. 信号连接到方法时, 方法在信号发出时自动调用. 这种机制使信号能被方法接收, 而不仅仅是信号处理程序.

如下所示, messageReceived 信号使用 connect() 连接三个方法:


  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方法可以通过前面所示的多个方法接收信号, 这在信号处理程序中是不可能的, 因为它们必须是唯一命名的. 此外, 当信号连接到动态创建的对象时, connect方法也很有用.

disconnect()方法用于删除连接的信号:


  Rectangle {
      id: relay
      //...

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

信号到信号连接

connect()方法可以连接信号到另一个信号, 组成不同的信号链.


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

      signal send()
      onSend: console.log("Send clicked")

      MouseArea {
          id: mousearea
          anchors.fill: parent
          onClicked: console.log("MouseArea clicked")
      }

      Component.onCompleted: {
          mousearea.clicked.connect(send)
      }
  }

MouseAreaclicked信号发出时, send信号也会自动发出.


  output:
      MouseArea clicked
      Send clicked