Kaynağa Gözat

add type hints and improve view updates during value propagation

Falko Schindler 3 yıl önce
ebeveyn
işleme
d061f61d33
1 değiştirilmiş dosya ile 33 ekleme ve 17 silme
  1. 33 17
      nicegui/binding.py

+ 33 - 17
nicegui/binding.py

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