PySide和QProgressBar在不同线程中的更新问题

4

这可能会是一篇有点长的帖子,所以非常感谢你能陪我读到最后。问题在于,我有一个主窗口,并且有一个菜单项叫做“Process”(处理)。以下是代码:

from PySide import QtCore, QtGui

class Ui_MainWindow(object):

      def setupUi(self, MainWindow, AppObj):
            .
            .
            self.statusbar = QtGui.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
            .
            .
            .
           self.actionProcess = QtGui.QAction(MainWindow)
           self.actionProcess.setObjectName("actionProcess")
           self.actionProcess.triggered.connect(self.myappObj.menuActionProcess) 
           .

这里的self.myappobj指的是我创建的一个应用程序类,它充当我的应用程序的主逻辑控制器。代码如下:

from PySide import QtCore, QtGui
from MainWindow import Ui_MainWindow

class App(QtGui.QDialog):
      def __init__(self, parent=None):
          self.__mainWindow = QtGui.QMainWindow()
          self.__mainWindowDesignContext = Ui_MainWindow()
          self.__mainWindowDesignContext.setupUi(self.__mainWindow, self)
          self.__mainWindow.show()
      def menuActionProcess(self):
          self.processThread = BatchProcesser()
          self.progressBar = QtGui.QProgressBar()
          statusBar.addWidget(self.progressBar)
          self.progressBar.show()
          self.progressBar.setMinimum(0)
          self.progressBar.setMaximum(100)
          QtCore.QObject.connect(self.processThread, QtCore.SIGNAL("progress(int)"),self.progressBar, QtCore.SLOT("setValue(int)"), QtCore.Qt.DirectConnection)
          if not self.processThread.isRunning():
              self.processThread.exiting = False
              self.processThread.start()

因此,很容易看出我在这里尝试的是创建一个主窗口。在那里添加一个名为“Process”的菜单,点击该菜单应该触发应用程序类中的回调菜单ActionProcess方法,在该方法中我正在创建一个进度条并将其与主窗口的状态栏附加在一起(实际代码有很多其他部分,我将必要的部分重新排列为伪例子)。最后,在上述代码中提到的BatchProcesser类中,我正在执行以下操作 -

from PySide.QtGui import *
from PySide.QtCore import *

class BatchProcesser(QThread):
     __errorHappened = False
     def __init__(self, parent=None):
         QThread.__init__(self, parent)
         self.exiting = False
     def run(self):
         for a in range(101):
            print a
            QThread.msleep(100)
            self.emit(SIGNAL("progress(int)"), a)
            print a

在我的理解中,这应该会在与主线程不同的线程中更新附加到状态栏的进度条。这将允许用户自由地与GUI交互。
现在,如果我尝试运行它,一切都很好,直到我点击“Process”菜单。然后,进度条出现了,但没有更新,控制台却充满了错误信息 -
0 QPixmap:在GUI线程外使用像素图不安全 QPixmap:在GUI线程外使用像素图不安全 QPixmap:在GUI线程外使用像素图不安全 QPixmap:在GUI线程外使用像素图不安全 0 1 QPixmap:在GUI线程外使用像素图不安全 QPixmap:在GUI线程外使用像素图不安全 QPixmap:在GUI线程外使用像素图不安全 QPixmap:在GUI线程外使用像素图不安全 [xcb]在出队时队列中有未知的请求 [xcb]最可能是多线程客户端,而XInitThreads没有被调用 [xcb]抱歉,我们正在终止。 python:../../src/xcb_io.c:178:dequeue_pending_request:Assertion `!xcb_xlib_unknown_req_in_deq'失败。 已放弃(核心已转储)
对我来说,任何帮助都非常必要。我无法找出和/或指出错误的原因并修复它。
1个回答

5
问题在于如何将信号连接到槽:
      QtCore.QObject.connect(self.processThread, QtCore.SIGNAL("progress(int)"),self.progressBar, QtCore.SLOT("setValue(int)"), QtCore.Qt.DirectConnection)

在此,您明确地强制执行了DirectConnection。这是错误的!它使得槽在调用者的线程中处理。这意味着progressBar的GUI更新发生在进程线程中,而不是GUI线程中。然而,在GUI线程中只允许绘图。

连接一个来自另一个线程的信号和槽是完全可以的。但是,您需要使用AutoConnection(默认情况下将识别线程并使用QueuedConnection)或者QueuedConnection

这行代码应该解决你的问题:

      QtCore.QObject.connect(self.processThread, QtCore.SIGNAL("progress(int)"),self.progressBar, QtCore.SLOT("setValue(int)"), QtCore.Qt.QueuedConnection)

请参见http://doc.qt.io/archives/qt-4.7/qt.html#ConnectionType-enum

谢谢ypnos。它像魔法一样奏效。现在,在阅读了您的答案和提供的链接之后,我实际上省略了连接类型,因此框架假定默认值(自动连接)。再次感谢。 - SRC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接