Anki插件开发必知必会:钩子函数与右键菜单定制

今天在使用Anki 背单词时,编辑笔记内容的时候,想查看下某个单词是否已经添加了,每次都要去点开浏览器窗口再手动输入再搜索,

有时候是在卡片浏览器界面中编辑笔记内容,这时候重新搜索的时候,刚才编辑的笔记就要重新找,真真的不方便,

看到安装的有些插件可以添加自定义右键菜单项,想着是不是我也可以写一个这样的插件,来添加一些功能,方便操作。这样就不用每次都得切来切去,打断心流状态,还烦得不行。

说干就干,去搜索官方帮助文档,开始鼓捣Anki插件开发。

结果,刚开始就被浇了一盆凉水,官方文档的开头有这么一句话:

Because of our limited resources, no official support is available for add-on writing.

啥意思,"资源有限,插件开发这块没啥官方说明",只有个大概简介,想研究,自己看源码!

我嘞个豆呀,这玩意这么火,这块还要自己研究?

好吧,谁让咱有这需求呢,接下来就是漫长页痛苦的Debug时间了......

好消息是,我能写这篇文章,肯定是跑通了功能,添加了菜单上去了,这里分享给有需求的小伙伴,至少知道往哪走,往哪努力吧,让你少掉几根头发!

🎯 核心摘要:本文能帮你解决什么

看完这篇,你能自己写出这样的Anki插件:

在卡片浏览器菜单中,添加"一键查单词"等菜单,点击后触发自定义逻辑。

核心就是两个东西------钩子函数菜单项定义

我会尽量用比喻的方式把它们的协作关系给你讲明白,后面也会给出可直接复用的代码骨架。
👩‍💻我是爱折腾的一名程序媛 ,喜欢研究全栈开发 的各种实践,热爱分享踩坑后的收获与思考 ,也享受用代码写出各种实用小工具解决问题的快乐。

如果你也在技术这条路上向前走,关注我,愿我们能彼此陪伴,一起成为更好的自己 🌱

🗺️ 主要内容脉络

🔹 为啥要自定义右键菜单------场景共鸣

🔹 钩子函数到底是个啥------生活化比喻

🔹 菜单定义的核心代码骨架

🔹 实战:给浏览器卡片右键加上"查词典"

🔹 必知必看的注意事项与奇技淫巧

🧩 第一部分:那个让人抓狂的下午

起因的话,开头已经说过了,还有个需求就是在复习英语卡片时,遇到个生僻词,想查在线词典。

正常操作是:选中文本 → 右键复制 → 切到浏览器 → 粘贴搜索 → 再切回来。重复几次,耐心直接清零了,关键是费时呀!

当时我就想,如果能在Anki里右键直接调接口查词,还能顺手把释义填进字段,该多爽。

前几天咱们写的 FastAPI 自定义本地词典是不是也可以发光发热!

其实Anki插件体系还是很灵活的,关键是把"什么时候触发 "和"触发后干什么 "串起来。

前者靠钩子 ,后者靠你写的函数,而右键菜单或顶部菜单只是触发方式之一。

🎯 第二部分:核心原理------钩子就是厨房里的传菜铃

别被"钩子"这词吓到。把它想象成餐厅后厨的传菜铃:

厨师(Anki)做完一道工序,就"叮"地按一下铃(触发钩子),服务员(你的插件)听到后去上菜(执行自定义代码)。

其实就是这么简单,Anki内置了一堆钩子点,比如:

