项目中表格需要显示5万条数据以上,并且实时刷新。开始使用的tableWidget,数据量一大显得力不从心,所以使用Qt的Model/View来重新实现。下面是更改之前编写的小Demo。
import sys
from untitled import Ui_Form
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex, QVariant, QThread, pyqtSignal
class WorkThread(QThread):
scrollBottomSignal = pyqtSignal()
def __init__(self, model):
super(WorkThread, self).__init__()
self.model = model
self.run_flag = True
def run(self):
while self.run_flag:
row = self.model.rowCount()
self.model.insertRows(row, 1, QModelIndex())
self.scrollBottomSignal.emit()
self.usleep(1) # 不加延迟界面会卡顿。
def stop(self):
self.run_flag = False
class MyTableModel(QAbstractTableModel):
def __init__(self):
super(MyTableModel, self).__init__()
self._data = [] # 要显示的数据
self._headers = ['行号', '姓名', '年龄', '性别'] # 表头
self.i = 0
def rowCount(self, parent=QModelIndex()):
"""
返回行数量。
"""
return len(self._data)
def columnCount(self, parent=QModelIndex()):
"""
返回列数量。
"""
return len(self._headers)
def insertRows(self, row, count, parent):
"""
插入行。
:param row: 插入起始行。
:param count: 插入行数量。
:param parent:
:return:
"""
self.beginInsertRows(QModelIndex(), row, row + count - 1)
for i in range(count):
self._data.insert(row, ['CZ', '25', '男'])
self.endInsertRows()
return True
def removeRows(self, row, count, parent):
self.beginRemoveRows(QModelIndex(), 0, row + count - 1)
for i in range(count):
self._data.pop(row + count - 1 - i) # 倒着删
self.endRemoveRows()
def clearView(self):
self.removeRows(0, self.rowCount(), QModelIndex())
def headerData(self, section, orientation, role):
"""
设置表头。
"""
if role == Qt.DisplayRole and orientation == Qt.Horizontal: # 限定只更改行表头
return self._headers[section]
def data(self, index, role=Qt.DisplayRole):
if not index.isValid() or not 0 <= index.row() < self.rowCount():
return QVariant()
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
if col == 0:
return str(row) # 行号
else:
return str(self._data[row][col-1]) # 数据
return QVariant()
class MainUI(QWidget, Ui_Form):
def __init__(self):
super(MainUI, self).__init__()
self.setupUi(self)
self.workThread = None
self.pushButton.clicked.connect(self.buttonClickedStart)
self.pushButton_2.clicked.connect(self.buttonClickedStop)
self.pushButton_3.clicked.connect(self.buttonClickedClear)
self.model = MyTableModel()
self.tableView.setModel(self.model)
self.tableView.show()
def buttonClickedStart(self):
"""开启线程,向表中插入数据。"""
self.workThread = WorkThread(self.model)
self.workThread.scrollBottomSignal.connect(self.scrollBottom)
self.workThread.start()
def buttonClickedStop(self):
"""停止线程向表中插入数据。"""
self.workThread.stop()
def buttonClickedClear(self):
"""清空表。"""
self.model.clearView()
def scrollBottom(self):
"""右侧滑动条保持在最下面。"""
self.tableView.scrollToBottom()
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = MainUI()
ui.show()
sys.exit(app.exec_())
程序运行起来之后的样子,速度是杠杠的:

功能:
(1)点击开始按钮表格开始刷数据。
(2)点击暂停按钮表格停止刷数据。
(3)点击清空按钮清空表格中的数据。
但是在测试过程中发现问题,在表中数据刷新时点击清空按钮,表格中会出现很多空行,如下图所示,这些空行不管我们怎么点击清空都删除不了。

这个问题卡了大概一天多,最后定位到原因,Qt中在子线程中不要操作界面,子线程中不要操作界面,子线程中不要操作界面,重要说三遍。
我将子线程更改成信号的形式增加数据已经解决此问题,下面是更改部分代码:
class WorkThread(QThread):
scrollBottomSignal = pyqtSignal()
addDataSignal = pyqtSignal()
def __init__(self, model):
super(WorkThread, self).__init__()
self.model = model
self.run_flag = True
def run(self):
while self.run_flag:
# row = self.model.rowCount()
# self.model.insertRows(row, 1, QModelIndex())
self.addDataSignal.emit()
self.scrollBottomSignal.emit()
self.usleep(1) # 不加延迟界面会卡顿。
def stop(self):
self.run_flag = False
class MyTableModel(QAbstractTableModel):
... ...
def addData(self):
self.insertRows(self.rowCount(), 1, QModelIndex())
... ...
class MainUI(QWidget, Ui_Form):
... ...
def buttonClickedStart(self):
"""开启线程,向表中插入数据。"""
self.workThread = WorkThread(self.model)
self.workThread.addDataSignal.connect(self.model.addData)
self.workThread.scrollBottomSignal.connect(self.scrollBottom)
self.workThread.start()
... ...
