Eurydice
Eurydice

Reputation: 8419

setCurrentIndex of QTableView places the currentIndex to a wrong place

I have a QTableView on which I override the keyPressEvent method such as when the user presses the tab key on the lowest right corner, a new line is appended to the underlying (pandas) model. Once this is done I want the current index to be located at the new lowest right corner. Here is an example classed that should do in principle the job:

from PyQt5 import QtCore, QtWidgets

class DL50TableView(QtWidgets.QTableView):

    def keyPressEvent(self, event):

        key = event.key()

        model = self.model()

        current_index = self.currentIndex()
        row = current_index.row()
        column = current_index.column()

        if key == QtCore.Qt.Key_Tab:
            if column == model.columnCount() - 1 and row == model.rowCount() - 1:
                model.insertRows(model.rowCount())
                index = model.index(model.rowCount() - 1,model.columnCount() - 1)
                self.setCurrentIndex(index)

        elif key == QtCore.Qt.Key_Backspace:
            if column == 0 and row == model.rowCount() - 1:
                model.removeRows(model.rowCount() - 1)
                index = model.index(model.rowCount() - 1,0)
                self.setCurrentIndex(index)

        super(DL50TableView, self).keyPressEvent(event)  

import pandas as pd

from PyQt5 import QtCore, QtGui

class DL50Model(QtCore.QAbstractTableModel):

    def __init__(self, *args, **kwargs):

        super(DL50Model,self).__init__(*args, **kwargs)        

        self._data = pd.DataFrame([self.default_line()], columns=['include',
                                                                  'dose',
                                                                  'response',
                                                                  'nominal',
                                                                  'log10(dose)',
                                                                  'contrib.\ndae',
                                                                  'prob.\nof\nresponse',
                                                                  'likelihood\ncontrib.',
                                                                  'prob.\nof\nresponse\n(sub)',
                                                                  'likelihood\ncontrib.\n(sub)',
                                                                  'prob.\nof\nresponse\n(over)',
                                                                  'likelihood\ncontrib.\n(over)'])

    def columnCount(self, parent=None):
        return len(self._data.columns)

    def data(self, index, role):

        if not index.isValid():
            return False

        row = index.row()
        col = index.column()
        if role == QtCore.Qt.BackgroundRole and col >= 3:
            return QtGui.QBrush(QtCore.Qt.gray)
        elif role == QtCore.Qt.CheckStateRole and col in [0,2,3]:
            return self._data.iloc[row][col]
        elif role == QtCore.Qt.DisplayRole:
            if col in [0,2,3]:
                return None
            return str(self._data.iloc[row][col])
        return None

    def default_line(self):
        return [False, 0.0,False, False, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
    
    def insertRows(self, position, rows=1, index=QtCore.QModelIndex()):
        """Insert row(s) into the model.
        
        Args:
            position (int): the position of the row(s) to iunsert
            rows (int): the number of rows to insert
            index (QModelIndex): the parent index
        """
        
        self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)

        default = [self.default_line()]*rows
        lines = pd.DataFrame(default, columns=self._data.columns)
        self._data = pd.concat([self._data.iloc[:position], lines, self._data.iloc[position:]]).reset_index(drop=True)

        self.endInsertRows()

        return True

    def removeRows(self, position, rows=1, parent=QtCore.QModelIndex()):
        """Remove row(s) from the model.

        Args:
            position (int): the position of the row(s) to delete
            rows (int): the number of rows to delete
            index (QModelIndex): the parent index
        """

        self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)

        print('BEFORE', len(self._data.index))
        self._data.drop(self._data.index[position:position+rows], inplace=True)
        print('AFTER', len(self._data.index))

        self.endRemoveRows()

        return True

    def rowCount(self, parent=None):
        return len(self._data.index)

When running that code, a new line is actually appended when I press tab but the currentIndex is set to (0,0) instead of the lowest right corner as expected by the code I wrote. Would you know what is wrong with my view/model ?

Upvotes: 2

Views: 2078

Answers (1)

eyllanesc
eyllanesc

Reputation: 243897

By overriding the keyPressEvent method since after implementing your custom logic you are invoking the default code that the tab uses to move the currentIndex, and since your currentIndex is in the bottonRight then it will move to the topLeft.

One possible solution is to prevent the default behavior from running using the "return" statement:

# ...
if key == QtCore.Qt.Key_Tab:
    if column == model.columnCount() - 1 and row == model.rowCount() - 1:
        model.insertRows(model.rowCount())
        index = model.index(model.rowCount() - 1, model.columnCount() - 1)
        self.setCurrentIndex(index)
        return
# ...

Upvotes: 2

Related Questions