anki个性化开发

本文最后更新于:1 个月前

Anki玩法

自定义卡片
  • 如官方介绍,说明是使用HTML页面的方式生成的模板,只需要将正确的字段填入即可自定义卡片

  • 经过测试,使用WPS将摘录的卡片数据先转换为excel文档然后分隔好数据字段,再转换为txt文档(UTF-8编码)就能导入到anki中

导入词典数据
  • 尝试导入MOJI辞书,格式不太好,并且不易处理,单词的内容也很一般,建议一个一个添加单词

  • 加入在线音频成功

可导入的数据类型
  • 图片怎么导入,能否用在线图片链接导入?
自定义软件界面/美化
  • 浪费时间了有点,版本不支持比较全的配置插件
  • 设置背景图就好
使用Markdown语法
  • 利用js脚本将markdown语法生成HTML页面
  • 修改引用本地样式表,沿用自Typora-mo-dark主题的CSS样式文件
  • 将冗余的js代码封装在render_md.js中,在卡片样式的代码编写上减少了工作

以问答形式的卡片模板为例子,简单可自定义

卡片正面

1
2
3
4
5
6
7
8
9
10
11
12
13

<link rel="stylesheet" href="md_sty.css">
<div id="front"><pre>{{Front}}</pre></div>
<script src="render_md.js"></script>
<script>
function render() {
renderMath("front");
markdown("front");
show();
}
function show() { document.getElementById("front").style.visibility = "visible";
}
</script>

卡片反面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<link rel="stylesheet" href="md_sty.css">
<div id="front"><pre>{{Front}}</pre></div>
<hr id=answer>
<div id="back"><pre>{{Back}}</pre></div>

<script src="render_md.js"></script>
<script>
function render() {
renderMath("front");
markdown("front");
renderMath("back");
markdown("back");
show();
}
function show() { document.getElementById("front").style.visibility = "visible";
document.getElementById("back").style.visibility = "visible";
}
</script>

插件自改-源于其他开发者的插件
1️⃣init.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from aqt import mw
from aqt.utils import showInfo
from anki.hooks import addHook
import anki
import os
import shutil
from .getStatics import HTMLforEditor, front, back, front_cloze, back_cloze, css

MODEL_NAME = 'MD_SUP'
CONF_NAME = 'MDKATEX'

#该方法是为了修改对于anki卡片笔记的预览界面(也就是添加笔记时候看到的页面)
def markdownPreview(editor):
#这里规定了笔记模板只在MD_SUP Basci 或 MD_SUP Cloze下启用插件功能,可以改进
if editor.note.model()["name"] in [MODEL_NAME + " Basic", MODEL_NAME + " Cloze"]:
editor.web.eval(HTMLforEditor)
#editor.web.eval可以通过传入的JavaScript脚本字符串进行相应的操作
editor.web.eval("""
var style = document.createElement('style');
style.type = 'text/css';
style.innerText = `
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
pre code {
background-color: #eee;
border: 1px solid #999;
display: block;
padding: 20px;
overflow: auto;
}`;
document.head.appendChild(style);
""")
else:
editor.web.eval("""
var area = document.getElementById('markdown-area');
if(area) area.remove();
""")

#加载笔记的时候调用该方法,会进行渲染
addHook("loadNote", markdownPreview)



def create_model_if_necessacy():
model = mw.col.models.byName(MODEL_NAME + " Basic")
model_cloze = mw.col.models.byName(MODEL_NAME + " Cloze")
m = mw.col.models

if not model:
create_model()
if not model_cloze:
create_model_cloze()

update()


def create_model():
m = mw.col.models
model = m.new(MODEL_NAME + " Basic")

field = m.newField("Front")
m.addField(model, field)

field = m.newField("Back")
m.addField(model, field)

template = m.newTemplate(MODEL_NAME + " Basic")
template['qfmt'] = front
template['afmt'] = back
model['css'] = css

m.addTemplate(model, template)
m.add(model)
m.save(model)


def create_model_cloze():
m = mw.col.models
model = m.new(MODEL_NAME + " Cloze")
model["type"] = anki.consts.MODEL_CLOZE

field = m.newField("Text")
m.addField(model, field)

