- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
Anki 使卡片能够使用 JavaScript。例如,一张卡片可以包含如下内容:
<script>
//JavaScript code here
</script>
JavaScript 代码将在卡片显示时执行。
为了通过使此类脚本与 Anki 后端交互来提供更大的灵 active (例如,为了更改笔记字段的值、添加标签、影响日程安排等),我想为 Anki(版本 2)编写一个插件,实现一些后端功能并启用卡片的 JavaScript 脚本来调用它们。
例如,假设我的插件中有一个与 Anki 对象交互的 (Python) 函数:
def myFunc():
# use plug-in's ability to interact with Anki's objects to do stuff
我希望能够允许卡片的 JavaScript 调用该函数,例如在卡片中包含这样的内容:
<script>
myFunc(); // This should invoke the plug-in's myFunc().
</script>
我知道如何添加 Hook ,以便各种 Anki 事件调用我的插件功能,但我想允许卡片中的 JavaScript 这样做。这完全可以做到吗?如果可以,那么怎么做?谢谢!
最佳答案
已阅读 post通过@Louis 链接,并与一些同事讨论了这个问题,并尝试了各种方法,我终于想出了一个解决方案:
思想可以概括为这两个关键点(和两个子关键点):
该插件可以创建一个或多个对象,这些对象将“公开”给卡片的 JavaScript 脚本,以便卡片脚本可以访问这些对象 - 它们的字段和方法 - 就好像它们是脚本的范围。
和
PyQt 提供了将此类对象“注入(inject)”到 WebView 的功能。
下面的代码展示了如何实现这一点。它为卡片脚本提供了一种检查当前状态(“问题”或“答案”)的方法,以及一种访问(读取,更重要的是 - 写入)笔记字段的方法。
from aqt import mw # Anki's main window object
from aqt import mw QObject # Our exposed object will be an instance of a subclass of QObject.
from aqt import mw pyqtSlot # a decorator for exposed methods
from aqt import mw pyqtProperty # a decorator for exposed properties
from anki.hooks import wrap # We will need this to hook to specific Anki functions in order to make sure the injection happens in time.
# a class whose instance(s) we can expose to card scripts
class CardScriptObject(QObject):
# some "private" fields - card scripts cannot access these directly
_state = None
_card = None
_note = None
# Using pyqtProperty we create a property accessible from the card script.
# We have to provide the type of the property (in this case str).
# The second argument is a getter method.
# This property is read-only. To make it writeable we would add a setter method as a third argument.
state = pyqtProperty(str, lambda self: self._state)
# The following methods are exposed to the card script owing to the pyqtSlot decorator.
# Without it they would be "private".
@pyqtSlot(str, result = str) # We have to provide the argument type(s) (excluding self),
# as well as the type of the return value - with the named result argument, if a value is to be returned.
def getField(self, name):
return self._note[name]
# Another method, without a return value:
@pyqtSlot(str, str)
def setField(self, name, value):
self._note[name] = value
self._note.flush()
# An example of a method that can be invoked with two different signatures -
# pyqtSlot has to be used for each possible signature:
# (This method replaces the above two.
# All three have been included here for the sake of the example.)
@pyqtSlot(str, result = str)
@pyqtSlot(str, str)
def field(self, name, value = None): # sets a field if value given, gets a field otherwise
if value is None: return self._note[name]
self._note[name] = value
self._note.flush()
cardScriptObject = CardScriptObject() # the object to expose to card scripts
flag = None # This flag is used in the injection process, which follows.
# This is a hook to Anki's reviewer's _initWeb method.
# It lets the plug-in know the reviewer's webview is being initialised.
# (It would be too early to perform the injection here, as this method is called before the webview is initialised.
# And it would be too late to do it after _initWeb, as the first card would have already been shown.
# Hence this mechanism.)
def _initWeb():
global flag
flag = True
# This is a hook to Anki's reviewer's _showQuestion method.
# It populates our cardScriptObject's "private" fields with the relevant values,
# and more importantly, it exposes ("injects") the object to the webview's JavaScript scope -
# but only if this is the first card since the last initialisation, otherwise the object is already exposed.
def _showQuestion():
global cardScriptObject, flag
if flag:
flag = False
# The following line does the injection.
# In this example our cardScriptObject will be accessible from card scripts
# using the name pluginObject.
mw.web.page().mainFrame().addToJavaScriptWindowObject("pluginObject", cardScriptObject)
cardScriptObject._state = "question"
cardScriptObject._card = mw.reviewer.card
cardScriptObject._note = mw.reviewer.card.note()
# The following hook to Anki's reviewer's _showAnswer is not necessary for the injection,
# but in this example it serves to update the state.
def _showAnswer():
global cardScriptObject
cardScriptObject._state = "answer"
# adding our hooks
# In order to already have our object injected when the first card is shown (so that its scripts can "enjoy" this plug-in),
# and in order for the card scripts to have access to up-to-date information,
# our hooks must be executed _before_ the relevant Anki methods.
mw.reviewer._initWeb = wrap(mw.reviewer._initWeb, _initWeb, "before")
mw.reviewer._showQuestion = wrap(mw.reviewer._showQuestion, _showQuestion, "before")
mw.reviewer._showAnswer = wrap(mw.reviewer._showAnswer, _showAnswer, "before")
就是这样!安装这样的插件后,卡片中的 JavaScript 脚本可以使用 pluginObject.state 检查它是作为问题的一部分还是作为答案的一部分运行(也可以通过将问题部分包装在答案模板中来实现)使用设置变量的脚本,但这更简洁),pluginObject.field(name) 从注释中获取字段的值(也可以通过使用 Anki 的预处理器将字段直接注入(inject) JavaScript 代码来实现)和 pluginObject.field(name, value) 来设置注释中字段的值(据我所知,到目前为止还不能完成)。当然,可以将许多其他功能编写到我们的 CardScriptObject 中,以允许卡片脚本执行更多操作(读取/更改配置、实现另一个问答机制、与调度程序交互等...)。
如果有人可以提出改进建议,我很想听听。具体来说,我感兴趣的是:
关于javascript - 插件如何增强 Anki 的 JavaScript?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23183105/
我在互联网上搜索了很多小时,但没有找到满意的结果,所以 -VSTO Addin 和 COM Addin(我们作为类库项目制作并使用 Excel 对象)之间有什么区别?VSTO 项目是否有任何限制,例如
我在互联网上搜索了很多小时,但没有找到满意的结果,所以 -VSTO Addin 和 COM Addin(我们作为类库项目制作并使用 Excel 对象)之间有什么区别?VSTO 项目是否有任何限制,例如
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
我正在寻找有关如何构建可扩展 WCF 服务器(具有动态加载的服务)的建议,最好使用 System.Addins 或 MEF。 服务器应托管实现最小“插件”API(StartService/StopSe
有没有一种方法可以使用加载浏览器扩展/插件/插件的 headless 浏览器(即 PhantomJS、Selenium)来运行自动测试? 更具体地说,我想模拟广告拦截器(如 Ghostery、ad-b
我是 gradle 的新手,我使用 artifactory 作为我的 repo 服务器。我在网上查看了如何将我的项目发布到我的 repo 服务器,发现我可以使用 maven-publish 或使用 a
我想禁用某些状态的点击/事件,并仅使少数状态可点击。我通读了http://newsignature.github.io/us-map/处的文档,并且找不到与此问题相关的任何内容。 最佳答案 http:
据我了解,在Intellij中使用idea插件打开Maven构建的项目并不是最好的方法,即调用: mvn idea:idea 但是直接打开pom文件(Intellij有默认的Maven插件);同样的事
使用Artifactory plugin对于 Jenkins pipeline 来说是一种幸福,只要遵循文档就可以了。但后来我介绍了Maven Flatten plugin解析父模块和子模块 mvn
我已经安装了Elasticsearch版本1.7.1。一切正常。我也安装了 JDBC 驱动程序。检查下面我的插件文件夹 目录E:\Xampp\htdocs\my-elastic\elasticsear
在我使用 webpack common chunks 插件创建包含第三方库(如 angular、react、lodash 等)的 vendor 包之前,但后来我知道了 webpack dll
我们正在尝试使用(Jenkins、sonar、eclipse ...)安装 CI 平台。 为了让每个开发人员都可以在提交之前对他的代码进行分析,我想知道两种选择: 使用 Sonar 插件运行本地分析。
我知道这是一个比较特殊的问题。尽管如此,也许有些人知道这一点: 我想在 Eclipse 中使用 Maven 编译 Hector=> 分支:0.7.0 和标签:hector-0.7.0-29(https
我卡住了。我一直在尝试寻找或自己创建一个简单的准系统示例,说明如何为 VS 2010 Express 创建 Outlook 插件。我知道这在 VS 2010 Pro 中更简单,但是,在快速版本中真的不
我有以下排除过滤器来忽略所有 R 文件类: findbugs-exclude-filter.xml 当我将它用于 FindBugs-IDEA 插件时,它可以
我刚开始玩 CakePHP,我发现了 Wildflower CMS .我喜欢这个想法,并打算开始修补它。不过,我有一个问题。 在自述文件中,我发现了以下内容:“Wildflower 不是也不会是 Ca
虽然现在大部分情况都是使用n-api来编写插件,但是底层毕竟是v8(和libuv),使用v8编写简单的插件,同时熟悉v8的使用。 本文介绍在写c++插件时,简单又常用的写法,其实本质上,写插件
本篇是 Python 系列教程第 3 篇,更多内容敬请访问我的 Python 合集 Visual Studio Code的安装非常简单,就不放这里增加文章篇幅了。 相比PyCharm,V
Maven – 插件 什么是 Maven 插件? Maven 实际上是一个依赖插件执行的框架,每个任务实际上是由插件完成。Maven 插件通常被用来: 创建 jar 文件 创建 war
我正在编写一个插件来添加带有标签 [deposit_page] 的页面;该标记应替换为一些 PHP 代码。 这就是我所拥有的,但它不起作用。有什么我遗漏或做错了什么吗? function deposi
我是一名优秀的程序员,十分优秀!