当我尝试通过dataChanged信号更新QTreeView时,出现访问冲突错误(进程以退出代码-1073741819(0xC0000005)完成)。
我在Windows 10 x64上使用Python 3.6,Pyqt5。
更新程序线程
class Queue_Updater(QThread):
clientSignal = pyqtSignal(dict)
processSignal = pyqtSignal(dict)
torrentSignal = pyqtSignal(dict)
distributedSignal = pyqtSignal(dict)
def __init__(self, queue=None, parent=None):
QThread.__init__(self, parent)
self.q = queue
self.threadactive=True
def run(self):
try:
while self.threadactive:
time.sleep(1)
for cmd in iter(self.q.get, None):
for cmd_id, data in cmd.items():
if (cmd_id == commands.peers) or (cmd_id == commands.leechers):
self.clientSignal.emit(data)
elif cmd_id == commands.processes:
self.processes = data[0]
self.clientSignal.emit(data[1])
self.processSignal.emit(data[0])
elif cmd_id == commands.list_of_torrents:
self.torrents = data
self.torrentSignal.emit(data)
elif cmd_id == events.successfully_distributed or events.unsuccessfully_distributed:
self.distributedSignal.emit(data)
except Exception:
print("Queue_Updater ")
def stop(self):
self.threadactive=False
self.wait()
QtShell-主应用程序
class QtShell(QMainWindow):
recheck_clients_by_list_sig = pyqtSignal()
def __init__(self, args):
...
self.ClientsView, self.ClPrModel = make_clientView(self, self.client_from_tasks, self.condition, self.mutex)
self.TaskView, self.TaskModel = make_taskview(self, str(self._outcome), self.condition, self.mutex)
self.updater = Queue_Updater(queue=self.queue_out, parent=self)
self.updater.processSignal.connect(self.update_proc_data)
self.updater.clientSignal.connect(self.TaskModel.upd_clients_data_intasks_slot)
self.updater.torrentSignal.connect(self.update_tasks_slot)
self.updater.distributedSignal.connect(self.TaskModel.upd_complexees_proc_status)
self.updater.start(QThread.LowPriority)
self.server = spawner(args, target=tracker_server, name='TrackerServer')
self.server.start()
...
@pyqtSlot(dict)
def update_proc_data(self, data):
if self.mutex.tryLock():
LeftUpperIndex, RightDownerIndex = self.ClPrModel.update_process(data)
#Here i'm trying to update view by direct call 'dataChanged'
#method. But effect with a send self.dataChanged() signal is a same.
self.ClientsView.dataChanged(QModelIndex(), QModelIndex())
self.mutex.unlock()
@pyqtSlot(dict)
def update_clients_data(self, data):
if self.mutex.tryLock():
LeftUpperIndex, RightDownerIndex = self.ClPrModel.update_clients(data)
self.ClientsView.dataChanged(QModelIndex(), QModelIndex())
self.mutex.unlock()
@pyqtSlot()
def update_tasks_slot(self):
if self.mutex:
if self.mutex.tryLock():
tasks = self.get_tasks_from_outcome(fpath=self._outcome)
self.TaskModel.task_update(tasks)
self._check_tasks_status(tasks)
self.mutex.unlock()
def get_tasks_from_outcome(self, fpath=None):
result = None
if fpath:
if not fpath == self._outcome:
address = "\\".join(fpath.split("\\")[:-1])
else:
address = self._outcome
if os.path.exists(address):
task_list = [f for f in os.listdir(address) if f.endswith(".task")]
config = configparser.ConfigParser()
for t in task_list:
config.read(os.path.join(address, t))
sections = config.sections()
if "MAIN" in sections:
if not result:
result = {}
result[t] = {}
for o in config.options("MAIN"):
result[t][o] = config["MAIN"][o]
else:
print("get_tasks_from_outcome: fpath doesn't exist")
return result
更新方法
def update_process(self, process):
LeftUpperIndex = None
RightDownerIndex = None
if isinstance(process, dict):
for uuid, proc in process.items():
id = self.root_item.get_child_id(uuid)
if id >= 0:
client = self.root_item.child(id)
if not LeftUpperIndex:
LeftUpperIndex = client.index()
list_for_remove = client.cut_through_the_list(proc)
for p in list_for_remove:
self.__remove_rows(p.row(), 1, client)
for num, data in proc.items():
is_in, proc_num = client.has_process(data[PROC_NAME])
if is_in:
process = client.child(proc_num)
process.setData(NUM, '{})'.format(proc_num))
process.setData(PROC_NAME, '{}: {}'.format(PROC_NAME, data[PROC_NAME]))
process.setData(PROC_STS, '{}: {}'.format(PROC_STS, data[PROC_STS]))
process.setData(PROC_PRGR, '{}: {}%'.format(PROC_PRGR, data[PROC_PRGR]))
else:
if not 'TrackerLoop' in data[PROC_NAME]:
item = ProcItem({NUM: '{})'.format(num),
EMPTY: "",
PROC_NAME: '{}: {}'.format(PROC_NAME, data[PROC_NAME]),
PROC_STS: '{}: {}'.format(PROC_STS, data[PROC_STS]),
PROC_PRGR: '{}: {}%'.format(PROC_PRGR, data[PROC_PRGR])},
parent=client)
client.appendChild(item)
if not RightDownerIndex and len(client.all_childs()) > 0:
last_child = client.child(-1)
RightDownerIndex = last_child.index()
return LeftUpperIndex, RightDownerIndex
def update_clients(self, client):
if isinstance(client, dict):
LeftUpperIndex = None
RightDownerIndex = None
if len(client) >= self.root_item.childCount():
for uuid, data in client.items():
id = self.root_item.get_child_id(uuid)
if id < 0:
item = ClientItem({CHECKED: "",
ID:uuid,
NAME:data['name'],
ADDRESS:data['addr'][0],
PORT:data['addr'][1],
DISTRIBUTOR:data[DISTRIBUTOR]},
parent=self.root_item)
self.root_item.appendChild(item)
RightDownerIndex = self.index(self.root_item.child(-1).row(),
0,
QModelIndex())
else:
remain = [uuid for uuid, data in client.items()]
excess = [c for c in self.root_item.childItems if c.id not in remain]
for client in excess:
self.__remove_rows(client.row(), 1, self.root_item)
leftUp, rightDown = self.recheck_clients_by_list_slot()
if RightDownerIndex:
LeftUpperIndex = self.index(0, 0, QModelIndex())
return LeftUpperIndex, RightDownerIndex
return QModelIndex(), QModelIndex()
def __remove_rows(self, position, rows, parent):
success = True
parentIndex = parent.index()
for i in range(position, position+rows):
self.beginRemoveRows(parentIndex, i, i)
success = success & parent.removeChild(parent.child(i))
self.endRemoveRows()
return success
我有一个QTreeView,它通过另一个线程的信号更新它自己的数据。在数据量增加之前,一切都很好。
当信号中的数据被迫从模型中删除一些数据时,问题就开始了。 再次:当我将数据添加到TreeModel(QAbstractItemModel)时,一切都很好。 当我从TreeModel(QAbstractItemModel)删除数据时,此后或一段时间后,但GUI进程崩溃并显示错误:
以退出代码-1073741819(0xC0000005)完成的过程
我尝试使用QMutex和multiprocessing.conditions。这是尝试将更新和删除TreeModel(QAbstractModel)中的数据的过程相互隔离。
它实际上是没有用的,因为据我所知,pyqtSignal / Slot架构师从一开始就是线程安全的。
我也尝试遵循这篇文章的建议: Qt QAbstractItemModel - item removal crashes 它建议使用'beginRemoveRows'和'endRemoveRows'删除数据,但这没有帮助。