从ANKI卡牌痛点到定制化Excel创建卡牌工具的开发之旅

一、创建背景

最近在备战软考,经朋友安利了解到ANKI卡牌这个通过记忆算法实现的个性化复习系统。它最为厉害的地方在于其卡牌系统的高度定制化,能够依据不同的知识自定义创建复习卡牌。

然而,也正是因为这种高度定制化,创建卡牌的过程变得颇为繁琐。而IT技术本身就是解决这类重复劳动的有力工具呀,所以我就萌生了一个想法:通过代码实现能够直接借助Excel来创建卡牌组,这样就能大大提高创建卡牌的效率啦。

二、开发过程

(一)初窥门径

在初步探究卡牌的组成结构后,我发现了Python的genanki包,它几乎完美地契合了我的需求。就像下面这段代码展示的那样,通过简单的设置就能创建一个卡牌的模型:

my_model = genanki.Model(
  1607392319,
  'Simple Model',
  fields=[
    {'name': 'Question'},
    {'name': 'Answer'},
  ],
  templates=[
    {
      'name': 'Card 1',
      'qfmt': '{{Question}}',
      'afmt': '{{FrontSide}}<hr id="answer">{{Answer}}',
      'css': ''
    },
  ])

再结合xlsx等相关包稍微进行一番开发,借助AI的帮助,短短几分钟我就完成了一个具备基本功能的demo版本。不过,这时候实现的卡牌在样式上实在是有些“惨不忍睹”呀。

(二)样式优化

好在我仔细阅读文档后发现可以添加CSS来美化样式,这可就对上我的“胃口”啦,毕竟CSS和HTML可是我擅长的领域呢。经过一番简单的样式调整,卡牌的颜值总算有了一定的提升,此时功能也算是完成了八成啦。

(三)功能拓展需求

但这时候简单的功能已经无法满足我日益增长的“小野心”啦,主要有以下几个方面的需求:

  1. 可视化操作界面(UI):之前那种通过运行命令的程序受众面实在太窄了,要是能有一个UI可以进行可视化操作,那使用起来就方便多了。
  2. 选择题交互优化:对于选择题来说,应该能够实现点击选项就可以翻转,这样就能及时得到正反馈,让学习体验更佳。
  3. 答案对比查漏补缺:在翻转后,最好可以知道自己选择了哪些选项,并且能够和正确答案进行对比,方便发现自己知识掌握的薄弱环节,及时查漏补缺。

(四)GUI界面绘制与翻转难题

针对UI界面的需求,我想到可以使用tkinter包来绘制,毕竟我目前也不需要特别复杂的页面嘛。可这翻转功能就没那么好实现啦,我在其他的卡牌组中看到有这个功能,但自己不管怎么尝试都没办法搞定,而且网络上关于这个的相关信息也比较少。

(五)“拿来主义”与新发现

就在我在Github上苦苦查找相关资料的时候,偶然发现了一个库:https://github.com/git9527/anki-awesome-select。它的模板中有一段代码引起了我的注意:

function flipToBack () {
    if (typeof pycmd!== "undefined") {
      pycmd("ans")
    } else if (typeof study!== "undefined") {
      study.drawAnswer()
    } else if (typeof AnkiDroidJS!== "undefined") {
      showAnswer()
    } else if (window.anki && window.sendMessage2) {
      window.sendMessage2("ankitap", "midCenter")
    }
  }

虽然不太清楚原作者是怎么找到这段代码的,但我秉持着“拿来主义”精神,先抄过来试一试,嘿,没想到还真行,当时就觉得自己可太棒啦!而且通过这个我还知道了这个模板是可以运行JS的,这又进入到我熟悉的领域啦。于是我就着手去解决翻转数据保留的问题,可事实证明我还是小瞧了这个问题呀,因为卡牌的正面和背面是两个互相不关联的HTML,无论我怎么修改,都没办法在背面获取到我选择的选项。

(六)最终难题攻克

继续在Github的“知识海洋”里遨游,功夫不负有心人,我又发现了这样一个库:anki-persistence。它里面的一段代码如下:

<script>
// v1.1.8 - https://github.com/SimonLammer/anki-persistence/blob/584396fea9dea0921011671a47a0fdda19265e62/script.js
if(void 0===window.Persistence){var e="github.com/SimonLammer/anki-persistence/",t="_default";if(window.Persistence_sessionStorage=function(){var i=!1;try{"object"==typeof window.sessionStorage&&(i=!0,this.clear=function(){for(var t=0;t<sessionStorage.length;t++){var i=sessionStorage.key(t);0==i.indexOf(e)&&(sessionStorage.removeItem(i),t--)}},this.setItem=function(i,n){void 0==n&&(n=i,i=t),sessionStorage.setItem(e+i,JSON.stringify(n))},this.getItem=function(i){return void 0==i&&(i=t),JSON.parse(sessionStorage.getItem(e+i))},this.removeItem=function(i){void 0==i&&(i=t),sessionStorage.removeItem(e+i)},this.getAllKeys=function(){for(var t=[],i=Object.keys(sessionStorage),n=0;n<i.length;n++){var s=i[n];0==s.indexOf(e)&&t.push(s.substring(e.length,s.length))}return t.sort()})}catch(n){}this.isAvailable=function(){return i}},window.Persistence_windowKey=function(i){var n=window[i],s=!1;"object"==typeof n&&(s=!0,this.clear=function(){n[e]={}},this.setItem=function(i,s){void 0==s&&(s=i,i=t),n[e][i]=s},this.getItem=function(i){return void 0==i&&(i=t),void 0==n[e][i]?null:n[e][i]},this.removeItem=function(i){void 0==i&&(i=t),delete n[e][i]},this.getAllKeys=function(){return Object.keys(n[e])},void 0==n[e]&&this.clear()),this.isAvailable=function(){return s}},window.Persistence=new Persistence_sessionStorage,Persistence.isAvailable()||(window.Persistence=new Persistence_windowKey("py")),!Persistence.isAvailable()){var i=window.location.toString().indexOf("title"),n=window.location.toString().indexOf("main",i);i>0&&n>0&&n-i<10&&(window.Persistence=new Persistence_windowKey("qt"))}}
</script>

{{Front}}

<div id="front"></div>

<script>
var number = 0.4;                 // Default to 0.4.
if (Persistence.isAvailable()) {  // Check whether Persistence works on the client.
  number = Persistence.getItem(); // Retrieve a previously stored number and override the default. (In case this is executed on the backside as well by {{FrontSide}})
  if (number == null) {           // If there was no number stored previously:
    number = Math.random();       //   1. Create a random number and override the default.
    Persistence.setItem(number);  //   2. Store that number
  }
}
window.front.appendChild(document.createTextNode(number)); // Print the number.
</script>

借助这个库,终于把所有的难点都攻克啦,接下来就是愉快的编码过程,不断完善整个工具啦。

(七)代码分享

完整的代码我已经提交到了我的Github仓库:https://github.com/Gaylen-Hu/creatANKIToExcel,感兴趣的小伙伴们可以去看看哦,希望能给大家在类似的开发或者ANKI卡牌定制化方面带来一些启发呢。

希望这篇博客能让各位程序员和技术爱好者们对我的这次开发之旅有所了解呀,要是有什么问题或者建议,欢迎大家在评论区留言哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值