【问题标题】:qprogressbar bar stops updating if new qprogressbar is opened in case of multithreading如果在多线程的情况下打开新的 qprogressbar,qprogressbar 栏将停止更新
【发布时间】:2013-02-15 19:14:38
【问题描述】:

我想在某些事件的进度条上更新进度。有多个事件/线程,因此与每个事件/线程相关联的进度条。但是当新线程启动时,前一个线程会停止更新进度条。这不应该发生。可以使用以下:

import sys
from PyQt4 import QtGui, QtCore

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s


class Ui_dialog_progress(object):
    def setupUi(self, dialog_progress):
        dialog_progress.setObjectName("dialog_progress")
        dialog_progress.resize(401, 165)
        self.gridLayout = QtGui.QGridLayout(dialog_progress)
        self.gridLayout.setObjectName("gridLayout")
        self.lblFileName = QtGui.QLabel(dialog_progress)
        self.lblFileName.setText("")
        self.lblFileName.setObjectName("lblFileName")
        self.gridLayout.addWidget(self.lblFileName, 0, 0, 1, 2)
        self.pbarFileSize = QtGui.QProgressBar(dialog_progress)
        self.pbarFileSize.setProperty("value", 0)
        self.pbarFileSize.setObjectName("pbarFileSize")
        self.gridLayout.addWidget(self.pbarFileSize, 1, 0, 1, 2)
        self.label_2 = QtGui.QLabel(dialog_progress)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
        self.pbarTotal = QtGui.QProgressBar(dialog_progress)
        self.pbarTotal.setProperty("value", 0)
        self.pbarTotal.setObjectName("pbarTotal")
        self.gridLayout.addWidget(self.pbarTotal, 3, 0, 1, 2)
        self.lblTotal = QtGui.QLabel(dialog_progress)
        self.lblTotal.setText("")
        self.lblTotal.setObjectName("lblTotal")
        self.gridLayout.addWidget(self.lblTotal, 4, 0, 1, 2)
        spacerItem = QtGui.QSpacerItem(213, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
        self.gridLayout.addItem(spacerItem, 6, 0, 1, 1)
        spacerItem1 = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.gridLayout.addItem(spacerItem1, 5, 1, 1, 1)
        self.btnPbarCancel = QtGui.QPushButton(dialog_progress)
        self.btnPbarCancel.setObjectName("btnPbarCancel")
        self.gridLayout.addWidget(self.btnPbarCancel, 6, 1, 1, 1)

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

    def retranslateUi(self, dialog_progress):
        dialog_progress.setWindowTitle(QtGui.QApplication.translate("dialog_progress", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.label_2.setText(QtGui.QApplication.translate("dialog_progress", "Total:", None, QtGui.QApplication.UnicodeUTF8))
        self.btnPbarCancel.setText(QtGui.QApplication.translate("dialog_progress", "Cancel", None, QtGui.QApplication.UnicodeUTF8))

class Ui_dialog_file(object):
    def setupUi(self, dialog_file):
        dialog_file.setObjectName(_fromUtf8("dialog_file"))
        dialog_file.resize(101, 59)
        self.btnCreate = QtGui.QPushButton(dialog_file)
        self.btnCreate.setGeometry(QtCore.QRect(10, 20, 77, 25))
        self.btnCreate.setObjectName(_fromUtf8("btnCreate"))
        self.retranslateUi(dialog_file)
        QtCore.QMetaObject.connectSlotsByName(dialog_file)

    def retranslateUi(self, dialog_file):
        dialog_file.setWindowTitle(QtGui.QApplication.translate("dialog_file", "file", None, QtGui.QApplication.UnicodeUTF8))
        self.btnCreate.setText(QtGui.QApplication.translate("dialog_file", "Create", None, QtGui.QApplication.UnicodeUTF8))

class ProgressDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent = None)
        self.ui = Ui_dialog_progress()
        self.ui.setupUi(self)
#        self.setWindowModality(QtCore.Qt.ApplicationModal)

class MyThread(QtCore.QThread):
    trigger = QtCore.pyqtSignal()
    updateFPD = QtCore.pyqtSignal()
    updateTPD = QtCore.pyqtSignal()
    def __init__(self, parent=None):
        super(MyThread, self).__init__(parent)
        self.pd = ProgressDialog()
        signal = QtCore.SIGNAL("clicked()")
        self.pd.ui.btnPbarCancel.connect(self.pd.ui.btnPbarCancel, signal, self.abort)
        self.aborted= False

    def run(self):
        self.trigger.emit()

    def abort(self):
        self.aborted = True
        print "aborted"
        self.pd.close()

    def setup(self, no):
        self.threadNo = no
        self.name = "Thread %d"%self.threadNo

class Main(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.ui = Ui_dialog_file()
        self.ui.setupUi(self)
        self.ui.btnCreate.clicked.connect(self.start_threads)
        self.count = 0
        self.threadPool = []

    def start_threads(self):
        self.mythread = MyThread(self)
        self.mythread.setup(self.count)
        self.count += 1
        self.mythread.trigger.connect(self.pdialog)
        self.mythread.start()
        self.threadPool.append(self.count)

    def abort(self):
        self.mythread.stop()

    def pdialog(self):
        self.mythread.pd.show()
        i = 1
        self.mythread.pd.ui.pbarTotal.setValue(0)
        self.mythread.pd.ui.pbarFileSize.setValue(0)
        self.mythread.pd.setWindowTitle(self.mythread.name)
        j = 1
        while i < 100:
            while j <= 100:
                self.mythread.pd.ui.lblFileName.setText(self.mythread.name)
                self.mythread.pd.ui.pbarFileSize.setValue(j)
                print "name: %s, fileprogress: %d"%(self.mythread.name,j)
                QtGui.qApp.processEvents()
                j += 1
            i += 1
            j = 1
            self.mythread.pd.ui.pbarTotal.setValue(i)
            print "name: %s, total progress %d"%(self.mythread.name, i)
            QtGui.qApp.processEvents()
            if self.mythread.aborted:
                return
        self.mythread.pd.close()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    mainwindow = Main()
    mainwindow.show()
    sys.exit(app.exec_())

请找到我的新代码:

import sys
from PyQt4 import QtGui, QtCore
import time


try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s


class Ui_dialog_progress(object):
    def setupUi(self, dialog_progress):
        dialog_progress.setObjectName("dialog_progress")
        dialog_progress.resize(401, 165)
        self.gridLayout = QtGui.QGridLayout(dialog_progress)
        self.gridLayout.setObjectName("gridLayout")
        self.lblFileName = QtGui.QLabel(dialog_progress)
        self.lblFileName.setText("")
        self.lblFileName.setObjectName("lblFileName")
        self.gridLayout.addWidget(self.lblFileName, 0, 0, 1, 2)
        self.pbarFileSize = QtGui.QProgressBar(dialog_progress)
        self.pbarFileSize.setProperty("value", 0)
        self.pbarFileSize.setObjectName("pbarFileSize")
        self.gridLayout.addWidget(self.pbarFileSize, 1, 0, 1, 2)
        self.label_2 = QtGui.QLabel(dialog_progress)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
        self.pbarTotal = QtGui.QProgressBar(dialog_progress)
        self.pbarTotal.setProperty("value", 0)
        self.pbarTotal.setObjectName("pbarTotal")
        self.gridLayout.addWidget(self.pbarTotal, 3, 0, 1, 2)
        self.lblTotal = QtGui.QLabel(dialog_progress)
        self.lblTotal.setText("")
        self.lblTotal.setObjectName("lblTotal")
        self.gridLayout.addWidget(self.lblTotal, 4, 0, 1, 2)
        spacerItem = QtGui.QSpacerItem(213, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
        self.gridLayout.addItem(spacerItem, 6, 0, 1, 1)
        spacerItem1 = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.gridLayout.addItem(spacerItem1, 5, 1, 1, 1)
        self.btnPbarCancel = QtGui.QPushButton(dialog_progress)
        self.btnPbarCancel.setObjectName("btnPbarCancel")
        self.gridLayout.addWidget(self.btnPbarCancel, 6, 1, 1, 1)

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

    def retranslateUi(self, dialog_progress):
        dialog_progress.setWindowTitle(QtGui.QApplication.translate("dialog_progress", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.label_2.setText(QtGui.QApplication.translate("dialog_progress", "Total:", None, QtGui.QApplication.UnicodeUTF8))
        self.btnPbarCancel.setText(QtGui.QApplication.translate("dialog_progress", "Cancel", None, QtGui.QApplication.UnicodeUTF8))

class Ui_dialog_file(object):
    def setupUi(self, dialog_file):
        dialog_file.setObjectName(_fromUtf8("dialog_file"))
        dialog_file.resize(101, 59)
        self.btnCreate = QtGui.QPushButton(dialog_file)
        self.btnCreate.setGeometry(QtCore.QRect(10, 20, 77, 25))
        self.btnCreate.setObjectName(_fromUtf8("btnCreate"))
        self.retranslateUi(dialog_file)
        QtCore.QMetaObject.connectSlotsByName(dialog_file)

    def retranslateUi(self, dialog_file):
        dialog_file.setWindowTitle(QtGui.QApplication.translate("dialog_file", "file", None, QtGui.QApplication.UnicodeUTF8))
        self.btnCreate.setText(QtGui.QApplication.translate("dialog_file", "Create", None, QtGui.QApplication.UnicodeUTF8))

class ProgressDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent=None)
        self.ui = Ui_dialog_progress()
        self.ui.setupUi(self)
#        self.setWindowModality(QtCore.Qt.ApplicationModal)

class MyThread(QtCore.QThread):
    trigger = QtCore.pyqtSignal()
    updateFPD = QtCore.pyqtSignal(int, QtCore.QString)
    updateTPD = QtCore.pyqtSignal(int, QtCore.QString)
    showpdSignal = QtCore.pyqtSignal(bool)

    def __init__(self, parent=None, no=0):
        super(MyThread, self).__init__(parent)
        self.aborted = False
        self.threadNo = no
#        self.name = "Thread %d"%self.threadNo

    def run(self):
        self.trigger.emit()

    def emitSignals(self, iprogress=-99, tprogress=-99, \
                updateFPDSignal=True, updateTPDSignal=None, \
                showpdSignal=False, peSignal=None, msg=None
                ):
        if updateFPDSignal:
            self.updateFPD.emit(iprogress, msg)
        if updateTPDSignal:
            self.updateTPD.emit(tprogress, msg)
        if showpdSignal:
            self.showpdSignal.emit(True)
        else:
            self.showpdSignal.emit(False)
        if peSignal:
            QtGui.QApplication.processEvents()

    def abort(self):
        self.aborted = True
        print "aborted"

    def stop(self):
        self.terminate()

class Main(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.ui = Ui_dialog_file()
        self.ui.setupUi(self)
        self.ui.btnCreate.clicked.connect(self.start_threads)
        self.count = 0
        self.threadPool = []

    def start_threads(self):
        self.mythread = MyThread()
        self.pd = ProgressDialog()
        self.pd.ui.btnPbarCancel.clicked.connect(self.abort)
        self.mythread.trigger.connect(self.pdialog)
        self.mythread.updateFPD.connect(self.updateFileProgress)
        self.mythread.updateTPD.connect(self.updateTotalProgress)
        self.mythread.start()
        self.threadPool.append(self.mythread)

    def abort(self):
        self.pd.close()
        self.mythread.aborted = True

    def updateFileProgress(self, j, name):
        self.pd.ui.lblFileName.setText(name)
        self.pd.ui.pbarFileSize.setValue(j)
#        print "name: %s, fileprogress: %d" % (name, j)

    def updateTotalProgress(self, i, name):
        self.pd.ui.pbarTotal.setValue(i)
#        print "name: %s, total progress %d" % (name, i)

    def showProgressDialog(self, show):
        if show:
            self.pd.show()
        else:
            self.pd.close()

    def pdialog(self):
        self.pd.show()
        i = 1
        self.threadPool[len(self.threadPool) - 1].emitSignals(iprogress=0 , tprogress=0, updateFPDSignal=True,
                                updateTPDSignal=True, showpdSignal=True, msg="")
        j = 1
        while i < 100:
            while j <= 100:
                self.threadPool[len(self.threadPool) - 1].emitSignals(iprogress=j, updateFPDSignal=True, \
                                        peSignal=True, msg="")
                j += 1
            i += 1
            j = 1
            self.threadPool[len(self.threadPool) - 1].emitSignals(tprogress=i, updateTPDSignal=True, \
                                          peSignal=True, msg="")
            if self.threadPool[len(self.threadPool) - 1].aborted:
                return
        self.threadPool[len(self.threadPool) - 1].sleep(2)
        self.pd.close()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    mainwindow = Main()
    mainwindow.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python multithreading pyqt


    【解决方案1】:

    小部件只能由主 ui 线程(即控制主事件循环的线程)控制。如果一个小部件存在于主线程之外,那么它很可能不会收到任何更新,也不会发布任何事件,例如被点击。不要让您的线程拥有和/或操作 ui 小部件。在这种特殊情况下,您根本不需要 MyThread

    让我举个例子。

    import time
    import sys
    from PyQt4 import QtGui, QtCore
    
    
    class MyThread(QtCore.QThread):
    
        progress_changed = QtCore.pyqtSignal(object)
    
        def run(self):
            for x in range(101):
                #we do not interect with the progress bar directly
                self.progress_changed.emit(x)
                time.sleep(0.1)
    
    
    class MyWidget(QtGui.QWidget):
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)
    
            self.progress_thread1 = MyThread()
            self.progress_thread2 = MyThread()
    
            self.progress_bar1 = QtGui.QProgressBar()
            self.progress_bar1.setMinimum(0)
            self.progress_bar1.setMaximum(100)
    
            self.progress_bar2 = QtGui.QProgressBar()
            self.progress_bar2.setMinimum(0)
            self.progress_bar2.setMaximum(100)
    
    
            """
            the progress is emmited through the thread's progress_changed signal
            we connect that signal to our updateProgress method which will
            be used to set the progress_bar's value
    
            you can also just connect it directly to the progress bar's setValue method
            self.progress_thread.progress_changed.connect(self.progress_bar.setValue)
            """
            self.progress_thread1.progress_changed.connect(self.updateProgress1)
    
            self.progress_thread2.progress_changed.connect(self.updateProgress2)
    
            mainLayout = QtGui.QVBoxLayout()
            mainLayout.addWidget(self.progress_bar1)
            mainLayout.addWidget(self.progress_bar2)
    
            self.setLayout(mainLayout)
    
            self.progress_thread1.start()
            self.progress_thread2.start()
    
    
        def updateProgress1(self, progress):
            self.progress_bar1.setValue(progress)
    
        def updateProgress2(self, progress):
            self.progress_bar2.setValue(progress)
    
    
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        widget = MyWidget()
        widget.show()
        r = app.exec_()
        sys.exit(r)
    

    将此复制到一个空的python脚本并运行它。

    【讨论】:

    • Vikki,但如果我想使用 MyThread 来做。那么你能修改现有的代码,让它工作吗?
    • 没有。它根本行不通。理想的方法是让工作在您的线程中完成,然后在进度发生变化时发出信号。然后,该信号应被 ui 线程上的任何事物消耗以更新进度条。 qt(和大多数其他 gui 框架)的工作方式,线程直接与小部件交互是不可行的。
    • Vikki,我也是这样做的。 MyThread 正在做这项工作。在这种情况下,GUI 会冻结或崩溃。寻找stackoverflow.com/questions/14855361/…
    • MyThreadProgressDialog 作为成员,您实际上是在尝试与线程中的gui 元素进行交互,这是错误的。在主线程中创建ProgressDialog
    • Vikki,我当时也做了同样的事情。我停止了与 MyThread 交互的 GUI 元素,并在主线程中创建了 ProgressDialog。现在,如果我生成多个线程(ProgressDialog),那么只有最新的 ProgressDialog 显示进度,而其他线程则不会。帮助解决这个问题。我是编码新手。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    相关资源
    最近更新 更多