如何在动态提要数据时限制 table 行并且不附加空白行?

How to limit table rows and don't append blank row while dynamic feed data?

我写了一个简单的 talbe 演示,限制 table 行数是 10.The table 工作正常,行数是 10,这是我 wanted.The 的问题就是当新数据超过10行时,TableView也会无休止的追加一个空行,这不是我wanted.Can有人知道怎么做或者给点意见或解决办法吗?

代码演示

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

TABLE_MAX_LINE = 10

class Model(QAbstractTableModel):
    def __init__(self) -> None:
        super().__init__()
        self._data = []
        self._columns = list("ABCD")

    def appendData(self, oneRecord):
        dataLen = len(self._data)

        self.rowsAboutToBeInserted.emit(QModelIndex(), dataLen, dataLen)
        # self.layoutAboutToBeChanged.emit()
        self._data.append(oneRecord)
        # self.layoutChanged.emit()
        self.rowsInserted.emit(QModelIndex(), dataLen, dataLen)

        # print(dataLen, TABLE_MAX_LINE)
        if dataLen > TABLE_MAX_LINE:
            del self._data[0]

    def headerData(self, section: int, orientation: Qt.Orientation, role: int):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._columns[section]
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return f"{section}"

    def rowCount(self, parent) -> int:
        return len(self._data)

    def columnCount(self, parent) -> int:
        return len(self._columns)

    def data(self, index, role: int):
        if index.isValid() and role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]


app = QApplication([])

v = QTableView()
v.show()
model = Model()
v.setModel(model)

def appendData():
    import random
    r = lambda: random.randint(1, 10000)
    model.appendData([r(), r(), r(), r()])

timer = QTimer()
timer.setInterval(1000)
timer.timeout.connect(appendData)
timer.start()
app.exec()

效果

问题是您在之前发出插入行信号检查数据长度是否大于允许的长度。

虽然从理论上讲,逻辑会说它 应该 工作,但它不是一个好的实现并且不能很好地与 Qt 项目视图实现一起工作,Qt 项目视图实现使用一些内部机制优化,如果数据模型的“大小”不匹配,则无法跟上。要记住的最重要的方面是视图 总是 调用 rowCount() 每当添加新行时, rowsInserted 信号将与实际不匹配行数,因为此时 self._data 的长度更大。

为了正确实现所需的行为,必须遵循以下几点:

  • 不要直接发出信号,而是使用现有的函数:beginInsertRows() and endInsertRows(),它将负责适当地更新模型,以及链接视图在适当的时候用适当的参数发出上述信号;
  • 仅在实际插入一行时调用上述函数

考虑到上述情况,每当模型达到最大行数时,它将不会添加新行(因为实际上没有添加行),但会发出 dataChanged 信号以确保 shifted 索引将正确使用新数据:

class Model(QAbstractTableModel):
    # ...
    def appendData(self, oneRecord):
        dataLen = len(self._data)
        if dataLen <= TABLE_MAX_LINE:
            self.beginInsertRows(QModelIndex(), dataLen, dataLen)
            self._data.append(oneRecord)
            self.endInsertRows()
        else:
            del self._data[0]
            self._data.append(oneRecord)
            self.dataChanged.emit(
                self.index(0, 0), 
                self.index(dataLen - 1, len(self._columns) - 1)
            )

请注意,如果您想保持视图正确更新(您可能应该这样做),索引也应随更改的模型一起更新,因此更好的解决方案会如果行数已达到最大值,则删除模型中的第一行,然后追加新行 anyway:

class Model(QAbstractTableModel):
    # ...
    def appendData(self, oneRecord):
        dataLen = len(self._data)
        if dataLen > TABLE_MAX_LINE:
            self.beginRemoveRows(QModelIndex(), 0, 0)
            del self._data[0]
            self.endRemoveRows()
        self.beginInsertRows(QModelIndex(), dataLen, dataLen)
        self._data.append(oneRecord)
        self.endInsertRows()

这样,如果选择了一个索引,则只要添加新行并且达到最大行数,选择就会正确更新(上移)。