فهرست منبع

update ui.run_javascript

Falko Schindler 2 سال پیش
والد
کامیت
7fdf1f2196
6فایلهای تغییر یافته به همراه47 افزوده شده و 5 حذف شده
  1. 3 5
      api_docs_and_examples.py
  2. 21 0
      nicegui/client.py
  3. 9 0
      nicegui/javascript.py
  4. 5 0
      nicegui/nicegui.py
  5. 8 0
      nicegui/templates/index.html
  6. 1 0
      nicegui/ui.py

+ 3 - 5
api_docs_and_examples.py

@@ -791,17 +791,15 @@ It also enables you to identify sessions over [longer time spans by configuring
 
 
 With `ui.run_javascript()` you can run arbitrary JavaScript code on a page that is executed in the browser.
 With `ui.run_javascript()` you can run arbitrary JavaScript code on a page that is executed in the browser.
 The asynchronous function will return after the command(s) are executed.
 The asynchronous function will return after the command(s) are executed.
-The result of the execution is returned as a dictionary containing the response string per websocket.
 You can also set `respond=False` to send a command without waiting for a response.
 You can also set `respond=False` to send a command without waiting for a response.
-''')
+''', skip=False)
     def javascript_example():
     def javascript_example():
         async def alert():
         async def alert():
             await ui.run_javascript('alert("Hello!")', respond=False)
             await ui.run_javascript('alert("Hello!")', respond=False)
 
 
         async def get_date():
         async def get_date():
-            response = await ui.run_javascript('Date()')
-            for socket, time in response.items():
-                ui.notify(f'Browser time on host {socket.client.host}: {time}')
+            time = await ui.run_javascript('Date()')
+            ui.notify(f'Browser time: {time}')
 
 
         ui.button('fire and forget', on_click=alert)
         ui.button('fire and forget', on_click=alert)
         ui.button('receive result', on_click=get_date)
         ui.button('receive result', on_click=get_date)

+ 21 - 0
nicegui/client.py

@@ -1,6 +1,7 @@
 import asyncio
 import asyncio
 import json
 import json
 import time
 import time
+import uuid
 from pathlib import Path
 from pathlib import Path
 from typing import Any, Dict, List, Optional
 from typing import Any, Dict, List, Optional
 
 
@@ -9,6 +10,7 @@ from fastapi.responses import HTMLResponse
 from . import globals, ui, vue
 from . import globals, ui, vue
 from .element import Element
 from .element import Element
 from .slot import Slot
 from .slot import Slot
+from .task_logger import create_task
 
 
 TEMPLATE = (Path(__file__).parent / 'templates' / 'index.html').read_text()
 TEMPLATE = (Path(__file__).parent / 'templates' / 'index.html').read_text()
 
 
@@ -30,6 +32,8 @@ class Client:
         self.content = ui.column().classes('q-ma-md')
         self.content = ui.column().classes('q-ma-md')
         globals.client_stack.pop()
         globals.client_stack.pop()
 
 
+        self.waiting_javascript_commands: Dict[str, str] = {}
+
     @property
     @property
     def ip(self) -> Optional[str]:
     def ip(self) -> Optional[str]:
         return self.environ.get('REMOTE_ADDR') if self.environ else None
         return self.environ.get('REMOTE_ADDR') if self.environ else None
@@ -63,3 +67,20 @@ class Client:
                 raise TimeoutError(f'No handshake after {timeout} seconds')
                 raise TimeoutError(f'No handshake after {timeout} seconds')
             await asyncio.sleep(check_interval)
             await asyncio.sleep(check_interval)
         self.is_waiting_for_handshake = False
         self.is_waiting_for_handshake = False
+
+    async def run_javascript(self, code: str, *,
+                             respond: bool = True, timeout: float = 1.0, check_interval: float = 0.01) -> Optional[str]:
+        request_id = str(uuid.uuid4())
+        command = {
+            'code': code,
+            'request_id': request_id if respond else None,
+        }
+        create_task(globals.sio.emit('run_javascript', command, room=str(self.id)))
+        if not respond:
+            return
+        deadline = time.time() + timeout
+        while request_id not in self.waiting_javascript_commands:
+            if time.time() > deadline:
+                raise TimeoutError('JavaScript did not respond in time')
+            await asyncio.sleep(check_interval)
+        return self.waiting_javascript_commands.pop(request_id)

+ 9 - 0
nicegui/javascript.py

@@ -0,0 +1,9 @@
+from typing import Optional
+
+from . import globals
+
+
+async def run_javascript(code: str, *,
+                         respond: bool = True, timeout: float = 1.0, check_interval: float = 0.01) -> Optional[str]:
+    client = globals.client_stack[-1]
+    return await client.run_javascript(code, respond=respond, timeout=timeout, check_interval=check_interval)

+ 5 - 0
nicegui/nicegui.py

@@ -71,6 +71,11 @@ def handle_event(sid: str, msg: Dict) -> None:
             sender.handle_event(msg)
             sender.handle_event(msg)
 
 
 
 
+@sio.on('javascript_response')
+def handle_event(sid: str, msg: Dict) -> None:
+    get_client(sid).waiting_javascript_commands[msg['request_id']] = msg['result']
+
+
 def get_client(sid: str) -> Client:
 def get_client(sid: str) -> Client:
     query_bytes: bytearray = sio.get_environ(sid)['asgi.scope']['query_string']
     query_bytes: bytearray = sio.get_environ(sid)['asgi.scope']['query_string']
     query = urllib.parse.parse_qs(query_bytes.decode())
     query = urllib.parse.parse_qs(query_bytes.decode())

+ 8 - 0
nicegui/templates/index.html

@@ -50,6 +50,13 @@
         return Vue.h(Vue.resolveComponent(element.tag), props, slots);
         return Vue.h(Vue.resolveComponent(element.tag), props, slots);
       }
       }
 
 
+      function run_javascript(code, request_id) {
+        const result = eval(code);
+        if (request_id) {
+          window.socket.emit("javascript_response", {request_id, result});
+        }
+      }
+
       const app = Vue.createApp({
       const app = Vue.createApp({
         data() {
         data() {
           return {
           return {
@@ -66,6 +73,7 @@
             Object.entries(msg.elements).forEach(([id, element]) => this.elements[element.id] = element);
             Object.entries(msg.elements).forEach(([id, element]) => this.elements[element.id] = element);
           });
           });
           window.socket.on("run_method", (msg) => this.$refs['r' + msg.id][msg.name](...msg.args));
           window.socket.on("run_method", (msg) => this.$refs['r' + msg.id][msg.name](...msg.args));
+          window.socket.on("run_javascript", (msg) => run_javascript(msg['code'], msg['request_id']));
           window.socket.on("notify", (msg) => Quasar.Notify.create(msg));
           window.socket.on("notify", (msg) => Quasar.Notify.create(msg));
           window.socket.on("disconnect", () => window.location.reload());
           window.socket.on("disconnect", () => window.location.reload());
         },
         },

+ 1 - 0
nicegui/ui.py

@@ -40,6 +40,7 @@ from .elements.toggle import Toggle as toggle
 from .elements.tooltip import Tooltip as tooltip
 from .elements.tooltip import Tooltip as tooltip
 from .elements.tree import Tree as tree
 from .elements.tree import Tree as tree
 from .elements.upload import Upload as upload
 from .elements.upload import Upload as upload
+from .javascript import run_javascript
 from .notify import notify
 from .notify import notify
 from .page import page
 from .page import page
 from .run import run
 from .run import run