Система событий сигналов и обработчиков
Компоненты приложения и пользовательского интерфейса должны взаимодействовать друг с другом. Например, кнопка должна знать, что пользователь нажал на нее. Кнопка может изменить цвет, чтобы указать свое состояние, или выполнить некоторую логику. Также приложение должно знать, нажимает ли пользователь на кнопку. Приложение может потребоваться передать это событие нажатия другим приложениям.
QML имеет механизм сигналов и обработчиков, где сигнал — это событие, а на сигнал реагируют через обработчик сигнала. При отправке сигнала вызывается соответствующий обработчик сигнала. Размещение логики, такой как скрипт или другие операции, в обработчике позволяет компоненту реагировать на событие.
Получение сигналов с помощью обработчиков сигналов
Чтобы получать уведомления при отправке определенного сигнала для определенного объекта, определение объекта должно объявлять обработчик сигнала с именем on<Signal>, где <Signal> — имя сигнала с заглавной первой буквой. Обработчик сигнала должен содержать JavaScript-код, который будет выполняться при вызове обработчика сигнала.
Например, тип Кнопка из модуля Qt Quick Controls имеет сигнал clicked, который отправляется всякий раз, когда нажимается кнопка. В этом случае обработчик сигнала для получения этого сигнала должен быть onClicked. В примере ниже, всякий раз, когда нажимается кнопка, вызывается обработчик 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);
}
}
} Обработчики сигналов изменения свойств
Сигнал автоматически отправляется при изменении значения свойства 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: function (message) { console.log(message) }
} Однако невозможно пропустить ведущие параметры, к которым вы заинтересованы, но вы можете использовать какое-то имя-заполнитель, чтобы указать читателям, что они не важны:
Status {
onErrorOccurred: (_, _, col) => console.log(`Error happened at column ${col}`)
} Примечание: Вместо использования функции можно, но не рекомендуется, использовать обычный блок кода. В этом случае все параметры сигнала будут внедрены в область видимости блока. Однако это может затруднить чтение кода, так как неясно, откуда берутся параметры, и приводит к более медленным поискам в QML-движке. Внедрение параметров таким образом устарело и вызовет предупреждения во время выполнения, если параметр фактически используется.
Использование типа Connections
В некоторых случаях может быть желательно получить доступ к сигналу за пределами объекта, который его отправляет. Для этих целей модуль QtQuick предоставляет тип Connections для подключения к сигналам произвольных объектов. Объект Connections может получать любой сигнал от своего указанного target.
Например, обработчик onClicked в предыдущем примере можно было получить от корневого Прямоугольника вместо этого, разместив обработчик 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 не реагирует на сигнал completed от типа Прямоугольник. Вместо этого объект типа Component прикрепленного типа со сигналом completed автоматически прикреплен к объекту Прямоугольник движком QML. Движок отправляет этот сигнал при создании объекта Rectangle, тем самым вызывая обработчик сигнала Component.onCompleted.
Прикрепленные обработчики сигналов позволяют уведомлять объекты об определенных сигналах, которые важны для каждого отдельного объекта. Если бы не было Component.onCompleted прикрепленного обработчика сигнала, например, объект не мог бы получить это уведомление без регистрации на какой-то специальный сигнал от какого-то специального объекта. Механизм прикрепленных обработчиков сигналов позволяет объектам получать определенные сигналы без дополнительного кода.
См. Прикрепленные свойства и прикрепленные обработчики сигналов для получения дополнительной информации о прикрепленных обработчиках сигналов.
Добавление сигналов в пользовательские типы QML
Сигналы можно добавить к пользовательским типам QML с помощью ключевого слова signal.
Синтаксис определения нового сигнала:
signal <name>[([<type> <parameter name>[, ...]])]
Сигнал отправляется путем вызова сигнала как метода.
Например, код ниже определен в файле с именем SquareButton.qml. У корневого объекта Прямоугольник есть сигнал activated, который отправляется всякий раз, когда дочерний TapHandler tapped. В этом конкретном примере сигнал activated отправляется с координатами 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 могут подключиться к сигналу activated с помощью обработчика сигнала onActivated:
// myapplication.qml
SquareButton {
onActivated: (xPosition, yPosition)=> console.log("Activated at " + xPosition + "," + yPosition)
} См. Атрибуты сигналов для получения более подробной информации о написании сигналов для пользовательских типов QML.
Подключение сигналов к методам и сигналам
Объекты сигналов имеют метод connect() для подключения сигнала либо к методу, либо к другому сигналу. Когда сигнал подключен к методу, метод автоматически вызывается всякий раз, когда сигнал отправляется. Этот механизм позволяет сигналу получать сигнал методом вместо обработчика сигнала.
Ниже сигнал messageReceived подключен к трем методам с помощью метода connect():
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 позволяет сигналу получать сигнал несколькими методами, как показано ранее, что невозможно сделать с обработчиками сигналов, так как они должны иметь уникальные имена. Кроме того, метод 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)
}
} Всякий раз, когда сигнал tapped TapHandler отправляется, сигнал send также будет автоматически отправлен.
output:
MouseArea clicked
Send clicked
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtqml-syntax-signals.html