|
@@ -2,6 +2,8 @@
|
|
|
import asyncio
|
|
|
from collections import defaultdict
|
|
|
from justpy.htmlcomponents import HTMLBaseComponent
|
|
|
+from typing import Any, Callable, Set, Tuple
|
|
|
+from .task_logger import create_task
|
|
|
|
|
|
bindings = defaultdict(list)
|
|
|
bindable_properties = dict()
|
|
@@ -9,53 +11,67 @@ active_links = []
|
|
|
|
|
|
async def loop():
|
|
|
while True:
|
|
|
- visited = set()
|
|
|
- invalidated_elements = []
|
|
|
+ visited: Set[Tuple[int, str]] = set()
|
|
|
+ visited_views: Set[HTMLBaseComponent] = set()
|
|
|
for link in active_links:
|
|
|
(source_obj, source_name, target_obj, target_name, transform) = link
|
|
|
value = transform(getattr(source_obj, source_name))
|
|
|
if getattr(target_obj, target_name) != value:
|
|
|
setattr(target_obj, target_name, value)
|
|
|
- propagate(target_obj, target_name, visited)
|
|
|
- if hasattr(target_obj, 'view') and isinstance(target_obj.view, HTMLBaseComponent):
|
|
|
- invalidated_elements.append(target_obj)
|
|
|
- for element in invalidated_elements:
|
|
|
- await element.view.update()
|
|
|
+ propagate(target_obj, target_name, visited, visited_views)
|
|
|
+ update_views(visited_views)
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
-def propagate(source_obj, source_name, visited=None):
|
|
|
+async def update_views_async(views: list[HTMLBaseComponent]):
|
|
|
+ for view in views:
|
|
|
+ await view.update()
|
|
|
+
|
|
|
+def update_views(views: list[HTMLBaseComponent]):
|
|
|
+ if asyncio._get_running_loop() is None:
|
|
|
+ return # NOTE: no need to update view if event loop is not running, yet
|
|
|
+ create_task(update_views_async(views))
|
|
|
+
|
|
|
+def propagate(source_obj,
|
|
|
+ source_name,
|
|
|
+ visited: Set[Tuple[int, str]] = None,
|
|
|
+ visited_views: Set[HTMLBaseComponent] = None) -> list[HTMLBaseComponent]:
|
|
|
if visited is None:
|
|
|
visited = set()
|
|
|
+ if visited_views is None:
|
|
|
+ visited_views = set()
|
|
|
visited.add((id(source_obj), source_name))
|
|
|
+ if isinstance(source_obj, HTMLBaseComponent):
|
|
|
+ visited_views.add(source_obj)
|
|
|
for _, target_obj, target_name, transform in bindings[(id(source_obj), source_name)]:
|
|
|
if (id(target_obj), target_name) in visited:
|
|
|
continue
|
|
|
target_value = transform(getattr(source_obj, source_name))
|
|
|
if getattr(target_obj, target_name) != target_value:
|
|
|
setattr(target_obj, target_name, target_value)
|
|
|
- propagate(target_obj, target_name, visited)
|
|
|
+ propagate(target_obj, target_name, visited, visited_views)
|
|
|
+ return visited_views
|
|
|
|
|
|
-def bind_to(self_obj, self_name, other_obj, other_name, forward):
|
|
|
+def bind_to(self_obj: Any, self_name: str, other_obj: Any, other_name: str, forward: Callable):
|
|
|
bindings[(id(self_obj), self_name)].append((self_obj, other_obj, other_name, forward))
|
|
|
if (id(self_obj), self_name) not in bindable_properties:
|
|
|
active_links.append((self_obj, self_name, other_obj, other_name, forward))
|
|
|
- propagate(self_obj, self_name)
|
|
|
+ update_views(propagate(self_obj, self_name))
|
|
|
|
|
|
-def bind_from(self_obj, self_name, other_obj, other_name, backward):
|
|
|
+def bind_from(self_obj: Any, self_name: str, other_obj: Any, other_name: str, backward: Callable):
|
|
|
bindings[(id(other_obj), other_name)].append((other_obj, self_obj, self_name, backward))
|
|
|
if (id(other_obj), other_name) not in bindable_properties:
|
|
|
active_links.append((other_obj, other_name, self_obj, self_name, backward))
|
|
|
- propagate(other_obj, other_name)
|
|
|
+ update_views(propagate(other_obj, other_name))
|
|
|
|
|
|
class BindableProperty:
|
|
|
|
|
|
- def __set_name__(self, _, name):
|
|
|
+ def __set_name__(self, _, name: str):
|
|
|
self.name = name
|
|
|
|
|
|
- def __get__(self, owner, _=None):
|
|
|
+ def __get__(self, owner: Any, _=None):
|
|
|
return getattr(owner, '_' + self.name)
|
|
|
|
|
|
- def __set__(self, owner, value):
|
|
|
+ def __set__(self, owner: Any, value: Any):
|
|
|
setattr(owner, '_' + self.name, value)
|
|
|
bindable_properties[(id(owner), self.name)] = owner
|
|
|
- propagate(owner, self.name)
|
|
|
+ update_views(propagate(owner, self.name))
|