Prechádzať zdrojové kódy

keep one view stack per task to allow async UI manipulation without mixing up the containers

Falko Schindler 2 rokov pred
rodič
commit
ee0fb9bdc5
4 zmenil súbory, kde vykonal 35 pridanie a 18 odobranie
  1. 2 4
      nicegui/elements/group.py
  2. 2 2
      nicegui/elements/scene.py
  3. 21 3
      nicegui/globals.py
  4. 10 9
      nicegui/page.py

+ 2 - 4
nicegui/elements/group.py

@@ -12,13 +12,11 @@ from .element import Element
 class Group(Element):
 
     def __enter__(self):
-        globals.view_stack.append(self.view)
+        globals.get_view_stack().append(self.view)
         return self
 
     def __exit__(self, *_):
-        globals.view_stack.pop()
-        if len(globals.view_stack) <= 1:
-            self.update()  # NOTE: update when we are back on top of the stack (only the first page is in view stack)
+        globals.get_view_stack().pop()
 
     def tight(self) -> Group:
         return self.classes(replace='').style(replace='')

+ 2 - 2
nicegui/elements/scene.py

@@ -108,14 +108,14 @@ class Scene(Element):
         super().__init__(SceneView(width=width, height=height, on_click=on_click))
 
     def __enter__(self):
-        globals.view_stack.append(self.view)
+        globals.get_view_stack().append(self.view)
         scene = self.view.objects.get('scene', SceneObject(self.view, self.page))
         Object3D.stack.clear()
         Object3D.stack.append(scene)
         return self
 
     def __exit__(self, *_):
-        globals.view_stack.pop()
+        globals.get_view_stack().pop()
 
     def move_camera(self,
                     x: Optional[float] = None,

+ 21 - 3
nicegui/globals.py

@@ -18,7 +18,7 @@ config: Optional[Config] = None
 server: Optional[Server] = None
 loop: Optional[asyncio.AbstractEventLoop] = None
 page_builders: Dict[str, 'PageBuilder'] = {}
-view_stack: List[jp.HTMLBaseComponent] = []
+view_stacks: Dict[List[jp.HTMLBaseComponent]] = {}
 tasks: List[asyncio.tasks.Task] = []
 log: logging.Logger = logging.getLogger('nicegui')
 connect_handlers: List[Union[Callable, Awaitable]] = []
@@ -34,11 +34,29 @@ def find_route(function: Callable) -> str:
     return routes[0]
 
 
+def get_task_id() -> int:
+    return id(asyncio.current_task()) if loop and loop.is_running() else 0
+
+
+def get_view_stack() -> List[jp.HTMLBaseComponent]:
+    task_id = get_task_id()
+    if task_id not in view_stacks:
+        view_stacks[task_id] = []
+    return view_stacks[task_id]
+
+
+def prune_view_stack() -> None:
+    task_id = get_task_id()
+    if not view_stacks[task_id]:
+        del view_stacks[task_id]
+
+
 @contextmanager
 def within_view(view: jp.HTMLBaseComponent) -> Generator[None, None, None]:
     child_count = len(view)
-    view_stack.append(view)
+    get_view_stack().append(view)
     yield
-    view_stack.pop()
+    get_view_stack().pop()
+    prune_view_stack()
     if len(view) != child_count:
         create_task(view.update())

+ 10 - 9
nicegui/page.py

@@ -168,9 +168,8 @@ def page(self,
                 on_disconnect=on_disconnect,
                 shared=shared,
             )
-            globals.view_stack.append(page.view)
-            await func() if is_coroutine(func) else func()
-            globals.view_stack.pop()
+            with globals.within_view(page.view):
+                await func() if is_coroutine(func) else func()
             return page
         builder = PageBuilder(decorated, shared)
         if globals.server:
@@ -181,13 +180,14 @@ def page(self,
 
 
 def find_parent_view() -> jp.HTMLBaseComponent:
-    if not globals.view_stack:
+    view_stack = globals.get_view_stack()
+    if not view_stack:
         if globals.loop and globals.loop.is_running():
             raise RuntimeError('cannot find parent view, view stack is empty')
         page = Page(shared=True)
-        globals.view_stack.append(page.view)
+        view_stack.append(page.view)
         jp.Route('/', page._route_function)
-    return globals.view_stack[-1]
+    return view_stack[-1]
 
 
 def error404() -> jp.QuasarPage:
@@ -204,14 +204,15 @@ def error404() -> jp.QuasarPage:
 
 
 def init_auto_index_page() -> None:
-    if not globals.view_stack:
+    view_stack = globals.get_view_stack()
+    if not view_stack:
         return  # there is no auto-index page on the view stack
-    page: Page = globals.view_stack.pop().pages[0]
+    page: Page = view_stack.pop().pages[0]
     page.title = globals.config.title
     page.favicon = globals.config.favicon
     page.dark = globals.config.dark
     page.view.classes = globals.config.main_page_classes
-    assert len(globals.view_stack) == 0
+    assert len(view_stack) == 0
 
 
 def create_page_routes() -> None: