Search code examples
pythonpyqt6

Qt Application crashs silently after couple hours running


I have been struggling with debugging this for a week now, it is a fairly big project but I think I have narrowed down what's causing the crash to a few possibilities.

output before crash :

This function's name is:check {'type': 'DISPO OCP', 'nom': 'AVAMYS 27,5 microgrammes/pulvérisation, suspension pour pulvérisation nasale', 'cip13': '3400938322446', 'quantité': 'N/A'} start of slot received : end of slot requete ocp n°1398 requete ocp n°1399 requete ocp n°1400 QThread: Destroyed while thread is still running

Application has a QMainWindow and a worker thread, worker thread fetches data on the internet and treats that data then emit a signal for the QMainWindow slot to catch. Worker thread is designed to run forever.

class MainWindowWrapper(Ui_MainWindow, QtWidgets.QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.setupUi(self)
        self.Set_Table_View()

Ui_MainWindow is a py file generated by Qt Designer.

def Set_Table_View(self):
    self.model_table_view = QtGui.QStandardItemModel()
    self.model_table_view.setHorizontalHeaderLabels(['Type', 'Nom', 'CIP 13', 'Quantité', "Horodatage"])
    #Setup middle filter
    self.proxyModel_combobox = QSortFilterProxyModel()
    self.proxyModel_combobox.setSourceModel(self.model_table_view)
    self.proxyModel_combobox.setFilterKeyColumn(0)
    #setup filter
    self.Table_proxyModel = MultiColumnFilterProxyModel()
    self.Table_proxyModel.setSourceModel(self.proxyModel_combobox)
    self.Table_proxyModel.setFilterKeyColumn(0)
    self.tableView.setModel(self.Table_proxyModel)
    #set largeurs colonnes
    self.tableView.setColumnWidth(0, 90)
    self.tableView.setColumnWidth(1, 350)
    self.tableView.setColumnWidth(2, 90)
    self.tableView.setColumnWidth(3, 50)
    self.tableView.setColumnWidth(4, 150)

This is how I start the worker thread:

    def HandleBot(self):
        self.lock = threading.Lock()
        self.bot_instance = Bot(self.lock)
        self.thread_handle = QThread(parent = self)
        self.bot_instance.moveToThread(self.thread_handle)
        self.bot_instance.log_text_signal.connect(self.add_text_to_plaintext)
        self.thread_handle.started.connect(self.bot_instance.call_check)
        self.thread_handle.start()
        return

Slot definition :

@QtCore.pyqtSlot(int, int, int, int)
def add_text_to_plaintext(self, int, int2, int3, int4):
    print("start of slot")
    debg = "received : " 
    print(debg)
    item_list = [QtGui.QStandardItem(str(int)), QtGui.QStandardItem(str(int2)), 
                 QtGui.QStandardItem(str(int3)), QtGui.QStandardItem(str(int4)),
                 QtGui.QStandardItem(str(datetime.datetime.now().replace(microsecond=0)))]
    self.model_table_view.appendRow(item_list)
    print("end of slot")

If i replace the item_list by this :

item_list = QtGui.QStandardItem()

Then application doesn't crash even though signals and slot still are emitted and the tableview is being updated by empty rows. This is the bot definition :

class Bot(QObject):
    log_text_signal = pyqtSignal(int, int, int, int)
    def __init__(self, lock : threading.Lock):
        super().__init__()
        self.lock = lock
        self.keep_bot_running = True
        self.numberExec = 0
        return
def call_check(self):
    pydevd.settrace(suspend=False)
    While True:
        .....do stuff....
        self.numberExec += 1
        print('requete ocp n°' + str(self.numberExec))
        debg = "This function's name is:" + self.current_function_name() + " " + str(info)
        print(debg)
        with self.lock:
        self.log_text_signal.emit(1, 0, 0, 0)

For days I thought I was handling signals and slots across thread the wrong way but I couldn't find the culprit, would appreciate if someone can give me clues on what really is causing the silent crahes.


Solution

  • I understand I did not provide a minimal reproducible example but this is because it is very hard to do with this matter as it appears to be a bug in pyqt that shows... sometimes. It turns out things between multithreading in pyqt and python's garbage collector can get messy. I have solved things out by having the GC be ran from the main thread only and on timer only. See this discussion for more information on how to solve this problem should you ever encounter it. Very messy bug to solve, took over a week. https://riverbankcomputing.com/pipermail/pyqt/2011-August/030378.html