Преглед изворни кода

WIP Pages Element

- Socket connections still TODO
Christoph Trappe пре 3 година
родитељ
комит
b5dfba5408

+ 10 - 2
main.py

@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
 #!/usr/bin/env python3
-from nicegui import ui, wp
+from nicegui import ui, initial_page
 from contextlib import contextmanager
 from contextlib import contextmanager
 import inspect
 import inspect
 from nicegui.elements.markdown import Markdown
 from nicegui.elements.markdown import Markdown
@@ -11,7 +11,7 @@ import re
 import asyncio
 import asyncio
 
 
 # add docutils css to webpage
 # add docutils css to webpage
-wp.head_html += docutils.core.publish_parts('', writer_name='html')['stylesheet']
+initial_page.head_html += docutils.core.publish_parts('', writer_name='html')['stylesheet']
 
 
 @contextmanager
 @contextmanager
 def example(content: Union[Element, str]):
 def example(content: Union[Element, str]):
@@ -346,4 +346,12 @@ with example(lifecycle):
 
 
     ui.on_startup(counter())
     ui.on_startup(counter())
 
 
+with example(ui.page):
+
+    with ui.page('/other_page') as other:
+        ui.label('Welcome on the other side')
+        ui.link('GO TO MAIN PAGE', '/').classes('text-decoration: underline text-blue')
+
+    ui.link('Visit other page', '/other_page').classes('text-decoration: underline text-blue')
+
 ui.run()
 ui.run()

+ 1 - 1
nicegui/__init__.py

@@ -1,2 +1,2 @@
-from nicegui.nicegui import app, ui, wp
+from nicegui.nicegui import app, ui, initial_page
 from nicegui import elements
 from nicegui import elements

+ 2 - 2
nicegui/elements/element.py

@@ -3,7 +3,7 @@ from binding.binding import BindableProperty
 
 
 class Element:
 class Element:
 
 
-    wp: None
+    wp_stack = []
     view_stack = []
     view_stack = []
 
 
     visible = BindableProperty
     visible = BindableProperty
@@ -14,7 +14,7 @@ class Element:
 
 
         self.parent_view = self.view_stack[-1]
         self.parent_view = self.view_stack[-1]
         self.parent_view.add(view)
         self.parent_view.add(view)
-        view.add_page(self.wp)
+        view.add_page(self.wp_stack[-1])
         self.view = view
         self.view = view
 
 
         self.visible = True
         self.visible = True

+ 1 - 1
nicegui/elements/log.py

@@ -28,7 +28,7 @@ class Log(Element):
 
 
         await asyncio.gather(*[
         await asyncio.gather(*[
             self.view.run_method(f'push("{urllib.parse.quote(line)}")', socket)
             self.view.run_method(f'push("{urllib.parse.quote(line)}")', socket)
-            for socket in WebPage.sockets[Element.wp.page_id].values()
+            for socket in WebPage.sockets[Element.wp_stack[-1].page_id].values()
         ])
         ])
 
 
     def push(self, line: str):
     def push(self, line: str):

+ 42 - 0
nicegui/elements/page.py

@@ -0,0 +1,42 @@
+import justpy as jp
+
+from .element import Element
+from pygments.formatters import HtmlFormatter
+
+
+class Page(jp.QuasarPage):
+
+    def __init__(self, route: str = '', title: str = 'NiceGUI', favicon: str = 'favicon.ico', **kwargs):
+        """Page
+
+        Creates a new page at the given path.
+
+        :param route: the route of the new page. All paths must start with '/', otherwise an error occurs.
+        """
+        super().__init__(**kwargs)
+
+        self.delete_flag = False
+        self.title = title
+        self.favicon = favicon
+
+        self.tailwind = True  # use Tailwind classes instead of Quasars
+        self.css = HtmlFormatter().get_style_defs('.codehilite')
+        self.head_html += '''
+            <script>
+                confirm = () => { setTimeout(location.reload.bind(location), 100); return false; };
+            </script>
+        '''  # avoid confirmation dialog for reload
+
+        self.view = jp.Div(a=self, classes='q-ma-md column items-start', style='row-gap: 1em')
+        self.view.add_page(self)
+
+        jp.Route(route, lambda: self)
+
+    def __enter__(self):
+        Element.wp_stack.append(self)
+        Element.view_stack.append(self.view)
+        return self
+
+    def __exit__(self, *_):
+        Element.wp_stack.pop()
+        Element.view_stack.pop()

