Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin inheritance from pupil plugins #663

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions core/developer/plugin-api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
For the plugin development process, we recommend to [run from source](/developer/#running-from-source).

### Language
Pupil is written in `Python 3.6`, but no "heavy lifting" is done in Python. High performance computer vision, media compression, display libraries, and custom functions are written in external libraries or c/c++ and accessed though [cython](http://cython.org/). Python plays the role of "glue" that sticks all the pieces together.

Check warning on line 38 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (cython)

We also like writing code in Python because it's *quick and easy* to move from initial idea to working proof-of-concept. If proof-of-concept code is slow, optimization and performance enhancement can happen in iterations of code.

Expand Down Expand Up @@ -104,7 +104,7 @@
|--------------|--------------------------------------------------------------------------------------------------------|---------------|----------------------------------------------------------------------|
| `uniqueness` | `"not_unique"`, `"by_class"`, `"by_base_class"` | `"by_class"` | Plugin instance replacement behavior. See below. |
| `order` | float in the range of [0.0, 1.0] | `0.5` | Defines the order in which plugins are loaded and called |
| `icon_font` | `"roboto"`, `"pupil_icons"`, `"opensans"`, any other font registered via `Plugin.g_pool.ui.add_font()` | `"roboto"` | Menu icon font |

Check warning on line 107 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (roboto)

Check warning on line 107 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (opensans)

Check warning on line 107 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (roboto)
| `icon_chr` | Any string whose letters are present in `icon_font`. Recommended to use a single letter string. | `"?"` | Menu icon |
| `alive` | `True`, `False` | `True` | Setting to `False` will shutdown the plugin in the next event cycle. |

Expand All @@ -115,7 +115,7 @@
- *unique by class* - only one plugin instance can exist at a time, e.g. blink
detector
- *unique by base class* - if two plugins share the same base class they are only
allowed to be active one at a time. Calibration choreographies are examples of *unique by base class* plugins.

Check warning on line 118 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (choreographies)
They each implement a separate *by-base-class*-unique plugin but since they all share the
same base class (`CalibrationChoreographyPlugin`) only one can be active at a time.

Expand All @@ -133,11 +133,11 @@

| Callback | Description |
|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `__init__(self, g_pool, **kwargs)` | Called when a plugin instance is started. `g_pool` provides access to the application. Calling `super().__init__(g_pool)` is strongly recommended. `kwargs` can be used for user preferences. See example below. |

Check warning on line 136 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (kwargs)

Check warning on line 136 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (kwargs)
| `init_ui(self)` | Called after `__init__` if the calling process provides a user interface. Allows the plugin to setup its settings menu, quick access buttons, etc. |
| `cleanup(self)` | Called when `Plugin.alive` is set to `True`, i.e. on application shutdown or if the plugin is being disabled |
| `deinit_ui(self)` | Called before `cleanup` and the calling process provides a user interface. The plugin is responsible for removing any UI elements added in `init_ui`. |

Check warning on line 139 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (deinit)
| `get_init_dict(self)` | Called on each active plugin instance on application shutdown. Returns a dictionary which is stored in the application's persistent session settings. On the next application launch, all previously active plugins will be restored by calling `__init__` and passing the dictionary as the `kwargs` arguments. See example below. |

Check warning on line 140 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (kwargs)

A typical use case of the session settings is to persistently store plugin parameters,
e.g. the minimum duration parameter of the fixation detector.
Expand All @@ -163,7 +163,7 @@

::: warning
The top-level keys of the `get_init_dict` dictionary must be of type `str` and its
values must be primitive Python types that can be encoded by [msgpack](https://msgpack.org/).

Check warning on line 166 in core/developer/plugin-api/index.md

View workflow job for this annotation

GitHub Actions / ✍️ Check spelling

Unknown word (msgpack)
:::

#### Processing callbacks
Expand Down Expand Up @@ -272,4 +272,74 @@
pass
```

### Plugin inheritance from Pupil Plugins

If you want to build upon existing solutions, you can create a plugin by inheritance from an existing Pupil Plugin. This development alternative requires an import of all necessary dependencies to the Plugin's scope.

The following template shows how to inherit from the `Surface_Tracker` class, the base class of Pupil's `Surface Tracker` plugin. A quick look at the source code shows that this class lives inside the `surface_tracker.surface_tracker` module. So, you can import it this way:

```python
from surface_tracker.surface_tracker import Surface_Tracker
from surface_tracker.surface_online import Surface_Online
from surface_tracker.gui import Heatmap_Mode

class Custom_Surface_Tracker_Online(Surface_Tracker):
"""
Describe your plugin here
"""
@classmethod
def parse_pretty_class_name(cls) -> str:
"""
A pretty name to show at the plugin manager list
"""
return "Custom Surface Tracker"

def __init__(self, g_pool, *args, **kwargs):
self.freeze_scene = False
self.frozen_scene_frame = None
self.frozen_scene_tex = None
super().__init__(g_pool, *args, use_online_detection=True, **kwargs)

self.menu = None
self.button = None
self.add_button = None

@property
def Surface_Class(self):
"""
A plugin for surfaces running in pupil capture
"""
return Surface_Online

@property
def supported_heatmap_modes(self):
"""
Heatmap mode
"""
return [Heatmap_Mode.WITHIN_SURFACE]

@property
def ui_info_text(self):
return (
"Write your presentation text here"
)

del Surface_Tracker # to avoid duplicates at the plugin manager list
```
::: tip
If you are not sure where a class lives to import it, you can figure it out using Python instrospection utilities. For example:

```python
# {desired-settings-path}/plugin/MyPlugin.py

# to inspect top-level modules
import pkgutil
print([tup[1] for tup in pkgutil.iter_modules()])

# to inspect a specific module
import surface_tracker
print(dir(surface_tracker))
```
:::

See examples of custom pupil detection plugins [here](https://github.com/pupil-labs/pupil-community/blob/master/README.md#pupil-detector-plugins).
Loading