Browse Source

Merge remote-tracking branch 'origin/refresh-with-args'

Rodja Trappe 1 year ago
parent
commit
412bd513be

+ 5 - 2
nicegui/events.py

@@ -274,13 +274,16 @@ def handle_event(handler: Optional[Callable[..., Any]],
     if handler is None:
         return
     try:
-        no_arguments = not any(p.default is Parameter.empty for p in signature(handler).parameters.values())
+        expects_arguments = any(p.default is Parameter.empty and
+                                p.kind is not Parameter.VAR_POSITIONAL and
+                                p.kind is not Parameter.VAR_KEYWORD
+                                for p in signature(handler).parameters.values())
         sender = arguments.sender if isinstance(arguments, EventArguments) else sender
         assert sender is not None and sender.parent_slot is not None
         if sender.is_ignoring_events:
             return
         with sender.parent_slot:
-            result = handler() if no_arguments else handler(arguments)
+            result = handler(arguments) if expects_arguments else handler()
         if isinstance(result, Awaitable):
             async def wait_for_result():
                 with sender.parent_slot:

+ 3 - 1
nicegui/functions/refreshable.py

@@ -58,12 +58,14 @@ class refreshable:
         self.targets.append(target)
         return target.run(self.func)
 
-    def refresh(self) -> None:
+    def refresh(self, *args: Any, **kwargs: Any) -> None:
         self.prune()
         for target in self.targets:
             if target.instance != self.instance:
                 continue
             target.container.clear()
+            target.args = args or target.args
+            target.kwargs.update(kwargs)
             result = target.run(self.func)
             if is_coroutine_function(self.func):
                 assert result is not None

+ 28 - 0
tests/test_refreshable.py

@@ -98,3 +98,31 @@ def test_multiple_targets(screen: Screen) -> None:
     screen.click('increment B')
     screen.should_contain('A = 2 (3)')
     screen.should_contain('B = 2 (4)')
+
+
+def test_refresh_with_arguments(screen: Screen):
+    a = 0
+
+    @ui.refreshable
+    def some_ui(*, b: int):
+        ui.label(f'a={a}, b={b}')
+
+    some_ui(b=0)
+    ui.button('Refresh 1', on_click=lambda: some_ui.refresh(b=1))
+    ui.button('Refresh 2', on_click=lambda: some_ui.refresh())
+    ui.button('Refresh 3', on_click=some_ui.refresh)
+
+    screen.open('/')
+    screen.should_contain('a=0, b=0')
+
+    a = 1
+    screen.click('Refresh 1')
+    screen.should_contain('a=1, b=1')
+
+    a = 2
+    screen.click('Refresh 2')
+    screen.should_contain('a=2, b=1')
+
+    a = 3
+    screen.click('Refresh 3')
+    screen.should_contain('a=3, b=1')

+ 18 - 0
website/more_documentation/refreshable_documentation.py

@@ -21,6 +21,24 @@ def main_demo() -> None:
 
 
 def more() -> None:
+    @text_demo('Refreshable UI with parameters', '''
+        Here is a demo of how to use the refreshable decorator to create a UI that can be refreshed with different parameters.
+    ''')
+    def refreshable_with_parameters():
+        from datetime import datetime
+
+        import pytz
+
+        @ui.refreshable
+        def clock_ui(timezone: str):
+            ui.label(f'Current time in {timezone}:')
+            ui.label(datetime.now(tz=pytz.timezone(timezone)).strftime('%H:%M:%S'))
+
+        clock_ui('Europe/Berlin')
+        ui.button('Refresh', on_click=clock_ui.refresh)
+        ui.button('Refresh for New York', on_click=lambda: clock_ui.refresh('America/New_York'))
+        ui.button('Refresh for Tokyo', on_click=lambda: clock_ui.refresh('Asia/Tokyo'))
+
     @text_demo('Refreshable UI for input validation', '''
         Here is a demo of how to use the refreshable decorator to give feedback about the validity of user input.
     ''')