PyQT4 and Threading: Closing MainWidget after QThread finished and ListWidget updated
Question: I'm trying to understand how threading works in Python. Therefore I wrote an
application that is installing some packages in a separate QThread.

Once the worker is finished (finished Signal emitted), I'd like to wait a few
seconds and then Close the MainWidget (see below):

    
    
    def finished(self):
            self.installbutton.setEnabled(True)
            self.listwidget.addItem(QtCore.QString("Process complete, have a nice day!"))
            time.sleep(5)
            self.close()
    

The Problem seems now, that calling self.close within the finished-callback
leads to a Situation where the ListWidget is no more updated anymore before
the window closes.

My guess is, that the ListWidget and the callback function live in the same
stack and therefore the ListWidget has no Chance to terminate any more. But
how to solve these kind of issues?

Any suggestions how I can overcome this?

By the way: I'm "forced" to use Python 2.7.5 and PyQt4 in order to be
compliant with the rest of my Company... that's why I'm using the old-style
signals

Here the code:

    
    
    import sys
    import os
    import time
    
    import subprocess
    import logging
    
    log = logging.getLogger(__name__)
    logging.basicConfig(level=logging.DEBUG)
    
    from PyQt4 import QtGui, QtCore
    
    
    def main():
    
        app = QtGui.QApplication(sys.argv)
        w = InstallWizzard()
        w.show()
        sys.exit(app.exec_())
    
    
    
    class InstallWizzard(QtGui.QMainWindow):
        def __init__(self, parent=None):
            QtGui.QMainWindow.__init__(self,parent)
    
            self.centralwidget = QtGui.QWidget(self)
            self.setCentralWidget(self.centralwidget)
    
            self.thread = Worker()
    
            hlayout = QtGui.QVBoxLayout()
            self.listwidget = QtGui.QListWidget()
            self.installbutton = QtGui.QPushButton()
            self.installbutton.setText("Install Brother now...")
    
            hlayout.addWidget(self.installbutton)
    
    
            self.listwidget.addItem(QtCore.QString(r'Click "Install Brother now... to start installation"'))
            self.centralwidget.setLayout(hlayout)
    
            self.connect(self.installbutton, QtCore.SIGNAL("clicked()"),self._on_installation_start)
            self.connect(self.thread, QtCore.SIGNAL("finished()"), self.finished)
            # self.connect(self.thread, QtCore.SIGNAL("terminated()"), self.updateUI)
            self.connect(self.thread, QtCore.SIGNAL("install_mssg(QString)"), self._cmd_processed)
    
            hlayout.addWidget(self.listwidget)
    
    
        def _cmd_processed(self, mssg):
            self.listwidget.addItem(QtCore.QString(mssg))
    
    
        def _on_installation_start(self):
    
            self.installbutton.setEnabled(False)
    
            cmds = [("Installing comtypes", r'easy_install comtypes')]
            self.thread.install(cmds)
    
    
        def finished(self):
            self.installbutton.setEnabled(True)
            self.listwidget.addItem(QtCore.QString("Process complete, have a nice day!"))
            time.sleep(5)
            self.close()
    
    
    class Worker(QtCore.QThread):
        def __init__(self, parent = None):
    
            QtCore.QThread.__init__(self,parent)
            print("Started Worker...")
    
    
        def install(self, cmds):
            self.cmds = cmds
            self.start()
    
    
        def run(self):
    
            for desc, cmd in self.cmds:
                self.emit(QtCore.SIGNAL("install_mssg(QString)"),QtCore.QString(desc + ": ..."))
                try:
                    self._write_cmd_line(cmd)
                    mssg = "... Successful"
                except Exception as e:
                    mssg = QtCore.QString(str("... Faillure: " + e))
    
                self.emit(QtCore.SIGNAL("install_mssg(QString)"),mssg)
    
            print("ond tschuss from worker")
    
    
        def __del__(self):
    
            self.quit()
            self.wait()
    
    
        def _write_cmd_line(self, cmd):
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
            a,b = p.communicate()
            if "cannot" in a or "error" in a or "not recognized" in a:
                    errormssg = "Could not proccess cmd!"
                    logging.error(errormssg)
                    raise Exception(errormssg)
    
            else:
                logging.debug(str(cmd) + " successful")
    
    
    if __name__ == '__main__':
        main()
    


Answer: after reading your question and your code.. in understand that you have two
thread the main thread and worker thread and when you're worker is done(I mean
here that thread is reached the last line of your code) you want to notify the
main thread to close the worker and switch to another window(Probably) or do
something else.

TODO this obviously you need to communicate with the UI thread(MainThread), so
you'll need to add this lines in your **Worker(Thread) Class**

    
    
    signal = QtCore.pyqtSignal(str) # Create a signal 
    

then go to the last line from your thread, and be sure that when your worker
reach that line the job will be done, and add this line to notify the main
thread

    
    
    self.signal.emit('Some String here') # this will send the string over the signal to your UI thread 
    

Now **Jump To Your InstallWizzard Class ** and add this lines inside your
__init__ method add this line below **self.thread = Worker()**

    
    
    self.thread.signal.connect(self.finished) # finished is a method inside your class
    

Now inside Your finished method you can terminate/close/quit you're method
easily using something like that

    
    
    self.thread.quit() # as you might guessed this will quit your thread.
    

also you can replace quit() with terminate() if you want to force the worker
to be closed please but please read this warning From the Qt documentation for
QThread::terminate:

> **Warning:** This function is dangerous and its use is discouraged. The
> thread can be terminated at any point in its code path. Threads can be
> terminated while modifying data. There is no chance for the thread to clean
> up after itself, unlock any held mutexes, etc. In short, use this function
> only if absolutely necessary.

and if you care about the string which it will be sent over signal from the
Worker add new parameter to you finished method to get access to the string.
and this is it :)

I know that I am too late to answer your question(1 years ago ) but it might
be helpful for other developers...anyway cheers

