threading queue blocks main pyqt5 GUI window

Solution for threading queue blocks main pyqt5 GUI window
is Given Below:

Although I read a ton of posts on the web, but couldn’t find a solution for my self.

Basically, I am using pyqt5 for making a GUI application and am using threading for creating threads and lastly using queue I tried to send signals between main and child thread.

I have a qt5 progress bar in which it is value needs to updated by the child thread progress. When I try to use progressbar.setValue(x) I get below error:

QObject::setParent: Cannot set parent, new parent is in a different thread

The reason for above is that I cannot update the progress bar value from child thread, it needs to happen from main thread.

So I tried to use queue and send a message to the main thread to do it.

I tried below:

def buttonclicked():
    global progressQueue
    progressQueue = Queue()
    
    thread = threading.Thread(target=childfunc, daemon=True, name="nemo", args=())
    thread.start()

    progressQueue.join()
    while True:
        if not progressQueue.empty():
            msg = progressQueue.get()
            pellow.setValue(msg)
    return 0

def childfunc():
    for i in range(1,100):
        progressQueue.put(i)

Here the problem is that, the while loop freezes the mainwindow/gui app from interaction. Can anyone suggest, what do I need to do here?

A complete working example:

import queue
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from threading import Thread

app = QApplication(sys.argv)
class mainwindow1(QMainWindow):
    def __init__(self):
        super().__init__()
        mainwindow = QWidget()
        mainwindow.setObjectName("mainwindow")
        mainwindow.setContentsMargins(100, 100, 100, 100)
        self.setCentralWidget(mainwindow)
        global pellow
        global scanProgressbar
        scanProgressbar = QProgressBar()

        pellow = QPushButton('Click Here')
        pellow.setObjectName("defaultbutton")
        cpscanlayout = QVBoxLayout(mainwindow)
        cpscanlayout.addWidget(scanProgressbar, alignment=Qt.AlignCenter, stretch=10)
        cpscanlayout.addWidget(pellow, alignment=Qt.AlignCenter, stretch=10)
        cpscanlayout.setAlignment(Qt.AlignCenter)
    # cpscanlayout.setContentsMargins(0, 0, 0, 0);
        cpscanlayout.setSpacing(5);
        pellow.setCursor(QCursor(Qt.PointingHandCursor))
        pellow.clicked.connect(buttonclicked)

        self.show()
        app.exec_()

def buttonclicked():
    global progressQueue
    progressQueue = Queue()

    thread = threading.Thread(target=childfunc, daemon=True, name="nemo", args=())
    thread.start()

    progressQueue.join()
    while True:
        if not progressQueue.empty():
            msg = progressQueue.get()
            scanProgressbar.setValue(msg)
    return 0

The code has several errors such as imports (import queue but it doesn’t look like it uses Queue, it imports Thread but it uses threading.Thread, etc).

On the other hand, the misconception is that in Qt you should not use code that blocks the eventloop, such as while True. If you want to exchange information between threads then in the case of Qt you should not use Queue since it does not notify when there is a new data, instead you must use the signals that are also thread-safe and do notify when there is new data.

import sys
import threading
import time


from PyQt5.QtCore import pyqtSignal, QObject, Qt
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QProgressBar,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class Signaller(QObject):
    progress_changed = pyqtSignal(int)


class mainwindow1(QMainWindow):
    def __init__(self):
        super().__init__()
        mainwindow = QWidget()
        mainwindow.setObjectName("mainwindow")
        mainwindow.setContentsMargins(100, 100, 100, 100)
        self.setCentralWidget(mainwindow)
        scanProgressbar = QProgressBar()

        pellow = QPushButton("Click Here")
        pellow.setObjectName("defaultbutton")
        cpscanlayout = QVBoxLayout(mainwindow)
        cpscanlayout.addWidget(scanProgressbar, alignment=Qt.AlignCenter, stretch=10)
        cpscanlayout.addWidget(pellow, alignment=Qt.AlignCenter, stretch=10)
        cpscanlayout.setAlignment(Qt.AlignCenter)
        # cpscanlayout.setContentsMargins(0, 0, 0, 0);
        cpscanlayout.setSpacing(5)
        pellow.setCursor(QCursor(Qt.PointingHandCursor))
        pellow.clicked.connect(self.buttonclicked)

        self.signaller = Signaller()
        self.signaller.progress_changed.connect(scanProgressbar.setValue)

    def buttonclicked(self):
        thread = threading.Thread(
            target=childfunc, args=(self.signaller,), daemon=True, name="nemo"
        )
        thread.start()


def childfunc(signaller):
    for i in range(1, 100):
        signaller.progress_changed.emit(i)
        time.sleep(0.1)


app = QApplication(sys.argv)
w = mainwindow1()
w.show()
app.exec_()