[英]PyQT4 and Threading: Closing MainWidget after QThread finished and ListWidget updated
我試圖了解線程在Python中的工作方式。 因此,我編寫了一個應用程序,該應用程序在單獨的QThread中安裝了一些軟件包。
工作人員完成操作(發出完成的信號)后,我要等待幾秒鍾,然后關閉MainWidget(如下所示):
def finished(self):
self.installbutton.setEnabled(True)
self.listwidget.addItem(QtCore.QString("Process complete, have a nice day!"))
time.sleep(5)
self.close()
現在的問題似乎是,在完成回調中調用self.close會導致出現這樣的情況,即在窗口關閉之前ListWidget不再更新。
我的猜測是,ListWidget和回調函數位於同一堆棧中,因此ListWidget沒有機會終止。 但是如何解決這類問題呢?
有什么建議可以克服這個問題嗎?
順便說一句:我被“強迫”使用Python 2.7.5和PyQt4以便與公司的其余部分兼容...這就是為什么我使用舊式信號
這里的代碼:
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()
在閱讀完您的問題和代碼之后..了解到您有兩個線程,即主線程和工作線程,當您完成工作時(我的意思是線程到達了代碼的最后一行),您想通知主線程關閉工作程序並切換到另一個窗口(可能)或執行其他操作。
顯然,要做到這一點,您需要與UI線程(MainThread)進行通信,因此需要在Worker(Thread)類中添加此行
signal = QtCore.pyqtSignal(str) # Create a signal
然后轉到線程的最后一行,並確保當您的工作人員到達該行時,作業將完成,並添加此行以通知主線程
self.signal.emit('Some String here') # this will send the string over the signal to your UI thread
現在跳轉到InstallWizzard類**並將此行添加到__init__方法中,並將此行添加到** self.thread = Worker()下
self.thread.signal.connect(self.finished) # finished is a method inside your class
現在在您完成的方法中,您可以使用類似的方法輕松地終止/關閉/退出您的方法
self.thread.quit() # as you might guessed this will quit your thread.
如果您想強制關閉工作器,也可以用終止()替換quit(),但是請閱讀此警告,該警告來自Qt的QThread :: terminate文檔:
警告:此功能很危險,不建議使用。 線程可以在其代碼路徑中的任何位置終止。 修改數據時可以終止線程。 線程自身無法清除,解鎖任何保持的互斥鎖等。總之,僅在絕對必要時才使用此功能。
如果您關心字符串,它將通過來自Worker的信號發送,則將新參數添加到您完成的方法中以獲取對該字符串的訪問權。 就是這樣:)
我知道我回答您的問題(為1年前)為時已晚,但這可能對其他開發人員有所幫助...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.