Sfoglia il codice sorgente

feat: add `run_js()` 、 `eval_js()`

wangweimin 5 anni fa
parent
commit
69f2f1a03f
4 ha cambiato i file con 92 aggiunte e 3 eliminazioni
  1. 11 0
      docs/spec.rst
  2. 55 1
      pywebio/session/__init__.py
  3. 19 0
      webiojs/src/handlers/script.ts
  4. 7 2
      webiojs/src/main.ts

+ 11 - 0
docs/spec.rst

@@ -247,6 +247,12 @@ output_ctl:
 * position: top/middle/bottom 与scroll_to一起出现, 表示滚动页面,让锚点位于屏幕可视区域顶部/中部/底部
 * remove: 将给定的scope连同scope处的内容移除
 
+run_script:
+^^^^^^^^^^^^^^^
+运行js代码
+
+命令 spec 字段为字符串格式的要运行的js代码
+
 Event
 ------------
 
@@ -289,3 +295,8 @@ from_cancel:
 
 事件 ``data`` 字段为 ``None``
 
+js_yield:
+^^^^^^^^^^^^^^^
+js代码提交数据
+
+事件 ``data`` 字段为相应的数据

+ 55 - 1
pywebio/session/__init__.py

@@ -2,6 +2,8 @@ r"""
 
 .. autofunction:: run_async
 .. autofunction:: run_asyncio_coroutine
+.. autofunction:: run_js
+.. autofunction:: eval_js
 .. autofunction:: register_thread
 .. autofunction:: defer_call
 .. autofunction:: hold
@@ -24,7 +26,8 @@ from ..utils import iscoroutinefunction, isgeneratorfunction, run_as_function, t
 # 当前进程中正在使用的会话实现的列表
 _active_session_cls = []
 
-__all__ = ['run_async', 'run_asyncio_coroutine', 'register_thread', 'hold', 'defer_call', 'data', 'get_info']
+__all__ = ['run_async', 'run_asyncio_coroutine', 'register_thread', 'hold', 'defer_call', 'data', 'get_info',
+           'run_js', 'eval_js']
 
 
 def register_session_implement_for_target(target_func):
@@ -132,6 +135,57 @@ def hold():
         yield next_client_event()
 
 
+def run_js(code):
+    """运行js代码.
+
+    代码运行在浏览器的JS全局作用域中
+
+    :param str code: js代码
+    """
+    from ..io_ctrl import send_msg
+    send_msg('run_script', spec=code)
+
+
+@chose_impl
+def eval_js(expression):
+    """执行js表达式,并获取表达式的值
+
+    :param str expression: js表达式. 表达式的值需要能JSON序列化
+    :return: js表达式的值
+
+    注意⚠️:在 :ref:`基于协程 <coroutine_based_session>` 的会话上下文中,需要使用 ``await eval_js(expression)`` 语法来进行调用。
+
+    Example::
+
+        current_url = eval_js("window.location.href")
+
+        function_res = eval_js('''(function(){
+            var a = 1;
+            a += 100;
+            return a;
+        })()''')
+    """
+    script = r"""
+    (function(WebIO){
+        let result = null;
+        try{
+            result = eval(%r);
+        }catch{};
+        
+        WebIO.sendMessage({
+            event: "js_yield",
+            task_id: WebIOCurrentTaskID,  // local var in run_script command
+            data: result || null
+        });
+    })(WebIO);""" % expression
+
+    run_js(script)
+
+    res = yield next_client_event()
+    assert res['event'] == 'js_yield', "Internal Error, please report this bug to us"
+    return res['data']
+
+
 @check_session_impl(CoroutineBasedSession)
 def run_async(coro_obj):
     """异步运行协程对象。协程中依然可以调用 PyWebIO 交互函数。 仅能在 :ref:`基于协程 <coroutine_based_session>` 的会话上下文中调用

+ 19 - 0
webiojs/src/handlers/script.ts

@@ -0,0 +1,19 @@
+import {Command, Session} from "../session";
+import {CommandHandler} from "./base";
+
+
+export class ScriptHandler implements CommandHandler {
+    session: Session;
+
+    accept_command = ['run_script'];
+
+    constructor(session: Session) {
+        this.session = session;
+    }
+
+    handle_message(msg: Command) {
+        let script = msg.spec as string;
+        const script_func = new Function('WebIOCurrentTaskID', script);
+        script_func(msg.task_id);
+    }
+}

+ 7 - 2
webiojs/src/main.ts

@@ -1,11 +1,12 @@
 import {config as appConfig, state} from "./state";
-import {Command, HttpSession, is_http_backend, Session, WebSocketSession} from "./session";
+import {ClientEvent, Command, HttpSession, is_http_backend, Session, WebSocketSession} from "./session";
 import {InputHandler} from "./handlers/input"
 import {OutputHandler} from "./handlers/output"
 import {DisplayAreaButtonOnClick} from "./models/output"
 import {CloseHandler, CommandDispatcher} from "./handlers/base"
 import {PopupHandler} from "./handlers/popup";
 import {openApp} from "./utils";
+import {ScriptHandler} from "./handlers/script";
 
 // 获取后端API地址
 function get_backend_addr() {
@@ -26,8 +27,9 @@ function set_up_session(webio_session: Session, output_container_elem: JQuery, i
     let input_ctrl = new InputHandler(webio_session, input_container_elem);
     let popup_ctrl = new PopupHandler(webio_session);
     let close_ctrl = new CloseHandler(webio_session);
+    let script_ctrl = new ScriptHandler(webio_session);
 
-    let dispatcher = new CommandDispatcher(output_ctrl, input_ctrl, popup_ctrl, close_ctrl);
+    let dispatcher = new CommandDispatcher(output_ctrl, input_ctrl, popup_ctrl, close_ctrl, script_ctrl);
 
     webio_session.on_server_message((msg: Command) => {
         let ok = dispatcher.dispatch_message(msg);
@@ -57,6 +59,9 @@ function startWebIOClient(output_container_elem: JQuery, input_container_elem: J
 // @ts-ignore
 window.WebIO = {
     'startWebIOClient': startWebIOClient,
+    'sendMessage': (msg: ClientEvent) => {
+        return state.CurrentSession.send_message(msg);
+    },
     'openApp': openApp,
     'DisplayAreaButtonOnClick': DisplayAreaButtonOnClick,
 };