我想做的是用另一个线程更新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