Signal- und Handler-Ereignissystem
Anwendungs- und Benutzerschnittstellenkomponenten müssen miteinander kommunizieren. So muss beispielsweise eine Schaltfläche wissen, dass der Benutzer sie angeklickt hat. Die Schaltfläche kann ihre Farbe ändern, um ihren Zustand anzuzeigen oder eine bestimmte Logik auszuführen. Außerdem muss die Anwendung wissen, ob der Benutzer die Schaltfläche anklickt. Möglicherweise muss die Anwendung dieses Klickereignis an andere Anwendungen weiterleiten.
QML verfügt über einen Signal- und Handler-Mechanismus, bei dem das Signal das Ereignis ist und auf das Signal durch einen Signal-Handler reagiert wird. Wenn ein Signal ausgelöst wird, wird der entsprechende Signal-Handler aufgerufen. Durch das Einfügen von Logik, wie z. B. einem Skript oder anderen Operationen, in den Handler kann die Komponente auf das Ereignis reagieren.
Empfangen von Signalen mit Signalhandlern
Um eine Benachrichtigung zu erhalten, wenn ein bestimmtes Signal für ein bestimmtes Objekt ausgegeben wird, sollte in der Objektdefinition ein Signal-Handler mit dem Namen on<Signal> deklariert werden, wobei <Signal> der Name des Signals ist, wobei der erste Buchstabe groß geschrieben wird. Der Signal-Handler sollte den JavaScript-Code enthalten, der ausgeführt werden soll, wenn der Signal-Handler aufgerufen wird.
Der Typ Button aus dem Modul Qt Quick Controls verfügt zum Beispiel über ein Signal clicked
, das immer dann ausgegeben wird, wenn die Schaltfläche angeklickt wird. In diesem Fall sollte der Signalhandler für den Empfang dieses Signals onClicked
sein. Im folgenden Beispiel wird bei jedem Anklicken der Schaltfläche der Handler onClicked
aufgerufen, der dem übergeordneten Rectangle eine zufällige Farbe zuweist:
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); } } }
Hinweis: Auch wenn Signal-Handler ein wenig wie JavaScript-Funktionen aussehen, sollten Sie sie nicht direkt aufrufen. Wenn Sie Code zwischen Signalhandlern und anderen Funktionen austauschen müssen, wandeln Sie ihn in eine separate Funktion um. Andernfalls sollten Sie das Signal immer ausgeben, wenn Sie möchten, dass der Signal-Handler aufgerufen wird. Für ein und dasselbe Signal kann es mehrere Handler in verschiedenen Bereichen geben.
Handler für Eigenschaftsänderungssignale
Ein Signal wird automatisch ausgesendet, wenn sich der Wert einer QML-Eigenschaft ändert. Dieser Signaltyp ist ein Eigenschaftsänderungssignal, und Signalhandler für diese Signale werden in der Form on<Property>Changed geschrieben, wobei <Property> der Name der Eigenschaft ist, wobei der erste Buchstabe groß geschrieben wird.
Zum Beispiel hat der Typ MouseArea eine Eigenschaft pressed. Um eine Benachrichtigung zu erhalten, wenn sich diese Eigenschaft ändert, schreiben Sie einen Signalhandler mit dem Namen onPressedChanged
:
import QtQuick Rectangle { id: rect width: 100; height: 100 TapHandler { onPressedChanged: console.log("taphandler pressed?", pressed) } }
Auch wenn in der Dokumentation TapHandler kein Signalhandler mit dem Namen onPressedChanged
dokumentiert ist, wird das Signal implizit durch die Tatsache bereitgestellt, dass die Eigenschaft pressed
existiert.
Signal-Parameter
Signale können Parameter haben. Um auf diese zuzugreifen, sollten Sie dem Handler eine Funktion zuweisen. Sowohl Pfeilfunktionen als auch anonyme Funktionen funktionieren.
Für die folgenden Beispiele betrachten wir eine Status-Komponente mit einem errorOccurred-Signal (siehe Hinzufügen von Signalen zu benutzerdefinierten QML-Typen für weitere Informationen darüber, wie Signale zu QML-Komponenten hinzugefügt werden können).
// 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}`) }
Hinweis: Die Namen der formalen Parameter in der Funktion müssen nicht mit denen im Signal übereinstimmen.
Wenn Sie nicht alle Parameter verarbeiten müssen, können Sie die nachgestellten Parameter weglassen:
Status { onErrorOccurred: message => console.log(message) }
Es ist nicht möglich, führende Parameter, an denen Sie interessiert sind, wegzulassen, aber Sie können einen Platzhalternamen verwenden, um den Lesern anzuzeigen, dass sie nicht wichtig sind:
Status { onErrorOccurred: (_, _, col) => console.log(`Error happened at column ${col}`) }
Hinweis: Anstelle einer Funktion kann auch ein einfacher Codeblock verwendet werden, wovon jedoch abgeraten wird. In diesem Fall werden alle Signalparameter in den Bereich des Blocks injiziert. Dies kann jedoch die Lesbarkeit des Codes erschweren, da unklar ist, woher die Parameter kommen, und führt zu langsameren Suchvorgängen in der QML-Engine. Die Injektion von Parametern auf diese Weise ist veraltet und führt zu Laufzeitwarnungen, wenn der Parameter tatsächlich verwendet wird.
Verwendung des speziellen Objekts arguments
In JavaScript können Sie auf das spezielle Objekt arguments
verweisen, das, wenn es verfügbar ist, den Zugriff auf die Werte der an eine Nicht-Pfeil-Funktion übergebenen Argumente als array-ähnliches Objekt ermöglicht.
Es ist normalerweise im Körper einer Funktion oder eines Codeblocks verfügbar, der einem Signalhandler zugewiesen ist.
Wenn ein Codeblock oder eine anonyme Funktion dem Signalhandler zugewiesen wird, stellt das spezielle Objekt arguments
die Argumente bereit, die vom Signal übergeben wurden.
Zum Beispiel werden die beiden folgenden Zeilen [object Arguments] world undefined
drucken:
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") }
Das Verhalten ist anders, wenn dem Signalhandler eine Pfeilfunktion zugewiesen wird. Dann ist es immer noch möglich, auf das spezielle Objekt arguments
zuzugreifen, aber es wird ein leeres Array-ähnliches Objekt sein.
Zum Beispiel wird das Folgende [object Arguments] undefined undefined
ausgeben:
import QtQml QtObject { id: root signal hello(message: string) onHello: () => { console.log(arguments, arguments[0], arguments[1]) } Component.onCompleted: root.hello("world") }
Der Unterschied im Verhalten ist auf die Art und Weise zurückzuführen, wie das spezielle Objekt arguments
mit den Pfeilfunktionen interagiert, entspricht aber dem allgemeinen Verhalten für Bindungen.
Laut Spezifikation besitzt eine Pfeilfunktion nicht ihr eigenes arguments
Spezialobjekt. Da eine Pfeilfunktion immer noch von ihrem umschließenden Kontext borgt, kann sie das spezielle Objekt arguments
borgen, wenn eines vorhanden ist.
Eine Bindung stellt bei der Auswertung ihren eigenen Bereich zur Verfügung. Insbesondere wird der Abruf der zugrundeliegenden Pfeilfunktion in dem Bereich durchgeführt, der durch die Auswertung der Bindung bereitgestellt wird.
Im Gültigkeitsbereich der Bindung wird kein Argument angegeben, so dass ein leeres arguments
Spezialobjekt verfügbar ist und von der Pfeilfunktion beim Abruf ausgeliehen wird.
Da eine Nicht-Pfeilfunktion das spezielle Objekt arguments
in ihrem eigenen Bereich zur Verfügung stellt, kann sie sich auf die Argumente beziehen, die der zugrunde liegenden Funktion selbst zur Verfügung gestellt wurden, d. h. auf die vom Signal bereitgestellten weitergeleiteten Argumente.
Die Verwendung des arguments
Spezialobjekts sollte generell zugunsten der Verwendung von benannten Parametern vermieden werden, die eindeutiger sind und unabhängig von der Verwendung einer Pfeil- oder Nicht-Pfeil-Funktion konsistent funktionieren.
Verwendung des Typs Connections
In manchen Fällen kann es wünschenswert sein, auf ein Signal außerhalb des Objekts zuzugreifen, das es aussendet. Für diese Zwecke bietet das Modul QtQuick
den Typ Connections für die Verbindung mit Signalen beliebiger Objekte. Ein Connections Objekt kann jedes Signal von seinem angegebenen target empfangen.
Zum Beispiel hätte der onClicked
Handler im vorherigen Beispiel stattdessen von der Wurzel Rectangle empfangen werden können, indem man den onClicked
Handler in einem Connections Objekt platziert, dessen target auf button
gesetzt ist:
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); } } }
Angehängte Signal-Handler
Ein angehängter Signal-Handler empfängt ein Signal von einem anhängenden Typ und nicht von dem Objekt, in dem der Handler deklariert ist.
Zum Beispiel ist Component.onCompleted ein angehängter Signal-Handler. Er wird häufig verwendet, um JavaScript-Code auszuführen, wenn sein Erstellungsprozess abgeschlossen ist. Hier ist ein Beispiel:
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) } }
Der onCompleted
Handler reagiert nicht auf ein completed
Signal vom Typ Rectangle. Stattdessen wurde von der QML-Engine automatisch ein Objekt des Typs Component
mit einem completed
-Signal an das Objekt Rectangle angehängt. Die Engine gibt dieses Signal aus, wenn das Rectangle-Objekt erstellt wird, und löst damit den Component.onCompleted
signal handler aus.
Durch angehängte Signalhandler können Objekte über bestimmte Signale benachrichtigt werden, die für jedes einzelne Objekt von Bedeutung sind. Gäbe es z. B. keinen Component.onCompleted
attached signal handler, könnte ein Objekt diese Benachrichtigung nicht erhalten, ohne sich für ein spezielles Signal von einem speziellen Objekt zu registrieren. Der Mechanismus der angehängten Signalhandler ermöglicht es Objekten, bestimmte Signale ohne zusätzlichen Code zu empfangen.
Weitere Informationen über angehängte Signalhandler finden Sie unter Angehängte Eigenschaften und angehängte Signalhandler.
Hinzufügen von Signalen zu benutzerdefinierten QML-Typen
Signale können zu benutzerdefinierten QML-Typen mit dem Schlüsselwort signal
hinzugefügt werden.
Die Syntax für die Definition eines neuen Signals lautet:
signal <name>[([<type> <parameter name>[, ...]])]
Ein Signal wird durch den Aufruf des Signals als Methode ausgelöst.
Der folgende Code ist beispielsweise in einer Datei namens SquareButton.qml
definiert. Das Stammobjekt Rectangle hat ein Signal activated
, das immer dann ausgegeben wird, wenn das untergeordnete Objekt TapHandler tapped
ist. In diesem speziellen Beispiel wird das aktivierte Signal mit den x- und y-Koordinaten des Mausklicks ausgegeben:
// 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 } }
Nun können beliebige Objekte des SquareButton
über einen onActivated
Signalhandler eine Verbindung zu dem activated
Signal herstellen:
// myapplication.qml SquareButton { onActivated: (xPosition, yPosition) => console.log(`Activated at {xPosition}, ${yPosition}`) }
Siehe Signalattribute für weitere Details zum Schreiben von Signalen für benutzerdefinierte QML-Typen.
Verbinden von Signalen mit Methoden und Signalen
Signalobjekte haben eine connect()
Methode um ein Signal entweder mit einer Methode oder einem anderen Signal zu verbinden. Wenn ein Signal mit einer Methode verbunden ist, wird die Methode automatisch aufgerufen, sobald das Signal ausgesendet wird. Dieser Mechanismus ermöglicht es, dass ein Signal von einer Methode anstelle eines Signalhandlers empfangen werden kann.
Im Folgenden wird das Signal messageReceived
mit drei Methoden verbunden, wobei die Methode connect()
verwendet wird:
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}`) } }
In vielen Fällen ist es ausreichend, Signale über Signalhandler zu empfangen, anstatt die connect()-Funktion zu verwenden. Mit der Methode connect
kann ein Signal jedoch, wie oben gezeigt, von mehreren Methoden empfangen werden, was mit Signal-Handlern nicht möglich wäre, da diese eindeutig benannt werden müssen. Außerdem ist die Methode connect
nützlich, wenn Signale mit dynamisch erstellten Objekten verbunden werden.
Es gibt eine entsprechende disconnect()
-Methode zum Entfernen von verbundenen Signalen:
Rectangle { id: relay //... function removeTelegraphSignal() { relay.messageReceived.disconnect(sendToTelegraph) } }
Signal zu Signal verbinden
Durch die Verbindung von Signalen mit anderen Signalen können mit der Methode connect()
verschiedene Signalketten gebildet werden.
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) } }
Immer wenn das Signal tapped
von TapHandler ausgegeben wird, wird automatisch auch das Signal send
ausgegeben.
output: MouseArea clicked Send clicked
Hinweis: Verbindungen zu Funktionsobjekten bleiben so lange bestehen, wie der Absender des Signals aktiv ist. Dieses Verhalten ist analog zu der 3-Argument-Version von QObject::connect() in C++.
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 }) } } } } } }
Im obigen Beispiel ist das Ziel, die Farbe jedes geraden Rechtecks so zu ändern, dass sie einer globalen Farbe folgt. Um dies zu erreichen, wird für jedes gerade Rechteck eine Verbindung zwischen dem Signal globalColorChanged und einer Funktion zum Setzen der Farbe des Rechtecks hergestellt. Dies funktioniert wie erwartet, solange die Rechtecke aktiv sind. Sobald jedoch die Löschtaste gedrückt wird, sind die Rechtecke verschwunden, aber die Funktion, die das Signal verarbeitet, wird immer noch jedes Mal aufgerufen, wenn das Signal ausgegeben wird. Dies lässt sich an den Fehlermeldungen erkennen, die von der Funktion ausgegeben werden, die versucht, im Hintergrund zu laufen, wenn sie die globale Farbe ändert.
In der derzeitigen Konfiguration würden die Verbindungen erst zerstört, wenn das Element, das globalColor enthält, zerstört wird. Um zu verhindern, dass die Verbindungen weiter bestehen bleiben, können sie explizit getrennt werden, wenn die Rechtecke zerstört werden.
© 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.