Reputation: 1661
I am really struggling to finish writing a GUI app which requires several different threads. At a high level I require:
A GUI Thread, which has a button to open a directory browser which the user selects a directory. On selecting the directory a thread starts which looks for a particular file type. This may take a long time so I know I need to put in a separate QThread.
Then once that browseFile thread has finished searching for files it returns a fileList, which is then chunked into sub fileLists. Each subfileList is then sent to a separate thread for processing which will take lots of time.
This is what I have coded so far:
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtCore import *
import os
class BrowseThread(QThread):
processdone = QtCore.pyqtSignal("QString") # Define custom signal.
def __init__(self, parent, *args, **kw):
QThread.__init__(self, parent)
self.search(*args, **kw)
def search(self, directory_path):
self.fileList = []
self.count = 0
for dirname, dirnames, filenames in os.walk(directory_path):
for filename in filenames:
if filename.endswith(".gz"):
self.fileList.append(os.path.join(directory_path,filename))
self.emit( SIGNAL('processdone'), "DONE")
return
class MyClass(QObject):
def __init__(self):
super(MyClass, self).__init__()
directory_path = r'C:\Data'
thread1 = BrowseThread(self, directory_path)
self.connect( thread1, SIGNAL("processdone"), self.thread1done)
thread1.start()
def thread1done(self, text):
print(text)
sys.exit()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
a = MyClass()
sys.exit(app.exec_())
Is there a better way to pass the directory path to the browseThread than using *args, **kw?
How can I return the fileList back to the main thread which I can then pass to a number of new processing threads.
I am sure I making this harder than it should be so hopefully someone can help me
thanks
Upvotes: 0
Views: 3721
Reputation: 1549
You should be able to use QT's signal and slot mechanism.
http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads
You can also use a message passing system.
There is nothing wrong with passing it as an arg either. Just make sure you do it safely: https://web.archive.org/web/20150423115746/http://effbot.org/zone/thread-synchronization.htm
Upvotes: 0
Reputation: 9798
I suggest to change the design of your code to a solution with a worker class which does the real work. This worker class will be moved to a normal thread like so (I don't know PyQt but it should be fairly similar to the C++ version):
class SearchWorker(QObject):
finished_sig = QtCore.pyqtSignal('') # finished signal
def __init__(self, your_stuff):
self.directory_path = ...
def process_slot(self):
# use self.directory_path
# your code
self.emit(SIGNAL('finished_sig'), '')
Now the main part is to connect the start signal to your worker in your class MyClass. Which gives something like this in C++:
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
// The start signal will actually launch your search method in the worker
connect(thread, SIGNAL(started()), worker, SLOT(process_slot()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
// May be useless in python
connect(worker, SIGNAL(finished_sig()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
The good point is that you can use the worker with or without a thread and you can easily pass parameters to the worker via setter methods.
To know more about Qt signals and slots for threads I suggest you to read:
Upvotes: 1