简体   繁体   中英

PyQT4 and Threading: Closing MainWidget after QThread finished and ListWidget updated

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()

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM