Simple App¶
This example demonstrates the fundamentals of a Pytonium application: custom schemes for local file loading, Python-to-JavaScript function bindings, and real-time state management.
Full Source
The complete source code is available in the pytonium_examples repository.
Python Entry Point¶
import os
import time
from datetime import datetime
from Pytonium import Pytonium, returns_value_to_javascript
pytonium = Pytonium()
@returns_value_to_javascript("any")
def get_greeting():
return {"message": "Hello from Python!", "answer": 42}
pytonium.bind_function_to_javascript(get_greeting, javascript_object="api")
pytonium.add_custom_scheme("app", os.path.dirname(os.path.abspath(__file__)) + "/")
pytonium.initialize("app://index.html", 800, 600)
while pytonium.is_running():
time.sleep(0.01)
pytonium.update_message_loop()
pytonium.set_state("app", "time", datetime.now().strftime("%H:%M:%S"))
HTML Page¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pytonium Simple App</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 600px;
margin: 60px auto;
padding: 0 20px;
background: #1a1a2e;
color: #e0e0e0;
}
h1 { color: #bb86fc; }
#greeting { margin: 20px 0; padding: 16px; background: #16213e; border-radius: 8px; }
#clock { font-size: 2rem; font-weight: bold; color: #03dac6; }
button {
padding: 10px 20px;
background: #bb86fc;
color: #1a1a2e;
border: none;
border-radius: 6px;
font-size: 1rem;
cursor: pointer;
}
button:hover { background: #9a67ea; }
</style>
</head>
<body>
<h1>Pytonium Simple App</h1>
<button id="greetBtn">Get Greeting from Python</button>
<div id="greeting"></div>
<p>Current time from Python state:</p>
<div id="clock">--:--:--</div>
<script>
document.addEventListener("PytoniumReady", function () {
// Call the Python function and display the result
document.getElementById("greetBtn").addEventListener("click", async function () {
const result = await api.get_greeting();
document.getElementById("greeting").textContent =
result.message + " The answer is " + result.answer;
});
// Subscribe to state updates from Python
Pytonium.registerForStateUpdates("app", "time", function (value) {
document.getElementById("clock").textContent = value;
});
});
</script>
</body>
</html>
How It Works¶
Custom Schemes¶
This registers the app:// URL scheme and maps it to the directory containing main.py. When the browser requests app://index.html, Pytonium serves the file from disk. This avoids the need for a local web server.
Scheme Registration
Custom schemes must be registered before calling initialize(). CEF registers schemes during process startup, so they cannot be added after the browser is running.
The @returns_value_to_javascript Decorator¶
@returns_value_to_javascript("any")
def get_greeting():
return {"message": "Hello from Python!", "answer": 42}
This decorator marks the function as one that returns a value to JavaScript. The "any" parameter specifies the return type hint. When called from JavaScript, the function returns a Promise that resolves with the Python return value:
Supported type hints: "any", "string", "int", "float", "bool", "list", "dict".
Binding Functions¶
This binds get_greeting to JavaScript as api.get_greeting(). The javascript_object parameter groups functions under a namespace object. You can bind multiple functions to the same object:
pytonium.bind_function_to_javascript(get_greeting, javascript_object="api")
pytonium.bind_function_to_javascript(get_data, javascript_object="api")
# Available in JS as: api.get_greeting(), api.get_data()
State Management¶
set_state(namespace, key, value) pushes a value from Python to the JavaScript side. The first argument is a namespace (for organizing state), the second is the key name, and the third is the value (string, number, dict, or list).
On the JavaScript side, subscribe to changes with registerForStateUpdates:
Pytonium.registerForStateUpdates("app", "time", function (value) {
document.getElementById("clock").textContent = value;
});
The callback fires every time the Python side calls set_state with the matching namespace and key.
The PytoniumReady Event¶
Wait for PytoniumReady
The Pytonium global object and all bound functions (like api) are injected after the page loads. Code that uses these APIs must wait for the PytoniumReady event. Accessing them before this event fires will result in undefined errors.
The Message Loop¶
Pytonium uses a manual message loop. update_message_loop() processes CEF events (rendering, input, network, IPC) and must be called regularly. The time.sleep(0.01) call prevents busy-waiting and keeps CPU usage low.
is_running() returns False when the user closes the window, which exits the loop.
Next Steps¶
- Frameless Window -- Remove the native titlebar and build custom window chrome.
- JavaScript Bindings Guide -- Full reference for binding functions and returning values.
- State Management Guide -- Advanced patterns for shared state.