0

I have 2 QListWidget list objects, first one contain some data before showing off main GUI, second one is filling with another data when something has been selected from the first list... I'm trying to fill the second list with 1 million items using multi-threading to not freeze the main GUI windows while that task is in process.

    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))

The GUI is crashing every time when i press some item from lst1, whats the problem and how to avoid this?

PYPL
  • 1,819
  • 1
  • 22
  • 45

2 Answers2

1

You're not supposed to interact with gui elements outside the main thread. I.e. you should emit a signal in the thread, and connect this signal to a slot which will do the actual adding-to-list business.

Note however that 1 million items is a HUGE amout of data to put in a QListWidget.

Anyway, something like that may work:

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))
  • You shouldn't call `self.lst2.clear()` from the thread! Also, while I don't have any hard evidence myself, apparently PyQt thread safe methods (like `emit()`) are only safe in a `QThread`, not a Python thread (see [this](http://stackoverflow.com/q/1595649/1994235)). However, personally I have used another thread safe method (`QApplication.postEvent()`) from a python thread with no obvious side effects, so I'm unsure if there is actually a problem doing what you have suggested. – three_pineapples May 07 '15 at 01:26
  • I see how the list is being filled but, `RenderBadGlyphSet (invalid GlyphSet parameter) 142` accures – PYPL May 15 '15 at 13:38
0

Even though it's been awhile since i asked this question, here is a solution for it which suits my problem very well.

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_())
PYPL
  • 1,819
  • 1
  • 22
  • 45