2013-12-18 66 views
15

我有這樣的代碼:的Python PySide和進度條線程

from PySide import QtCore, QtGui 
import time 

class Ui_Dialog(object): 
    def setupUi(self, Dialog): 
     Dialog.setObjectName("Dialog") 
     Dialog.resize(400, 133) 
     self.progressBar = QtGui.QProgressBar(Dialog) 
     self.progressBar.setGeometry(QtCore.QRect(20, 10, 361, 23)) 
     self.progressBar.setProperty("value", 24) 
     self.progressBar.setObjectName("progressBar") 
     self.pushButton = QtGui.QPushButton(Dialog) 
     self.pushButton.setGeometry(QtCore.QRect(20, 40, 361, 61)) 
     self.pushButton.setObjectName("pushButton") 

     self.retranslateUi(Dialog) 
     QtCore.QMetaObject.connectSlotsByName(Dialog) 

    def retranslateUi(self, Dialog): 
     Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) 
     self.pushButton.setText(QtGui.QApplication.translate("Dialog", "PushButton", None, QtGui.QApplication.UnicodeUTF8)) 
     self.progressBar.setValue(0) 
     self.pushButton.clicked.connect(self.progress) 

    def progress(self): 
     self.progressBar.minimum = 1 
     self.progressBar.maximum = 100 
     for i in range(1, 101): 
      self.progressBar.setValue(i) 
      time.sleep(0.1) 

if __name__ == "__main__": 
    import sys 
    app = QtGui.QApplication(sys.argv) 
    Dialog = QtGui.QDialog() 
    ui = Ui_Dialog() 
    ui.setupUi(Dialog) 
    Dialog.show() 
    sys.exit(app.exec_()) 

我想有一個單獨的線程的進度條,所以它不凍結的應用程序,但我似乎無法到找到如何做到這一點。

任何人都可以請幫忙嗎?

回答

25

我想你可能是錯的。你希望你在一個單獨的線程中做的工作,所以它不會凍結應用程序。但是你也希望能夠更新進度條。您可以通過使用QThread創建工人類來實現此目的。 QThreads能夠發出信號,您的用戶界面可以聽取並採取適當的行動。

首先,我們來創建你的工人類。

#Inherit from QThread 
class Worker(QtCore.QThread): 

    #This is the signal that will be emitted during the processing. 
    #By including int as an argument, it lets the signal know to expect 
    #an integer argument when emitting. 
    updateProgress = QtCore.Signal(int) 

    #You can do any extra things in this init you need, but for this example 
    #nothing else needs to be done expect call the super's init 
    def __init__(self): 
     QtCore.QThread.__init__(self) 

    #A QThread is run by calling it's start() function, which calls this run() 
    #function in it's own "thread". 
    def run(self): 
     #Notice this is the same thing you were doing in your progress() function 
     for i in range(1, 101): 
      #Emit the signal so it can be received on the UI side. 
      self.updateProgress.emit(i) 
      time.sleep(0.1) 

所以,現在你有一個工人班,是時候利用它了。您需要在Ui_Dialog類中創建一個新功能來處理髮射的信號。

def setProgress(self, progress): 
    self.progressBar.setValue(progress) 

雖然你在那裏,你可以刪除你的progress()函數。

retranslateUi()你會想從

self.pushButton.clicked.connect(self.progress) 

更新按鈕事件處理程序

self.pushButton.clicked.connect(self.worker.start) 

最後,在setupUI()功能,您將需要創建你的工人類的一個實例並將它的信號連接到你的setProgress()功能。

在此之前:

self.retranslateUi(Dialog) 

補充一點:

self.worker = Worker() 
self.worker.updateProgress.connect(self.setProgress) 

下面是最終代碼:

from PySide import QtCore, QtGui 
import time 


class Ui_Dialog(object): 
    def setupUi(self, Dialog): 
     Dialog.setObjectName("Dialog") 
     Dialog.resize(400, 133) 
     self.progressBar = QtGui.QProgressBar(Dialog) 
     self.progressBar.setGeometry(QtCore.QRect(20, 10, 361, 23)) 
     self.progressBar.setProperty("value", 24) 
     self.progressBar.setObjectName("progressBar") 
     self.pushButton = QtGui.QPushButton(Dialog) 
     self.pushButton.setGeometry(QtCore.QRect(20, 40, 361, 61)) 
     self.pushButton.setObjectName("pushButton") 

     self.worker = Worker() 
     self.worker.updateProgress.connect(self.setProgress) 

     self.retranslateUi(Dialog) 
     QtCore.QMetaObject.connectSlotsByName(Dialog) 

     self.progressBar.minimum = 1 
     self.progressBar.maximum = 100 

    def retranslateUi(self, Dialog): 
     Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) 
     self.pushButton.setText(QtGui.QApplication.translate("Dialog", "PushButton", None, QtGui.QApplication.UnicodeUTF8)) 
     self.progressBar.setValue(0) 
     self.pushButton.clicked.connect(self.worker.start) 

    def setProgress(self, progress): 
     self.progressBar.setValue(progress) 

#Inherit from QThread 
class Worker(QtCore.QThread): 

    #This is the signal that will be emitted during the processing. 
    #By including int as an argument, it lets the signal know to expect 
    #an integer argument when emitting. 
    updateProgress = QtCore.Signal(int) 

    #You can do any extra things in this init you need, but for this example 
    #nothing else needs to be done expect call the super's init 
    def __init__(self): 
     QtCore.QThread.__init__(self) 

    #A QThread is run by calling it's start() function, which calls this run() 
    #function in it's own "thread". 
    def run(self): 
     #Notice this is the same thing you were doing in your progress() function 
     for i in range(1, 101): 
      #Emit the signal so it can be received on the UI side. 
      self.updateProgress.emit(i) 
      time.sleep(0.1) 

if __name__ == "__main__": 
    import sys 
    app = QtGui.QApplication(sys.argv) 
    Dialog = QtGui.QDialog() 
    ui = Ui_Dialog() 
    ui.setupUi(Dialog) 
    Dialog.show() 
    sys.exit(app.exec_()) 

QThreads有一些內置自動發射的信號。你可以看到它們,關於QThreads的更多信息in the documentation

+1

是的,這就是我想要的樣子,謝謝! – Benny

9

認爲你總是需要使用多線程處理這樣的事情是錯誤的。

如果您可以將長時間運行的任務分解爲一系列小步驟,您需要做的就是確保任何待處理事件的處理次數足以使GUI保持響應。這可以安全地從內主界面線程的使用processEvents來完成,像這樣:

for i in range(1, 101): 
     self.progressBar.setValue(i) 
     QtGui.qApp.processEvents() 
     time.sleep(0.1) 

由於它的簡單,它總是值得選擇瞭如多一個更重量級的解決方案之前,至少考慮到這種技術線程或多處理。

+0

這很簡單!解決了我的問題! – user1036908