I am trying to implement a program with multiple threads. In the __init__
of the main window, the threads are created. The GUI starts up, while the threads get run in the background. The problem is that it keeps crashing. But if I add/uncomment a line with a print statement, the program works fine.
class TestThreadingWin(QtGui.QMainWindow):
def __init__(self, parent=rsui.getMayaMainWindow()):
''' Constructor '''
super(TestThreadingWin, self).__init__(parent)
self.setWindowTitle('Test Threading')
self.centralWidget = QtGui.QPlainTextEdit()
self.setCentralWidget(self.centralWidget)
self.centralWidget.appendPlainText("test")
numThreads = 7
self._threads = []
for i in range(numThreads):
#print self._threads # <-- Uncomment this line, and it works?!
testThread = QtCore.QThread()
# Keep a reference to the thread object or it will be deleted when
# it goes out of scope, even if it has not finished processing.
self._threads.append(testThread)
worker = TestThreadWorker(i)
worker.moveToThread(testThread)
worker.finishedProcessing.connect(self.updateStuff)
worker.finishedProcessing.connect(testThread.quit)
testThread.started.connect(worker.doStuff)
testThread.finished.connect(self.deleteThread)
testThread.start()
QtCore.QCoreApplication.processEvents()
print 'done creating all threads'
def deleteThread(self):
""" Destroy the thread object and remove the reference to it from the
self._threads list. """
print 'delete thread'
threadToDelete = self.sender()
threadIndex = self._threads.index(threadToDelete)
del self._threads[threadIndex]
threadToDelete.deleteLater()
def updateStuff(self, message):
self.centralWidget.appendPlainText(message)
class TestThreadWorker(QtCore.QObject):
finishedProcessing = QtCore.Signal(str)
def __init__(self, num):
super(TestThreadWorker, self).__init__()
self._num = num
def doStuff(self):
time.sleep(1)
choiceList = ['cat', 'bat', 'hat', 'tissue', 'paper', 'qwerty', 'mouse']
stuff = random.choice(choiceList)
stuff2 = '{0} {1}'.format(self._num, stuff)
self.finishedProcessing.emit(stuff2)
def openThreadingWin():
'''This ensures that only one instance of the UI is open at a time.'''
global testingThreadingWin
try:
testingThreadingWin.close()
testingThreadingWin.deleteLater()
except: pass
testingThreadingWin = TestThreadingWin()
testingThreadingWin.show()
It is weird that a print statement would make it stop crashing. What am I overlooking?
I finally got it working by using QThreadPool. It manages the threads for me, so I don't have to worry about trying to access something that was already destroyed. Key differences:
__init__
function of both parent classes must be called, or the program will crash.Here is the new code:
class TestThreadPoolWin(QtGui.QMainWindow):
def __init__(self, parent=rsui.getMayaMainWindow()):
''' Constructor '''
super(TestThreadPoolWin, self).__init__(parent)
self.setWindowTitle('Test Threading')
self.centralWidget = QtGui.QPlainTextEdit()
self.setCentralWidget(self.centralWidget)
self.centralWidget.appendPlainText("test")
numThreads = 7
threadPool = QtCore.QThreadPool.globalInstance()
for i in range(numThreads):
runnable = TestRunnableWorker(i)
runnable.finishedProcessing.connect(self.updateStuff)
threadPool.start(runnable)
print 'done creating all threads'
def updateStuff(self, message):
self.centralWidget.appendPlainText(message)
class TestRunnableWorker(QtCore.QObject, QtCore.QRunnable):
finishedProcessing = QtCore.Signal(str)
def __init__(self, num, parent=None):
# Be sure to run the __init__ of both parent classes, or else you will
# get a crash.
QtCore.QObject.__init__(self, parent)
QtCore.QRunnable.__init__(self)
self._num = num
def run(self):
time.sleep(1)
choiceList = ['cat', 'bat', 'hat', 'tissue', 'paper', 'qwerty', 'mouse']
stuff = random.choice(choiceList)
stuff2 = '{0} {1}'.format(self._num, stuff)
self.finishedProcessing.emit(stuff2)
def openThreadPoolWin():
'''This ensures that only one instance of the UI is open at a time.'''
global testingThreadPoolWin
try:
testingThreadPoolWin.close()
testingThreadPoolWin.deleteLater()
except: pass
testingThreadPoolWin = TestThreadPoolWin()
testingThreadPoolWin.show()