1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
import json
from typing import Any, Callable
from typing_extensions import Self
from threading import Thread
try:
from enum import StrEnum # Python 3.11 and above
except ImportError:
from enum import Enum
class StrEnum(str, Enum):
pass
class StateEventType(StrEnum):
CHANGE = 'change'
DELETE = 'delete'
class State(dict):
_serializable = False
def __init__(self, window):
self.__event_handlers = []
self.__window = window
def __update_js(self, key, value):
special_key = '__pywebviewHaltUpdate__' + key
script = f"window.pywebview.state.{special_key} = JSON.parse('{json.dumps(value)}')"
self.__window.run_js(script)
def __notify_handlers(self, type, key, value=None):
def notify_handlers():
for handler in self.__event_handlers:
if callable(handler):
if type == StateEventType.CHANGE:
handler(type, key, value)
elif type == StateEventType.DELETE:
handler(type, key, value)
t = Thread(target=notify_handlers)
t.start()
def __setattr__(self, key, value, should_update_js=True):
def update_and_notify():
self.__update_js(key, value)
self.__notify_handlers(StateEventType.CHANGE, key, value)
if key.startswith('_State__'):
super().__setattr__(key, value)
return
if key not in self or self[key] != value:
self[key] = value
if not should_update_js:
self.__notify_handlers(key, value)
return
if self.__window.events.loaded.is_set():
update_and_notify()
else:
self.__window.events.loaded += update_and_notify
def __getattr__(self, key):
if key in self:
return self[key]
raise AttributeError(f"'{type(self).__key__}' object has no attribute '{key}'")
def __delattr__(self, key):
if key.startswith('__pywebviewHaltUpdate__'):
key = key.replace('__pywebviewHaltUpdate__', '')
halt_update = True
else:
halt_update = False
if key in self:
old_value = self[key]
del self[key]
if not halt_update:
special_key = '__pywebviewHaltUpdate__' + key
self.__window.run_js(f"delete window.pywebview.state.{special_key}")
self.__notify_handlers(StateEventType.DELETE, key, old_value)
else:
raise AttributeError(f"'{type(self).__key__}' object has no attribute '{key}'")
def __add__(self, item: Callable[..., Any]) -> Self:
self.__event_handlers.append(item)
return self
def __sub__(self, item: Callable[..., Any]) -> Self:
self.__event_handlers.remove(item)
return self
def __iadd__(self, item: Callable[..., Any]) -> Self:
self.__event_handlers.append(item)
return self
def __isub__(self, item: Callable[..., Any]) -> Self:
self.__event_handlers.remove(item)
return self
|