Python QWidget Controls not Showing on Form

Is there any code please which I can look at where a Main GUI window, opens a new window with a progress bar in some sort of timer. Eg 20 seconds the progress bar goes from 0 to 100%. These need to be in a thread as the second progress bar window will close once 100% is reached.

Ive been using QThread, Pyside and QWidget

Ive tried alsorts but my Main Gui goes white waiting for a long running task in the main gui window.

I have used a seperate thread but I just can’t get it to work

If there is anyone kind enough to cobble together these 2 short classes id be grateful and I’ll work n trying to emulate it.

Not sure how you are using threads.

But bear in mind that all Qt calls must be made on the main thread.
You cannot run GUI code on background threads.

There are ways to send events from background threads to the main threads Qt event loop.

So I have attached what I am doing I am hoping that you are able to guide me please?

Main GUI (Not in a thread just in the normal PySide event Loop):

                    #Create a progress Bar on a seperate window with a new Timer Thread
                    self.guiProgress = TimerThread.GuiProgressBar()
                    self.guiProgress.StartProgressBar()
                    # This query downloads data from BigQuery and stops the GUI Progress Bar from updating
                    self.bigQueryControl.copyTable(src, dest)

These are the thread classes which don’t appear to be constructed properly:

import sys
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PySide6.QtGui import *


class WorkerThread(QThread):
    changeValue = Signal(int)
    finished = Signal()

    def __init__(self):
        super().__init__()
        self.runs = False
        self.count = -1
        self.REMAIN_OPEN_IN_SECONDS = 15
        return

    def run(self):
        count = 0
        while self.runs and (0 <= count < self.REMAIN_OPEN_IN_SECONDS):
            QCoreApplication.processEvents()
            time.sleep(1)
            count = count + 1
            self.changeValue.emit(count)
        self.finished.emit()

    def startThread(self):
        self.runs = True
        self.count = 0
        self.start()

    def stopThread(self):
        self.runs = False
        self.count = -1
        self.exit()


class GuiProgressBar(QWidget):
    finished = Signal()

    def __init__(self):
        super().__init__()
        self.REMAIN_OPEN_IN_SECONDS = 15
        self.btnStop = None
        self.btnStart = None
        self.label1 = None
        self.w1 = None
        self.barThread = WorkerThread()
        self.barThread.finished.connect(self.threadStopped)
        self.barThread.changeValue.connect(self.UpdateProgressBar)
        self.hprogress1 = None
        self.gui()
        self.initialise()
        self.show()

    def initialise(self):
        self.hprogress1.setAlignment(Qt.AlignCenter)
        self.hprogress1.setMinimum(0)
        self.hprogress1.setMaximum(self.REMAIN_OPEN_IN_SECONDS)

    def gui(self):
        self.w1 = self
        self.w1.setAutoFillBackground(True)
        self.w1.setWindowTitle("")
        self.w1.resize(320, 170)
        self.w1.setCursor(Qt.ArrowCursor)
        self.w1.setToolTip("")
        self.label1 = QLabel(self.w1)
        self.label1.setText("Please wait while the BigQuery queries are run")
        self.label1.move(20, 20)
        self.label1.resize(290, 22)
        self.label1.setCursor(Qt.ArrowCursor)
        self.label1.setToolTip("")
        self.hprogress1 = QProgressBar(self.w1)
        self.hprogress1.setMinimum(0)
        self.hprogress1.setMaximum(100)
        self.hprogress1.setValue(0)
        self.hprogress1.setOrientation(Qt.Horizontal)
        self.hprogress1.move(20, 50)
        self.hprogress1.resize(270, 22)
        self.hprogress1.setCursor(Qt.ArrowCursor)
        self.hprogress1.setToolTip("")
        self.btnStart = QToolButton(self.w1)
        self.btnStart.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.btnStart.setText("Start")
        self.btnStart.move(20, 90)
        self.btnStart.resize(90, 22)
        self.btnStart.setCursor(Qt.ArrowCursor)
        self.btnStart.setToolTip("")
        self.btnStart.clicked.connect(self.StartProgressBar)
        self.btnStop = QToolButton(self.w1)
        self.btnStop.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.btnStop.setText("Stop")
        self.btnStop.move(110, 90)
        self.btnStop.resize(90, 22)
        self.btnStop.setCursor(Qt.ArrowCursor)
        self.btnStop.setToolTip("")
        self.btnStop.clicked.connect(self.StopProgressBar)
        return self.w1

    def StartProgressBar(self):
        if not self.barThread.isRunning():
            self.barThread.startThread()
        else:
            return
            #print("Thread is already running")

    def StopProgressBar(self):
        if not self.barThread.isRunning():
            return
            #print("No thread is running")
        else:
            self.barThread.stopThread()
            print(self.barThread.isRunning())

    def threadStopped(self):
        self.finished.emit()

    def UpdateProgressBar(self, value):
        self.hprogress1.setValue(value)


I’ve attached an image of the ProgressBar window which just sits there. Eventually when the BigQuery query returns, the progress bar updates and is at 100%.

