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.