State Management¶
Pytonium provides a built-in state management system for sharing data between Python and JavaScript. State is organized by namespaces, and both sides can read, write, and subscribe to changes in real time.
Concepts¶
- Namespace -- A logical grouping for related state (e.g.
"user","settings","dashboard"). - Key -- A string identifier within a namespace.
- Value -- Any serializable type:
int,float,str,bool,dict, orlist.
State flows bidirectionally:
Python --set_state()---> Shared State <---setState()-- JavaScript
Python <--update_state-- Shared State --DOM event---> JavaScript
Setting State from Python¶
Use set_state(namespace, key, value) to push a value into the shared state. Any subscribed handlers (Python or JavaScript) are notified immediately.
p = Pytonium()
# Set individual values
p.set_state("user", "name", "Alice")
p.set_state("user", "score", 42)
p.set_state("settings", "theme", "dark")
# Set complex values
p.set_state("dashboard", "stats", {
"cpu": 45.2,
"memory": 68.1,
"processes": ["python", "chrome", "vscode"]
})
Subscribing from Python¶
Create a handler class with an update_state(self, namespace, key, value) method, then register it with add_state_handler().
class DashboardHandler:
def update_state(self, namespace: str, key: str, value):
print(f"State changed: {namespace}.{key} = {value}")
handler = DashboardHandler()
p.add_state_handler(handler, namespaces=["dashboard", "settings"])
| Parameter | Type | Description |
|---|---|---|
state_handler | object | An object with an update_state method. |
namespaces | list[str] | List of namespace strings to subscribe to. |
Handler requirements
The handler object must have an update_state method. If it does not, Pytonium emits a UserWarning and the handler is ignored.
Register before initialize
State handlers should be registered before calling initialize() to ensure they receive all state changes from the start.
JavaScript API¶
On the JavaScript side, state is accessed through the Pytonium.appState namespace.
Setting State¶
Pytonium.appState.setState("user", "name", "Bob");
Pytonium.appState.setState("settings", "theme", "light");
Getting State¶
Removing State¶
Subscribing to Changes¶
Use registerForStateUpdates() to receive state changes as DOM events.
Pytonium.appState.registerForStateUpdates(
"onDashboardUpdate", // Custom event name
["dashboard"], // Namespaces to watch
true, // Receive updates from JavaScript setState
true // Receive updates from Python set_state
);
| Parameter | Type | Description |
|---|---|---|
eventName | string | The custom DOM event name to dispatch. |
namespaces | string[] | Array of namespace strings to subscribe to. |
getUpdatesFromJavascript | boolean | Receive events triggered by JS setState. |
getUpdatesFromPytonium | boolean | Receive events triggered by Python set_state. |
Once registered, listen for the event on window:
window.addEventListener("onDashboardUpdate", (event) => {
const { namespace, key, value } = event.detail;
console.log(`${namespace}.${key} changed to`, value);
});
Selective subscriptions
You can create multiple registrations with different event names for different namespaces. The fromJS and fromPython flags let you filter the direction of updates.
Real-Time Update Pattern¶
A common pattern is pushing data from Python (e.g., sensor readings, system stats) to the JavaScript UI in real time.
import time
import random
from Pytonium import Pytonium
p = Pytonium()
p.add_custom_scheme("app", "./web/")
p.initialize("app://index.html", 800, 600)
last_update = 0
while p.is_running():
p.update_message_loop()
now = time.time()
if now - last_update > 1.0:
p.set_state("sensors", "temperature", round(random.uniform(20, 30), 1))
p.set_state("sensors", "humidity", round(random.uniform(40, 80), 1))
last_update = now
time.sleep(0.016)
p.shutdown()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Live Sensors</title>
<style>
.sensor { font-size: 2rem; margin: 1rem; }
</style>
</head>
<body>
<div class="sensor">Temperature: <span id="temp">--</span> C</div>
<div class="sensor">Humidity: <span id="hum">--</span>%</div>
<script>
function onReady() {
Pytonium.appState.registerForStateUpdates(
"sensorUpdate",
["sensors"],
false,
true
);
window.addEventListener("sensorUpdate", (e) => {
const { key, value } = e.detail;
if (key === "temperature") {
document.getElementById("temp").textContent = value;
} else if (key === "humidity") {
document.getElementById("hum").textContent = value;
}
});
}
if (window.PytoniumReady) {
onReady();
} else {
window.addEventListener("PytoniumReady", onReady);
}
</script>
</body>
</html>
Complete Example: Settings Panel¶
This example shows bidirectional state flow -- JavaScript sets preferences, and Python reacts to them.
import time
from Pytonium import Pytonium
class SettingsHandler:
def update_state(self, namespace, key, value):
if namespace == "settings":
print(f"Setting changed: {key} = {value}")
if key == "volume":
# Apply volume change in your audio system
pass
p = Pytonium()
handler = SettingsHandler()
p.add_state_handler(handler, namespaces=["settings"])
# Set defaults
p.set_state("settings", "volume", 75)
p.set_state("settings", "theme", "dark")
p.add_custom_scheme("app", "./web/")
p.initialize("app://settings.html", 600, 400)
while p.is_running():
p.update_message_loop()
time.sleep(0.016)
p.shutdown()
function onReady() {
// Subscribe to settings changes from Python (for defaults)
Pytonium.appState.registerForStateUpdates(
"settingsChanged", ["settings"], true, true
);
window.addEventListener("settingsChanged", (e) => {
const { key, value } = e.detail;
if (key === "volume") {
document.getElementById("volume").value = value;
}
if (key === "theme") {
document.body.className = value;
}
});
// Send changes back to Python
document.getElementById("volume").addEventListener("input", (e) => {
Pytonium.appState.setState("settings", "volume", parseInt(e.target.value));
});
document.getElementById("themeToggle").addEventListener("click", () => {
const current = Pytonium.appState.getState("settings", "theme");
const next = current === "dark" ? "light" : "dark";
Pytonium.appState.setState("settings", "theme", next);
});
}
if (window.PytoniumReady) {
onReady();
} else {
window.addEventListener("PytoniumReady", onReady);
}