You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 18:25:13