Falko Schindler 2 лет назад
Родитель
Сommit
ff53a438d9
6 измененных файлов с 27 добавлено и 11 удалено
  1. 1 1
      api_docs_and_examples.py
  2. 1 1
      nicegui/config.py
  3. 2 1
      nicegui/nicegui.py
  4. 21 7
      nicegui/page.py
  5. 1 0
      nicegui/page_builder.py
  6. 1 1
      nicegui/run.py

+ 1 - 1
api_docs_and_examples.py

@@ -751,7 +751,7 @@ You can call `ui.run()` with optional arguments:
 - `host` (default: `'0.0.0.0'`)
 - `host` (default: `'0.0.0.0'`)
 - `port` (default: `8080`)
 - `port` (default: `8080`)
 - `title` (default: `'NiceGUI'`)
 - `title` (default: `'NiceGUI'`)
-- `favicon` (default: `'favicon.ico'`)
+- `favicon`: relative filepath to a favicon (default: `None`, NiceGUI icon will be used)
 - `dark`: whether to use Quasar's dark mode (default: `False`, use `None` for "auto" mode)
 - `dark`: whether to use Quasar's dark mode (default: `False`, use `None` for "auto" mode)
 - `main_page_classes`: configure Quasar classes of main page (default: `'q-ma-md column items-start'`)
 - `main_page_classes`: configure Quasar classes of main page (default: `'q-ma-md column items-start'`)
 - `binding_refresh_interval`: time between binding updates (default: `0.1` seconds, bigger is more cpu friendly)
 - `binding_refresh_interval`: time between binding updates (default: `0.1` seconds, bigger is more cpu friendly)

+ 1 - 1
nicegui/config.py

@@ -10,7 +10,7 @@ class Config():
     port: int = int(os.environ.get('PORT', '8080'))
     port: int = int(os.environ.get('PORT', '8080'))
     title: str = 'NiceGUI'
     title: str = 'NiceGUI'
     reload: bool = True
     reload: bool = True
-    favicon: str = 'favicon.ico'
+    favicon: Optional[str] = None
     dark: Optional[bool] = False
     dark: Optional[bool] = False
     main_page_classes: str = 'q-ma-md column items-start gap-4'
     main_page_classes: str = 'q-ma-md column items-start gap-4'
     binding_refresh_interval: float = 0.1
     binding_refresh_interval: float = 0.1

+ 2 - 1
nicegui/nicegui.py

@@ -11,7 +11,7 @@ if True:  # NOTE: prevent formatter from mixing up these lines
     builtins.print = print_backup
     builtins.print = print_backup
 
 
 from . import binding, globals
 from . import binding, globals
-from .page import create_page_routes, init_auto_index_page
+from .page import create_favicon_routes, create_page_routes, init_auto_index_page
 from .task_logger import create_task
 from .task_logger import create_task
 from .routes import create_exclude_routes
 from .routes import create_exclude_routes
 from .timer import Timer
 from .timer import Timer
@@ -33,6 +33,7 @@ def startup():
     globals.loop = asyncio.get_running_loop()
     globals.loop = asyncio.get_running_loop()
     init_auto_index_page()
     init_auto_index_page()
     create_page_routes()
     create_page_routes()
+    create_favicon_routes()
     create_exclude_routes()
     create_exclude_routes()
     globals.tasks.extend(create_task(t.coro, name=t.name) for t in Timer.prepared_coroutines)
     globals.tasks.extend(create_task(t.coro, name=t.name) for t in Timer.prepared_coroutines)
     Timer.prepared_coroutines.clear()
     Timer.prepared_coroutines.clear()

+ 21 - 7
nicegui/page.py

@@ -12,13 +12,14 @@ import justpy as jp
 from addict import Dict as AdDict
 from addict import Dict as AdDict
 from pygments.formatters import HtmlFormatter
 from pygments.formatters import HtmlFormatter
 from starlette.requests import Request
 from starlette.requests import Request
-from starlette.routing import compile_path
+from starlette.responses import FileResponse
+from starlette.routing import Route, compile_path
 from starlette.websockets import WebSocket
 from starlette.websockets import WebSocket
 
 
 from . import globals
 from . import globals
 from .helpers import is_coroutine
 from .helpers import is_coroutine
 from .page_builder import PageBuilder
 from .page_builder import PageBuilder
-from .routes import convert_arguments
+from .routes import add_route, convert_arguments
 
 
 
 
 class Page(jp.QuasarPage):
 class Page(jp.QuasarPage):
@@ -39,11 +40,11 @@ class Page(jp.QuasarPage):
 
 
         if globals.config:
         if globals.config:
             self.title = title or globals.config.title
             self.title = title or globals.config.title
-            self.favicon = favicon or globals.config.favicon
+            self.set_favicon(favicon or globals.config.favicon)
             self.dark = dark if dark is not ... else globals.config.dark
             self.dark = dark if dark is not ... else globals.config.dark
         else:
         else:
             self.title = title
             self.title = title
-            self.favicon = favicon
+            self.set_favicon(favicon)
             self.dark = dark if dark is not ... else None
             self.dark = dark if dark is not ... else None
         self.tailwind = True  # use Tailwind classes instead of Quasars
         self.tailwind = True  # use Tailwind classes instead of Quasars
         self.css = css
         self.css = css
