【问题标题】:Filling QListWidget object using Multi Threading使用多线程填充 QListWidget 对象
【发布时间】:2015-07-16 05:43:06
【问题描述】:

我有 2 个QListWidget 列表对象,第一个在展示主 GUI 之前包含一些数据,第二个在从第一个列表中选择某些内容时填充另一个数据...我正在尝试填充第二个使用多线程列出 100 万个项目,以在该任务正在进行时不冻结主 GUI 窗口。

    self.lst1= QtGui.QListWidget(self.groupBox)
    self.lst2= QtGui.QListWidget(self.groupBox)

    self.lst1.itemSelectionChanged.connect(lambda: self.thread_list_filler(idx = 0))

    def thread_list_filler(self, idx):
        if idx == 0:
            th = Thread(target = self.fill_List2)
        th.start()

    def fill_List2(self):
        self.lst2.clear()
        for i in range(1,1000000+1):
            self.lst2.addItem(str(i))

每次按下lst1 中的某个项目时,GUI 都会崩溃,这是什么问题以及如何避免这种情况?

【问题讨论】:

  • 你没有发布thread_list_filler方法,我们怎么知道哪里出了问题?
  • @laike9m 我的错,打错了
  • 还有self.fillList2?
  • 我很遗憾 ;( 还有 1 个错字
  • 我不熟悉QT,但是你确定你的lst2可以容纳1000000个项目吗?尝试将它减少到 10,看看它是否仍然给你错误。

标签: python multithreading user-interface pyqt


【解决方案1】:

您不应该与主线程之外的 gui 元素进行交互。 IE。您应该在线程中发出一个信号,并将此信号连接到一个插槽,该插槽将执行实际的添加到列表业务。

但是请注意,100 万个项目是要放入 QListWidget 的大量数据。

无论如何,这样的事情可能会奏效:

class MyWidget(QtGui.QWidget):

    addRequested = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super(MyWidget, self).__init__(parent)

        layout = QtGui.QVBoxLayout(self)
        self.groupBox = QtGui.QGroupBox('Test', self)
        layout.addWidget(self.groupBox)

        vlayout = QtGui.QVBoxLayout(self.groupBox)
        self.button = QtGui.QPushButton("Fill it", self.groupBox)
        self.lst2 = QtGui.QListWidget(self.groupBox)
        vlayout.addWidget(self.button)
        vlayout.addWidget(self.lst2)

        self.button.clicked.connect(self.thread_list_filler)
        self.addRequested.connect(self.lst2.addItem)

    def thread_list_filler(self):
        self.lst2.clear()
        th = threading.Thread(target = self.fill_List2)
        th.start()

    def fill_List2(self):
        for i in range(1,1000000+1):
            self.addRequested.emit(str(i))

【讨论】:

  • 你不应该从线程中调用self.lst2.clear()!此外,虽然我自己没有任何确凿的证据,但显然 PyQt 线程安全方法(如 emit())仅在 QThread 中是安全的,而不是 Python 线程(参见 this)。但是,我个人从 python 线程中使用了另一种线程安全方法 (QApplication.postEvent()),没有明显的副作用,所以我不确定按照你的建议是否真的有问题。
  • 我看到列表是如何填写的,但是,RenderBadGlyphSet (invalid GlyphSet parameter) 142 accures
【解决方案2】:

尽管我问这个问题已经有一段时间了,但这里有一个非常适合我的问题的解决方案。

from PyQt4 import QtGui, QtCore
from qTest import Ui_Form
import sys
from time import sleep

class WorkerThread(QtCore.QThread):
    def __init__(self, parent):
        super(WorkerThread, self).__init__(parent)
        self.stopFlag = False

    def run(self):
        for i in xrange(0, 1000000):
            if self.stopFlag:
                break
            self.emit(QtCore.SIGNAL('addIntoList(int)'), i)
            sleep(0.001)
        self.stopFlag = False

    def stop(self):
        self.stopFlag = True

class TEST(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(self.stopThread)
        self.ui.pushButton_2.clicked.connect(self.close)

        self.lst1 = self.ui.listWidget_1
        self.lst2 = self.ui.listWidget_2

        self.qThread = WorkerThread(self)
        self.connect(self.qThread, QtCore.SIGNAL("addIntoList(int)"), self.addIntoList)

        for i in range(10):
            self.lst1.addItem("%d" % i)
        self.lst1.currentRowChanged.connect(self.thread_list_filler)

    @QtCore.pyqtSlot(int)
    def addIntoList(self, item):
        self.lst2.addItem(str(item))

    def stopThread(self):
        self.qThread.stop()

    def thread_list_filler(self, row):
        if self.qThread.isRunning():
            self.qThread.stop()
            self.qThread.wait()
        self.lst2.clear()
        if row == 0:
            self.qThread.start()


QtGui.QApplication.setStyle('cleanlooks')
font = QtGui.QFont()
font.setPointSize(10)
font.setFamily('Arial')
app = QtGui.QApplication(sys.argv)
app.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus,False)
app.setFont(font)

window = TEST()
window.show()
sys.exit(app.exec_())

【讨论】:

    猜你喜欢
    • 2013-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    • 2015-02-26
    相关资源
    最近更新 更多