0

I'm trying to write a QT Gui with Python, using a main window and background threads that do the heavy lifting. The main window should have buttons, that launch one background thread, open a separate window that shows the background threads progress in a progress bar and closes the window when done.

I tried to strip away all unnecessary code to yield the following minimal, self-contained example:

import sys
import time

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5 import QtWidgets, uic

from PyQt5.QtWidgets import QMainWindow, QWidget

from pathlib import Path


BAR_UIC = Path(r'bar.ui')
BUTTON_UIC = Path(r'button.ui')


class AThread(QThread):

    increasing = pyqtSignal(int)
    done = pyqtSignal()

    def run(self):
        count = 0
        while count < 100:
            time.sleep(1)
            print("A Increasing")
            count += 10
            self.increasing.emit(count)
        self.done.emit()

class BarWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        uic.loadUi(str(BAR_UIC), self)

    def update(self, signal):
        self.progressBar.setValue(signal)

class ButtonWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        uic.loadUi(str(BUTTON_UIC), self)
        self.pushButton.clicked.connect(self.launch_thread)

    def launch_thread(self):
        barwin = BarWindow()
        self.thread = AThread()
        self.thread.done.connect(barwin.close)
        self.thread.increasing.connect(barwin.update)
        barwin.show()
        self.thread.start()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = ButtonWindow()
    mainWin.show()
    app.exec_()

The two uic files that are loaded are a simple QMainWindow with a single pushButton (button.uic):

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>572</width>
    <height>337</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>220</x>
      <y>130</y>
      <width>104</width>
      <height>32</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>572</width>
     <height>29</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

and a QWidget with a ProgressBar (bar.ui):

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QProgressBar" name="progressBar">
   <property name="geometry">
    <rect>
     <x>140</x>
     <y>120</y>
     <width>118</width>
     <height>23</height>
    </rect>
   </property>
   <property name="value">
    <number>24</number>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

When I run this application (Win 10, python 3.7.4), the main window shows. When I press the button, the second window (with the progress bar) pops up for a second and goes away immediately, wheras I want it to stay until the thread has finished. The console output, shows the print output of the thread. My second question is, how this console output is possible, as I thought that the QThread is not running in the main shell?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Dschoni
  • 3,714
  • 6
  • 45
  • 80
  • 2
    The second window disappears immediately because you initiate is as a parentless local variable of `ButtonWindow.launch_thread`. This means that the widget is garbage-collected as soon as the function finishes. There are several ways to make the widget persistent. You could for example assign it to a instance variable (i.e. use `self.barwin = BarWindow()` instead of `barwin = BarWindow()`). Another option is to set the parent of `barwin`, to self, although in that case I would use `QDialog` instead of `QWidget` as the base class of `BarWindow`. – Heike Sep 23 '19 at 12:44
  • @eyllanesc: Thanks for the link to the dupe. Once I know, that that's the problem, it makes total sense :) But to figure that in my case the GC of the local scope variables lead to this problem was non-trivial (at least for me). – Dschoni Sep 24 '19 at 09:08
  • @Heike could you expand on why to use QDialog rather than QWidget and also put your comment in an answer, so I can accept it? – Dschoni Sep 24 '19 at 09:10

0 Answers0