QTableView is a truly amazing Qt component. It allows us view and edit every custom table model we can imagine. However, the default way of editing is rather clunky, especially when compared with “industry standard” applications like Microsoft Excel or Google Sheets. Wouldn’t it be nice to edit the QTableView contents like in one of these applications?
Default behaviour
Let’s focus on the behaviour of Enter and Tab keys in QTableView. By default, the Tab key does exactly what we want:
- It selects the cell to the right of the currently selected cell.
- If the currently selected cell is being edited, the editing ends and the progress is saved.
- If the currently selected cell is the last cell in the row, it selects the first cell in the next row.
However, pressing the Enter key does not do much. It only ends editing, without moving the selection down or right. It even does not enable editing of the currently selected cell.
Desired behaviour
What we want is to modify the behaviour of Enter key, so it behaves like in Microsoft Excel or Google Sheets:
- If the selected cell is not being edited, start editing the cell.
- If the selected cell is being edited, end the editing, save the value and select the cell below.
Solution : Subclass QTableView!
If we want to customize behaviour of a key, we need to override the QAbstractItemView::keyPressEvent() method. Once we override it, we are in full control of what happens when the given key is pressed. We can e.g. set the left arrow to act like the right arrow, we can modify the Delete key so it deletes an entire row, or (you guessed it right) we can modify the Enter key to make it behave like we specified above.
void keyPressEvent(QKeyEvent *pEvent) override
{
if (pEvent->key() == Qt::Key_Return)
{
// we captured the Enter key press, now we need to move to the next row
qint32 nNextRow = currentIndex().row() + 1;
if (nNextRow + 1 > model()->rowCount(currentIndex()))
{
// we are all the way down, we can't go any further
nNextRow = nNextRow - 1;
}
if (state() == QAbstractItemView::EditingState)
{
// if we are editing, confirm and move to the row below
QModelIndex oNextIndex = model()->index(nNextRow, currentIndex().column());
setCurrentIndex(oNextIndex);
selectionModel()->select(oNextIndex, QItemSelectionModel::ClearAndSelect);
}
else
{
// if we're not editing, start editing
edit(currentIndex());
}
}
else
{
// any other key was pressed, inform base class
QAbstractItemView::keyPressEvent(pEvent);
}
}
The result
You can download the result here. After compiling and running, it shall look like this:
So there you have it! In the end, it was quite easy, wasn’t it? :-)
If you find an easier solution or have a question, see you in the comments!
QTableView::item::selected {border: 2px solid green; border-radius: 0px;border-bottom-right-radius: 0px;border-style: double;}
QTableView::item::focus {border: 2px solid green; border-radius: 0px;border-bottom-right-radius: 0px;border-style: double;}