如何在动态提要数据时限制 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()
这样,如果选择了一个索引,则只要添加新行并且达到最大行数,选择就会正确更新(上移)。
我写了一个简单的 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()
andendInsertRows()
,它将负责适当地更新模型,以及链接视图在适当的时候用适当的参数发出上述信号; - 仅在实际插入一行时调用上述函数;
考虑到上述情况,每当模型达到最大行数时,它将不会添加新行(因为实际上没有添加行),但会发出 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()
这样,如果选择了一个索引,则只要添加新行并且达到最大行数,选择就会正确更新(上移)。