pyqtqplaintexteditqtconsoleqshortcut

'Shift+Return' QShortcut in PyQt QPlainTextEdit


I'm attempting to write a small command line console in PyQt, similar to the Jupyter Qtconsole . As a simple first step I'd like to be able to execute commands when the key combination 'Shift+Return' is pressed. The code example below suggests there's a specific interaction between 'Shift+Return' on QPlainTextEdit so that it behaves differently from a random different shortcut, like 'Ctrl+k'. With 'Shift+Return' the below code does not print "Bar" when 'Shift+Return' is pressed, whereas 'Ctrl+k' does print "Foo".

The questions QShortcut & QKeySequence with Shift+Return in a QPlainTextEdit element is similar, and proposes a solution and QPlainTextEdit - change shift+return behaviour both are about the same topic, and propose similar solutions. But I'm curious why this is even a problem in the first place. Why doesn't 'Shirt+Return' work with the QPlainTextEdit?

import PyQt6.QtWidgets as qw
import PyQt6.QtGui as qg
import PyQt6.QtCore as qc



class Console(qw.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My App")
        self.qptext = qw.QPlainTextEdit() 
        
        self.shortcut1 = qg.QShortcut(
            qg.QKeySequence('Ctrl+k'),
            self)
        self.shortcut1.activated.connect(lambda : print("Foo"))
        self.shortcut2 = qg.QShortcut(
            qg.QKeySequence('Shift+Return'),
            self)
        self.shortcut2.activated.connect(lambda : print("Bar"))

        self.setCentralWidget(self.qptext)


app = qw.QApplication([])

window = Console()
window.show() 

app.exec()

Solution

  • It's ignored because the text editor already consumes the shortcut: if you look at the standard shortcut list of QKeySequence you'll see that on Windows and Linux Shift+Enter corresponds to the InsertLineSeparator sequence.

    Note that while the list only shows the Enter key, the combination actually involves Return too (see the QPlatformTheme sources).

    The difference with the standard Enter/Return is not relevant for QPlainTextEdit, but if you were using a QTextEdit (which shares the same private control for text interaction) it wouldn't be the same: Enter/Return creates a new paragraph (a new <p></p> block if exporting to HTML), while using the Shift modifier will break the current line (<br>) within the same paragraph.

    In fact, if you change the shortcut to use Enter, you'll see that it would be the same QKeySequence:

        self.shortcut2 = qw.QShortcut(
            qg.QKeySequence('Shift+Enter'),
            self)
        print(self.shortcut2.key() == qg.QKeySequence(qg.QKeySequence.InsertLineSeparator))
    

    When a widget accepts a keyboard event, it is not propagated to the parent (or, in the case of shortcuts, the context) so that shortcut is not triggered at all.

    If you want to override the behavior because you're not interested in that key combination, just install an event filter on the widget and ignore the event:

            ...
            self.qptext.installEventFilter(self)
    
    
        def eventFilter(self, obj, event):
            if (
                event.type() in (event.Type.KeyPress, event.Type.ShortcutOverride)
                and event.key() == qc.Qt.Key.Key_Return
                and event.modifiers() == qc.Qt.KeyboardModifier.ShiftModifier
            ):
                event.ignore()
                return True
            return super().eventFilter(obj, event)