項目中表格需要顯示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()
... ...
