manipulating widget in PySide2 QThread causes python3 not responding

0

What I am trying to do is to update the QTreeWidget with another thread in order not to hang the UI. So I make a demo as followed:

Environment: win10 x64, Python3.6.8 x32, PySide2 5.12.1

# -*- coding: utf-8 -*-
import os

from PySide2 import QtWidgets, QtGui, QtCore


class ShowFolderTreeThread(QtCore.QThread):

    def __init__(self, p, treeWidget: QtWidgets.QTreeWidget, root_dir: str = "."):
        super().__init__(p)
        self.root_dir = root_dir
        self.treeWidget = treeWidget

    def list_folder(self, parent_path: str, parent_item=None, max_depth: int = 3):
        if max_depth <= 0:
            return
        try:
            for content in os.listdir(parent_path):
                absolute_path = os.path.join(parent_path, content)
                is_dir: bool = os.path.isdir(absolute_path)
                item = QtWidgets.QTreeWidgetItem(parent_item or self.treeWidget)
                item.setText(0, content)
                item.setText(1, "Folder" if is_dir else os.path.splitext(content)[1])
                if is_dir:
                    self.list_folder(absolute_path, item, max_depth - 1)
        except:
            pass

    def run(self):
        try:
            self.treeWidget.clear()
            self.list_folder(self.root_dir)
        except Exception as e:
            print(type(e), e)


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.treeWidget = QtWidgets.QTreeWidget(self)
        self.treeWidget.setHeaderLabel("name")
        self.pushButton = QtWidgets.QPushButton(self)
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("Refresh")
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.treeWidget)
        layout.addWidget(self.pushButton)
        self.setLayout(layout)
        self.resize(400, 600)
        self.workThread = ShowFolderTreeThread(self, self.treeWidget)
        self.workThread.setObjectName("workThread")

        self.workThread.finished.connect(self.thread_finished)
        self.pushButton.clicked.connect(self.start_thread)

    def start_thread(self):
        self.pushButton.setDisabled(True)  # fixme: cause Python not responding if set btn to disabled and then enabled
        self.workThread.start()

    def thread_finished(self):
        self.pushButton.setEnabled(True)  # fixme: cause Python not responding if set btn to disabled and then enabled
        print("thread_finished")


if __name__ == '__main__':
    import sys

    try:
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())
    except Exception as e:
        print(type(e), e)
        raise e

Everything works fine except after serveral times of clicking the push button, a "python not responding" dialog shows up and the program exits with exit code -1073741819 (0xC0000005). Someone says this exit code is a null pointer exception.

Phenomenon disappears if I comment out the following lines:

line57: self.pushButton.setDisabled(True)
line61: self.pushButton.setEnabled(True)

I suspect the push button ref causes this. So I do another try to check this: leave the lines above and comment out the following lines:

line21: item = QtWidgets.QTreeWidgetItem(parent_item or self.treeWidget)
line22: item.setText(0, content)
line23: item.setText(1, "Folder" if is_dir else os.path.splitext(content)[1])

Phenomenon disappears as well. Does anyone know what's wrong with my python code?

Edit 1: remove all the UI code in QThread, still not works. terminal error:

Fatal Python error: GC object already tracked

Process finished with exit code 255

# -*- coding: utf-8 -*-
import os

from PySide2 import QtWidgets, QtGui, QtCore


class ShowFolderTreeThread(QtCore.QThread):
    addTopItem = QtCore.Signal(str, str, tuple)

    def __init__(self, p, root_dir: str = "."):
        super().__init__(p)
        self.root_dir = root_dir

    def list_folder(self, parent_path: str, max_depth: int = 3, parent_item_ids=tuple()):
        if max_depth <= 0:
            return
        try:
            for row, content in enumerate(os.listdir(parent_path)):
                content_path = os.path.join(parent_path, content)
                is_dir: bool = os.path.isdir(content_path)
                self.addTopItem.emit(content, "Folder" if is_dir else os.path.splitext(content)[1], parent_item_ids)
                if is_dir:
                    del is_dir
                    self.list_folder(content_path, max_depth - 1, (*parent_item_ids, row))
        except Exception as e:
            print("list_folder", type(e), e)

    def run(self):
        try:
            self.list_folder(self.root_dir)
        except Exception as e:
            print(type(e), e)


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.treeWidget = QtWidgets.QTreeWidget(self)
        self.treeWidget.setColumnCount(2)
        self.treeWidget.setHeaderLabels(["name", "type", ])
        self.treeWidget.setColumnWidth(0, 250)

        self.pushButton = QtWidgets.QPushButton(self)
        self.pushButton.setText("&Refresh")

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.treeWidget)
        layout.addWidget(self.pushButton)
        self.setLayout(layout)

        self.resize(400, 600)

        home = os.path.expanduser('~')
        self.workThread = ShowFolderTreeThread(self, home)

        self.workThread.finished.connect(self.thread_finished)
        self.workThread.addTopItem.connect(self.addTopItem)
        self.pushButton.clicked.connect(self.start_thread)

        self.timer = QtCore.QTimer()
        self.timer.setInterval(10)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.pushButton.click)
        self.timer.start()

    def addTopItem(self, content: str, content_type: str, parent_item_ids: tuple):
        item = QtWidgets.QTreeWidgetItem()
        item.setText(0, content)
        item.setText(1, content_type)
        if parent_item_ids:
            index, *item_ids = parent_item_ids
            parent_item = self.treeWidget.topLevelItem(index)
            for i in item_ids:
                parent_item = parent_item.child(i)
            parent_item.addChild(item)
        else:
            self.treeWidget.addTopLevelItem(item)

    def start_thread(self):
        self.pushButton.setDisabled(True)
        self.treeWidget.clear()
        self.workThread.start()

    def thread_finished(self):
        self.pushButton.setEnabled(True)
        self.timer.start()

    def closeEvent(self, event: QtGui.QCloseEvent):
        self.timer.stop()
        super().closeEvent(event)


if __name__ == '__main__':
    import sys

    try:
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())
    except Exception as e:
        print(type(e), e)
        raise e
python
pyside2
asked on Stack Overflow Mar 11, 2019 by wzzgdcn • edited Mar 12, 2019 by wzzgdcn

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0