简体   繁体   中英

How to capture key-press events when using QWebEngineView?

## minimal code
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import Qt

class Browser(QWebEngineView):
    def __init__(self, parent=None):
        super(Browser, self).__init__(parent)
        self.setHtml(r"""
        <div><textarea></textarea></div>
        <style>
        *{margin:0;}
        div {
            height: 100%;
            position: relative;
            background:grey;
        }
        textarea {
            background:red;
            height: 50%;
            width: 50%;
            resize: none;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
        </style>
        """)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        central=QtWidgets.QWidget(self)
        self.setCentralWidget(central)
        layout = QtWidgets.QGridLayout(central)
        self.browser = Browser(central)
        layout.addWidget(self.browser)
        another=QtWidgets.QWidget(central)
        layout.addWidget(another)
        another.setFocusPolicy(Qt.StrongFocus)

    def keyPressEvent(self, e):
        print("KEY PRESSED")
        return super().keyPressEvent(e)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.setGeometry(300, 50, 800, 600)
    window.show()

    sys.exit(app.exec_())

Current behavior:

Case 1:

  1. Click on the white area(a widget).
  2. Press a key.
  3. KEY PRESSED is logged into the console.

Case 2:

  1. Click on the grey area(the webview).
  2. Press a key.
  3. KEY PRESSED is logged into the console.

Case 3:

  1. Click on the red area(the textarea inside the webview).
  2. Press a key.
  3. Nothing is logged into the console.

Required behavior

I want all of the cases to log KEY PRESSED once into the console.

Things I tried

I tried adding an eventFilter to the MainWindow but it gives the same outout.

I also tried the solution from this answer by forwarding the key events to MainWindow but then it logs KEY PRESSED twice for Case 2.

I am not able to differentiate between the events that was propagated to MainWindow (Case 2) and those that weren't (Case 3) so that I could've implemented something to ignore the excess function call.

The problem is that Qt must pass the events to the internal Chrome browser, which complicates the normal process of event-propagation. The simplest solution I could find is to install an event-filter on the internal web-view delegate, whilst also ignoring any events that come from the delegate in the main key-press handler:

from PyQt5.QtCore import QEvent

class Browser(QWebEngineView):
    ...    
    def eventFilter(self, source, event):
        if event.type() == QEvent.KeyPress and source.parent() is self:
            print("KEY PRESSED:", source)
        return super().eventFilter(source, event)

    def childEvent(self, event):
        if event.added() and event.child().isWidgetType():
            event.child().installEventFilter(self)
        super().childEvent(event)

class MainWindow(QtWidgets.QMainWindow):
    ...    
    def keyPressEvent(self, e):
        target = QtWidgets.QApplication.focusWidget()
        if target.parentWidget() is not self.browser:
            print("KEY PRESSED:", target)
        return super().keyPressEvent(e)

What I did was forward the key events from the browser to the MainWindow using the ForwardKeyEvent class from this post . I ignored any QKeyEvent sent by the browser and accepted the ones that were sent artificially. This was more convenient for me since all the logging was done by one function, which is the function I needed to be called when there was a QKeyEvent .

class ForwardKeyEvent(QObject):
    ## https://stackoverflow.com/a/57012924/14908508
    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() == QEvent.KeyPress:
            new_event = QKeyEvent(
                QEvent.KeyPress,
                event.key(),
                event.modifiers(),
                event.text(),
            )
            new_event.artificial = True
            QCoreApplication.postEvent(self.m_receiver, new_event)
        return super().eventFilter(obj, event)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        ...
        
        self.FKE = ForwardKeyEvent(self.browser.focusProxy(), self)
    
    def keyPressEvent(self, e):
        target = QtWidgets.QApplication.focusWidget()
        if target.parentWidget() is not self.browser or hasattr(e,"artificial"):
            print("KEY PRESSED:", target)
        return super().keyPressEvent(e)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM