2

I am trying to offload a heavy background job to a multiprocessing process. I just want the separate process to be able to report it's progress to my GUI. Here's my last try, the GUI is simple, a couple of buttons and a progress bar:

from PySide.QtGui import *
from PySide.QtCore import *
import sys
from multiprocessing import Process, Pipe
import time

class WorkerClass:
#This class has the job to run
    def worker(self, pipe):
        for i in range(101):
            pipe.send(i)
            time.sleep(.02)

class WorkStarter(QThread):
#this thread takes a widget and updates it using progress sent from
#process via Pipe
    def __init__(self, progressBar):
        super().__init__()
        self.progress_bar = progressBar

    def run(self):
        worker_obj = WorkerClass()
        myend, worker_end = Pipe(False)
        self.p = Process(target=worker_obj.worker, args=(worker_end,))
        self.p.start()
        while True:
            val = myend.recv()
            self.progress_bar.setValue(val)
            if val == 100:
                break

class WorkingWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle('Blue collar widget')
        layout = QHBoxLayout()
        start_btn = QPushButton('Start working')
        start_btn.clicked.connect(self.startWorking)
        end_btn = QPushButton('End working')
        end_btn.clicked.connect(self.endWorking)
        layout.addWidget(start_btn)
        layout.addWidget(end_btn)
        self.progress_bar = QProgressBar()
        layout.addWidget(self.progress_bar)
        self.setLayout(layout)

    def startWorking(self):
        self.thread = WorkStarter(self.progress_bar)
        self.thread.start()

    def endWorking(self):
        self.thread.terminate()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = WorkingWidget()
    main.show()
    sys.exit(app.exec_())

I cannot pass any QObject as an argument to the process, since that is not pickleable:

#cannot do the following
...
def startWorking(self):
    self.worker_obj = WorkerClass()
    #pass the progress bar to the process and the process updates the bar
    self.p = Process(target=self.worker_obj.worker, args=(self.progress_bar,))

The problem is that this gui some times works, other times it freezes (So please press 'start' multiple times until it freezes :) ), and here on Windows it says : pythonw.exe has stopped working... Any clue what's the reason for that?. I cannot figure it out by myself. Thanks

MadeOfAir
  • 2,933
  • 5
  • 31
  • 39
  • I think I have a working example, see my post here: http://stackoverflow.com/questions/15698251/multiprocessing-gui-schemas-to-combat-the-not-responding-blocking/19296108#19296108 – Caleb Hattingh Oct 10 '13 at 13:11
  • I thank you for you attention, but that doesn't answer the question, Why does this work some times and crash some other times? – MadeOfAir Oct 11 '13 at 19:44

1 Answers1

2

You are not supposed to create the object inside "run" method of QThread, emit signal from "run", implement a function say "callerFunction" create object in this function and finally call this function on signal which is emitted by the "run" function.

  • You can emit the signal in the while loop that you have already created.
  • Have a look at this solution
  • don't create a python process, QThread is sufficient for this job
Community
  • 1
  • 1
qurban
  • 3,885
  • 24
  • 36
  • Sorry, you didn't clarify why the gui does freeze. But i'll check the link you provided when i have the chance. – MadeOfAir Sep 29 '13 at 18:24
  • Sorry, the example you pointed at is just stupid. I don't have PyQt installed, but I don't think by moving the line that runs the thread from inside the class to outside will do any magic. – MadeOfAir Oct 01 '13 at 21:50
  • 1
    Sorry for late but I always do this what I told you and it always worked fine for me. – qurban Oct 19 '13 at 17:41