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