То, что я пытаюсь сделать, это обновить QTreeWidget другим потоком, чтобы не зависать пользовательский интерфейс.Поэтому я делаю демонстрацию следующим образом:
Среда: 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 not response», и программа завершит работу с кодом выхода -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, все еще не работает.ошибка терминала:
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