如何在 QML 应用程序中生成 KeyEvents?
How to generate KeyEvents in QML application?
我正在使用 PyQt5 和 QML 一起创建一个应用程序。我需要能够从 PyQT 到我的 QML 对象或从 QML 本身模拟键盘板事件。
我正在使用 QQmlApplicationEngine 加载我的 QML,我正在使用 Python 中的 "back end" QObject 连接到 QML 中的信号和槽。
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty("backend", backend)
engine.load('./qml/main.qml')
app.setEngine(engine)
稍后我尝试发送一个按键事件:
app.sendEvent(engine.rootObjects()[0].focusObject(), QKeyEvent(QEvent.KeyRelease, Qt.Key_Down, Qt.NoModifier))
在我的 QML 中,我有一个具有焦点的列表视图。如果我按键盘上的上下键,列表中的焦点项目会按预期更改。但是,当我尝试使用上面的代码发送按键事件时,列表视图没有反应。
打印时engine.rootObjects()[0]是一个QObject。
QML 片段:
ApplicationWindow {
// list model here
// list delegate here
ListView {
id: menuView
anchors.fill: parent
focus: true
model: menuModel
delegate: menuDelegate
keyNavigationEnabled: true
keyNavigationWraps: true
}
}
或者,我想知道是否可以通过与 ApplicationWindow 对象的 activeFocusItem 交互从 QML 本身生成一个按键事件?我也没能让这个工作。
您有 2 个错误:
focusObject() 方法 returns 具有焦点的 QObject,在您的特定情况下,它是 ListView 委托的项目之一,尽管接收鼠标事件不会改变所选项目,因为只有 ListView 可以。
如果要发送鼠标事件,必须先发送KeyPress,否则永远不会触发keyRelease方法,虽然很多时候只需要第一个事件
考虑到以上,解决方法是除了发送QEvent::KeyPress外,直接将事件发送给ListView,或者发送给另一个转发给ListView的对象,比如window。在以下使用计时器的示例中,事件将分别从 python 和 QML 发送到 window 或对象:
from functools import partial
from PyQt5 import QtCore, QtGui, QtQml
class KeyboardBackend(QtCore.QObject):
@QtCore.pyqtSlot(QtCore.QObject)
def moveUp(self, obj):
print("up")
event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier
)
QtCore.QCoreApplication.sendEvent(obj, event)
@QtCore.pyqtSlot(QtCore.QObject)
def moveDown(self, obj):
print("down")
event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier
)
QtCore.QCoreApplication.sendEvent(obj, event)
if __name__ == "__main__":
import os
import sys
app = QtGui.QGuiApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
keyboard_backed = KeyboardBackend()
engine.rootContext().setContextProperty("keyboard_backed", keyboard_backed)
file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(file))
if not engine.rootObjects():
sys.exit(-1)
root = engine.rootObjects()[0]
timer = QtCore.QTimer(timeout=partial(keyboard_backed.moveUp, root), interval=1000)
QtCore.QTimer.singleShot(500, timer.start)
sys.exit(app.exec())
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
ListModel {
id: menuModel
Component.onCompleted:{
['A', 'B', 'C', 'D'].forEach(function(letter) {
menuModel.append({"name": letter})
});
}
}
Component{
id: menuDelegate
Text {
text: name
}
}
Timer{
interval: 1000; running: true; repeat: true
onTriggered: keyboard_backed.moveDown(lv)
}
ListView {
id: lv
anchors.top: parent.top
focus: true
model: menuModel
delegate: menuDelegate
keyNavigationEnabled: true
keyNavigationWraps: true
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
height: 100
}
}
在查看 QtGamepad class 如何生成按键事件后,这最终成功了:
QGuiApplication.sendEvent(app.focusWindow(), QKeyEvent(QEvent.KeyPress, Qt.Key_Down, Qt.NoModifier))
现在我的 QML 应用程序的响应方式与用户按下某个键的方式相同。
我正在使用 PyQt5 和 QML 一起创建一个应用程序。我需要能够从 PyQT 到我的 QML 对象或从 QML 本身模拟键盘板事件。
我正在使用 QQmlApplicationEngine 加载我的 QML,我正在使用 Python 中的 "back end" QObject 连接到 QML 中的信号和槽。
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty("backend", backend)
engine.load('./qml/main.qml')
app.setEngine(engine)
稍后我尝试发送一个按键事件:
app.sendEvent(engine.rootObjects()[0].focusObject(), QKeyEvent(QEvent.KeyRelease, Qt.Key_Down, Qt.NoModifier))
在我的 QML 中,我有一个具有焦点的列表视图。如果我按键盘上的上下键,列表中的焦点项目会按预期更改。但是,当我尝试使用上面的代码发送按键事件时,列表视图没有反应。
打印时engine.rootObjects()[0]是一个QObject。
QML 片段:
ApplicationWindow {
// list model here
// list delegate here
ListView {
id: menuView
anchors.fill: parent
focus: true
model: menuModel
delegate: menuDelegate
keyNavigationEnabled: true
keyNavigationWraps: true
}
}
或者,我想知道是否可以通过与 ApplicationWindow 对象的 activeFocusItem 交互从 QML 本身生成一个按键事件?我也没能让这个工作。
您有 2 个错误:
focusObject() 方法 returns 具有焦点的 QObject,在您的特定情况下,它是 ListView 委托的项目之一,尽管接收鼠标事件不会改变所选项目,因为只有 ListView 可以。
如果要发送鼠标事件,必须先发送KeyPress,否则永远不会触发keyRelease方法,虽然很多时候只需要第一个事件
考虑到以上,解决方法是除了发送QEvent::KeyPress外,直接将事件发送给ListView,或者发送给另一个转发给ListView的对象,比如window。在以下使用计时器的示例中,事件将分别从 python 和 QML 发送到 window 或对象:
from functools import partial
from PyQt5 import QtCore, QtGui, QtQml
class KeyboardBackend(QtCore.QObject):
@QtCore.pyqtSlot(QtCore.QObject)
def moveUp(self, obj):
print("up")
event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier
)
QtCore.QCoreApplication.sendEvent(obj, event)
@QtCore.pyqtSlot(QtCore.QObject)
def moveDown(self, obj):
print("down")
event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier
)
QtCore.QCoreApplication.sendEvent(obj, event)
if __name__ == "__main__":
import os
import sys
app = QtGui.QGuiApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
keyboard_backed = KeyboardBackend()
engine.rootContext().setContextProperty("keyboard_backed", keyboard_backed)
file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(file))
if not engine.rootObjects():
sys.exit(-1)
root = engine.rootObjects()[0]
timer = QtCore.QTimer(timeout=partial(keyboard_backed.moveUp, root), interval=1000)
QtCore.QTimer.singleShot(500, timer.start)
sys.exit(app.exec())
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
ListModel {
id: menuModel
Component.onCompleted:{
['A', 'B', 'C', 'D'].forEach(function(letter) {
menuModel.append({"name": letter})
});
}
}
Component{
id: menuDelegate
Text {
text: name
}
}
Timer{
interval: 1000; running: true; repeat: true
onTriggered: keyboard_backed.moveDown(lv)
}
ListView {
id: lv
anchors.top: parent.top
focus: true
model: menuModel
delegate: menuDelegate
keyNavigationEnabled: true
keyNavigationWraps: true
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
height: 100
}
}
在查看 QtGamepad class 如何生成按键事件后,这最终成功了:
QGuiApplication.sendEvent(app.focusWindow(), QKeyEvent(QEvent.KeyPress, Qt.Key_Down, Qt.NoModifier))
现在我的 QML 应用程序的响应方式与用户按下某个键的方式相同。