Question

I override keyPressEven() of widget QTextEdit:

void myTextEdit::keyPressEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_0)
    {
        qDebug() << "Ok";
    }
}

Button 0 works - show "Ok", but does not write in field of QTextEdit. Why? Thanks advance.

Was it helpful?

Solution

You need to call the base class implementation if you want to keep the default behaviour:

void myTextEdit::keyPressEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_0)
    {
        qDebug() << "Ok";
    }
    QTextEdit::keyPressEvent(e);
}

See the docs for keyPressEvent.

OTHER TIPS

In case someone using PySide2 is having trouble overriding QTextEdit's built-in keybindings, I post my solution here. Hopefully this is also useful for C++.


Scenario:

We are using a QTextEdit in an application and want to distribute a series of keybindings, but the text editor has already several hardcoded bindings. We want the editor to ignore them and hand them over to its parent, so they can be eventually handled by our code.

Problem:

While the docs say that whenever an event is ignored (e.g. by returning True in the installed eventFilter method) it automatically gets passed on to the parent, the truth is that when doing that for predefined keybindings QTextEdit did not hand them over: the event got ignored AND absorbed. So any textedit built-in keybindings filtered this way will be effectively globally disabled.

Direct event passing via sendEvent inside the editor's eventFilter had an interesting effect:

  • When calling sendEvent and returning super().sendEvent, the keybinding got executed by the editor AND the event passed to the receiver.
  • When calling sendEvent and returning True, the keybinding didn't get executed, and the event didn't get passed to the receiver.
  • When calling sendEvent and returning False, the keybinding didn't get executed, and the event passed to the receiver twice.

Furthermore:

  • Using event.ignore() didn't have any effect: the editor executed the built-in anyway.
  • Trying to discriminate via event.spontaneous() caused a segfault due to a missing pointer. Probably something got GCed but didn't try to debug that.
  • Trying to replace the event with a "dummy event" and call super also didn't work. Magically, the text editor kept executing the built-ins.

Maybe I missed something. Anyway, below I detail the approach that worked for me.


Solution:

The plan is to completely block the event, but broadcast it via signals, and then connect to them wherever we want. In your text editor instance, define the signal e.g. as follows:

eventCatched = QtCore.Signal(QtCore.QEvent)

Then, e.g. the following event filter will prevent execution of a few keybindings, and emit them once via eventCatched:

def eventFilter(self, obj, evt):
    """
    Remember to install via self.installEventFilter(self)
    """
    catch_control_keys = {QtCore.Qt.Key_Left, QtCore.Qt.Key_Right}
    catch = False
    # documentation for keys and modifiers:
    # https://doc.qt.io/qtforpython-5/PySide2/QtCore/Qt.html
    if evt.type() == QtCore.QEvent.KeyPress:
        modifiers = evt.modifiers()
        ctrl = bool(modifiers & QtCore.Qt.ControlModifier)
        shift = bool(modifiers & QtCore.Qt.ShiftModifier)
        alt = bool(modifiers & QtCore.Qt.AltModifier)
        key = evt.key()
        # catch all undo/redo builtins
        if ((ctrl and shift and key == QtCore.Qt.Key_Z) or
            evt.matches(QtGui.QKeySequence.Undo) or
            evt.matches(QtGui.QKeySequence.Redo)):
            catch = True
        # catch specified control-keys
        if ctrl and not shift and not alt:
            if key in catch_control_keys:
                catch = True
    #
    if catch:
        # block event but send it as signal
        self.eventCatched.emit(evt)
        return True
    else:
        return super().eventFilter(obj, evt)

Then, we are free to connect the signal wherever we want to, we just need a method that handles events. In my case, I just wanted to pass them to the main window, which can be done with the following one-liner in the constructor:

text_editor.eventCatched.connect(lambda evt: QtCore.QCoreApplication.sendEvent(self, evt))

This way, whenever we catch an event in the text editor, it will be ignored and won't be propagated the standard way. Instead, a signal will be emitted, and we can subscribe to that signal to e.g. restart the propagation tree at a different point, as shown here via sendEvent.

Hope this helps!
Cheers,
Andres

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top