Skip to content

The MixlarPlugin API

Every Mixlar plugin is a single Python class that subclasses MixlarPlugin. You set a handful of metadata attributes, then override only the hooks you actually use — everything else has a safe, do-nothing default.

from mixlar import MixlarPlugin
class MyPlugin(MixlarPlugin):
plugin_id = "my_plugin"
plugin_name = "My Plugin"
plugin_version = "1.0.0"
plugin_author = "You"
plugin_description = "What it does."
plugin_icon = "fa5s.puzzle-piece" # qtawesome name
plugin_icon_color = "#FF4D14"
widget_id = "my_widget" # pairs with widgets/my_widget/
Attribute Default Meaning
plugin_id "" Unique id; should match plugin.json’s "id"
plugin_name "Unnamed Plugin" Display name (manifest "name" wins if both are set)
plugin_version "0.0.0" Manifest "version" wins
plugin_author "" Shown on the plugin card
plugin_description "" Shown on the plugin card
plugin_icon "fa5s.puzzle-piece" qtawesome icon name
plugin_icon_color "" Hex color for the icon
plugin_icons_dir "" Folder of PNGs installed into the app’s icon set
widget_id "" Pairs with widgets/<widget_id>/widget.json; namespaces every push_widget_data key

See Plugin Packages for how these interact with plugin.json.

Method Signature Purpose
on_load (self, settings) -> None Called once at startup with the host settings object. The base implementation stashes it as self._settings for _pget/_pset
on_unload (self) -> None Called on app shutdown / warm-reload

Four methods the app’s macro editor and executor call. You can override them directly, or declare actions once with MacroActions and @action (below) and skip writing them by hand.

Method Signature Purpose
get_macro_actions (self) -> list[(id, name)] Actions offered in the macro step picker
get_macro_action_groups (self) -> list[(group, [(id, name), ...])] Same, bucketed into tabs
get_macro_action_icons (self) -> dict[id, "fa5s.icon"] Per-action icons
get_macro_step_defaults (self) -> dict Default fields for a newly-added step
execute_macro_step (self, step: dict) -> None Run the action (called off the GUI thread)
describe_macro_step (self, step: dict) -> str One-line summary shown in the step list
build_macro_editor (self, step, row, editor_lay, upd, upd_refresh, lbl) -> None Add custom Qt editor widgets under the action selector
prime_client (self, settings) -> None Pre-init a client (e.g. open a socket) on the main thread before execution
from mixlar import MixlarPlugin
from mixlar.macros import MacroActions, action
class MyPlugin(MacroActions, MixlarPlugin):
plugin_id = "my_plugin"
@action("mute", "Mute Mic", icon="fa5s.microphone-slash", group="Audio")
def _mute(self, step):
...

Put MacroActions ahead of MixlarPlugin in the base list so its methods win the MRO. It scans the class once (cached) for @action-decorated methods and derives get_macro_actions, get_macro_action_groups, get_macro_action_icons, and execute_macro_step from them — dispatch reads step["<plugin_id>_action"] (the key convention the app’s built-in editor writes), falling back to a plain "action" key. Override any of the four methods directly on your subclass and that override wins — nothing here is mandatory, it’s purely a shortcut for the common case.

See Macros for the full walkthrough.

Method Signature Purpose
get_slider_mode (self) -> (mode_id, name) | None Opt a fader into this plugin’s mode
build_slider_config_ui (self, idx, parent_layout, settings) -> None Build the fader’s config panel
save_slider_config (self, idx) -> dict Settings to persist for that fader
handle_slider_value (self, idx: int, value: int, pct: float) -> None Fader moved (value 0-100, pct 0.0-1.0)
get_slider_color (self, idx) -> str | None Fixed hex color for the fader, or default
from mixlar import MixlarPlugin
from mixlar.sliders import SliderMode
class MyPlugin(SliderMode, MixlarPlugin):
plugin_id = "my_plugin"
slider_mode_id = "my_plugin_slider"
slider_mode_name = "My Plugin"
slider_color = "#0a84ff"
def handle_slider_value(self, idx, value, pct):
... # you still implement this — it's the one hook with real side effects

SliderMode derives get_slider_mode/get_slider_color from plain class attributes. Leave slider_mode_id blank (the default) to opt out entirely — get_slider_mode() then returns None, same as the base class.

See Sliders for the full walkthrough.

Method Signature Purpose
build_settings_ui (self, layout, settings) -> None Build a Qt settings panel (imperative path)
has_settings_ui (self) -> bool True if build_settings_ui is overridden

For a declarative alternative that needs no Qt code, ship a settings.schema.json instead — see Settings.

Method Signature Purpose
on_widget_event (self, widget_id, element_id, action) -> None An interactive element fired ("press"/"hold" for buttons, "on"/"off" for toggles, a value-carrying action for sliders). Broadcast to every plugin; filter by the ids you own
on_widget_shown (self, widget_id) -> None Your paired widget came on screen — push full state, start timers
on_widget_hidden (self, widget_id) -> None Your paired widget left the screen — go quiet, stop timers
widget_bundle_dir (self) -> str | None Path to <plugin dir>/widgets/<widget_id>/, if it exists
widget_bundle_dirs (self) -> list[(widget_id, dir)] Every bundled widget declared in the manifest
push_widget_data (self, key, value, widget=None) -> bool Push a live value; namespaced <widget_id>.<key> automatically; False no-op with no device attached
push_widget_image (self, key, source, w, h, widget=None) -> bool Stream an image to an img element; blocks — call from a worker thread

Full widget-authoring detail, including the element reference and the wire protocol, lives under Widgets — see in particular Elements and Data & Events.

from mixlar import MixlarPlugin
from mixlar.macros import MacroActions
from mixlar.events import SystemEventsMixin
class MyPlugin(SystemEventsMixin, MacroActions, MixlarPlugin):
def on_device_connect(self):
self.push_widget_data("status", "online")

Six no-op-by-default hooks: on_app_launch(exe), on_app_terminate(exe), on_system_wake(), on_device_connect(), on_device_disconnect(), and on_deep_link(url). Override only what you need.

Method Signature Purpose
_pget (self, key, default="") -> Any Read a namespaced setting (plugin_<id>_<key>); goes through the host settings object when present, an in-memory dict off-app
_pset (self, key, value) -> None Write a namespaced setting, same storage

These are the low-level primitives build_settings_ui and a settings.schema.json-driven UI both end up writing through — reach for them directly for anything not covered by the declarative schema.

  1. Subclass MixlarPlugin and set the metadata attributes.
  2. Mix in MacroActions, SliderMode, and/or SystemEventsMixin ahead of MixlarPlugin in the base list, depending on which surfaces you use.
  3. Override only the hooks you need — every other method already has a safe default.
  4. Validate and run it locally with the Mixlar CLI before packaging and signing it — see Pack, Sign & Publish.