'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 |
|---|
