【问题标题】:Qt Event Propagation in QWebEngineViewQWebEngineView 中的 Qt 事件传播
【发布时间】:2019-11-22 11:36:24
【问题描述】:

我有一个名为generate_input_event 的函数。我正在尝试使用此功能来模拟 QWebEngineView 中的按键。

def generate_input_event(window_id, key_code, modifiers, low_level_data, x, y):
    modifiers_flag = create_modifiers_flag(modifiers)
    logging.info("generate input, window: {} code: {}, modifiers {}".format(
        window_id, key_code, modifiers_flag))
    event = QKeyEvent(QEvent.KeyPress, key_code, modifiers_flag)
    event.artificial = True
    event_window = window.get_window(window_id)
    QCoreApplication.sendEvent(event_window.qtwindow, event)

每当我运行我的程序并在 QWebEngineView 中突出显示一个输入字段并调用 generate_input_event 时,预计它会将该字母输入到输入字段中。

我还设置了一个事件过滤器来捕获所有按键,除了我的人工生成的按键。

class EventFilter(QWidget):
    def __init__(self, parent=None):
        super(EventFilter, self).__init__(parent)
        qApp.installEventFilter(self)

    def eventFilter(self, obj, event):
        if (event.type() == QEvent.KeyPress and hasattr(event, 'artificial')):
            logging.info("artificial event")
            return False. # send to widget
        elif (event.type() == QEvent.KeyPress and not is_modifier(event.key())):
            modifiers = create_modifiers_list(event.modifiers())
            key_string = create_key_string(event)
            key_code = event.key()
            logging.info("send code: {} string: {} modifiers {}".format(
                key_code, key_string, modifiers))
            return True. # do not forward to widgets
        return False

但是,当我实际运行我的代码时,我得到以下输出:

INFO:root:send code: 65 string: a modifiers ['']
INFO:root:generate input, window: 1 code: 65, modifiers <PyQt5.QtCore.Qt.KeyboardModifiers object at 0x106a4ea58>
INFO:root:artificial event

输出看起来正确,但是,QWebEngineView 的输入字段实际上从来没有得到由generate_input_event 人为生成的字母。

附:如果您出于上下文原因希望查看整个文件/项目,请在此处查看此分支/文件:https://github.com/atlas-engineer/next/blob/generate_events/ports/pyqt-webengine/utility.py

【问题讨论】:

  • 我不明白你要获取什么,即日志的哪一部分不正确。您还使用未声明的代码采样了 2 个令人困惑的代码。
  • 日志没有错。问题是,虽然正在生成一个人为事件,但我实际上并没有看到在我关注的输入字段中输入了一个字母。
  • 这是最重要的,你没有指出清楚,我建议你编辑你的问题并指出它。
  • 我已经这样做了,如果现在清楚请告诉我。
  • 这更好,但它比您在问题开始时期望的信号更好,也就是说:“您想模拟键盘事件,以便文本显示在输入中在 QWebEngineView 中,为此我尝试了 X,但我遇到了问题 Y"

标签: python pyqt pyqt5 qtwebengine qwebengineview


【解决方案1】:

Qt Webengine 使用 RenderWidgetHostViewQtDelegateWidget 进行渲染,这是在加载页面后创建的,因此您必须在 load() 或 setHtml() 之后访问它,因此小部件必须发送这些事件。

以下示例将显示 QWebEngineView 和 QLineEdit,在显示两个窗口后,您在 QLineEdit 中键入的内容将显示在 QWebEngineView 中。

from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets


class EventFilter(QtCore.QObject):
    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.KeyPress and hasattr(
            event, "artificial"
        ):
            print("event:", event.key(), event.text())
            return False
        return super().eventFilter(obj, event)


class ForwardKeyEvent(QtCore.QObject):
    def __init__(self, sender, receiver, parent=None):
        super(ForwardKeyEvent, self).__init__(parent)
        self.m_sender = sender
        self.m_receiver = receiver
        self.m_sender.installEventFilter(self)

    def eventFilter(self, obj, event):
        if self.m_sender is obj and event.type() == QtCore.QEvent.KeyPress:
            # self.m_receiver.setFocus()
            new_event = QtGui.QKeyEvent(
                QtCore.QEvent.KeyPress,
                event.key(),
                event.modifiers(),
                event.text(),
            )
            new_event.artificial = True
            QtCore.QCoreApplication.postEvent(self.m_receiver, new_event)
        return super().eventFilter(obj, event)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    ef = EventFilter()
    app.installEventFilter(ef)

    lineedit = QtWidgets.QLineEdit()
    lineedit.show()

    view = QtWebEngineWidgets.QWebEngineView()
    view.resize(640, 480)
    view.show()

    view.load(QtCore.QUrl("https://www.google.com/"))
    # RenderWidgetHostViewQtDelegateWidget is created after loading a page 
    # so you must access it after load() or setHtml().
    render_widget = view.findChild(QtWidgets.QWidget)
    print(render_widget.metaObject().className())
    assert(render_widget)
    fe = ForwardKeyEvent(lineedit, render_widget)
    sys.exit(app.exec_())

【讨论】:

  • 我想将您的答案标记为正确,但它只是技术上正确并且依赖于实现细节。正确的答案是使用 QWebEngine 的 focusProxy,所以在这种情况下它会是 view.focusProxy() 不过我非常感谢你带领我走上正确的道路!
  • @jmercouris 在后台都是同样的解决方案,focusProxy是一种更优雅的获取RenderWidgetHostViewQtDelegateWidget的方式,为它+1。
【解决方案2】:

可以通过以下最小示例演示发布和侦听 QWebengineview 事件的正确方法:


from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import Qt


class ForwardKeyEvent(QtCore.QObject):
    def __init__(self, sender, receiver, parent=None):
        super(ForwardKeyEvent, self).__init__(parent)
        self.m_sender = sender
        self.m_receiver = receiver
        self.m_sender.installEventFilter(self)

    def eventFilter(self, obj, event):
        if self.m_sender is obj and event.type() == QtCore.QEvent.KeyPress:
            new_event = QtGui.QKeyEvent(
                QtCore.QEvent.KeyPress,
                65,
                Qt.KeyboardModifiers(),
                "a",
            )
            new_event.artificial = True
            QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
            return True
        return False


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    lineedit = QtWidgets.QLineEdit()
    lineedit.show()
    view = QtWebEngineWidgets.QWebEngineView()
    view.resize(640, 480)
    view.show()
    view.load(QtCore.QUrl("https://www.google.com/"))
    # RenderWidgetHostViewQtDelegateWidget is created after loading a page
    # so you must access it after load() or setHtml().
    fe = ForwardKeyEvent(lineedit, view)
    sys.exit(app.exec_())

【讨论】:

  • 你确定这有效吗?我只在 QtWidgets 处于焦点时检测到按键,而不是 QWebEngineView。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多