field = m.newField("Back Extra")
m.addField(model, field)

template = m.newTemplate(MODEL_NAME + " Cloze")
template['qfmt'] = front_cloze
template['afmt'] = back_cloze
model['css'] = css

m.addTemplate(model, template)
m.add(model)
m.save(model)


def update():
model = mw.col.models.byName(MODEL_NAME + " Basic")
model_cloze = mw.col.models.byName(MODEL_NAME + " Cloze")

model['tmpls'][0]['qfmt'] = front
model['tmpls'][0]['afmt'] = back
model['css'] = css

model_cloze['tmpls'][0]['qfmt'] = front_cloze
model_cloze['tmpls'][0]['afmt'] = back_cloze
model_cloze['css'] = css

mw.col.models.save(model)
mw.col.models.save(model_cloze)

if os.path.isdir(os.path.join(mw.col.media.dir(), "_katex")):
shutil.rmtree(os.path.join(mw.col.media.dir(), "_katex"))

if os.path.isdir(os.path.join(mw.col.media.dir(), "_markdown-it")):
shutil.rmtree(os.path.join(mw.col.media.dir(), "_markdown-it"))

addon_path = os.path.join(os.path.dirname(os.path.realpath(__file__)))

_add_file(os.path.join(addon_path, "_katex.min.js"), "_katex.min.js")
_add_file(os.path.join(addon_path, "_katex.css"), "_katex.css")
_add_file(os.path.join(addon_path, "_auto-render.js"), "_auto-render.js")
_add_file(os.path.join(addon_path, "_markdown-it.min.js"),
"_markdown-it.min.js")
_add_file(os.path.join(addon_path, "_highlight.css"), "_highlight.css")
_add_file(os.path.join(addon_path, "_highlight.js"), "_highlight.js")
_add_file(os.path.join(addon_path, "_mhchem.js"), "_mhchem.js")
_add_file(os.path.join(addon_path, "_markdown-it-mark.js"), "_markdown-it-mark.js")

for katex_font in os.listdir(os.path.join(addon_path, "fonts")):
_add_file(os.path.join(addon_path, "fonts", katex_font), katex_font)


def _add_file(path, filename):
if not os.path.isfile(os.path.join(mw.col.media.dir(), filename)):
mw.col.media.add_file(path)


addHook("profileLoaded", create_model_if_necessacy)

由此思考,可以改进的地方:

  • 将功能的启用由原本只限于两种模板拓展至多个想要使用的模板——包含MD_即可触发
2️⃣ getStatics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#该变量是用于渲染笔记编辑器的预览样式,由于与其他变量有一些不同,暂时不做修改
HTMLforEditor = """
var area = document.getElementById('markdown-area');
if(area) area.remove();
area = document.createElement('markdown-area');
area.id = 'markdown-area';
area.style.display = 'inline-block';
area.style.overflowY = 'auto';
area.style.padding = '1%';
area.style.visibility = 'hidden';
area.style.width = '98%';
area.style.height = '100%';

var fields = document.getElementById('fields').children;

keyupFunc = function() {
var text = '# Field 1\\n' + fields[0].children[1].shadowRoot.children[2].innerHTML;
text += "\\n# Field 2\\n" + fields[1].children[1].shadowRoot.children[2].innerHTML;
render(text);
}

document.body.appendChild(area);
function replaceHTMLElementsInString(str) {
str = str.replace(/&nbsp;/gi, " ");
str = str.replace(/&tab;/gi, " ");
str = str.replace(/&gt;/gi, ">");
str = str.replace(/&lt;/gi, "<");
return str.replace(/&amp;/gi, "&");
}
"""
...
...

观察可知,该文件只是为了存储JavaScript的代码字段至几个需要被调用的变量中,并且存在很多重复的代码
改进:

  • 删除冗余的代码,并且将代码存储在js文件中,同样可以由ajax获取得到并存储至collection-media文件内(即媒体文件)
  • 引用外部文件添加自己的脚本,我的脚本先上传至自己的文件仓库,并通过GitHub文件转jsDelivr形成链接来访问下载;或者直接存放脚本文件到插件项目文件夹下
  • 由原本的两个字段渲染改进为多个可编辑字段的渲染

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!