Can you create a separate QThread class and only call a specific function from it? - pyqt4

I have tried to read as much as I can about PyQt4's QThread and the idea of the worker thread. My question is, instead of building a QThread class to run everything in it from the def run(self): by the blahblah.start() command is there a way to create that individual thread class that has,say, 4 functions and you only call function 2 and then close that thread right after?

Subclassing QThread is a practice that is in general discouraged although often used. [see comment below]
In my opinion, this is a good example of how to use a thread in pyqt. You would create a Worker and a Thread, where the Worker is some general class of type QObject and the Thread is a QThread which you do not subclass. You'd then move the Worker to the Threat and start it.
self.worker = WorkerObject()
self.worker_thread = QtCore.QThread()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
Inside the Worker you can basically do whatever you want, it can have arbitrary many methods and so on.
The one big thing to keep in mind is that the Worker needs to be separate from the main loop. So the methods should not return anything that is used in the main loop (better not return anything at all) and the Worker's results should be collected using signals and slots.
self.button_start.clicked.connect(self.worker.startWork)
self.button_do_something_else.clicked.connect(self.worker.function2)
self.worker.signalStatus.connect(self.updateStatus)
Also make sure not to use any PyQt/GUI objects inside the worker, as this would also build a bridge between Worker and main loop through PyQt itself.

Related

Easy way to call a function on the main thread in Qt (PySide2)

