简体   繁体   English

如何覆盖 QTextEdit 的 keyPressEvent?

[英]How to override keyPressEvent of QTextEdit?

I override keyPressEven() of widget QTextEdit:我覆盖了小部件 QTextEdit 的 keyPressEven():

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.按钮 0 有效 - 显示“确定”,但不在 QTextEdit 的字段中写入。 Why?为什么? Thanks advance.先谢谢了。

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 .请参阅keyPressEvent的文档。

In case someone using PySide2 is having trouble overriding QTextEdit 's built-in keybindings, I post my solution here.如果有人使用PySide2无法覆盖QTextEdit的内置键绑定,我会在此处发布我的解决方案。 Hopefully this is also useful for C++.希望这对 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.我们在应用程序中使用QTextEdit并希望分发一系列键绑定,但文本编辑器已经有几个硬编码绑定。 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 (eg 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 .虽然文档说当一个事件被忽略时(例如通过在安装的eventFilter方法中返回True )它会自动传递给父级,但事实是,当为预定义的键绑定执行此操作时QTextEdit并没有将它们移交给:事件得到忽略和吸收 So any textedit built-in keybindings filtered this way will be effectively globally disabled.因此,以这种方式过滤的任何 textedit 内置键绑定都将被有效地全局禁用。

Direct event passing via sendEvent inside the editor's eventFilter had an interesting effect:在编辑器的eventFilter通过sendEvent直接传递事件有一个有趣的效果:

  • When calling sendEvent and returning super().sendEvent , the keybinding got executed by the editor AND the event passed to the receiver.当调用sendEvent并返回super().sendEvent ,编辑器执行键绑定并将事件传递给接收器。
  • When calling sendEvent and returning True , the keybinding didn't get executed, and the event didn't get passed to the receiver.当调用sendEvent并返回True ,键绑定没有被执行,事件也没有传递给接收者。
  • When calling sendEvent and returning False , the keybinding didn't get executed, and the event passed to the receiver twice .当调用sendEvent并返回False ,键绑定没有被执行,事件传递给接收者两次

Furthermore:此外:

  • Using event.ignore() didn't have any effect: the editor executed the built-in anyway.使用event.ignore()没有任何效果:编辑器无论如何都会执行内置的。
  • Trying to discriminate via event.spontaneous() caused a segfault due to a missing pointer.由于缺少指针,尝试通过event.spontaneous()进行区分会导致段错误。 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.尝试用“虚拟事件”替换该事件并调用 super 也不起作用。 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.计划是完全阻止事件,但通过signals广播它,然后在我们想要的任何地方连接到它们。 In your text editor instance, define the signal eg as follows:在您的文本编辑器实例中,定义信号,例如如下:

eventCatched = QtCore.Signal(QtCore.QEvent)

Then, eg the following event filter will prevent execution of a few keybindings, and emit them once via eventCatched :然后,例如以下事件过滤器将阻止执行一些键绑定,并通过eventCatched发出一次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 eg restart the propagation tree at a different point, as shown here via sendEvent .相反,将发出一个信号,我们可以订阅该信号以例如在不同的点重新启动传播树,如此处通过sendEvent所示。

Hope this helps!希望这有帮助!
Cheers,干杯,
Andres安德烈斯

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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