python端的函數名是如何傳遞給js端的
核心步驟:將函數名列表注入到動態生成的 eel.js
中,這樣前端一開始引用的eel.js本身已經包含有py_function的函數名列表了。你打開開發者工具看看瀏覽器中的 eel.js文件源代碼就知道了。
具體實現:
# 讀取eel.js源文件,把代碼放入_eel_js這個變量中
mimetypes.add_type('application/javascript', '.js')
_eel_js_file: str = pkg.resource_filename('eel', 'eel.js')
_eel_js: str = open(_eel_js_file, encoding='utf-8').read()
# Bottle Routesdef _eel() -> str:# 設置窗口大小start_geometry = {'default': {'size': _start_args['size'],'position': _start_args['position']},'pages': _start_args['geometry']}# 把py函數名注入eel.js的源碼中(改寫eel.js的源碼)page = _eel_js.replace('/** _py_functions **/','_py_functions: %s,' % list(_exposed_functions.keys()))page = page.replace('/** _start_geometry **/','_start_geometry: %s,' % _safe_json(start_geometry))btl.response.content_type = 'application/javascript' # 由Bottle服務器對外提供/eel.js供訪問_set_response_headers(btl.response)return page
JS端的函數名是如何傳遞給python端的
?核心步驟:python端掃描/讀取eel.init(path)中的path整個目錄(含子目錄)的所有.js和.html文件,通過正則表達式匹配?eel.expose(xxxx),來獲得暴露的函數名,然后創建同名的python函數。
你甚至可以專門建一個目錄,這個目錄只存放一個文本文件,把所有暴露的js函數名以eel.expose(js_function_name) 的形式記錄到一個文件中,并以.js為擴展名命名,也可以。
//expose_js_function_name.jseel.expose(say_hello_js);
eel.expose(my_js_function_1);
eel.expose(my_js_function_2);
eel.expose(my_js_function_3);
eel.expose(my_js_function_4);
具體來說,eel.init(path)是通過遍歷path文件夾及其子目錄的全部指定擴展名的文件,并通過語法解析器 EXPOSED_JS_FUNCTIONS (基于PyParsing構建)進行匹配。
EXPOSED_JS_FUNCTIONS的解釋規則是:用正則表達式匹配,解析得到函數名,這些函數名被存儲在js_functions這個集合中。
得到這些js函數名后,通過_mock_js_function() 構建同名函數,構建的這個函數對于eel這個類來說是全局函數,所以對于main.py來說,就是【eel.同名函數】,就可以通過eel.js_function_name() 調用了。
?
官方源代碼:
# 如果程序未被PyInstaller打包成exe,則返回path的絕對路徑,否則exe創建的臨時資源目錄_MEIPASS
def _get_real_path(path: str) -> str:if getattr(sys, 'frozen', False):return os.path.join(sys._MEIPASS, path) # type: ignore # sys._MEIPASS is dynamically added by PyInstallerelse:return os.path.abspath(path)'''
當你使用 PyInstaller 將腳本+資源打包成一個exe后。運行exe時,會動態創建一個臨時目錄(通常是在系統的臨時文件夾中),并將可執行文件內部的所有資源解壓到這個臨時目錄。sys._MEIPASS 就是這個臨時目錄的路徑。
'''
def init(path: str, allowed_extensions: List[str] = ['.js', '.html', '.txt', '.htm','.xhtml', '.vue'], js_result_timeout: int = 10000) -> None:global root_path, _js_functions, _js_result_timeoutroot_path = _get_real_path(path)js_functions = set()for root, _, files in os.walk(root_path): # 遍歷它的子目錄for name in files:if not any(name.endswith(ext) for ext in allowed_extensions):continuetry:with open(os.path.join(root, name), encoding='utf-8') as file:contents = file.read()expose_calls = set()matches = EXPOSED_JS_FUNCTIONS.parseString(contents).asList() # 對文件進行解釋,把【暴露給python的js函數】匹配出來。for expose_call in matches:# Verify that function name is validmsg = "eel.expose() call contains '(' or '='"assert rgx.findall(r'[\(=]', expose_call) == [], msgexpose_calls.add(expose_call) # 收集此文件的暴露函數js_functions.update(expose_calls) # 收集全部文件的暴露函數except UnicodeDecodeError:pass # Malformed file probably_js_functions = list(js_functions)for js_function in _js_functions:_mock_js_function(js_function) # 將找到的JS函數名稱保存起來,并準備在 websocket 連接時使用_js_result_timeout = js_result_timeout