Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Qyro Logo

⚡ Qyro Runtime Engine

Runtime engine for Python GUI apps with a shared application context.

Python GitHub Release GitHub Issues GitHub Issues Closed GitHub forks GitHub stars License Sponsor

What it is

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.

What it is not

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.

Supported UI adapters

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

Installation

This package is configured with Poetry extras.

Base package:

poetry add qyro-engine

With 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 kivy

With telemetry helper:

poetry add qyro-engine -E sentry

Everything enabled:

poetry add qyro-engine -E all

Expected project layout

Qyro 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/

Quick start (Qt)

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())

Quick start (Tkinter)

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()

API Reference

This section documents the public runtime API that is available in the current codebase.

Top-level API (qyro_engine)

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() -> bool

Behavior 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.

ApplicationContext

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 (qyro_engine.ui.component)

Component is a lifecycle mixin designed for UI classes.

Lifecycle hook order:

  1. component_will_mount
  2. allow_bg
  3. render or render_
  4. component_did_mount
  5. set_css or set_CSS
  6. 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.

Reactive state API contract

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.

Reactive widgets API (qyro_engine.reactive.widgets)

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 store and state_key are provided, widgets auto-subscribe and push updates both ways.
  • Each wrapper forwards unknown attributes to the native Qt widget via getattr.

Reactive state and signals

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 = False

Reactive widgets (Qt only)

The 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)

Notes on compatibility

  • 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.

Repository pointers

  • 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

Status

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.

About

Universal Python application engine for Desktop & Mobile. Build native apps across Qt, Kivy, and Tkinter using a unified React-inspired component architecture: declarative lifecycles (render, props, hooks), responsive styling, runtime context, and seamless multi-target packaging.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages