Templates & tools to develop Qt GUIs in Python.
One use case is accepting user input while running another task asynchronously (so that the UI is still responsive).
UIFormWidget
: a class to help creating Qt forms programmatically, useable inQDockWidgets
andQWidget
FormDialog
: aQDialog
with a form inside with OK and Cancel buttonsWorker
: a class that defines aQRunnable
to handle worker thread setup, signals and wrap up
Via pip
/conda
/mamba
, i.e. any of the following:
python -m pip install eqt
conda install -c conda-forge eqt
mamba install -c conda-forge eqt
See the examples
directory, e.g. how to launch a QDialog
with a form inside using eqt
's QWidget
or FormDialog
.
To run a function in a separate thread we use a Worker
which is a subclass of a QRunnable
.
For the Worker
to work one needs to define:
- the function that does what you need
- Optional callback methods to get the status of the thread by means of
QtCore.QSignal
s
On initialisation of the Worker
the user needs to pass the function that has to run in the thread, i.e. fn
below, and additional optional positional and keyword arguments, which will be passed on to the actual function that is run in the QRunnable
.
class Worker(QtCore.QRunnable):
def __init__(self, fn, *args, **kwargs):
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
In practice the user will need to pass to the Worker
as many parameters as there are listed in the function to be run.
result = self.fn(*self.args, **self.kwargs)
But Worker
will add to the **kwargs
the following QSignal
.
# Add progress callback to kwargs
self.kwargs['progress_callback'] = self.signals.progress
self.kwargs['message_callback'] = self.signals.message
self.kwargs['status_callback'] = self.signals.status
Therefore it is advisable to always have **kwargs
in the function fn
signature so that you can access the QSignal
and emit the signal required. For instance one could emit a progress by:
def fn(num_iter, **kwargs):
progress_callback = kwargs.get('progress_callback', None)
for i in range(num_iter):
do_something
if progress_callback is not None:
progress_callback.emit( i )
This is done just after one has defined the Worker
:
def handle_progress(num_iter):
# do something with the progress
print ("Current progress is ", num_iter)
worker = Worker(fn, 10)
worker.signals.progress.connect(handle_progress)
So, each time fn
comes to progress_callback.emit( i )
the function handle_progress
will be called with the parameter i
of its for
loop.
The signals that are available in the Worker
class are defined in WorkerSignal
and are the following. Below you can also see the type of data that each signal can emit.
finished = QtCore.Signal()
error = QtCore.Signal(tuple)
result = QtCore.Signal(object)
progress = QtCore.Signal(int)
message = QtCore.Signal(str)
status = QtCore.Signal(tuple)
Read more on Qt signals and slots and on how to use them in PySide2.
See CONTRIBUTING.md.