Catching child widget keyPressEvents
-
Hi,
We are making an internal configuration tool using PyQt5 5.15.4.
Development is done on macOS Catalina 10.15.7
The final version will run on both OSX and Windows 10I have a custom widget containing a QLineEdit.
I would like to catch the QLineEdit's KeyPressEvents without having to make a custom QLineEdit class.Here is what I have done so far.
In my custom widget class I override keyPressEvent. I then redirect the QLineEdit keyPressEvent to the one I wrote in the parent widget.class CustomWidget(QWidget): def __init__(self, parent): super(QWidget, self).__init__(parent) def keyPressEvent(self, event, widget=None, widgetType=None): if event.key() == Qt.Key_Return: self.returnKeyPressEventHandler() return if widget is None or widgetType is None: super(TerminalWidget, self).keyPressEvent(event) else: super(widgetType, widget).keyPressEvent(event) def initKeyPressEvents(self): self.lineEdit.keyPressEvent = lambda event: self.keyPressEvent(event, widget=self.lineEdit, widgetType=QLineEdit)
This seems to redirect the keyPressEvent from the QLineEdit to the one I wrote in the parent class without issues.
However, the lineelse: super(widgetType, widget).keyPressEvent(event)
does not work. I want to only catch some specific key presses, if the key pressed is not the return key, I want the QLineEdit to behave normally.
I thought the way to achieve this would be with "super".Is there a way to achieve what I want? I just want to catch keyPressEvents from the custom widget's children without having to make custom classes for each child.
Thanks,
FVez -
Hi,
You should rather use an event filter as described in the QObject::installEventFilter.
Out of curiosity, what other keys beside enter do you want to catch ?
-
Hi SGaist,
I'll have a look at the installEventFilter, it does seem to be what I am looking for.
A couple different presses, mostly the return key and some copy paste shortcuts for our custom tables.Thanks, I'll post here again if I can't get the installEventFilter to work.
FVez
-
Hi SGaist,
So I'm having some issues implementing the installEventFilter.
Here is my custom filter class :class KeyPressEventHandler(QObject): def __init__(self, # No modifier key returnKeyPressEventHandler=None, upKeyPressEventHandler=None, downKeyPressEventHandler=None, deleteKeyPressEventHandler=None, # Control and Shift modifier keys controlShiftZKeyPressEventHandler=None, # Shift modifier key shiftReturnKeyPressEventHandler=None, # Control modifier key controlFKeyPressEventHandler=None, controlZKeyPressEventHandler=None, controlCKeyPressEventHandler=None, controlXKeyPressEventHandler=None, controlVKeyPressEventHandler=None, controlBackspaceKeyPressEventHandler=None): super().__init__() # No modifier key self.returnKeyPressEventHandler = returnKeyPressEventHandler self.upKeyPressEventHandler = upKeyPressEventHandler self.downKeyPressEventHandler = downKeyPressEventHandler self.deleteKeyPressEventHandler = deleteKeyPressEventHandler # Control and Shift modifier keys self.controlShiftZKeyPressEventHandler = controlShiftZKeyPressEventHandler # Shift modifier key self.shiftReturnKeyPressEventHandler = shiftReturnKeyPressEventHandler # Control modifier key self.controlFKeyPressEventHandler = controlFKeyPressEventHandler self.controlZKeyPressEventHandler = controlZKeyPressEventHandler self.controlCKeyPressEventHandler = controlCKeyPressEventHandler self.controlXKeyPressEventHandler = controlXKeyPressEventHandler self.controlVKeyPressEventHandler = controlVKeyPressEventHandler self.controlBackspaceKeyPressEventHandler = controlBackspaceKeyPressEventHandler def keyPressEventHandler(self, obj, event): print('keyPressEventHandler') if event.type() == QEvent.KeyPress: # Control and Shift modifier keys if event.modifiers() & Qt.ControlModifier: if event.modifiers() & Qt.ShiftModifier: if event.key() == Qt.Key_Z: self.controlShiftZKeyPressEventHandler() return True # Shift modifier key if event.modifiers() & Qt.ShiftModifier: if event.key() == Qt.Key_Return: if self.shiftReturnKeyPressEventHandler is not None: self.shiftReturnKeyPressEventHandler() return True # Control modifier key if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_F: if self.controlFKeyPressEventHandler is not None: self.controlFKeyPressEventHandler() return True elif event.key() == Qt.Key_Z: if self.controlZKeyPressEventHandler is not None: self.controlZKeyPressEventHandler() return True elif event.key() == Qt.Key_C: if self.controlCKeyPressEventHandler is not None: self.controlCKeyPressEventHandler() return True elif event.key() == Qt.Key_X: if self.controlXKeyPressEventHandler is not None: self.controlXKeyPressEventHandler() return True elif event.key() == Qt.Key_V: if self.controlVKeyPressEventHandler is not None: self.controlVKeyPressEventHandler() return True elif event.key() == Qt.Key_Backspace: if self.controlBackspaceKeyPressEventHandler is not None: self.controlBackspaceKeyPressEventHandler() return True # No modifier key if event.key() == Qt.Key_Return: if self.returnKeyPressEventHandler is not None: self.returnKeyPressEventHandler() return True elif event.key() == Qt.Key_Up: if self.upKeyPressEventHandler is not None: self.upKeyPressEventHandler() return True elif event.key() == Qt.Key_Down: if self.downKeyPressEventHandler is not None: self.downKeyPressEventHandler() return True elif event.key() == Qt.Key_Delete: if self.deleteKeyPressEventHandler is not None: self.deleteKeyPressEventHandler() return True return QObject.eventFilter(obj, event)
And here is how I've implemented it in my terminal class :
class TerminalWidget(QWidget): def __init__(self, main, mainWindow, parent): super(QWidget, self).__init__(parent) self.main = main self.mainWindow = mainWindow self.parent = parent self.osName = self.main.osName self.terminalSearch = TerminalSearch(self, self.mainWindow) self.initWidget() self.initCriticalWidgets() def initKeyPressEventHandler(self): self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.terminalSearch.search, upKeyPressEventHandler=self.terminalSearch.iterateUp, downKeyPressEventHandler=self.terminalSearch.iterateDown, shiftReturnKeyPressEventHandler=self.terminalSearch.iterateUp, controlFKeyPressEventHandler=self.lineEdit_terminalSearch.selectAll) self.lineEdit_terminalSearch.installEventFilter(self.lineEdit_terminalSearch_keyPressEventHandler) self.textEdit_terminal_keyPressEventHandler = KeyPressEventHandler(self, controlFKeyPressEventHandler=self.textEdit_terminal_controlFKeyPressEventHandler) self.textEdit_terminal.installEventFilter(self.textEdit_terminal_keyPressEventHandler) self.lineEdit_terminal_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.pushButton_send_terminal.animateClick, upKeyPressEventHandler=self.main.terminalCommandHistory.getPreviousCommand, downKeyPressEventHandler=self.main.terminalCommandHistory.getNextCommand, controlFKeyPressEventHandler=self.lineEdit_terminal_controlFKeyPressEventHandler) self.lineEdit_terminal.installEventFilter(self.lineEdit_terminal_keyPressEventHandler)
I have one KeyPressEventHandler class for every widget from which I want to catch the key presses. I then pass the functions to call directly to the class init call.
I am having some issues with the implementation. Here is my first problem.
When I run the application, I get the following error.self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, TypeError: __init__() got multiple values for argument 'returnKeyPressEventHandler'
I am not sure why I get the TypeError since I am apassing a single function to the init call...
Could you help with this? Do you see why it sees "multiple values" for the returnKeyPressEventHandler argument?Thanks!
FVez -
Hi SGaist,
So I'm having some issues implementing the installEventFilter.
Here is my custom filter class :class KeyPressEventHandler(QObject): def __init__(self, # No modifier key returnKeyPressEventHandler=None, upKeyPressEventHandler=None, downKeyPressEventHandler=None, deleteKeyPressEventHandler=None, # Control and Shift modifier keys controlShiftZKeyPressEventHandler=None, # Shift modifier key shiftReturnKeyPressEventHandler=None, # Control modifier key controlFKeyPressEventHandler=None, controlZKeyPressEventHandler=None, controlCKeyPressEventHandler=None, controlXKeyPressEventHandler=None, controlVKeyPressEventHandler=None, controlBackspaceKeyPressEventHandler=None): super().__init__() # No modifier key self.returnKeyPressEventHandler = returnKeyPressEventHandler self.upKeyPressEventHandler = upKeyPressEventHandler self.downKeyPressEventHandler = downKeyPressEventHandler self.deleteKeyPressEventHandler = deleteKeyPressEventHandler # Control and Shift modifier keys self.controlShiftZKeyPressEventHandler = controlShiftZKeyPressEventHandler # Shift modifier key self.shiftReturnKeyPressEventHandler = shiftReturnKeyPressEventHandler # Control modifier key self.controlFKeyPressEventHandler = controlFKeyPressEventHandler self.controlZKeyPressEventHandler = controlZKeyPressEventHandler self.controlCKeyPressEventHandler = controlCKeyPressEventHandler self.controlXKeyPressEventHandler = controlXKeyPressEventHandler self.controlVKeyPressEventHandler = controlVKeyPressEventHandler self.controlBackspaceKeyPressEventHandler = controlBackspaceKeyPressEventHandler def keyPressEventHandler(self, obj, event): print('keyPressEventHandler') if event.type() == QEvent.KeyPress: # Control and Shift modifier keys if event.modifiers() & Qt.ControlModifier: if event.modifiers() & Qt.ShiftModifier: if event.key() == Qt.Key_Z: self.controlShiftZKeyPressEventHandler() return True # Shift modifier key if event.modifiers() & Qt.ShiftModifier: if event.key() == Qt.Key_Return: if self.shiftReturnKeyPressEventHandler is not None: self.shiftReturnKeyPressEventHandler() return True # Control modifier key if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_F: if self.controlFKeyPressEventHandler is not None: self.controlFKeyPressEventHandler() return True elif event.key() == Qt.Key_Z: if self.controlZKeyPressEventHandler is not None: self.controlZKeyPressEventHandler() return True elif event.key() == Qt.Key_C: if self.controlCKeyPressEventHandler is not None: self.controlCKeyPressEventHandler() return True elif event.key() == Qt.Key_X: if self.controlXKeyPressEventHandler is not None: self.controlXKeyPressEventHandler() return True elif event.key() == Qt.Key_V: if self.controlVKeyPressEventHandler is not None: self.controlVKeyPressEventHandler() return True elif event.key() == Qt.Key_Backspace: if self.controlBackspaceKeyPressEventHandler is not None: self.controlBackspaceKeyPressEventHandler() return True # No modifier key if event.key() == Qt.Key_Return: if self.returnKeyPressEventHandler is not None: self.returnKeyPressEventHandler() return True elif event.key() == Qt.Key_Up: if self.upKeyPressEventHandler is not None: self.upKeyPressEventHandler() return True elif event.key() == Qt.Key_Down: if self.downKeyPressEventHandler is not None: self.downKeyPressEventHandler() return True elif event.key() == Qt.Key_Delete: if self.deleteKeyPressEventHandler is not None: self.deleteKeyPressEventHandler() return True return QObject.eventFilter(obj, event)
And here is how I've implemented it in my terminal class :
class TerminalWidget(QWidget): def __init__(self, main, mainWindow, parent): super(QWidget, self).__init__(parent) self.main = main self.mainWindow = mainWindow self.parent = parent self.osName = self.main.osName self.terminalSearch = TerminalSearch(self, self.mainWindow) self.initWidget() self.initCriticalWidgets() def initKeyPressEventHandler(self): self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.terminalSearch.search, upKeyPressEventHandler=self.terminalSearch.iterateUp, downKeyPressEventHandler=self.terminalSearch.iterateDown, shiftReturnKeyPressEventHandler=self.terminalSearch.iterateUp, controlFKeyPressEventHandler=self.lineEdit_terminalSearch.selectAll) self.lineEdit_terminalSearch.installEventFilter(self.lineEdit_terminalSearch_keyPressEventHandler) self.textEdit_terminal_keyPressEventHandler = KeyPressEventHandler(self, controlFKeyPressEventHandler=self.textEdit_terminal_controlFKeyPressEventHandler) self.textEdit_terminal.installEventFilter(self.textEdit_terminal_keyPressEventHandler) self.lineEdit_terminal_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.pushButton_send_terminal.animateClick, upKeyPressEventHandler=self.main.terminalCommandHistory.getPreviousCommand, downKeyPressEventHandler=self.main.terminalCommandHistory.getNextCommand, controlFKeyPressEventHandler=self.lineEdit_terminal_controlFKeyPressEventHandler) self.lineEdit_terminal.installEventFilter(self.lineEdit_terminal_keyPressEventHandler)
I have one KeyPressEventHandler class for every widget from which I want to catch the key presses. I then pass the functions to call directly to the class init call.
I am having some issues with the implementation. Here is my first problem.
When I run the application, I get the following error.self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, TypeError: __init__() got multiple values for argument 'returnKeyPressEventHandler'
I am not sure why I get the TypeError since I am apassing a single function to the init call...
Could you help with this? Do you see why it sees "multiple values" for the returnKeyPressEventHandler argument?Thanks!
FVez@SGaist
Sorry for tagging you, I forgot to mark the post as a direct reply to your comment. -
Short version of your class:
class KeyPressEventHandler(QObject): def __init__(self, # No modifier key returnKeyPressEventHandler=None, # etc.
Instanciation code:
self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.terminalSearch.search,
The first parameter you pass is self, which is going to go into the first parameter of the init function which is returnKeyPressEventHandler and then you explicitly set returnKeyPressEventHandler hence your error.
The self parameter of class member functions is not a parameter you pass when calling the method.
-
Hi SGaist,
So by removing self in the init calls
FROMself.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.terminalSearch.search, upKeyPressEventHandler=self.terminalSearch.iterateUp, downKeyPressEventHandler=self.terminalSearch.iterateDown, shiftReturnKeyPressEventHandler=self.terminalSearch.iterateUp, controlFKeyPressEventHandler=self.lineEdit_terminalSearch.selectAll)
TO
self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(returnKeyPressEventHandler=self.terminalSearch.search, upKeyPressEventHandler=self.terminalSearch.iterateUp, downKeyPressEventHandler=self.terminalSearch.iterateDown, shiftReturnKeyPressEventHandler=self.terminalSearch.iterateUp, controlFKeyPressEventHandler=self.lineEdit_terminalSearch.selectAll)
It fixed my multiple values for argument problem. I thought I was always supposed to pass self in the class init but it seems like I was mistaken.
-
Short version of your class:
class KeyPressEventHandler(QObject): def __init__(self, # No modifier key returnKeyPressEventHandler=None, # etc.
Instanciation code:
self.lineEdit_terminalSearch_keyPressEventHandler = KeyPressEventHandler(self, returnKeyPressEventHandler=self.terminalSearch.search,
The first parameter you pass is self, which is going to go into the first parameter of the init function which is returnKeyPressEventHandler and then you explicitly set returnKeyPressEventHandler hence your error.
The self parameter of class member functions is not a parameter you pass when calling the method.
@SGaist
I see!Big thanks!
FVez -
No, "self" is a reference to the object itself. This allows you to call other functions of the object, use member variables, etc.