0

How to create a loading warning for long processing thread on python?

I edit some code refer to the above website, in the following part there is new code:

import sys
import time
import _thread

from PyQt5.QtCore import QObject, pyqtSignal, Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QInputDialog, QApplication, QProgressDialog

class Message(QObject):
    finished = pyqtSignal()

def createAddress(password, name, obj):
    print('Count in child thread. 0 sec')
    time.sleep(1)
    print('Count in child thread. 1 sec')
    time.sleep(1)
    print('Count in child thread. 2 sec')
    time.sleep(1)
    print('Count in child thread. 3 sec')
    time.sleep(1)
    print('Count in child thread. 4 sec')
    time.sleep(1)
    obj.finished.emit()


class Widget(QWidget):
    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        lay = QVBoxLayout(self)
        button = QPushButton("Start processing")
        lay.addWidget(button)
        button.clicked.connect(self.start_task)
        self.message_obj = Message()

    def start_task(self):
        password = "password"
        name, ok = QInputDialog.getText(None, 'Name the address', 'Enter the address name:')
        if ok:
            self.progress_indicator = QProgressDialog(self)
            self.progress_indicator.setWindowModality(Qt.WindowModal)
            self.progress_indicator.setRange(0, 0)
            self.progress_indicator.setAttribute(Qt.WA_DeleteOnClose)
            self.message_obj.finished.connect(self.progress_indicator.close, Qt.QueuedConnection)
            self.progress_indicator.show()
            _thread.start_new_thread(createAddress, (password, name, self.message_obj, ))
        print('Count in main thread. 0 sec')
        time.sleep(1)
        print('Count in main thread. 1 sec')
        time.sleep(1)
        print('Count in main thread. 2 sec')
        time.sleep(1)



if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle("fusion")
    w = Widget()
    w.show()
    sys.exit(app.exec_())

When the button clicked, CMD print the word in mainThread(start_task) and the word in subThread(createAddress) correctly. However, the progress bar is not shown until the mainThread complete to count.

How to make the progress bar show correctly while mainThread executing, Thanks.

Cassiotia
  • 1
  • 1
  • Maybe you should know that there was a change to threading between Python2 and Python3: https://stackoverflow.com/q/6319268/2932052 – Wolf May 13 '20 at 07:48

2 Answers2

1

You shouldn't run a time consuming task (like time.sleep()) in the main thread where the GUI is running since it blocks the event loop, so an alternative is to run it in another thread just as part of your code it is doing it but elsewhere it is still running time.sleep() in the main thread causing the problem you are seeing, so first you should be clear:

  1. If you have periodic tasks that consume little time then you should use a QTimer.

  2. If periodic tasks are very time consuming then you should run that task in another thread.

In your case, the periodic task is to print, so you must use option 1

import sys
import time
import _thread

from PyQt5.QtCore import QObject, pyqtSignal, Qt, pyqtSlot, QTimer
from PyQt5.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QPushButton,
    QInputDialog,
    QApplication,
    QProgressDialog,
)


class Message(QObject):
    finished = pyqtSignal()


def createAddress(password, name, obj):
    print("Count in child thread. 0 sec")
    time.sleep(1)
    print("Count in child thread. 1 sec")
    time.sleep(1)
    print("Count in child thread. 2 sec")
    time.sleep(1)
    print("Count in child thread. 3 sec")
    time.sleep(1)
    print("Count in child thread. 4 sec")
    time.sleep(1)
    obj.finished.emit()


class Widget(QWidget):
    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        lay = QVBoxLayout(self)
        button = QPushButton("Start processing")
        lay.addWidget(button)
        button.clicked.connect(self.start_task)
        self.message_obj = Message()

        self.counter = 0
        self.timer = QTimer(interval=1000, timeout=self.on_timeout)

    def start_task(self):
        password = "password"
        name, ok = QInputDialog.getText(
            None, "Name the address", "Enter the address name:"
        )
        if ok:
            self.progress_indicator = QProgressDialog(self)
            self.progress_indicator.setWindowModality(Qt.WindowModal)
            self.progress_indicator.setRange(0, 0)
            self.progress_indicator.setAttribute(Qt.WA_DeleteOnClose)
            self.message_obj.finished.connect(
                self.progress_indicator.close, Qt.QueuedConnection
            )
            self.progress_indicator.show()
            _thread.start_new_thread(createAddress, (password, name, self.message_obj,))
            self.counter = 0
            self.timer.start()

    @pyqtSlot()
    def on_timeout(self):
        print("Count in main thread. {} sec".format(self.counter))
        self.counter += 1
        if self.counter > 3:
            self.timer.stop()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("fusion")
    w = Widget()
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
0

Your code works fine for me when adapting the threading library and increase the interval for the subprocess, as the process bar takes time to appear.

import sys
import time

import threading
from PyQt5.QtCore import QObject, pyqtSignal, Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QInputDialog, QApplication, QProgressDialog

class Message(QObject):
    finished = pyqtSignal()

def createAddress(password, name, obj):
    print('Count in child thread. 0 sec')
    time.sleep(2)
    print('Count in child thread. 2 sec')
    time.sleep(2)
    print('Count in child thread. 4 sec')
    time.sleep(2)
    print('Count in child thread. 6 sec')
    time.sleep(2)
    print('Count in child thread. 8 sec')
    time.sleep(2)
    obj.finished.emit()


class Widget(QWidget):
    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        lay = QVBoxLayout(self)
        button = QPushButton("Start processing")
        lay.addWidget(button)
        button.clicked.connect(self.start_task)
        self.message_obj = Message()

    def start_task(self):
        password = "password"
        name, ok = QInputDialog.getText(None, 'Name the address', 'Enter the address name:')
        if ok:

            self.progress_indicator = QProgressDialog(self)
            self.progress_indicator.setWindowModality(Qt.WindowModal)
            self.progress_indicator.setRange(0, 0)
            self.progress_indicator.setAttribute(Qt.WA_DeleteOnClose)
            self.message_obj.finished.connect(self.progress_indicator.close, Qt.QueuedConnection)
            self.progress_indicator.show()
            t1 = threading.Thread(target=createAddress, args=(password, name, self.message_obj))
            t1.start()

        print('Count in main thread. 0 sec')
        time.sleep(1)
        print('Count in main thread. 1 sec')
        time.sleep(1)
        print('Count in main thread. 2 sec')
        time.sleep(1)
        #t1.join()



if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle("fusion")
    w = Widget()
    w.show()
    sys.exit(app.exec_())
Wolf
  • 9,679
  • 7
  • 62
  • 108
FloLie
  • 1,820
  • 1
  • 7
  • 19