Is there any easy way to call a function or method in the main thread from any other thread or QThread?
I heard that Slots and Signals can be used as a proxy between a thread and the main thread but it feels like too much work to create such a proxy for every single time I want to transfer data to my main thread.
(My answer describes a very universal way to acclompish this so I won't be providing a "minimal" example here, you can look at the answer.)
Qt has a function called invokeMethod (https://doc.qt.io/qt-5/qmetaobject.html#invokeMethod) that can be used to invoke a method on the main thread by using the Qt.QueuedConnection connection type.
In PySide however this won't work if you want to call a function with arguments!
Solution:
So to allow a similar and even easier functionality with PySide, I have written this class which can be used every time you want to run a function or method on the main thread:
from typing import Callable
from PySide2.QtCore import QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication
class InvokeMethod(QObject):
def __init__(self, method: Callable):
"""
Invokes a method on the main thread. Taking care of garbage collection "bugs".
"""
super().__init__()
main_thread = QGuiApplication.instance().thread()
self.moveToThread(main_thread)
self.setParent(QGuiApplication.instance())
self.method = method
self.called.connect(self.execute)
self.called.emit()
called = Signal()
#Slot()
def execute(self):
self.method()
# trigger garbage collector
self.setParent(None)
This will internally create a Signal and a Slot without any parameters. The Slot however will be called on the main thread as it has been connected using Qt.AutoConnection (the default) and moved to the main thread with moveToThread(...).
To make sure no function arguments get lost due to the garbage collector, the parent of the class is temporarily set to the QGuiApplication instance (you might need to change this if you don't rely on the QGuiApplication. Any QObject will be fine as the parent).
Here is an example on how to use this class:
InvokeMethod(lambda: print("hello"))

QT Multithreading Data Pass from Main Thread to Worker Thread

I am using multithreading in my QT program. I need to pass data to the worker object that lives in the worker thread from the main gui thread. I created a setData function in a QObject subclass to pass all the necessary data from the main gui thread. However I verified the function is called from the main thread by looking at QThread::currentThreadId() in the setData function. Even though the worker object function is called from the main thread does this ensure that the worker thread still has its own copy of the data as is required for a reentrant class? Keep in mind this is happening before the worker thread is started.
Also if basic data types are used in a class without dynamic memory and no static global variables is that class reentrant as long as all of its other member data is reentrant? (it's got reentrant data members like qstrings, qlists etc in addition the the basic ints bools etc)
Thanks for the help
Edited new content:
My main question was simply is it appropriate to call a QObject subclass method living in another thread from the main gui thread in order to pass my data to the worker thread to be worked on (in my case custom classes containing backup job information for long-pending file scans and copies for data backup). The data pass all happens before the thread is started so there's no danger of both threads modifying the data at once (I think but I'm no multithreading expert...) It sounds like the way to do this from your post is to use a signal from the main thread to a slot in the worker thread to pass the data. I have confirmed my data backup jobs are reentrant so all I need to do is assure that the worker thread works on its own instances of these classes. Also the transfer of data currently done by calling the QObject subclass method is done before the worker thread starts - does this prevent race conditions and is it safe?
Also here under the section "Accessing QObject Subclasses from Other Threads" it looks a little dangerous to use slots in the QObject subclass...
OK here's the code I've been busy recently...
Edited With Code:
void Replicator::advancedAllBackup()
{
updateStatus("<font color = \"green\">Starting All Advanced Backups</font>");
startBackup();
worker = new Worker;
worker->moveToThread(workerThread);
setupWorker(normal);
QList<BackupJob> jobList;
for (int backupCount = 0; backupCount < advancedJobs.size(); backupCount++)
jobList << advancedJobs[backupCount];
worker->setData(jobList);
workerThread->start();
}
The startBackup function sets some booleans and updates the gui.
the setupWorker function connects all signals and slots for the worker thread and worker object.
the setData function sets the worker job list data to that of the backend and is called before the thread starts so there is no concurrency.
Then we start the thread and it does its work.
And here's the worker code:
void setData(QList<BackupJob> jobs) { this->jobs = jobs; }
So my question is: is this safe?
There are some misconceptions in your question.
Reentrancy and multithreading are orthogonal concepts. Single-threaded code can be easily forced to cope with reentrancy - and is as soon as you reenter the event loop (thus you shouldn't).
The question you are asking, with correction, is thus: Are the class's methods thread-safe if the data members support multithreaded access? The answer is yes. But it's a mostly useless answer, because you're mistaken that the data types you use support such access. They most likely don't!
In fact, you're very unlikely to use multithread-safe data types unless you explicitly seek them out. POD types aren't, most of the C++ standard types aren't, most Qt types aren't either. Just so that there are no misunderstandings: a QString is not multithread-safe data type! The following code is has undefined behavior (it'll crash, burn and send an email to your spouse that appears to be from an illicit lover):
QString str{"Foo"};
for (int i = 0; i < 1000; ++i)
QtConcurrent::run([&]{ str.append("bar"); });
The follow up questions could be:
Are my data members supporting multithreaded access? I thought they did.
No, they aren't unless you show code that proves otherwise.
Do I even need to support multithreaded access?
Maybe. But it's much easier to avoid the need for it entirely.
The likely source of your confusion in relation to Qt types is their implicit sharing semantics. Thankfully, their relation to multithreading is rather simple to express:
Any instance of a Qt implicitly shared class can be accessed from any one thread at a given time. Corollary: you need one instance per thread. Copy your object, and use each copy in its own thread - that's perfectly safe. These instances may share data initially, and Qt will make sure that any copy-on-writes are done thread-safely for you.
Sidebar: If you use iterators or internal pointers to data on non-const instances, you must forcibly detach() the object before constructing the iterators/pointers. The problem with iterators is that they become invalidated when an object's data is detached, and detaching can happen in any thread where the instance is non-const - so at least one thread will end up with invalid iterators. I won't talk any more of this, the takeaway is that implicitly shared data types are tricky to implement and use safely. With C++11, there's no need for implicit sharing anymore: they were a workaround for the lack of move semantics in C++98.
What does it mean, then? It means this:
// Unsafe: str1 potentially accessed from two threads at once
QString str1{"foo"};
QtConcurrent::run([&]{ str1.apppend("bar"); });
str1.append("baz");
// Safe: each instance is accessed from one thread only
QString str1{"foo"};
QString str2{str1};
QtConcurrent::run([&]{ str1.apppend("bar"); });
str2.append("baz");
The original code can be fixed thus:
QString str{"Foo"};
for (int i = 0; i < 1000; ++i)
QtConcurrent::run([=]() mutable { str.append("bar"); });
This isn't to say that this code is very useful: the modified data is lost when the functor is destructed within the worker thread. But it serves to illustrate how to deal with Qt value types and multithreading. Here's why it works: copies of str are taken when each instance of the functor is constructed. This functor is then passed to a worker thread to execute, where its copy of the string is appended to. The copy initially shares data with the str instance in the originating thread, but QString will thread-safely duplicate the data. You could write out the functor explicitly to make it clear what happens:
QString str{"Foo"};
struct Functor {
QString str;
Functor(const QString & str) : str{str} {}
void operator()() {
str.append("bar");
}
};
for (int i = 0; i < 1000; ++i)
QtConcurrent::run(Functor(str));
How do we deal with passing data using Qt types in and out of a worker object? All communication with the object, when it is in the worker thread, must be done via signals/slots. Qt will automatically copy the data for us in a thread-safe manner so that each instance of a value is ever only accessed in one thread only. E.g.:
class ImageSource : public QObject {
QImage render() {
QImage image{...};
QPainter p{image};
...
return image;
}
public:
Q_SIGNAL newImage(const QImage & image);
void makeImage() {
QtConcurrent::run([this]{
emit newImage(render());
});
}
};
int main(int argc, char ** argv) {
QApplication app...;
ImageSource source;
QLabel label;
label.show();
connect(source, &ImageSource::newImage, &label, [&](const QImage & img){
label.setPixmap(QPixmap::fromImage(img));
});
source.makeImage();
return app.exec();
}
The connection between the source's signal and the label's thread context is automatic. The signal happens to be emitted in a worker thread in the default thread pool. At the time of signal emission, the source and target threads are compared, and if different, the functor will be wrapped in an event, the event posted the label, and the label's QObject::event will run the functor that sets the pixmap. This is all thread-safe and leverages Qt to make it almost effortless. The target thread context &label is critically important: without it, the functor would run in the worker thread, not the UI thread.
Note that we didn't even have to move the object to a worker thread: in fact, moving a QObject to a worker thread should be avoided unless the object does need to react to events and does more than merely generate a piece of data. You'd typically want to move e.g. objects that deal with communications, or complex application controllers that are abstracted from their UI. Mere generation of data can be usually done using QtConcurrent::run using a signal to abstract away the thread-safety magic of extracting the data from the worker thread to another thread.
In order to use Qt's mechanisms for passing data between threads with queues, you cannot call the object's function directly. You need to either use the signal/slot mechanism, or you can use the QMetaObject::invokeMethod call:
QMetaObject::invokeMethod(myObject, "mySlotFunction",
Qt::QueuedConnection,
Q_ARG(int, 42));
This will only work if both the sending and receiving objects have event queues running - i.e. a main or QThread based thread.
For the other part of your question, see the Qt docs section on reentrancy:
http://doc.qt.io/qt-4.8/threads-reentrancy.html#reentrant
Many Qt classes are reentrant, but they are not made thread-safe,
because making them thread-safe would incur the extra overhead of
repeatedly locking and unlocking a QMutex. For example, QString is
reentrant but not thread-safe. You can safely access different
instances of QString from multiple threads simultaneously, but you
can't safely access the same instance of QString from multiple threads
simultaneously (unless you protect the accesses yourself with a
QMutex).

QPointer in multi-threaded programs

According to http://doc.qt.io/qt-5/qpointer.html, QPointer is very useful. But I found it could be inefficient in the following context:
If I want to show label for three times or do something else, I have to use
if(label) label->show1();
if(label) label->show2();
if(label) label->show3();
instead of
if(label) { label->show1();label->show2();label->show3(); }
just because label might be destroyed in another thread after label->show1(); or label->show2();.
Is there a beautiful way other than three ifs to get the same functionality?
Another question is, when label is destroyed after if(label), is if(label) label->show1(); still wrong?
I don't have experience in multi-threaded programs. Any help is appreciated. ;)
I think the only safe way to do it is to make sure you only access your QWidgets from within the main/GUI thread (that is, the thread that is running Qt's event loop, inside QApplication::exec()).
If you have code that is running within a different thread, and that code wants the QLabels to be shown/hidden/whatever, then that code needs to create a QEvent object (or a subclass thereof) and call qApp->postEvent() to send that object to the main thread. Then when the Qt event loop picks up and handles that QEvent in the main thread, that is the point at which your code can safely do things to the QLabels.
Alternatively (and perhaps more simply), your thread's code could emit a cross-thread signal (as described here) and let Qt handle the event-posting internally. That might be better for your purpose.
Neither of your approaches is thread-safe. It's possible that your first thread will execute the if statement, then the other thread will delete your label, and then you will be inside of your if statement and crash.
Qt provides a number of thread synchronization constructs, you'll probably want to start with QMutex and learn more about thread-safety before you continue working on this program.
Using a mutex would make your function would look something like this:
mutex.lock();
label1->show();
label2->show();
label3->show();
mutex.unlock()
As long as your other thread is using locking that same mutex object then it will prevented from deleting your labels while you're showing them.

Basic QT Event handling / Threading questions?

Greetings ,
I am new to QT (4.6) and have some basic questions regarding its event mechanism.I come from Swing background so I am trying to compare it with QT.
1) Does Event-processing-loop run in seperate thread? (like EventDispatch thread in Swing) ?
2) If we open several 'QMainWindow' do they run in several threads?
3) Whats the best way to run an intensive process in a seperate thread? (like SwingWorker in Swing ? )
4) If intesive-process runs in a seperate thread ,is it possible to call UI methods like update(),repaint() from that process?
thanks in advance.
1 Event loop running in the same thread
2 All UI elements are living in the same thread the one in which your main() function executed.
3 There are QThread class which allows you to have a thread with separate event loop. There is QRunable abstract class to be able to run repeating long running tasks in separate threads using QThreadPool.
4 update() and repaint() are slots and the best way to call them from separate thread is to use queued connection with a signal in your object which lives in separate thread (read QObject::connect documentation anbout connection types)
You can find all necessary information by reading documentation of classes I've mentioned.

Can I use waitForReadyRead in a QThread that belongs to the mainthread?

I have a QThread that contains a QUDPsocket (socket is member not local to QThread::run(), maybe I should change that from what I am reading). This QThread is instantiated in my QMainWindow class ie the GUI thread(I am not calling move to thread). Is it still safe to use waitForReadyRead or do I absolutly need to instantiate the QThread in main.cpp or call moveToThread() for it to be thread safe. I am getting intermittent double free exception inside the call to waitForReadyRead in the present way of doing it(sometimes I dont get it for days sometimes after 3 minutes).
Have a look at the Qt documentation for QUdpSocket. There is a note there explaining the class is reentrant. Also from the Qt documentation:
...a class is said to be reentrant if its member functions can be called safely from multiple threads, as long as each thread uses a different instance of the class.
Thus, to answer your question, it does not really matter what the parent of the QThread is, as long as you make sure that the QUdpSocket instance you are using is instantiated within the context of the thread you are using it in.

Resources