Metadata-Version: 2.1
Name: eqt
Version: 0.7.1
Summary: A number of templates and tools to develop Qt GUIs with Python effectively
Author-email: Edoardo Pasca <edoardo.pasca@stfc.ac.uk>, Laura Murgatroyd <laura.murgatroyd@stfc.ac.uk>
Maintainer-email: Casper da Costa-Luis <casper.dcl@physics.org>
License: Apache-2.0
Project-URL: documentation, https://github.com/TomographicImaging/eqt#readme
Project-URL: repository, https://github.com/TomographicImaging/eqt
Project-URL: changelog, https://github.com/TomographicImaging/eqt/releases
Keywords: Qt
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyside2
Requires-Dist: qdarkstyle
Provides-Extra: dev
Requires-Dist: pytest >=6 ; extra == 'dev'
Requires-Dist: pytest-cov ; extra == 'dev'
Requires-Dist: pytest-timeout ; extra == 'dev'

# eqt: Qt Elements

[![Tests](https://img.shields.io/github/actions/workflow/status/TomographicImaging/eqt/test.yml?branch=main&label=Tests&logo=GitHub)](https://github.com/casperdcl/cdcl/actions)

A number of templates and tools to develop Qt GUI's with Python effectively.

Developing GUIs I often find myself creating forms to pass some input and also
running some task asynchronously so that the interface is still responsive.

This little package tries to address both recurring requirements.

1. `UIFormWidget`, a class to help creating Qt forms
   programmatically, useable in `QDockWidgets` and `QWidget`
2. `FormDialog`, a `QDialog` with a form inside with OK and Cancel button
3. `Worker`, a class that defines a `QRunnable` to
   handle worker thread setup, signals and wrap up

## Installation

You may install via `pip` or `conda`

```bash
python -m pip install eqt
# or
conda install eqt -c paskino
```

## Examples

In the `example` directory there is an example on how to launch a `QDialog` with a form inside using `eqt`'s [`QWidget`](examples/dialog_example.py) or [`FormDialog`](examples/dialog_example_2.py).

### Running asynchronous tasks

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:

1. the function that does what you need
2. Optional callback methods to get the status of the thread by means of `QtCore.QSignal`s

On [initialisation](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L32-L38) 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`.

```python
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](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L56) to be run.

```python
result = self.fn(*self.args, **self.kwargs)
```

But `Worker` will [add](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L41-L43) to the `**kwargs` the following `QSignal`.

```python
        # 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:

```python
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 )
```

### Passing a signal to a Worker

This is done just after one has defined the `Worker`:

```python
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.

### Signals available

The signals that are available in the `Worker` class are defined in [`WorkerSignal`](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L66) and are the following. Below you can also see the type of data that each signal can emit.

```python
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](https://doc.qt.io/qt-5/signalsandslots.html) and on how to use them in [pyside2](https://wiki.qt.io/Qt_for_Python_Signals_and_Slots).