@@ -61,6 +62,9 @@ class Page(jp.QuasarPage):
         self.view = jp.Div(a=self, classes=classes, temp=False)
         self.view = jp.Div(a=self, classes=classes, temp=False)
         self.view.add_page(self)
         self.view.add_page(self)
 
 
+    def set_favicon(self, favicon: Optional[str]) -> None:
+        self.favicon = f'_favicon/{favicon}' if favicon else 'favicon.ico'
+
     async def _route_function(self, request: Request) -> Page:
     async def _route_function(self, request: Request) -> Page:
         with globals.within_view(self.view):
         with globals.within_view(self.view):
             for handler in globals.connect_handlers + ([self.connect_handler] if self.connect_handler else []):
             for handler in globals.connect_handlers + ([self.connect_handler] if self.connect_handler else []):
@@ -167,7 +171,7 @@ class page:
 
 
         :param route: route of the new page (path must start with '/')
         :param route: route of the new page (path must start with '/')
         :param title: optional page title
         :param title: optional page title
-        :param favicon: optional favicon
+        :param favicon: optional relative filepath to a favicon (default: `None`, NiceGUI icon will be used)
         :param dark: whether to use Quasar's dark mode (defaults to `dark` argument of `run` command)
         :param dark: whether to use Quasar's dark mode (defaults to `dark` argument of `run` command)
         :param classes: tailwind classes for the container div (default: `'q-ma-md column items-start gap-4'`)
         :param classes: tailwind classes for the container div (default: `'q-ma-md column items-start gap-4'`)
         :param css: CSS definitions
         :param css: CSS definitions
@@ -226,7 +230,7 @@ class page:
             except Exception as e:
             except Exception as e:
                 globals.log.exception(e)
                 globals.log.exception(e)
                 return error(500, str(e))
                 return error(500, str(e))
-        builder = PageBuilder(decorated, self.shared)
+        builder = PageBuilder(decorated, self.shared, self.favicon)
         if globals.state != globals.State.STOPPED:
         if globals.state != globals.State.STOPPED:
             builder.create_route(self.route)
             builder.create_route(self.route)
         globals.page_builders[self.route] = builder
         globals.page_builders[self.route] = builder
@@ -287,7 +291,7 @@ def init_auto_index_page() -> None:
         return  # there is no auto-index page on the view stack
         return  # there is no auto-index page on the view stack
     page: Page = view_stack.pop().pages[0]
     page: Page = view_stack.pop().pages[0]
     page.title = globals.config.title
     page.title = globals.config.title
-    page.favicon = globals.config.favicon
+    page.set_favicon(globals.config.favicon)
     page.dark = globals.config.dark
     page.dark = globals.config.dark
     page.view.classes = globals.config.main_page_classes
     page.view.classes = globals.config.main_page_classes
     assert len(view_stack) == 0
     assert len(view_stack) == 0
@@ -297,3 +301,13 @@ def create_page_routes() -> None:
     jp.Route("/{path:path}", lambda: error(404), last=True)
     jp.Route("/{path:path}", lambda: error(404), last=True)
     for route, page_builder in globals.page_builders.items():
     for route, page_builder in globals.page_builders.items():
         page_builder.create_route(route)
         page_builder.create_route(route)
+
+
+def create_favicon_routes() -> None:
+    for page_builder in globals.page_builders.values():
+        if page_builder.favicon:
+            add_route(None, Route(f'/static/_favicon/{page_builder.favicon}',
+                                  lambda _, filepath=page_builder.favicon: FileResponse(filepath)))
+    if globals.config.favicon:
+        add_route(None, Route(f'/static/_favicon/{globals.config.favicon}',
+                              lambda _: FileResponse(globals.config.favicon)))

+ 1 - 0
nicegui/page_builder.py

@@ -15,6 +15,7 @@ if TYPE_CHECKING:
 class PageBuilder:
 class PageBuilder:
     function: Callable[[], Awaitable['Page']]
     function: Callable[[], Awaitable['Page']]
     shared: bool
     shared: bool
+    favicon: Optional[str] = None
 
 
     _shared_page: Optional['Page'] = None
     _shared_page: Optional['Page'] = None
 
 

+ 1 - 1
nicegui/run.py

@@ -20,7 +20,7 @@ def run(self, *,
         host: str = os.environ.get('HOST', '0.0.0.0'),
         host: str = os.environ.get('HOST', '0.0.0.0'),
         port: int = int(os.environ.get('PORT', '8080')),
         port: int = int(os.environ.get('PORT', '8080')),
         title: str = 'NiceGUI',
         title: str = 'NiceGUI',
-        favicon: str = 'favicon.ico',
+        favicon: Optional[str] = None,
         dark: Optional[bool] = False,
         dark: Optional[bool] = False,
         main_page_classes: str = 'q-ma-md column items-start gap-4',
         main_page_classes: str = 'q-ma-md column items-start gap-4',
         binding_refresh_interval: float = 0.1,
         binding_refresh_interval: float = 0.1,