Runtime engine for Python GUI apps with a shared application context.
Qyro Engine currently provides:
- A runtime container that wires settings, resource lookup, state store, telemetry hook, and UI adapter.
- A single ApplicationContext API for Qt, Tkinter, and Kivy style apps.
- Automatic settings loading from JSON files.
- Resource resolution that works in source mode and frozen mode.
- A small reactive state/signal API.
- A component lifecycle mixin for UI classes.
Qyro Engine is not the build/distribution CLI.
- Build, bundle, signing, notarization: qyro-cli concern.
- Hot reloading: not built into qyro-engine.
- Web bridge APIs from legacy PPG examples: not part of qyro-engine public API.
Framework adapters available in the engine:
- PySide6
- PyQt6
- PySide2
- PyQt5
- Kivy
- Tkinter
- Headless fallback
Adapter selection behavior:
- If binding/framework is provided in settings, it is used.
- Otherwise the engine auto-detects in this order: PySide6 -> PyQt6 -> PySide2 -> PyQt5 -> Kivy -> Tkinter -> Headless
This package is configured with Poetry extras.
Base package:
poetry add qyro-engineWith specific GUI stack:
poetry add qyro-engine -E pyside6
poetry add qyro-engine -E pyqt6
poetry add qyro-engine -E pyside2
poetry add qyro-engine -E pyqt5
poetry add qyro-engine -E kivyWith telemetry helper:
poetry add qyro-engine -E sentryEverything enabled:
poetry add qyro-engine -E allQyro Engine looks for settings and resources in common locations.
Settings discovery (first directory that contains base.json):
- settings/
- build/settings/
- src/main/settings/
- src/build/settings/
- project root
Resources are resolved from OS-specific folders first, then base folders. Typical layout:
my-app/
├─ main.py
├─ settings/
│ ├─ base.json
│ ├─ windows.json
│ ├─ mac.json
│ └─ linux.json
└─ resources/
├─ base/
├─ windows/
├─ mac/
└─ linux/
import sys
from PySide6.QtWidgets import QMainWindow, QLabel
from qyro_engine import ApplicationContext
from qyro_engine.ui.component import Component
class MyWindow(QMainWindow, Component, ApplicationContext):
def component_will_mount(self):
self.resize(640, 480)
def render(self):
label = QLabel(
f"App: {self.window_title}\n"
f"Platform: {self.platform.value}\n"
f"Frozen: {self.is_frozen}",
parent=self,
)
label.move(24, 24)
if __name__ == "__main__":
window = MyWindow()
window.show()
sys.exit(window.exec())import tkinter as tk
from qyro_engine import ApplicationContext
from qyro_engine.ui.component import Component
class MyTkApp(tk.Tk, Component, ApplicationContext):
def component_will_mount(self):
self.geometry("480x240")
def render(self):
tk.Label(
self,
text=f"{self.window_title} | {self.platform.value} | frozen={self.is_frozen}",
).pack(padx=16, pady=16)
if __name__ == "__main__":
app = MyTkApp()
app.exec()This section documents the public runtime API that is available in the current codebase.
Exports:
- ApplicationContext
- Component
- init_lifecycle
- PPGLifeCycle (compat alias of Component)
- EngineContainer
- PlatformDetector
- PlatformType
- ExecutionMode
- AppMetadata
- QyroEngineError
- ResourceNotFoundError
- SettingsNotFoundError
- FrameworkNotAvailableError
- get_resource
- load_build_settings
- is_frozen
- app_is_frozen (compat alias)
Function signatures:
def get_resource(*segments: str, required: bool = True) -> str
def load_build_settings() -> dict
def is_frozen() -> boolBehavior notes:
- get_resource returns an absolute path string.
- If required=True and a resource is not found, the resolver may raise a runtime error.
- app_is_frozen is an alias kept for compatibility.
Constructor:
ApplicationContext(
framework: str | None = None,
custom_root: Path | None = None,
enable_sentry: bool = True,
initial_state: dict[str, Any] | None = None,
argv: list[str] | None = None,
*args,
**kwargs,
)Parameter reference:
| Parameter | Type | Default | Description |
|---|---|---|---|
| framework | str | None | None | Forces a specific adapter (for example: pyside6, pyqt6, kivy, tkinter, headless). |
| custom_root | Path | None | None | Overrides project root used by settings and resource resolvers. |
| enable_sentry | bool | True | Enables sentry hook only if sentry_dsn is available in loaded settings. |
| initial_state | dict | None | None | Initial key/value snapshot for reactive state store. |
| argv | list[str] | None | None | Optional argv passed to framework app creation. |
Property reference:
| Property | Type | Description |
|---|---|---|
| container | EngineContainer | Underlying dependency container instance. |
| app | Any | Native framework app instance (QApplication, Tk root, Kivy app, etc.). |
| metadata | AppMetadata | App metadata object loaded from settings. |
| app_settings | dict[str, Any] | Final merged settings dictionary. |
| is_frozen | bool | True when running from a frozen bundle. |
| platform | PlatformType | Detected platform enum. |
| execution_mode | ExecutionMode | Source/frozen execution mode enum. |
| window_title | str | Current window title, with getter/setter behavior. |
| app_icon | str | None | Resolved app icon path, with setter support. |
Method reference:
| Method | Signature | Returns | Notes |
|---|---|---|---|
| get_default_window_title | get_default_window_title() |
str | Uses metadata/app_name fallback chain. |
| get_window_title | get_window_title() |
str | Reads current title from native window when possible. |
| set_window_title | set_window_title(title, window=None) |
bool | Best-effort cross-toolkit title assignment. |
| get_app_icon_path | get_app_icon_path() |
str | None | Auto-discovers icon from settings and resource conventions. |
| set_window_icon | set_window_icon(icon_path_or_relative, window=None) |
bool | Accepts absolute path or relative resource path. |
| get_resource | get_resource(*segments, required=True) |
str | Resolves resource to absolute path string. |
| get_state | get_state(key, default=None) |
Any | Reads state value by key. |
| set_state | set_state(key, value, sender="ui") |
None | Updates state; identical-value writes are ignored by store adapter. |
| subscribe | subscribe(key, callback) |
StateSubscription | Callback receives current value immediately if non-None. |
| emit_signal | emit_signal(name, data=None) |
None | Emits a named signal payload to listeners. |
| on_signal | on_signal(name, callback) |
StateSubscription | Registers callback for named signals. |
| run | run() |
int | Starts event loop through active framework adapter. |
| exec | exec() |
int | Compatibility runner for Qt/Tk/Kivy event loop variants. |
| exec_ | exec_() |
int | Qt5 compatibility alias to exec(). |
Subscription lifecycle:
- subscribe and on_signal return StateSubscription objects.
- Disable a subscription by setting
subscription.is_active = False.
Component is a lifecycle mixin designed for UI classes.
Lifecycle hook order:
- component_will_mount
- allow_bg
- render or render_
- component_did_mount
- set_css or set_CSS
- responsive_ui or responsive_UI
Primary hooks:
| Hook | Signature | Purpose |
|---|---|---|
| component_will_mount | component_will_mount() |
Pre-render initialization. |
| render | render() |
Build widgets/layouts. |
| component_did_mount | component_did_mount() |
Post-render setup. |
| set_css | set_css(path_or_qss=None) |
Apply QSS from path or inline string. |
| responsive_ui | responsive_ui() |
Responsive behavior on mount and resize. |
Compatibility aliases:
- render_
- set_CSS
- responsive_UI
- destroyComponent
Utility methods:
| Method | Signature | Description |
|---|---|---|
| calc | calc(a, b) |
Returns percentage-based integer value. |
| find | find(target_type, name="") |
Delegates to native findChild when available. |
| destroy_component | destroy_component() |
Detach and schedule widget cleanup safely. |
| get_resource | get_resource(*segments, required=True) |
Top-level resource resolver shortcut. |
Decorator:
from qyro_engine import init_lifecycle
@init_lifecycle
class MyWidget(...):
...Use init_lifecycle only when not inheriting from Component directly.
The default state adapter is an in-memory thread-safe store.
Behavior contract:
- set_state performs no-op on equal values.
- subscribe may invoke callback immediately if key already has a non-None value.
- signal listeners are isolated from key subscriptions.
- listener exceptions are caught and reported without stopping dispatch.
Current widgets are Qt-focused wrappers with bidirectional store binding.
Constructors:
ReactiveLineEdit(state_key: str = "", store: Any = None, parent: Any = None)
ReactiveCheckBox(text: str = "", state_key: str = "", store: Any = None, parent: Any = None)
ReactiveLabel(text: str = "", state_key: str = "", store: Any = None, parent: Any = None)
ReactiveSlider(orientation: Any = None, state_key: str = "", store: Any = None, parent: Any = None)
ReactiveSpinBox(state_key: str = "", store: Any = None, parent: Any = None)Shared binding primitive (mixin-level):
bind_state(store_or_context, state_key, read_transform=None, write_transform=None)Notes:
- When
storeandstate_keyare provided, widgets auto-subscribe and push updates both ways. - Each wrapper forwards unknown attributes to the native Qt widget via getattr.
from qyro_engine import ApplicationContext
ctx = ApplicationContext()
ctx.set_state("username", "Ada")
print(ctx.get_state("username"))
sub = ctx.subscribe("username", lambda value: print("changed:", value))
ctx.set_state("username", "Lin")
ctx.on_signal("saved", lambda payload: print("saved:", payload))
ctx.emit_signal("saved", {"ok": True})
sub.is_active = FalseThe built-in reactive widgets module is Qt-focused. Current classes include:
- ReactiveLineEdit
- ReactiveCheckBox
- ReactiveLabel
- ReactiveSlider
- ReactiveSpinBox
Example:
from qyro_engine import ApplicationContext
from qyro_engine.reactive.widgets import ReactiveLineEdit, ReactiveLabel
ctx = ApplicationContext(initial_state={"name": "Ada"})
name_input = ReactiveLineEdit(state_key="name", store=ctx)
name_label = ReactiveLabel(state_key="name", store=ctx)- The engine keeps several compatibility aliases from older code style:
- PPGLifeCycle -> Component
- app_is_frozen -> is_frozen helper
- exec_() alias for Qt5 style calls
- Automatic icon/title assignment is best-effort and depends on toolkit capabilities and available files.
- Public entrypoint: qyro_engine/init.py
- Application context facade: qyro_engine/client/context.py
- Component lifecycle mixin: qyro_engine/client/component.py
- Framework adapters: qyro_engine/adapters/frameworks/
- State adapter: qyro_engine/adapters/state/reactive_store.py
- Resource resolver: qyro_engine/adapters/resources/filesystem_resources.py
- Settings loader: qyro_engine/adapters/settings/json_settings.py
Version in this repository snapshot:
- qyro-engine 0.1.0
The package is usable today for runtime concerns, but some ecosystem features live in qyro-cli or remain outside qyro-engine scope.