Skip to content

GUI Skills (GUIInterface)

In a nutshell

When an OVOS device has a screen, a skill can show things on it: text, images, a page of results. This page describes the small Python toolkit a skill author uses to push data to the screen and ask for a page to be displayed. It is a developer topic, and the screen system is being rebuilt (see GUI Adapters). Voice should stay the main way you interact, while the screen is a bonus. See the Glossary for terms.

The OVOS GUI is deprecated — see Screens on OVOS Today for the full picture

This page documents the legacy skill GUI API. There is no generally usable OVOS GUI right now, and a replacement is Upcoming. Voice should remain the primary interface.

📐 Formal specification

The forward model a skill targets is OVOS-GUI-1 — GUI Display Subsystem (a formal architecture spec). Under it, self.gui declares display intent by naming a template from the closed SYSTEM_* vocabulary (SYSTEM_text, SYSTEM_image, SYSTEM_list, SYSTEM_weather, SYSTEM_confirm, …) and pushing flat session-data. Interchangeable render backends draw it, routed by session_id. Two rules matter for skill authors. A skill MUST NOT invent template names or ship its own home/resting screen (the resting display is owned entirely by the backend, §6.9). Image keys carry an http(s) URL or data: URI, never a local filesystem path. The show_* helpers below map onto these templates. The custom-.qml path is legacy and unsupported under OVOS-GUI-1. Where this page and the spec differ, the spec is the canonical target.

Many OVOS devices have a screen. A skill can drive that screen the same way it speaks: through a small Python API. You set some values, ask for a page to be shown, and any connected GUI client (Qt shell, web, terminal, ...) renders it.

In plain terms: self.gui is a dict-like object. You put data in it (self.gui["name"] = "OVOS"), then call a show_* method to display a page.

Under the hood, self.gui is a SkillGUI instance (a subclass of GUIInterface) created automatically for every OVOSSkill. On current ovos-workshop the base class lives in ovos_bus_client.apis.gui.GUIInterface. The skill wrapper is ovos_workshop.skills.ovos.SkillGUI, namespaced to your skill_id.

Upcoming — unreleased

A planned breaking change rebinds self.gui to a GUIInterface from the standalone ovos-gui-api-client package (instead of ovos_bus_client.apis.gui) and drops the ui_directories constructor argument (today, ui_directories is a dict mapping a framework name, e.g. "qt5", to its local resource directory, which GUIInterface searches when resolving a relative image/page name to a file on disk), since skills under the GUI rework no longer ship QML. This is not on a released ovos-workshop. On stable installs self.gui is still the ovos_bus_client.apis.gui.GUIInterface-based SkillGUI described above. Tracked in ovos-workshop#420.

Quick start

def handle_hello(self, message):
    self.gui["name"] = "OpenVoiceOS"
    self.gui.show_text("Hello from OVOS!")

self.gui behaves like a dictionary. Values you set are synced to the active page whenever it (re)renders. The keys you set are visible to the page under the skill's namespace.

You should see

On a device or emulator with a GUI client connected, triggering handle_hello should pop up a text page reading "Hello from OVOS!". If nothing appears, confirm a GUI client (e.g. ovos-gui or the browser-based ovos-gui-plugin-shell-companion — warning: archived, deprecated; superseded by pyhtmx-gui-client) is connected to the same messagebus as the skill.


Standard page templates

To get a unified look and feel without writing any UI code, GUIInterface provides ready-made SYSTEM_* page templates. You call a helper, it sets the needed values and shows the matching template. These render on every GUI client according to its local capabilities.

Text

Display simple strings of text (auto-paginated).

gui.show_text(text, title=None, override_idle=None, override_animations=False)

Static Image

Display a static image such as a jpeg or png.

gui.show_image(url, caption=None, title=None, fill=None,
               background_color=None, override_idle=None,
               override_animations=False)

fill accepts "PreserveAspectFit", "PreserveAspectCrop", or "Stretch". url may be a local file path or an http(s) URL. Missing local files are logged and the call returns without showing anything.

Local paths work today, but the spec says not to rely on them

The installed show_image() (ovos_bus_client.apis.gui.GUIInterface) does accept and resolve a local filesystem path. That is the current, working behavior described above. The OVOS-GUI-1 spec box further up this page nonetheless says image keys should carry only an http(s) URL or data: URI, never a local path. That is the forward-looking contract for interchangeable render backends, not a rule the current code enforces. Prefer a URL or data: URI in new skills if you want to stay forward-compatible.

Animated Image

Display an animated image such as a gif (same arguments as show_image).

gui.show_animated_image(url, caption=None, title=None, fill=None,
                        background_color=None, override_idle=None,
                        override_animations=False)

HTML Snippet

Display a local HTML snippet. Complex JavaScript may not be supported by all clients.

gui.show_html(html, resource_url=None, override_idle=None,
              override_animations=False)

Remote URL

Display a webpage. Only supported by clients with a full browser engine.

gui.show_url(url, override_idle=None, override_animations=False)

Input Box

Show a fullscreen text-entry UI with confirm/cancel buttons.

gui.show_input_box(title=None, placeholder=None, confirm_text=None,
                   exit_text=None, override_idle=None,
                   override_animations=False)

Notifications

gui.show_notification(content, duration=10, action=None,
                      noticetype="transient", style="info", callback_data=None)
gui.show_controlled_notification(content, style="info")
gui.remove_controlled_notification()

style is one of info, warning, success, error. noticetype is transient (auto-timeout) or sticky.

override_idle

override_idle=True keeps your page up indefinitely. An int delays the return to the idle/home screen for that many seconds. override_animations=True disables platform transition animations for the page.

The shipped GUIInterface also carries three animation helpers beyond the templates above: show_face(awake=True, ...) (the classic assistant face), show_loading_animation(text, ...), and show_status_animation(text, success, ...).

Upcoming — a wider template surface in the standalone package

The templates above are what today's ovos_bus_client.apis.gui.GUIInterface ships. The forward-looking standalone ovos-gui-api-client GUIInterface (the package ovos-workshop#420 rebinds self.gui to) adds more SYSTEM_* helpers: show_list, show_grid, show_table, show_weather, show_clock, show_timer, show_map, show_media_player, and the two voice-first dialogue helpers show_confirm(question, ...) and show_select(items, prompt=None, ...). These are not on a released ovos-workshop. Treat them as the template set new skills will target once the rebind lands.

Two behavioural notes for that package:

  • fill vocabulary changes. The standalone FillMode enum uses "fit" / "crop" / "stretch", not the "PreserveAspectFit" / "PreserveAspectCrop" / "Stretch" values the installed ovos_bus_client show_image() expects. The value set flips when self.gui rebinds.
  • Dialogue helpers round-trip. These are visual accompaniments to a spoken question. The skill must still ask and handle the voice response, and must not block on a GUI event. Where the display layer supports a touch shortcut, show_confirm fires <skill_id>.confirm.response with {"confirmed": bool} and show_select fires <skill_id>.select.response with {"value": ...}. The interface also auto-registers an inbound <skill_id>.set handler so the display layer can push value changes back into the skill's session data.

Custom pages

Templates cover common cases, but you can also ship your own pages. Place a gui/ folder in your skill with your UI files and request them by name:

def handle_food_places(self, message):
    self.gui["foodPlaces"] = results
    self.gui.show_page("foodplaces")   # resolves your gui/ resource

For the Qt/QML client, custom pages are .qml files. See Mycroft-GUI QT5 for the QML component reference, theming, event handling, and resting faces. Other clients resolve the same logical page name to their own format.

Useful page-control methods:

gui.show_pages(page_names, index=0, override_idle=None,
               override_animations=False, remove_others=False)
gui.remove_page(page)
gui.remove_pages(page_names)
gui.clear()     # reset values + pages (does NOT close the screen)
gui.release()   # tell the platform the skill is done with the screen

Usage in Skills

self.gui provides a GUIInterface under the self.skill_id namespace, so two skills setting self.gui["name"] never collide.

def handle_hello(self, message):
    self.gui["name"] = "OpenVoiceOS"
    self.gui.show_text("Hello from OVOS!")

To react to clicks/events coming back from the page, register a bus handler:

def initialize(self):
    self.gui.register_handler("skill.foo.event", self.handle_foo_event)

def handle_foo_event(self, message):
    self.speak(message.data["string"])

For OCP media players and idle/home screens see the GUI Service Reference.


Read next: Runtime Requirements in OVOS Related: Resource Files · Screens on OVOS Today · GUI Service (ovos-gui) · GUI Adapter Plugins