【问题标题】:How to generate KeyEvents in QML application?如何在 QML 应用程序中生成 KeyEvents?
【发布时间】:2019-12-11 23:32:55
【问题描述】:

我正在使用 PyQt5 和 QML 来创建一个应用程序。我需要能够模拟从 PyQT 到我的 QML 对象或从 QML 本身的键盘板事件。

我正在使用 QQmlApplicationEngine 来加载我的 QML,并且我正在使用 Python 中的“后端”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 sn-p:

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 自身内部生成一个关键事件?我也无法让它工作。

【问题讨论】:

    标签: python pyqt qml pyqt5 keyevent


    【解决方案1】:

    在查看了 QtGamepad 类如何生成关键事件之后,这最终成功了:

    QGuiApplication.sendEvent(app.focusWindow(), QKeyEvent(QEvent.KeyPress, Qt.Key_Down, Qt.NoModifier))
    

    现在我的 QML 应用程序的响应方式与用户按下某个键的方式相同。

    【讨论】:

      【解决方案2】:

      您有 2 个错误:

      • focusObject() 方法返回具有焦点的 QObject,在您的特定情况下,它是 ListView 委托的项目之一,尽管接收鼠标事件不会更改所选项目,因为只有 ListView 会.

      • 如果要发送鼠标事件,必须先发送KeyPress,否则永远不会触发keyRelease方法,虽然很多时候只需要第一个事件。

      考虑到上述情况,解决方案是除了发送QEvent::KeyPress之外,直接将事件发送到ListView或者发送到另一个转发给ListView的对象,例如窗口。在以下使用计时器的示例中,事件将分别从 python 和 QML 发送到窗口或对象:

      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
          }
      }
      

      【讨论】:

      • 感谢您的回复。我需要将事件转到窗口,然后将事件传递给当前具有焦点的项目。具有焦点的项目不一定是列表视图。我设法让它工作(见我的回答)。
      • @MagicUK 您的回答与我的回答相同(实际上您的回答是我回答的一半),在我的回答中,我不仅坚持对您有用的内容,而且尝试解释您的初始代码为何如此通过解释 2 种可能的解决方案不起作用:我使用 Window(你的方法)+ 使用 ListView,我解释了为什么两者是等效的:Window 将事件发送到 Delegate 项,但 ListView 拦截它。
      • @MagicUK 建议:您不仅要保留“解决方案”,而且要理解它,因为将来它可以帮助您解决其他问题:-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-19
      • 1970-01-01
      • 2012-11-14
      • 2018-09-07
      • 1970-01-01
      • 1970-01-01
      • 2021-11-21
      相关资源
      最近更新 更多