EDIT: I’ve just tried removing line:
self.bigQueryControl.copyTable(src, dest)
and the GUI still goes white and hangs?

Attempted to fix these for nearly 3 days now and I just don’t understand what I need to do.

Hi Barry, please see my response above. Thanks for your help.

Unless you found docs saying you can run multiple event loops i think its not allowed and you are lucky not to crash the process.
My knowledge of this could be out of date.

You can see how I solved the problem of progress bars and background threads in code, but its a large code base: GitHub - barry-scott/scm-workbench: SCM Workbench is a GUI to easily work with Git Mercurial (hg) and Subversion (svn) repositories in the style of PySVN WorkBench

Have a look at scm-workbench/Source/Common/wb_background_thread.py at master · barry-scott/scm-workbench · GitHub
that does the communication between threads.

I didn’t think I was running multiple event loops? Or at least I don’t think I am but you suggest I am so I’m confused.

Your codebase is enormous and I don’t think I’m skilled enough to be able to decipher where it would be in your code unfortunately.

I really didn’t think it would be that hard to be honest to display a new window and progress bar…

Not to worry

Thanks for your help.

This looks like the 2nd event loop.

1 Like

In a typical PyQt/PySide program there is a bit of code in main that sets up the app and started it running the event loop.

This is an example:

#!/usr/bin/env python3
import sys

from PyQt6 import QtCore, QtWidgets
from PyQt6.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget
from PyQt6.QtCore import QSize

class HelloWindow(QMainWindow):
    def __init__( self ):
        super().__init__()

        if sys.maxsize > (2**31):
            size_int_t = 64
        else:
            size_int_t = 32

        py_ver = '%d.%d-%d' % (sys.version_info.major, sys.version_info.minor, size_int_t)

        self.setMinimumSize( QSize(350, 100) )
        self.setWindowTitle( 'GUI test - python %s' % (py_ver,) )

        self.setCentralWidget( QLabel( ' GUI test - python %s ' % (py_ver,), self ) )

def main( argv ):
    app = QtWidgets.QApplication( argv )
    main_win = HelloWindow()
    main_win.show()
    return app.exec()

if __name__ == "__main__":
    sys.exit( main( sys.argv ) )

Its the app.exec() that is running the event loop.

I have assumed that you have that code in your app, but you did not share that code. That is your first event loop.

Now this is your 2nd event loop where you call QCoreApplication.processEvents(). But that must only be used from the main thread, not a worker thread.
It also will interfer with the main thread’s event loop in unexpected ways.

You could pop up the progress bar from the main GUI logic and on a QTimer update it by looking at the state of your worker thread.

1 Like

I have added a thread to a small example to update the progress bar from a background thread. It use the ability to send events from a background thread into the main threads event loop.

This is PyQt6 code, you will have to look at how PySide does the same thing.

#!/usr/bin/env python
import sys
import threading
import time

from PyQt6 import QtCore, QtWidgets
from PyQt6.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget, QProgressBar
from PyQt6.QtCore import QSize


def backgroundActivitity(window):
    for value in range( 1, 11 ):
        time.sleep( 1 )
        window.foreground_progress_signal.emit( value )

class HelloWindow(QMainWindow):
    foreground_progress_signal = QtCore.pyqtSignal( [int] )

    def __init__( self ):
        super().__init__()

        if sys.maxsize > (2**31):
            size_int_t = 64
        else:
            size_int_t = 32

        py_ver = '%d.%d-%d' % (sys.version_info.major, sys.version_info.minor, size_int_t)

        self.label = QLabel( ' GUI test - python %s ' % (py_ver,), self )
        self.progress = QProgressBar( self )
        self.progress.setRange( 0, 10 )

        self.widget = QWidget()
        self.grid = QGridLayout( self.widget )
        self.grid.addWidget( self.label, 0, 0 )
        self.grid.addWidget( self.progress, 1, 0 )

        self.progress.setValue( 0 )

        self.setMinimumSize( QSize(350, 100) )
        self.setWindowTitle( 'GUI test - python %s' % (py_ver,) )

        self.setCentralWidget( self.widget )

        self.foreground_progress_signal.connect( self.updateProgress, type=QtCore.Qt.ConnectionType.QueuedConnection )

        self.thread = threading.Thread( target=backgroundActivitity, args=(self,) )
        self.thread.start()

    def updateProgress( self, value ):
        self.progress.setValue( value )

def main( argv ):
    app = QtWidgets.QApplication( argv )
    main_win = HelloWindow()
    main_win.show()
    return app.exec()

if __name__ == "__main__":
    sys.exit( main( sys.argv ) )

Thank you very much for your help.

I’ve now achieved what I needed to do.

I moved the Progress Bar Class into the main GUI python file so that all the GUI components and code relating to the Gui were together. I turned the BigQuery code I had into a thread of it’s own instead if using a timer thread to update a gui control.

The progress bar is now a pulsed process bar instead of using an arbitrary timer.

I’m now happy I understand how it needs to work, at least for next time.

Many thanks for your help

1 Like