'PyQt5 QTableWidget GUI update is too slow when there are many cells

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class MyWindow(QMainWindow):
    count = 0

    def __init__(self):
        super().__init__()

        self.mdi = QMdiArea()
        self.mdi.setDocumentMode(True)
        self.mdi.setOption(QMdiArea.DontMaximizeSubWindowOnActivation)
        self.mdi.setViewMode(QMdiArea.TabbedView)
        self.mdi.setTabPosition(QTabWidget.South)
        self.mdi.setTabsClosable(True)
        self.mdi.setTabsMovable(True)

        self.setCentralWidget(self.mdi)

        self.bar = self.menuBar()

        self.file = self.bar.addMenu("File")
        self.file.addAction("New")
        self.file.addAction("cascade")
        self.file.addAction("Tiled")
        self.file.triggered[QAction].connect(self.windowaction)

        self.action2 = QAction("Plus1(GUI update Main Thread)")
        self.bar.addAction(self.action2)
        self.action2.triggered.connect(self.plus)

        self.action3 = QAction("Plus2(Thread Communication with Signal&Slot")
        self.bar.addAction(self.action3)
        self.action3.triggered.connect(self.plus2)

        self.action4 = QAction("Plus3(GUI update in Thread")
        self.bar.addAction(self.action4)
        self.action4.triggered.connect(self.plus3)

        self.setWindowTitle("MDI demo")

        self.show()

    def plus(self):
        self.timer = QTimer(self)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.timeout)
        self.timer.start()

    def plus2(self):
        self.timer2 = QTimer(self)
        self.timer2.setInterval(1000)
        self.timer2.timeout.connect(self.timeout2)
        self.timer2.start()

    def plus3(self):
        self.timer3 = QTimer(self)
        self.timer3.setInterval(10)
        self.timer3.timeout.connect(self.timeout3)
        self.timer3.start()

    def timeout(self):
        for i in range(self.rows):
            item = self.tblWgt.item(i, 0)
            item.setText(str(item.data(Qt.ItemDataRole.UserRole) + 1) + "%")
            item.setData(Qt.ItemDataRole.UserRole, item.data(Qt.ItemDataRole.UserRole) + 1)

    def timeout2(self):
        self.r = Thread(self)
        self.r.updater.connect(self.updater)
        self.r.start()

    def timeout3(self):
        self.r2 = Thread2(self)
        self.r2.start()

    def updater(self, text, num, idx):
        item = self.tblWgt.item(idx, 0)
        item.setText(text)
        item.setData(Qt.ItemDataRole.UserRole, num)

    def windowaction(self, q):
        print("triggered")

        if q.text() == "New":
            self.tblWgt = QTableWidget()
            self.tblWgt.setSortingEnabled(True)

            self.tblWgt.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            self.tblWgt.setSelectionMode(QAbstractItemView.SingleSelection)

            self.tblWgt.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
            self.tblWgt.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

            self.rows = 300
            self.columns = 10
            self.tblWgt.setRowCount(self.rows)
            self.tblWgt.setColumnCount(self.columns)

            columnList = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]

            for i, column in enumerate(columnList):
                self.tblWgt.setHorizontalHeaderItem(i, QTableWidgetItem(column))

            for i in range(self.rows):
                item = NumericItem()
                item.setFlags(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled)
                item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
                item.setForeground(QColor(0, 0, 255))
                item.setText(str(i + 1 - 10) + "%")
                item.setData(Qt.ItemDataRole.UserRole, i + 1 - 10)
                self.tblWgt.setItem(i, 0, item)

            self.sub = QMdiSubWindow()
            self.sub.setWidget(self.tblWgt)

            self.sub.setGeometry(0, 0,
                             self.tblWgt.horizontalHeader().length() + self.tblWgt.verticalHeader().width() + self.tblWgt.contentsMargins().left() + self.tblWgt.contentsMargins().right() + 8 * 2 + 17
                             , 1000)  # self.tblWgt.verticalHeader().length() + self.tblWgt.horizontalHeader().height() + self.tblWgt.contentsMargins().top() + self.tblWgt.contentsMargins().bottom() + QStyle.PM_TitleBarHeight + 1 + 8)

            self.sub.setWindowTitle("subwindow" + str(self.count))
            self.count = self.count + 1
            self.mdi.addSubWindow(self.sub)
            self.sub.show()

        if q.text() == "cascade":
            self.mdi.cascadeSubWindows()

        if q.text() == "Tiled":
            self.mdi.tileSubWindows()


class Thread(QThread):
    updater = pyqtSignal(str, int, int)

    def __init__(self, app):
        super(Thread, self).__init__()
        self.app = app

    def run(self):
        for i in range(self.app.rows):
            item = self.app.tblWgt.item(i, 0)
            self.updater.emit(str(item.data(Qt.ItemDataRole.UserRole) + 1) + "%", item.data(Qt.ItemDataRole.UserRole) + 1, i)


class Thread2(QThread):
    def __init__(self, app):
        super(Thread2, self).__init__()
        self.app = app

    def run(self):
        for i in range(self.app.rows):
            item = self.app.tblWgt.item(i, 0)
            item.setText(str(item.data(Qt.ItemDataRole.UserRole) + 1) + "%")
            item.setData(Qt.ItemDataRole.UserRole, item.data(Qt.ItemDataRole.UserRole) + 1)


class NumericItem(QTableWidgetItem):
    def __lt__(self, other):
        return self.data(Qt.UserRole) < other.data(Qt.UserRole)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyWindow()
    app.exec_()

I uploaded the full source, so please test it as it is.

Updating GUI on the main thread is of course slow.(Plus1 Button) So, I used the way of updating GUI through the signal slot after working through the thread, but this is also still slow.(Plus2 Button) If I update GUI in a thread, the internal value changes, but GUI does not change unless an event such as resize occurs and this way is not thread-safe..(Plus3 Button) Then, how can data of many cells be updated in real time?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source