Search code examples
pythonqtpyqt5tableviewqabstracttablemodel

how to diseable select row behavior when adding/deleating a row in QTableView


I have a subclassed QAbstractTableModel with a insert/deleate rows method

in the main window I have a methods insert_data thats insert a string when the cell is currently selected (I turned on singe selection)
Here is the behavior that I want to change:
when I insert a value in a cell with the setData method of the subclassed QAbstractTableModel or with insert_data
and deleate that row --->the row above gets selected (the row changes its color to grey ),
so the method automatically inserts the string
the conclusion is that the add/del methods change the added/deleated rows to being selected when executed
What ive tryed:
add/del rows methods are connected with two pushbuttons

 self.addrow_button.clicked.connect(
            lambda: self.datamodel.insertRows(-1, 1))
        self.deleaterow_button.clicked.connect(
            lambda: self.datamodel.removeRows(-1, 1))

so I connectet them to selectionModel().clearSelection()) to clear the selection of that row when a row gets added/deleated

self.addrow_button.clicked.connect(lambda: self.mytable_view.selectionModel().clearSelection())
        self.deleaterow_button.clicked.connect(lambda: self.mytable_view.selectionModel().clearSelection())

but this only remove the grey color of the cell---> the cell is still selected, behavior continues

is there a way to diseable that the above/below row is beeing selected when the adding/deleating row method gets executed ?


code example



"""
Testing Template for throw away experiment

"""


import sys
import os
import re

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg



class DataModel(qtc.QAbstractTableModel):



    def __init__(self, input_data=None):
        super().__init__()

        self.input_data = input_data or [[None], [None], [None], [None], [None]]


    def data(self, index, role):

        if role == qtc.Qt.DisplayRole:
            try:
                text = self.input_data[index.row()][index.column()]
                # self.scaledatachanged_signal.emit() dont need it
            except IndexError:
                text = None

            return text

    def rowCount(self, index=qtc.QModelIndex()):
        return 0 if index.isValid() else len(self.input_data)


    def columnCount(self, index):
        return len(self.input_data[0])

    def headerData(self, section, orientation, role):
        if role == qtc.Qt.DisplayRole:

            if orientation == qtc.Qt.Vertical:
                return "row  " + str(section + 1)



    def flags(self, index):
        return qtc.Qt.ItemIsEditable | qtc.Qt.ItemIsSelectable | qtc.Qt.ItemIsEnabled

    def setData(self, index, value, role=qtc.Qt.EditRole):

        if role == qtc.Qt.EditRole:  # The data in a form suitable for editing in an editor. returns string
            try:
                row = index.row()
                column = index.column()

                # filter floats and digits and comma stuff

                pattern = '^[\d]+(?:[,.][\d]+)?$'  # execepts "," and "."

                if re.fullmatch(pattern, value, flags=0):
                    pattern = '^.*[,].*$'

                    if re.fullmatch(pattern, value, flags=0):

                        value = value.replace(',', '.')

                        self.input_data[row][column] = float(value)

                        print(type(value))

                    else:
                        self.input_data[row][column] = float(value)  # float

                        print(type(value))
                else:
                    pass

                return True

            except ValueError:
                return False

    def insertRows(self, position, rows, parent=qtc.QModelIndex()):
        position = (position + self.rowCount()) if position < 0 else position
        start = position
        end = position + rows - 1
        if end <= 8:
            self.beginInsertRows(parent, start, end)
            self.input_data.append([None])
            self.endInsertRows()
            return True
        else:
            return False

    def removeRows(self, position, rows, parent=qtc.QModelIndex()):
        position = (position + self.rowCount()) if position < 0 else position

        start = position

        end = position + rows - 1
        if end >= 1:
            self.beginRemoveRows(parent, start, end)
            del self.input_data[start:end + 1]
            self.endRemoveRows()
            return True
        else:
            return False

class MainWindow(qtw.QWidget):
    def __init__(self):
        super().__init__()



        self.mytable_view = qtw.QTableView()

        self.mytable_view.setSelectionMode(qtw.QAbstractItemView.SingleSelection)


        self.datamodel = DataModel()
        self.mytable_view.setModel(self.datamodel)

        # widget
        self.addrow_button = qtw.QPushButton("add row")
        self.deleaterow_button = qtw.QPushButton("deleate row")


    # set the layout
        layout = qtw.QVBoxLayout()
        layout.addWidget(self.mytable_view)
        layout.addWidget(self.addrow_button)
        layout.addWidget(self.deleaterow_button)

        self.setLayout(layout)


    # -------------------------------- #
        self.addrow_button.clicked.connect(
            lambda: self.datamodel.insertRows(-1, 1))
        self.deleaterow_button.clicked.connect(
            lambda: self.datamodel.removeRows(-1, 1))

        self.mytable_view.selectionModel().selectionChanged.connect(self.insert_data)


        self.addrow_button.clicked.connect(lambda: self.mytable_view.selectionModel().clearSelection())
        self.deleaterow_button.clicked.connect(lambda: self.mytable_view.selectionModel().clearSelection())



    def insert_data(self):

        x = self.mytable_view.selectionModel().currentIndex().row()
        y = self.mytable_view.selectionModel().currentIndex().column()


        self.datamodel.input_data[x][y] = "insert this string if cell is selected"
        self.datamodel.layoutChanged.emit()
        self.mytable_view.selectionModel().clearSelection()




if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)

    main = MainWindow()
    main.show()

    sys.exit(app.exec_())






Solution

  • Whenever an item that is the current index is removed, the previous one is always automatically selected. Note that current index could not match the item selection: if you select an index and then clear the selection, that index will still be the current index.

    The most simple solution is to set the current index to an invalid one btyusing a new instance of QModelIndex (which is an invalid index) and before deleting the row.

            # ...
            self.deleaterow_button.clicked.connect(self.delete_row)
            # ...
    
        def delete_row(self):
            self.mytable_view.setCurrentIndex(qtc.QModelIndex())
            self.datamodel.removeRows(-1, 1)