I am trying to elaborate a QTableWidget like the excel tables, using PyQt5 and Python.
For this I want to activate the option of pressing the enter button the cell below goes into focus.
How to do this?
Here is my attempt:
def keyPressEvent(self, event):
super().keyPressEvent(event)
row = self.tableWidget.currentRow()
col = self.tableWidget.currentColumn()
if event.key() == Qt.Key_Enter and row < self.tableWidget.rowCount():
row += 1
self.tableWidget.setCurrentCell(row, col)
self.tableWidget.edit(self.tableWidget.currentIndex())
Your attempt didn't work because the keyPressEvent
that you want to react to must be received by the table, which is not your case, as you're clearly trying to do that from the parent window that contains that table.
When a key event happens, the current focused widget receives it; if that widget is not interested in that event, it is sent back to the widget's parent, and so on until some widget is actually able to react to that event and, possibly, accept it; once the event has been accepted, no parent widget will receive it.
While some widgets just ignore some or any keyboard events (for instance, QLabel), other just "eat" almost any event, and that's the case of item views like QTableWidget. If the table has focus, when it receives the return key event it will accept it, that event will not be propagated anymore and that's why you're not able to receive it from your keyPressEvent
override.
The most used solution is to subclass the table and implement the keyPressEvent
for that class (as explained in the answer given by Gibus).
A common alternative is to use an event filter, and it's very useful if you're using a GUI created in Designer, as it allows to add small features without the need to subclass and use promoted widgets.
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.tableWidget = QtWidgets.QTableWidget
self.setCentralWidget(self.tableWidget)
self.tableWidget.installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.KeyPress and
event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter)):
# ensure that the table receives the key event first
res = super().eventFilter(source, event)
current = self.tableWidget.currentIndex()
nextIndex = current.sibling(current.row() + 1, current.column())
if nextIndex.isValid():
self.tableWidget.setCurrentIndex(nextIndex)
self.tableWidget.edit(nextIndex)
return res
return super().eventFilter(source, event)
Finally, there is the "monkey patching", which can be used whenever very simple class modifications are required without the need to create subclasses. This also works for Designer created GUIs.
Note that monkey patching does not work for everything: some methods cannot be overwritten, especially not once the instance has been; notably, PyQt uses cached function binding which means that if a base implementation method is called prior overriding it, there's no way to override it afterwards.
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.tableWidget = QtWidgets.QTableWidget
self.setCentralWidget(self.tableWidget)
self.tableWidget.keyPressEvent = self.tableKeyPressEvent
def tableKeyPressEvent(self, event):
# call the base implementation, do *not* use super()!
QtWidgets.QTableWidget.keyPressEvent(self.tableWidget, event)
if event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
current = self.tableWidget.currentIndex()
nextIndex = current.sibling(current.row() + 1, current.column())
if nextIndex.isValid():
self.tableWidget.setCurrentIndex(nextIndex)
self.tableWidget.edit(nextIndex)