在PySide2 QThread中操作小部件导致python3不响应

时间:2019-03-11 08:41:05

标签: python pyside2

我想做的是用另一个线程更新QTreeWidget,以免挂起UI。因此,我进行了如下演示:

环境: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

一切正常,除了在单击按钮的服务器时间后,将显示“ python没有响应”对话框,并且程序退出,退出代码为-1073741819(0xC0000005)。有人说此退出代码是空指针异常。

如果我注释掉以下几行,现象就会消失:

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

我怀疑按钮ref导致了这种情况。 因此,我再次尝试进行检查:保留上面的行并注释掉以下行:

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])

现象也消失了。 有人知道我的python代码有什么问题吗?

编辑1:删除QThread中的所有UI代码,仍然无法使用。 终端错误:

Fatal Python error: GC object already tracked

进程结束,退出代码为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

0 个答案:

没有答案