Tastaturfokus in Qt Quick
Wenn eine Taste gedrückt oder losgelassen wird, wird ein Tastenereignis erzeugt und an den fokussierten Qt Quick Item gesendet. Um die Konstruktion von wiederverwendbaren Komponenten zu erleichtern und um einige der einzigartigen Fälle von flüssigen Benutzeroberflächen zu adressieren, fügen die Qt Quick Elemente eine scope-basierte Erweiterung des traditionellen Tastaturfokusmodells von Qt hinzu.
Überblick über die Tastenbehandlung
Wenn der Benutzer eine Taste drückt oder loslässt, geschieht Folgendes:
- Qt empfängt die Tastenaktion und erzeugt ein Tastenereignis.
- Wenn QQuickWindow die focus window der Anwendung ist, wird das Tastenereignis an sie geliefert.
- Das Tastenereignis wird von der Szene an das Item mit aktivem Fokus weitergeleitet. Wenn kein Element den aktiven Fokus hat, wird das Tastenereignis ignoriert.
- Wenn das QQuickItem mit aktivem Fokus das Tastenereignis annimmt, wird die Weitergabe beendet. Andernfalls wird das Ereignis an das übergeordnete Element gesendet, bis das Ereignis akzeptiert wird oder das Stammelement erreicht ist.
Wenn der Typ
Rectangle
im folgenden Beispiel den aktiven Fokus hat und die TasteA
gedrückt wird, wird das Ereignis nicht weiter übertragen. Wenn die TasteB
gedrückt wird, wird das Ereignis an das Stammelement weitergeleitet und somit ignoriert.Rectangle { width: 100; height: 100 focus: true Keys.onPressed: (event)=> { if (event.key == Qt.Key_A) { console.log('Key A was pressed'); event.accepted = true; } } }
- Wenn das Stammelement Item erreicht wird, lautet das Tastenereignis ignored und die normale Qt-Tastenbehandlung wird fortgesetzt.
Siehe auch die Keys attached property und KeyNavigation attached property.
Abfrage des aktiven Fokus-Elements
Ob ein Item den aktiven Fokus hat oder nicht, kann über die Eigenschaft Item::activeFocus
abgefragt werden. Hier haben wir zum Beispiel einen Text Typ, dessen Text dadurch bestimmt wird, ob er den aktiven Fokus hat oder nicht.
Text { text: activeFocus ? "I have active focus!" : "I do not have active focus" }
Erlangung des Fokus und Fokusbereiche
Ein Item fordert den Fokus an, indem es die Eigenschaft focus
auf true
setzt.
In sehr einfachen Fällen reicht es manchmal aus, einfach die Eigenschaft focus
zu setzen. Wenn wir das folgende Beispiel mit dem qml-Tool ausführen, sehen wir, dass der Typ keyHandler
den aktiven Fokus hat und das Drücken der Tasten A
, B
oder C
den Text entsprechend ändert.
Rectangle { color: "lightsteelblue"; width: 240; height: 25 Text { id: myText } Item { id: keyHandler focus: true Keys.onPressed: (event)=> { if (event.key == Qt.Key_A) myText.text = 'Key A was pressed' else if (event.key == Qt.Key_B) myText.text = 'Key B was pressed' else if (event.key == Qt.Key_C) myText.text = 'Key C was pressed' } } }
Wenn das obige Beispiel jedoch als wiederverwendbare oder importierte Komponente verwendet werden soll, ist diese einfache Verwendung der Eigenschaft focus
nicht mehr ausreichend.
Zur Veranschaulichung erstellen wir zwei Instanzen unserer zuvor definierten Komponente und legen fest, dass die erste den Fokus haben soll. Die Absicht ist, dass beim Drücken der Tasten A
, B
oder C
die erste der beiden Komponenten das Ereignis empfängt und entsprechend reagiert.
Der Code, der zwei MyWidget-Instanzen importiert und erstellt:
//Window code that imports MyWidget Rectangle { id: window color: "white"; width: 240; height: 150 Column { anchors.centerIn: parent; spacing: 15 MyWidget { focus: true //set this MyWidget to receive the focus color: "lightblue" } MyWidget { color: "palegreen" } } }
Der MyWidget-Code:
Rectangle { id: widget color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true Text { id: label; anchors.centerIn: parent} focus: true Keys.onPressed: (event)=> { if (event.key == Qt.Key_A) label.text = 'Key A was pressed' else if (event.key == Qt.Key_B) label.text = 'Key B was pressed' else if (event.key == Qt.Key_C) label.text = 'Key C was pressed' } }
Wir möchten, dass das erste MyWidget
Objekt den Fokus erhält, also setzen wir seine focus
Eigenschaft auf true
. Durch Ausführen des Codes kann es jedoch passieren, dass das zweite Widget den Fokus erhält.
Wenn man sich den Code von MyWidget
und window
ansieht, ist das Problem offensichtlich - es gibt drei Typen, die die Eigenschaft focus
auf true
setzen. Die beiden MyWidget
setzen die Eigenschaft focus
auf true
und die Komponente window
setzt ebenfalls den Fokus. Letztendlich kann nur ein Typ den Tastaturfokus haben, und das System muss entscheiden, welcher Typ den Fokus erhält. Da QML nicht garantiert, welches Element seine Eigenschaften zuerst initialisiert bekommt, kann es sein, dass das letzte MyWidget
den anfänglichen Fokus erhält.
Dieses Problem ist auf die Sichtbarkeit zurückzuführen. Die Komponente MyWidget
möchte gerne den Fokus haben, kann diesen aber nicht kontrollieren, wenn sie importiert oder wiederverwendet wird. Ebenso kann die Komponente window
nicht wissen, ob ihre importierten Komponenten den Fokus anfordern.
Um dieses Problem zu lösen, führt QML ein Konzept ein, das als Fokusbereich bekannt ist. Für Qt-Benutzer ist ein Fokusbereich wie ein automatischer Fokus-Proxy. Ein Fokusbereich wird durch Deklaration des Typs FocusScope erstellt.
Im nächsten Beispiel wird ein FocusScope Typ zur Komponente hinzugefügt und das visuelle Ergebnis gezeigt.
FocusScope { //FocusScope needs to bind to visual properties of the Rectangle property alias color: rectangle.color x: rectangle.x; y: rectangle.y width: rectangle.width; height: rectangle.height Rectangle { id: rectangle anchors.centerIn: parent color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true Text { id: label; anchors.centerIn: parent } focus: true Keys.onPressed: (event)=> { if (event.key == Qt.Key_A) label.text = 'Key A was pressed' else if (event.key == Qt.Key_B) label.text = 'Key B was pressed' else if (event.key == Qt.Key_C) label.text = 'Key C was pressed' } } }
Konzeptuell sind Fokusbereiche recht einfach.
- In jedem Fokusbereich kann für ein Objekt
Item::focus
die Eigenschafttrue
festgelegt werden. Wenn für mehr als ein Item die Eigenschaftfocus
festgelegt ist, erhält der Typ, für den zuletzt die Eigenschaftfocus
festgelegt wurde, den Fokus, und die anderen sind nicht festgelegt, ähnlich wie bei fehlenden Fokusbereichen. - Wenn ein Fokusbereich den aktiven Fokus erhält, erhält der enthaltene Typ mit der Eigenschaft
focus
(falls vorhanden) ebenfalls den aktiven Fokus. Wenn dieser Typ auch ein FocusScope ist, wird das Proxying-Verhalten fortgesetzt. Sowohl der Fokusbereich als auch das subfokussierte Element haben die EigenschaftactiveFocus
gesetzt.
Da der Typ FocusScope kein visueller Typ ist, müssen die Eigenschaften seiner Kinder dem übergeordneten Element des Typs FocusScope mitgeteilt werden. Layouts und Positionierungstypen verwenden diese visuellen und Styling-Eigenschaften, um das Layout zu erstellen. In unserem Beispiel kann der Typ Column
die beiden Widgets nicht richtig anzeigen, da FocusScope keine eigenen visuellen Eigenschaften besitzt. Die Komponente MyWidget bindet sich direkt an die Eigenschaften von rectangle
, damit der Typ Column
das Layout erstellen kann, das die Kinder von FocusScope enthält.
Bisher wurde im Beispiel die zweite Komponente statisch ausgewählt. Es ist nun trivial, diese Komponente so zu erweitern, dass sie anklickbar wird, und sie der ursprünglichen Anwendung hinzuzufügen. Wir setzen immer noch eines der Widgets standardmäßig als fokussiert ein. Wenn nun eines der beiden MyClickableWidgets angeklickt wird, erhält es den Fokus und das andere Widget verliert den Fokus.
Der Code, der zwei MyClickableWidget-Instanzen importiert und erstellt:
Rectangle { id: window color: "white"; width: 240; height: 150 Column { anchors.centerIn: parent; spacing: 15 MyClickableWidget { focus: true //set this MyWidget to receive the focus color: "lightblue" } MyClickableWidget { color: "palegreen" } } }
Der MyClickableWidget-Code:
FocusScope { id: scope //FocusScope needs to bind to visual properties of the children property alias color: rectangle.color x: rectangle.x; y: rectangle.y width: rectangle.width; height: rectangle.height Rectangle { id: rectangle anchors.centerIn: parent color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true Text { id: label; anchors.centerIn: parent } focus: true Keys.onPressed: (event)=> { if (event.key == Qt.Key_A) label.text = 'Key A was pressed' else if (event.key == Qt.Key_B) label.text = 'Key B was pressed' else if (event.key == Qt.Key_C) label.text = 'Key C was pressed' } } MouseArea { anchors.fill: parent; onClicked: { scope.focus = true } } }
Wenn ein QML Item explizit den Fokus aufgibt (indem es seine focus
Eigenschaft auf false
setzt, während es den aktiven Fokus hat), wählt das System nicht automatisch einen anderen Typ aus, der den Fokus erhält. Das heißt, es ist möglich, dass kein aktiver Fokus vorhanden ist.
Unter Qt Quick Beispiele - Tasteninteraktion finden Sie eine Demonstration des Verschiebens des Tastaturfokus zwischen mehreren Bereichen mit FocusScope Typen.
Erweiterte Verwendungen von Fokusbereichen
Fokusbereiche ermöglichen eine einfache Aufteilung des Fokus auf Zuweisungen. Mehrere QML-Elemente verwenden sie zu diesem Zweck.
ListViewSo ist zum Beispiel die Funktion "Fokus" selbst ein Fokusbereich. Im Allgemeinen fällt dies nicht auf, da ListView normalerweise keine manuell hinzugefügten visuellen Kinder hat. Da ListView ein Fokusbereich ist, kann es das aktuelle Listenelement fokussieren, ohne sich Gedanken darüber zu machen, wie sich dies auf den Rest der Anwendung auswirkt. Dadurch kann der Delegat des aktuellen Elements auf Tastendrücke reagieren.
Dieses konstruierte Beispiel zeigt, wie dies funktioniert. Wenn Sie die Taste Return
drücken, wird der Name des aktuellen Listenelements gedruckt.
Rectangle { color: "lightsteelblue"; width: 100; height: 50 ListView { anchors.fill: parent focus: true model: ListModel { ListElement { name: "Bob" } ListElement { name: "John" } ListElement { name: "Michael" } } delegate: FocusScope { width: childrenRect.width; height: childrenRect.height x:childrenRect.x; y: childrenRect.y TextInput { focus: true text: name Keys.onReturnPressed: console.log(name) } } } }
Das Beispiel ist zwar einfach, aber hinter den Kulissen geht eine Menge vor sich. Immer wenn sich das aktuelle Element ändert, setzt ListView die Eigenschaft Item::focus
des Delegaten. Da ListView ein Fokusbereich ist, hat dies keine Auswirkungen auf den Rest der Anwendung. Wenn jedoch ListView selbst den aktiven Fokus hat, erhält der Delegat selbst den aktiven Fokus. In diesem Beispiel ist der Root-Typ des Delegaten ebenfalls ein Fokusbereich, der wiederum den aktiven Fokus an den TextInput -Typ weitergibt, der die eigentliche Arbeit der Handhabung der Taste Return
ausführt.
Alle QML-Ansichtsklassen, wie z. B. PathView und GridView, verhalten sich auf ähnliche Weise, um die Tastenbehandlung in ihren jeweiligen Delegaten zu ermöglichen.
© 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.