Updating the main screen from within worker thread

I’m still having some issues with dynamically updating my UI (widgets) when updating my database (MySQL). I thought I had this solved by using show, update and events on various widgets at different points in my code but then I stumbled across app.processEvents().

This function appeared to solve all my previous problems and allowed me update widgets from anywhere in my code. Then I read that using this is a bad practice due to possible unplanned events firing when I don’t need them to and also using it slows down the code…

OK … so I did some additional digging and came across Martin’s short Threading Walk Through showing a better way of handling data updates in a different thread. I’ve modified my code utilize this and have run into a new set of problems.

The bottom portion of my screen layouts I have a section reserved for buttons, prompts and messages as my application runs. This area continually updates via a messagebox() (my code). I need the thread to use this function when it completes.

What’s happening currently is the thread does complete as expected but the call to messagebox() is outputting to it’s own thread. I need it output to the main thread.

Any ideas ? I can show the worker definition and messagebox() but my app currently is huge.

thanks in advance,

After some additional googling and some further testing I don’t see any real need to involve a separate thread with my application. From a speed aspect I don’t see anything worth the trouble of trying to implement this within my existing code so I’m abandoning trying to implement a separate thread. The few times I use app.processEvents() I don’t see any great risks in doing this.

Use signals and slots to trigger specific update events in the main thread from the sub-thread.

See: cheatsheet/qt_update_gui_from_python_thread.md at master · RubendeBruin/cheatsheet

I think the second example is what you need.

Thank you very much for this input. Very much appreciated.

The one thing I’ve come accustomed to with Application Development is there’s always more than one way to skin a cat (not literally … but I am more a dog person) and just when you think you have the answer to resolve your latest issue someone points you in another direction and this now becomes the better way of handling it !

Thanks again

Building on your second example I think I’ve got it ! All I need is to get my head around using QMutex and I can add this to my existing app.

import sys
from time import sleep


from PyQt6.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import QWidget, QGridLayout, QLabel, QPlainTextEdit, QPushButton, QApplication


class Worker(QObject):

    finished = pyqtSignal(str)
    progress = pyqtSignal(int)

    terminate = False

    def run(self):

        """Long-running task."""

        i = 1

        while not self.terminate and i < 101:

            sleep(1)
            self.progress.emit(i)

            if i == 10:
                break

            i += 1


        # Finish UP

        if self.terminate:
            self.finished.emit("Worker Terminated")
        else:
            self.finished.emit("Worker Completed")



class Window(QWidget):
    def __init__(self):
        super().__init__()

        self.thread = QThread()

        # Screen

        layout = QGridLayout()

        self.label = QLabel()
        self.label.setText("test")

        self.log_screen = QPlainTextEdit()

        self.button = QPushButton("Terminate")
        self.button.pressed.connect(self.terminate_work)

        layout.addWidget(self.label)
        layout.addWidget(self.log_screen)
        layout.addWidget(self.button)

        self.setLayout(layout)
        self.show()

        self.worker = Worker()

        self.worker.moveToThread(self.thread)

        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.show_finished_state)
        self.worker.progress.connect(self.show_progress)

        self.thread.start()

    @pyqtSlot()
    def update_gui(self, txt):
        self.label.setText(txt)

    def show_progress(self, progress_value):
        self.label.setText("Progress: {}".format(progress_value))

    def show_finished_state(self, text):

        if text == "Worker Terminated":
            finishing_text = "Worker Terminated ...\n"
        else:
            finishing_text = "Worker Completed ...\n"

        self.log_screen.appendPlainText(finishing_text)

        self.thread.quit()
        self.log_screen.appendPlainText("Thread Stopped")

        self.worker.deleteLater()
        self.log_screen.appendPlainText("Worker Deleted")

        self.thread.deleteLater()
        self.log_screen.appendPlainText("Thread Deleted\n")

        self.log_screen.appendPlainText("Finished")

    def terminate_work(self):
        self.worker.terminate = True

app = QApplication(sys.argv)
window = Window()
app.exec()