Pyqt5实现model/View,解决tableView出现空白行问题。

时间:2024-03-10 11:59:00

 项目中表格需要显示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()

  ... ...