+ 1 - 1
nicegui/elements/scene_object3d.py

@@ -34,7 +34,7 @@ class Object3D:
         return self
         return self
 
 
     def run_command(self, command: str, socket=None):
     def run_command(self, command: str, socket=None):
-        sockets = [socket] if socket else WebPage.sockets.get(Element.wp.page_id, {}).values()
+        sockets = [socket] if socket else WebPage.sockets.get(Element.wp_stack[-1].page_id, {}).values()
         for socket in sockets:
         for socket in sockets:
             asyncio.get_event_loop().create_task(self.view.run_method(command, socket))
             asyncio.get_event_loop().create_task(self.view.run_method(command, socket))
 
 

+ 6 - 17
nicegui/nicegui.py

@@ -2,25 +2,11 @@
 from typing import Awaitable, Callable
 from typing import Awaitable, Callable
 import asyncio
 import asyncio
 import binding
 import binding
-from pygments.formatters import HtmlFormatter
+
 from .ui import Ui  # NOTE: before justpy
 from .ui import Ui  # NOTE: before justpy
 import justpy as jp
 import justpy as jp
-from .elements.element import Element
 from .timer import Timer
 from .timer import Timer
 
 
-wp = jp.QuasarPage(delete_flag=False, title=Ui.config.title, favicon=Ui.config.favicon)
-wp.tailwind = True  # use Tailwind classes instead of Quasars
-wp.css = HtmlFormatter().get_style_defs('.codehilite')
-wp.head_html += '''
-    <script>
-        confirm = () => { setTimeout(location.reload.bind(location), 100); return false; };
-    </script>
-'''  # avoid confirmation dialog for reload
-
-main = jp.Div(a=wp, classes='q-ma-md column items-start', style='row-gap: 1em')
-main.add_page(wp)
-
-jp.justpy(lambda: wp, start_server=False)
 
 
 async def binding_loop():
 async def binding_loop():
     while True:
     while True:
@@ -46,8 +32,11 @@ def shutdown():
     [t() for t in Ui.shutdown_tasks if isinstance(t, Callable)]
     [t() for t in Ui.shutdown_tasks if isinstance(t, Callable)]
     [t.cancel() for t in tasks]
     [t.cancel() for t in tasks]
 
 
-Element.wp = wp
-Element.view_stack = [main]
 
 
 app = jp.app
 app = jp.app
 ui = Ui()
 ui = Ui()
+
+initial_page = ui.page('/', ui.config.title, ui.config.favicon)
+initial_page.__enter__()
+jp.justpy(lambda: initial_page, start_server=False)
+

+ 1 - 3
nicegui/run.py

@@ -13,11 +13,9 @@ if not config.interactive and config.reload and not inspect.stack()[-2].filename
     uvicorn.run('nicegui:app', host=config.host, port=config.port, lifespan='on', reload=True)
     uvicorn.run('nicegui:app', host=config.host, port=config.port, lifespan='on', reload=True)
     sys.exit()
     sys.exit()
 
 
-def run(self, *, host='0.0.0.0', port=80, title='NiceGUI', favicon='favicon.ico', reload=True, show=True):
+def run(self, *, host='0.0.0.0', port=80, reload=True, show=True):
 
 
     if config.interactive or reload == False:  # NOTE: if reload == True we already started uvicorn above
     if config.interactive or reload == False:  # NOTE: if reload == True we already started uvicorn above
         if show:
         if show:
             webbrowser.open(f'http://{host if host != "0.0.0.0" else "127.0.0.1"}:{port}/')
             webbrowser.open(f'http://{host if host != "0.0.0.0" else "127.0.0.1"}:{port}/')
-        Element.wp.title = title
-        Element.wp.favicon = favicon
         uvicorn.run(jp.app, host=host, port=port, lifespan='on')
         uvicorn.run(jp.app, host=host, port=port, lifespan='on')

+ 1 - 0
nicegui/ui.py

@@ -18,6 +18,7 @@ class Ui:
     from .elements.menu import Menu as menu
     from .elements.menu import Menu as menu
     from .elements.notify import Notify as notify
     from .elements.notify import Notify as notify
     from .elements.number import Number as number
     from .elements.number import Number as number
+    from .elements.page import Page as page
     from .elements.radio import Radio as radio
     from .elements.radio import Radio as radio
     from .elements.scene import Scene as scene
     from .elements.scene import Scene as scene
     from .elements.select import Select as select
     from .elements.select import Select as select