簡體   English   中英

PyQT4和線程:QThread完成並更新ListWidget之后關閉MainWidget

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM