Plugin windows (Plugin Window SPI)
Third parties can create QML windows that are multi-instance, positionable, always-on-top, click-through and capture-excluded, without any host source changes. Use cases: OCR translation overlays, standalone assistant windows, tool HUDs, floating hint boards.
Use this page's API for functional persistent windows; for theme/decorative slot replacement see UI slots.
Create
python
host.create_plugin_window(
plugin_id: str,
qml_path: str, # path relative to the plugin package, or a file:// URL
*,
window_id: str = "", # auto-generated when omitted; recreating the same ID closes the old window first
title: str = "",
x: int | None = None,
y: int | None = None,
width: int = 480,
height: int = 320,
always_on_top: bool = True,
frameless: bool = False,
click_through: bool = False,
exclude_from_capture: bool = False,
context: dict | None = None, # extra properties injected into the root object
) -> str # window_id; "" on failureControl
python
host.show_plugin_window(window_id: str, visible: bool = True) -> bool
host.close_plugin_window(window_id: str) -> bool
host.close_plugin_windows(plugin_id: str | None = None) -> bool
host.move_plugin_window(window_id: str, x: int, y: int) -> bool
host.resize_plugin_window(window_id: str, width: int, height: int) -> bool
host.set_plugin_window_flags(
window_id: str,
*,
always_on_top: bool | None = None,
click_through: bool | None = None,
exclude_from_capture: bool | None = None,
) -> bool
host.list_plugin_windows(plugin_id: str = "") -> list[dict]
host.get_plugin_window(window_id: str) -> dictQML conventions
- The root element can be:
Window— fully self-managed (you setvisible/ position / flags yourself);Item— the host wraps it in aPluginWindowShellautomatically (recommended: less work, consistent styling).
- The host injects:
pluginId,windowId,bridge, plus every key fromcontext. - Frameless windows handle their own dragging.
click_through/exclude_from_captureuse host platform capabilities (the latter isSetWindowDisplayAffinity, see Speech & feedback).- A plugin's
shutdown()should callclose_plugin_windows(self.id); a destroyed window leaves the registry automatically.
Minimal example
python
# A positioned overlay window (e.g. OCR translation)
wid = self.host.create_plugin_window(
self.id, "Overlay.qml",
title="OCR",
x=100, y=80, width=420, height=120,
always_on_top=True, frameless=True,
click_through=True,
exclude_from_capture=True,
context={"scale": 1.0},
)
if wid:
self.host.move_plugin_window(wid, 100, 80)qml
// Overlay.qml (root is an Item; the host wraps it in the shell)
import QtQuick 2.15
Item {
property string pluginId: ""
property string windowId: ""
property var bridge: null
property real scale: 1.0 // ← injected via context
Text { text: "识别结果…"; color: "white" }
}How this differs from UI slots
| Plugin windows | UI slots | |
|---|---|---|
| Count | Multiple | Usually a single instance (same-level slots are mutually exclusive) |
| Position | Freely positioned/resized at runtime | Decided by the host layout |
| Typical use | Tool HUD, standalone assistant, overlay | Swap backplate / VU meter / caption view |
| Declaration | Code call | ui_extensions in mod.json |
Related
- UI slots
- Speech & feedback — capture exclusion
- File pickers & long tasks
- tool