🔹 卡片浏览器加载完菜单时(browser.setupMenus

🔹 复习界面显示问题前(reviewer.didShowQuestion

🔹 卡片添加、删除、修改时......

我们定义右键菜单,其实就是告诉Anki:

"嗨,在浏览器右键菜单那个钩子触发时,帮我塞进去一个'查词典'选项。"这背后对应的钩子叫 browser.setupMenus

官方文档虽然列出了,但新手容易忽略的是,你必须在这个钩子回调里去调用菜单对象的 addAction,否则菜单项死活不会出现。

还有一个大坑提醒:老版本Anki的钩子命名不太一样,比如 setupEditorButtonsbrowser.setupMenus 容易搞混。
如果你用的 Anki 版本 >= 2.1.55,认准 browser.setupMenus 就行。版本太低的话,赶紧升吧,好多好用的钩子都没有。

再说一点就是,Anki就算升级到了新版本,还是要注意下它里面的有些属性和函数,因为版本差异,就是死活找不到,这时候,你只能去源码里"移至定义",慢慢找了,结合出错信息提示,会快一些!

⚙️ 第三部分:实战演示------三步给菜单加"查词典"功能

好,咱们直接上代码骨架。假设你已经在插件文件夹里新建了 __init__.py

第一步:导入必要的模块
from aqt import mw

from aqt.browser import Browser

from aqt.qt import QAction

from anki.hooks import addHook

import webbrowser

这里 mw 是主窗口对象,各种全局资源都通过它访问。

第二步:定义菜单添加函数,并绑定钩子
def setup_context_menu(browser: Browser):

创建一个QAction,这就是菜单项

action = QAction("🔍 一键查词典", browser)

当用户点击时,执行查询函数

action.triggered.connect(lambda: lookup_word(browser))

把action塞进浏览器菜单项里

browser.form.menuEdit.addAction(action)

addHook("browser.setupMenus", setup_context_menu)

注意啊,browser.form.menuEdit 是浏览器顶栏"编辑"菜单的对象。如果你想加到其他菜单项下,要看下源码里的命名,不然会报错,找不到对象。

如果要添加到右键菜单项中,我在卡片浏览器源码里没找到相应钩子,不过我需要的是在笔记编辑界面 中添加右键菜单项,这个钩子函数我找到了,

就最下面代码里的editor_will_show_context_menu,使用方式上,和上面有点不同,但意思大差不差!
gui_hooks.editor_will_show_context_menu.append(setup_editor_context_menu)

第三步:实现查词函数
def lookup_word(browser: Browser):

获取浏览器当前选中的文本

selected = browser.current_card

if not selected:

return

这里只是示例,实际可从卡片字段取词

word = selected.note()"Front"

webbrowser.open(f"https://dict.youdao.com/search?q={word}")

这个例子是直接用电脑默认浏览器来查词典。

你完全可以结合 FastAPI 接口,把查询结果写回卡片字段,比如调个API后修改 note()["Back"]note().flush()

你可能会问:"那我怎么知道有啥钩子能用?"

最好的办法不是死记硬背,因为它可能因为版本不同,就变了,而是去Anki源码里搜 runHookaddHook 等关键词,看它本身是怎么调用的,

或者在插件开发文档里找 Hooks Reference

另外,用 aqt.utils.showInfo 打日志是最朴素的调试法,插件控制台不弹错误时,全靠它保命。

🔧 第四部分:注意事项与进阶思考

🔹 千万别在主线程里干重活

像调API、大量IO操作,记得用 mw.taskman.run_in_background 包起来,否则Anki直接卡死给你看。

🔹 菜单对象生命周期

不要在钩子外面预先创建 QAction 然后反复添加,每次 setupMenus 触发时都可能重建菜单,你只管每次新建并加入,系统会管理销毁,否则容易出重复菜单项。

🔹 多版本兼容

如果你想让插件同时兼容新旧Anki,可以加个 if hasattr(Browser, 'form') 之类的判断,但不如直接声明最低版本要求,省心。

再说个容易翻车的点:修改了代码后,记得重启Anki,网上说的什么"开发者模式"下的"重载插件"功能,我找了半天,我这个版本里也没有找到,还是重启来的干脆,还省了缓存干扰。

💬 来个小结

其实自定义Anki插件就是个搭积木 的过程------钩子 告诉你"哪里能插",菜单定义 告诉它"长什么样",你的函数 决定"插进去干什么"。

一旦捅破这层窗户纸,你会发现Anki的扩展性强得离谱。

好了,今天就聊到这啦!老样子,如果你觉得对你有那么一...点点收获 ,那就顺手点击收藏加个关注 吧,防止找不到,也让我看到我的辛苦总结能帮到各位!

如果你在编写插件时还遇到什么解决不了的问题,也欢迎评论区里聊聊,我和你一起分析,咱们一起学习,一起进步、成长!🎯

相关推荐
技术工小李1 小时前
现场评分系统|景泰2026“农创客”杯大赛实战应用宣传
python
2501_942389551 小时前
水平姿态的总装和测试
beautifulsoup·排序算法·散列表·fastapi·启发式算法·最小二乘法
nanawinona2 小时前
2026年AI量化学习,交易认知和技术实现要一起走
人工智能·python
Csvn3 小时前
Python 开发技巧 · 生成器(Generator)进阶 —— 从 yield 到 yield from,写出省内存的生产者-消费者模式
后端·python
GEO_ai_zhijian3 小时前
口碑好的工业GEO优秀企业
python·搜索引擎
mikejahn3 小时前
解析ISO网站单个标准的静态网址
python
Java面试题总结4 小时前
Python 开发技巧 · 高级装饰器 —— 从基础到工业级实战
开发语言·python
纸小铭5 小时前
[MAF预定义ChatClient中间件-01]LoggingChatClient——在调用LLM前后输出日志
python·中间件·flask
aiqianji5 小时前
有哪些支持长文的AI生成短篇小说软件?
人工智能·python