PyQT5: Access violation ((0xC0000005) error after update QTreeView

0

I'm getting Access violation error (Process finished with exit code -1073741819 (0xC0000005)) when i try to update QTreeView by dataChanged signal.

I use Python 3.6, Pyqt5 on Windows 10 x64.

updater thread

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 - the main application

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

update methods

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

I have a QTreeView, which update it's own data by a signal from another thread. Until amount of data increases, everything is fine.

Problems began when data from a signal inforced to remove some data from model. Again: when i ADD data to TreeModel(QAbstractItemModel) everything is fine. When i REMOVE data from TreeModel(QAbstractItemModel), right after, or in some time, but GUI process is crashing with error:

Process finished with exit code -1073741819 (0xC0000005)

I have try to use QMutex and multiprocessing.conditions. it was a try to isolate process of updating and removing data from TreeModel(QAbstractModel) from each other.

It actually was useless, because pyqtSignal/Slot architect as far as I know, is a thread-safe from it's beginning.

i also have tryed to follow advice from this post: Qt QAbstractItemModel - item removal crashes It propose to use 'beginRemoveRows' and 'endRemoveRows' for remove data, but it didn't help.

model-view-controller
pyqt5
python-3.6
asked on Stack Overflow Jun 12, 2019 by Alexander Khaustov • edited Jun 12, 2019 by marc_s

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0