Python PySide和进度条线程

Python PySide和进度条线程,python,multithreading,qt,pyside,Python,Multithreading,Qt,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.pr

我有以下代码:

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_())
我想让进度条在一个单独的线程中,这样它就不会冻结应用程序,但我似乎找不到如何做到这一点


谁能帮忙吗?

我想你可能弄错了。您希望在一个单独的线程中执行所做的工作,这样它就不会冻结应用程序。但您也希望能够更新进度条。您可以通过使用
QThread
创建工人类来实现这一点。QThread能够发出信号,您的UI可以监听这些信号并进行适当的操作

首先,让我们创建worker类

#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\u对话框
类中创建一个新函数来处理发出的信号

def setProgress(self, progress):
    self.progressBar.setValue(progress)
在那里,您可以删除
progress()
函数

retranslateUi()
中,您需要从更新按钮事件处理程序

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

最后,在
setupUI()
函数中,您需要创建一个worker类的实例,并将其信号连接到
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_())


QThread具有一些自动发出的内置信号。您可以看到它们,以及有关QThreads的更多信息。如果认为您总是需要使用多线程来处理这样的事情,那就大错特错了

如果您可以将长期运行的任务分解为一系列小步骤,那么您需要做的就是确保任何挂起的事件都得到足够频繁的处理,以便GUI保持响应。这可以通过在主GUI线程中使用以下命令安全地完成:

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

考虑到它的简单性,在选择更重的解决方案(如多线程或多处理)之前,至少应该考虑这种技术。

如果不锁定GUI,这并不完美。例如,如果在更新进程栏时打开QFileDialog。它只会停止qthread,time.sleep仅用于测试目的。不适用于Qt
    for i in range(1, 101):
        self.progressBar.setValue(i)
        QtGui.qApp.processEvents()
        time.sleep(0.1)