1. 程式人生 > 實用技巧 >Pyqt5實現model/View,解決tableView出現空白行問題。

Pyqt5實現model/View,解決tableView出現空白行問題。

專案中表格需要顯示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()

  ... ...