Very often, programs have to use multithreading. Sometimes these are monstrous pools of threads with complex interactions, but more often this is simple code, the main requirement for which is not to freeze the interface.
PyQt has two main high-level threading tools: Python threading and Qt's QThread. For me, QThread turned out to be preferable due to better communication with the signal-slot mechanism in Qt.
So, the tool is selected, everything works fine, but over time I wanted to make working with threads somewhat easier and more convenient. There was an idea to make a
bike a module for working with streams for simple cases.
')
simple_thread
This
module is designed to work with threads in classes inherited from QObject. Using it, you can force any class method to run in a separate thread, while from the inside of the method you can access (albeit limited) the attributes and methods of the class.
Let's look at a simple example:
The class
Foo is inherited from
QLabel , and we want to change the text of the label once every half second, without freezing the interface. With the text output we will deal with the
bar method. To make this method work in a separate thread, before declaring the method, set the decorator
@SimpleThread .
From the inside of the method, we will need access to the
digits attribute - a list of output words. We also need to update the label text, for this we will call the
setText method.
Access to attributes
The first problem is that
digits can be used not only by this thread.
The simple_thread module allows you to work around this problem. When we receive an attribute, it is not a link to this attribute that is returned to us, but a copy of it. In this case, all actions occur in the context of the main thread, and we do not need to worry about simultaneous access to an attribute from several threads.
In this way, you can refer to lists, dictionaries, and all immutable class attributes (strings, tuples, etc.).
There is one thing here - it works rather slowly, so you should not overdo it with access to attributes.
Method call
Problem number two is a call to the
setText method. The problem is similar to the first one - Qt will throw an exception when trying to access a graphics class method not from the main thread. As in the first case, this is solved by suspending our thread and calling the method from the main thread.
There are three different ways to invoke methods, depending on the
thr_method argument:
- thr_method = 'b' : when calling a method, our thread is suspended until the method ends. The execution of the method occurs in the main thread. Only in this variant, get the return value of the method;
- thr_method = 'q' : in this case, our thread does not stop. The method is also executed in the main thread;
- thr_method = None or not set : the method is executed in the context of our thread. Here you need to be careful - all restrictions related to multithreading take effect, in particular, the method cannot refer to class attributes.
The thr_method argument is not passed to the method being called.
Setting Attributes
Setting attributes from another thread is also possible.
self.newAttr = 'text'
Attributes can be of any type.
As in the other cases, all work is carried out in the main thread and does not cause problems, it is worth remembering that by setting the attribute, you can wipe the same name as the existing one.
Start stream
To run our code, we simply execute the
bar method, specifying the argument
thr_start = True , so that the thread starts immediately.
There is another way, it is useful if we want to process signals called from another thread or signals of the
QThread class (
started ,
finished ,
terminated ).
thread = foo.bar('From thread') thread.finished.connect(self.barFinished) thread.start()
Here we have connected the finished signal of the
finished thread to the
barFinished method and started the stream.
Stop flow
If for some reason we need to stop the running thread, we can do this by calling the
thr_stop method.
thread = foo.bar('From thread', thr_start = True) ... thread.thr_stop()
This method sets the
thr_stopFlag = True flag, the state of which we should monitor in our method and, if true, terminate the work of our method.
It is worth noting that the
thr_stop method
does not wait for the thread to stop, returning control immediately. If we need to wait for the thread to finish working, after
thr_stop, we need to call the
wait method.
The simple_thread module has two functions for stopping all active threads -
terminateThreads and
closeThreads . The first function rigidly interrupts the execution of all threads, which may be unsafe. The second function works as if we called
thr_stop for each thread, and then
wait .
All comments, suggestions and the like are welcome. If something like this has already been implemented by someone, I will be happy to link.