Home > other >  Using QThread to Prevent Freeze when Clicking on QTreeWidget
Using QThread to Prevent Freeze when Clicking on QTreeWidget

Time:07-06

Every time the user clicks on an item in QTreeWidget, the computer starts to perform some heavy duty tasks. My only objective is to use QThread to prevent freeze.


Consider the following code:

from PyQt5.QtWidgets import*
from PyQt5.QtCore import*
import time

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('MainWindow')
        self.layout = QVBoxLayout()
        
        self.treewidget = MyTreeWidget()
        self.treewidget.itemClicked.connect(self.tree_click)
        self.layout.addWidget(self.treewidget)
        
        self.label = QLabel("Label", self)
        self.layout.addWidget(self.label)
        
        widget = QWidget()
        widget.setLayout(self.layout)
        self.setCentralWidget(widget)
        self.show()
        
    @pyqtSlot(QTreeWidgetItem)
    def tree_click(self, item):
        time.sleep(2)
        self.label.setText(item.text(0))
        
class MyTreeWidget(QTreeWidget):
    def __init__(self):
        super().__init__()
        item_1 = QTreeWidgetItem()
        item_1.setText(0, 'one item')
        item_2 = QTreeWidgetItem()
        item_2.setText(0,'another item')
        self.addTopLevelItem(item_1)
        item_1.addChild(item_2)
        self.setHeaderHidden(True)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    main = MainWindow()
    app.exec()

How to modify the above code to prevent freeze and what is the most appropriate way of doing it?


I tried by following the protocol given in Background thread with QThread in PyQt, the second answer:

from PyQt5.QtWidgets import*
from PyQt5.QtCore import*
import time

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('MainWindow')
        self.layout = QVBoxLayout()
        
        self.treewidget = MyTreeWidget()
        self.treewidget.itemClicked.connect(self.tree_click)
        self.layout.addWidget(self.treewidget)
        
        self.label = QLabel("Label", self)
        self.layout.addWidget(self.label)
        
        widget = QWidget()
        widget.setLayout(self.layout)
        self.setCentralWidget(widget)
        self.show()
        
    @pyqtSlot(QTreeWidgetItem)
    def tree_click(self, item):
        self.obj = Worker()
        self.thread = QThread()
        self.obj.finished.connect(self.change_text)
        self.obj.moveToThread(self.thread)
        self.obj.finished.connect(self.thread.quit)
        self.thread.started.connect(self.obj.work)
        self.thread.start()
    
    def change_text(self):
        self.label.setText(item.text(0))
        
class Worker(QObject):
    finished = pyqtSignal()
    @pyqtSlot()
    def work(self):
        time.sleep(2)
    finished = pyqtSignal()
        
class MyTreeWidget(QTreeWidget):
    def __init__(self):
        super().__init__()
        item_1 = QTreeWidgetItem()
        item_1.setText(0, 'one item')
        item_2 = QTreeWidgetItem()
        item_2.setText(0,'another item')
        self.addTopLevelItem(item_1)
        item_1.addChild(item_2)
        self.setHeaderHidden(True)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    main = MainWindow()
    app.exec()

It does not look like I am doing the right thing.

CodePudding user response:

I made a couple of minor adjustments to make your worker class run properly. This will prevent the rest of the gui from freezing but the label text that depends on the long running process will continue to take that long to update.

Also if the task implemented in the separate thread is repeated frequently you may want to consider using a QThreadPool.

I also included some inline comments to explain some of the changes.

from PyQt5.QtWidgets import*
from PyQt5.QtCore import*
import time

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('MainWindow')
        self.layout = QVBoxLayout()

        self.treewidget = MyTreeWidget()
        self.treewidget.itemClicked.connect(self.tree_click)
        self.layout.addWidget(self.treewidget)

        self.label = QLabel("Label", self)
        self.layout.addWidget(self.label)

        widget = QWidget()
        widget.setLayout(self.layout)
        self.setCentralWidget(widget)
        self.show()
        self.threads = []  # make shift threads container

    @pyqtSlot(QTreeWidgetItem)
    def tree_click(self, item):
        self.obj = Worker()
        thread = QThread()  
        self.obj.text = item.text(0) # the obj needs a copy of the text to emit
        self.obj.textReady.connect(self.change_text)
        self.obj.moveToThread(thread)
        self.obj.finished.connect(thread.quit)  
        thread.finished.connect(thread.deleteLater) # delete the threads once finished
        thread.started.connect(self.obj.work)
        thread.start()
        self.threads.append(thread)

    def change_text(self, text):
        self.label.setText(text)

class Worker(QObject):
    finished = pyqtSignal()
    textReady = pyqtSignal([str])

    @pyqtSlot()
    def work(self):
        time.sleep(2)
        self.textReady.emit(self.text) # emit the text that needs to be changed 
        self.finished.emit() 
                             
class MyTreeWidget(QTreeWidget):
    def __init__(self):
        super().__init__()
        item_1 = QTreeWidgetItem()
        item_1.setText(0, 'one item')
        item_2 = QTreeWidgetItem()
        item_2.setText(0,'another item')
        self.addTopLevelItem(item_1)
        item_1.addChild(item_2)
        self.setHeaderHidden(True)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    main = MainWindow()
    app.exec()
  • Related