0

I have a QTableView using a QAbstractTableModel and I am trying create some remove row functionality. I have written in the removeRow code like so -

def removeRows(self, row, count, parent=QtCore.QModelIndex()):
    self.beginRemoveRows(parent, row, row + count - 1)
    for i in range(count):
        del self._data[row]
    self.endRemoveRows()
    return True

When calling a removeRow however it doesn't immediately refresh the table, it will only refresh after I click somewhere on the table. Do I need to call a layoutChanged.emit() after the removal is ran? and would I need to call a layoutAboutToBeChanged if I am calling a layoutChanged?

Also I wanted to be able to remove rows that are not in span. Like if the user cntrl selected random rows (like maybe row 1 and row 7 for example). Clearly this was designed to be setup to only delete rows in a span so not sure if its possible to delete them in random locations.

Edit: So i think I was able to set it up to remove random rows when give an list of rows, it seems to be working.

def removeRows(self, row_indexes, parent=QtCore.QModelIndex()):
    self.layoutAboutToBeChanged.emit()
    row_indexes.sort(reverse=True)
    for row in row_indexes:
        self.beginRemoveRows(parent, row, row)
        del self._data[row]
        self.endRemoveRows()
    self.layoutChanged.emit()
    return True

And to call it I do this

row_index = set()
selected_rows = self.output.selectedIndexes()
for row in selected_rows:
    row_index.add(row.row())
self.model.removeRows(list(row_index), self.output_list.currentIndex())

I added the layoutToBeChanged.emit() and layoutchanged.emit() before and after doing the removal, and it is now refreshing the table. Not sure if that is the right way to do it, without the layoutchange it doesn't refresh it automatically.

This is my entire QabstractTableModel class

class FileTableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
    super(FileTableModel, self).__init__()
    self._data = data
    self.headers = ['File Name', 'Path', 'File Size', 'Modified Date', 'Creation Date']

def data(self, index, role):
    if role == QtCore.Qt.DisplayRole:
        value = self._data[index.row()][index.column()]

        if isinstance(value, datetime):
            return datetime.strftime(value, "%m/%d/%Y %X %p")
        elif isinstance(value, int):
            return size_conversion(value)
        else:
            return value

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

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

def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
    if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
        return self.headers[section]

def sort(self, column, order=QtCore.Qt.AscendingOrder):
    self.layoutAboutToBeChanged.emit()
    self._data.sort(key=lambda x: x[column], reverse=order == QtCore.Qt.DescendingOrder)
    self.layoutChanged.emit()

def removeRows(self, row_indexes, parent=QtCore.QModelIndex()):
    self.layoutAboutToBeChanged.emit()
    row_indexes.sort(reverse=True)
    for row in row_indexes:
        self.beginRemoveRows(parent, row, row)
        del self._data[row]
        self.endRemoveRows()
    self.layoutChanged.emit()
    return True
ghost654
  • 161
  • 2
  • 18
  • You don't need to call any further methods, assuming that you didn't do anything wrong somewhere else. Please provide a valid [mre], including the part where you're calling `removeRow()`. Also, there's no method to directly remove multiple rows that are not consecutive, you can only call `removeRow()` for unique rows or `removeRows()` if you're able to get "groups" of consecutive rows. – musicamante May 10 '23 at 18:42
  • I can't reproduce your non-updating view. Without the layoutChanged signals the view is immediately refreshed when removeRows is called. – mahkitah May 11 '23 at 17:33
  • An easier way to get a list of selected rows from the view: `[i.row() for i in self.output.selectionModel().selectedRows()]`. This assumes that selectionbehavior is set to SelectRows – mahkitah May 11 '23 at 17:34
  • oh good call on the list comprehension. Yeah im not sure why mine isn't updating. I removed the layoutchanged again and it only updates when I click on the table again. Seems to be working fine when i add in layout changed though... – ghost654 May 12 '23 at 15:48

1 Answers1

1

Here's my own implementation for removing multiple rows. The list of rows is chopped up into continuous slices so the removal can be done as efficiently as possible.

@staticmethod
def continuous_slices(numbers):
    if not numbers:
        return
    numbers.sort(reverse=True)
    start_idx = 0
    for idx in range(1, len(numbers)):
        if numbers[idx - 1] > (numbers[idx] + 1):
            yield numbers[idx - 1], numbers[start_idx]
            start_idx = idx
    yield numbers[-1], numbers[start_idx]

def removeRows(self, row_indices, parent=QtCore.QModelIndex()):
    for first, last in self.continuous_slices(row_indices):
        self.beginRemoveRows(parent, first, last)
        del self._data[first: last + 1]
        self.endRemoveRows()
    return True
mahkitah
  • 562
  • 1
  • 6
  • 19