如何从Anki插件WebView的JS调用Python函数并解决通信与报错问题?
解决Anki插件中JS与Python双向通信及钩子报错问题
先解决钩子注册的报错问题
你遇到的TypeError是因为对webview_did_receive_js_message钩子的参数格式理解有误。这个钩子属于普通钩子(而非FilterHook),注册的处理函数不需要接收handled参数,正确的签名是接受message和context两个参数,返回一个布尔值表示是否已经处理该消息。
修正后的基础钩子注册代码:
from typing import Any from aqt import gui_hooks from aqt.webview import AnkiWebView def handle_js_message(message: str, context: Any) -> bool: if not message.startswith('myaddon:'): return False # 未匹配到自定义消息,交给其他处理逻辑 # 后续处理逻辑写在这里 return True # 标记消息已处理,阻止其他钩子继续处理 # 注意是用append方法注册钩子,不是直接调用钩子 gui_hooks.webview_did_receive_js_message.append(handle_js_message)
实现JS与Python的双向通信(获取返回值)
pycmd本身是单向通信机制,JS只能发消息给Python,无法直接获取返回值。我们可以通过回调函数的方式实现双向通信,步骤如下:
1. 前端JS代码(复习器WebView内)
我们在全局对象中存储回调函数,调用pycmd时携带唯一的回调ID,等待Python处理完成后触发回调:
// 全局存储回调函数,避免内存泄漏使用后及时清理 window.pyCallbacks = {}; /** * 调用Python并获取返回值 * @param {string} command - 要执行的Python命令标识 * @param {function} callback - 接收返回结果的回调函数 */ function callPython(command, callback) { // 生成唯一回调ID const callbackId = `cb_${Math.random().toString(36).slice(2, 12)}`; // 存储回调函数 window.pyCallbacks[callbackId] = callback; // 发送消息给Python,格式:自定义前缀 + 命令 + 回调ID pycmd(`myaddon:${command}|${callbackId}`); } // 使用示例:调用Python获取用户学习数据 callPython('get_review_stats', (result) => { console.log('Python返回的复习统计:', result); // 这里可以处理返回的结果,比如渲染到页面上 });
2. 后端Python处理代码
在钩子函数中解析消息,处理业务逻辑后,通过webview.eval()主动调用JS的回调函数,传递返回值:
from typing import Any import json from aqt import gui_hooks from aqt.webview import AnkiWebView def handle_js_message(message: str, context: Any) -> bool: if not message.startswith('myaddon:'): return False # 拆分自定义前缀、命令和回调ID content = message[len('myaddon:'):] if '|' not in content: return True command, callback_id = content.split('|', 1) webview: AnkiWebView = context # context就是发送消息的WebView实例 # 根据命令处理业务逻辑,生成返回结果 result = None if command == 'get_review_stats': # 示例:模拟获取复习统计数据 result = { 'today_reviews': 25, 'remaining_cards': 18, 'avg_score': 92 } # 将结果转为安全的JSON字符串 result_json = json.dumps(result) # 执行JS代码触发回调,并清理已使用的回调函数 webview.eval(f""" if (window.pyCallbacks?.['{callback_id}']) {{ window.pyCallbacks['{callback_id}']({result_json}); delete window.pyCallbacks['{callback_id}']; // 清理避免内存泄漏 }} """) return True gui_hooks.webview_did_receive_js_message.append(handle_js_message)
关键注意事项
- 回调ID唯一性:确保每个请求的回调ID唯一,避免不同请求的回调被混淆。
- JSON转义:使用
json.dumps()处理返回结果,避免JS语法错误或XSS风险。 - 内存清理:触发回调后立即删除全局存储的回调函数,防止内存泄漏。
- WebView实例:
context参数对应发送消息的WebView实例,确保我们能准确地向发起请求的页面返回结果。
内容的提问来源于stack exchange,提问作者user3310334
相关产品推荐
相关产品推荐

