I am at a dead end after spending the last year of my life building a PySide2 application that rapidly displays data using a TreeWidget. After starting the application, the data is appended without issue. However, after a few minutes, the program becomes very unstable/slow and eventually becomes unresponsive. Sometimes it outright crashes and closes. There is no error message, stack trace, or logs to go off of. (Is there some type of log I should be probing?)
When the freeze occurs, CPU usage jumps from about 9% to 20%.
The problem comes sooner if the program is minimized or the cursor interacts with the TreeWidget. It appears the TreeWidget cannot keep up with the frequency of data being added to it.
Originally, I thought the issue was with scrollToItem
(the program seems slightly more stable without it), but when I built the below example to illustrate, I cannot get it to crash like it does on my main program. It is extremely frustrating because I have no idea what part of my code could be affecting the application differently than the example.
In the example, you may notice a few stutters when the window is unfocused or minimized. However, it always returns to normal when focus is given back to the window. In my main program, it will hang indefinitely. Like the example, the mechanism pushing data to the TreeWidget does so in another thread via emitting signals. (Except one class is used to hold all signals shared by all threads - I don't think this should be a problem, right?)
What else could I possibly be missing? How can I narrow down the problem short of posting my entire project? Are there performance implications for QTreeView
? Is there any alternative like a ListView
with multiple columns?
from PySide2 import QtCore, QtWidgets, QtGui
import random
class MainWindow(QtWidgets.QApplication):
def __init__(self):
super().__init__()
win = QtWidgets.QMainWindow()
v = CANView()
b = QtWidgets.QPushButton('Go')
b.clicked.connect(self.onButtonPush)
g = QtWidgets.QVBoxLayout()
g.addWidget(b)
g.addWidget(v)
f = QtWidgets.QFrame()
f.setLayout(g)
win.setCentralWidget(f)
win.show()
self.thread = CANThread()
self.thread.newMessageSignal.connect(v.onFrame)
self.exec_()
def onButtonPush(self):
self.thread.start()
class CANView(QtWidgets.QTreeWidget):
def __init__(self):
super().__init__()
self.setHeaderLabels(['Timestamp', 'CAN ID', 'Data'])
self.setIndentation(0)
def onFrame(self, a, b, c):
treeItem = RandomItem(a, b, c)
if(self.invisibleRootItem().childCount() > 5000):
self.takeTopLevelItem(0)
self.addTopLevelItem(treeItem)
self.scrollToItem(treeItem, QtWidgets.QAbstractItemView.ScrollHint.PositionAtBottom)
class CANThread(QtCore.QThread):
newMessageSignal = QtCore.Signal(int, int, int)
def __init__(self):
super().__init__()
self.counter = 0
def run(self):
while(True):
self.newMessageSignal.emit(self.counter, random.randint(0, 0x7FFFFFFF), random.randint(0, 0x7FFFFFFF))
self.counter += 1
QtCore.QThread.msleep(random.randint(0, 15))
fontMono = QtGui.QFont('Lucida Console')
colorLightFont = QtGui.QColor('#888888')
class RandomItem(QtWidgets.QTreeWidgetItem):
def __init__(self, a, b, c):
super().__init__()
self.setText(0, f'{a:X}')
self.setText(1, f'{b:X}')
self.setText(2, f'{c:X}')
self.setFont(1, fontMono)
self.setFont(2, fontMono)
if __name__ == '__main__':
MainWindow()
User contributions licensed under CC BY-SA 3.0