一日一技 | 用 AutoHotkey 偷偷背单词,甚至读本书
作者通过AutoHotkey编写脚本,在工作间隙利用工具提示窗口实现轻量级背单词和阅读功能。该脚本支持随机显示单词及其释义、收藏单词、测试模式以及逐行阅读书籍内容,并可通过热键操作实现便捷切换和退出。 2025-7-28 03:30:30 Author: sspai.com(查看原文) 阅读量:19 收藏

一日一技 | 用 AutoHotkey 偷偷背单词,甚至读本书

Matrix 首页推荐 

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。 

文章代表作者个人观点,少数派仅对标题和排版略作修改。


同许多人一样,我在人生的每个阶段里,都多次启动过背单词的计划,当然,也每次都没有坚持下去。我认为,这其中一个原因在于,无论是小时候背单词书,还是现在使用手机上的单词软件,都过于强调学习计划。然而,生活中的变化和诱惑实在太多,在没有考试等因素驱动的情况下,只要有一天没有按照计划行事,那么遗忘和放弃就是自然而然的结果。

总而言之,作为工作中实际上不是特别需要使用英文的打工人,我没有很强的动力去每天背上十个或者更多单词。我只想要在打工的间隙,悄无声息地偶尔学习一下,无须低头看手机,不强求每天背单词的数量,不考虑科学的记忆曲线,背到了算赚到,没记住那拜拜下次再见。

出于这个目的,我以「桌面」和「背单词」为关键字进行了检索,发现市面上的此类软件不少,应当也有很多优秀的产品。可惜的是,这些软件对我来说都不够轻量,显得太正式,界面和丰富,功能太复杂了。其中看起来最符合我要求的一款,叫 ToastFish,据说是让用户可以在通知栏背单词,然而,它在我这台破工作电脑上并没有运行成功。另外,从介绍上看,这款软件也逃脱不了「本次背多少个单词」这一让我感觉有些焦虑的设定。

于是,我很自然地又想起了那个已经在我电脑后台默默运行的 AutoHotkey 脚本,自从我因为工作而开始使用 Windows 办公后,AutoHotkey 给予我很大的帮助,具体可参见这篇文章。上一次我使用 AutoHotkey 编写脚本,是为了写划词翻译和截图 OCR,具体可以参见这篇文章。结合我的目标,很容易联想到 AutoHotkey 自带的 ToolTip 函数,其效果是显示一个工具提示窗口。窗口非常小,用来不着痕迹地背单词可以说是刚刚好。

AutoHotkey 的语法足够简单,配合上现在的 AI 工具,经过一点尝试,我就完成了一个基本可用的背单词小脚本。想到工具提示窗口小巧、不易察觉的特性,我又顺手把它改成了一个阅读文章的小脚本,相比起网上使用 Word 文档或者记事本的摸鱼技巧,利用工具提示窗口更加轻便和隐蔽。

即便算上空行,两个脚本加起来也不过 200 行左右。不需要额外下载软件(当然单词本和书籍是需要另外准备的),就拥有了两个不同类型软件的核心功能,我认为是足够经济实惠的。因此,我也认为值得在这里分享。

下面,我先将脚本实现的全部功能展示如下。诸位如果对此感兴趣,可以继续看后文,我会解说其中的核心功能部分,并在最后列举一些额外功能的实现思路,以方便大家能够按需修改。现有功能的完整的代码我还是会放在文末。

按下设定的热键进入或退出背单词模式(所有热键均可自定义,下同):

  • 按下热键,在鼠标所在的位置会随机显示一个单词,再点击热键可以查看单词的音标和释义;
  • 在此基础上,可以通过热键调用其他词典(比如 GoldenDict)以进一步查看单词的其他释义、例句(下图未展示),或者收藏单词以便下次复习,又或者彻底删除太简单的单词;
  • 可以随时切换单词表为已收藏的单词,进入「复习模式」开始复习;
  • 也可以随时切换以进入「测试模式」,此时按下热键首先显示的不是单词本身,而是对应的音标和释义,方便检查自己是否学会了单词的拼写;
  • 如果有一段时间未学习单词,会自动退出单词模式(防止误触热键)。
单词模式:从开始到 abandon

按下设定的另一组热键进入或退出看书模式:

  • 按下热键开关显示一行文字(推荐使用鼠标中键);
  • 按下热键上下换行(推荐使用鼠标滚轮);
  • 如果有一段时间没有看书,会自动退出看书模式(防止误触热键);
  • 下次看书时,自动从上次退出时的一行开始。
看书模式:波澜不惊,心旷神怡

第一项功能:背个单词

首先,我们需要一张词汇表。这里必须感谢具有分享精神的每个人,如今要取得一份 TXT 格式的单词本并不困难,比如我自己使用的单词本来自这里

总体来说,满足如下条件的文档就非常符合我的要求:

  1. 每行一个单词;
  2. 单词后跟着音标、释义等,并以空格隔开。
像这样的单词表就很合适

话虽如此,第一个条件不满足也可以,只不过在读取单词表的时候要多写一些判断条件,不以按「行」区分单词,而是用分割线或其他符号来标记。

第二个条件不满足也没有任何关系,因为我们完全可以设计成只调用其他词典来显示释义。

接下来是脚本,我给单词模式添加了开关功能,这就像是打开了一个软件。这么做的好处是,我不需要把各种功能刻意放在一些特别复杂或不常用的热键上。

在 AutoHotkey 实现「开关」并不困难,只需要将一个变量设置为单词模式开关的标记。然后,在后续的所有操作上,一律使用 #HotIf,表示满足特定条件下时热键才生效。我自己使用的是 Ctrl 加上鼠标的一个侧键(XButton1)来开关单词模式。

words_flag:= false ; 标记是否进入了单词模式
^XButton1::
{
global words_flag
words_flag:= !words_flag ; 每次按下热键,改变标记的状态
; ……
}

#HotIf words_flag ; 仅在 word_flag 为 true 的时候,后续的热键才生效
; ……
#HotIf

在打开单词模式的同时,有必要预读取一次单词本,防止我的破电脑在后续显示单词时因反复读取而产生任何卡顿。

一开始我在获取单词表时,中文和音标总是显示为乱码。由于我对文字编码问题从来是搞不明白的,所以主要的解决思路就是尝试。最终,我在这台电脑上的解决方法是:

  • 将下载下来的 TXT 单词本手动保存为带有 BOM 的 UTF-8 编码文件;
  • 在使用 AutoHotkey 写入时指定使用「UTF-8」编码;
  • 在使用 AutoHotkey 读取时指定使用「UTF-8-RAW」编码。
记得选择编码

AutoHotkey 的常用的编码还包括「UTF-8」和「CP936」(GBK/GB2312),如果出现乱码,不妨一试。

global words_list
Loop Read, words_path . "words.txt", "UTF-8-RAW" ; 读取单词本内容
words_list.Push(A_LoopReadLine)

接下来,我们需要按下热键显示一个单词。我使用的热键是同样是鼠标侧键,如果使用的鼠标没有侧键,我建议使用一个相对不容易误操作的组合键作为热键。产生单词的部分,最简单的写法是生成一个随机数,然后显示单词表里对应行数的内容。

XButton1::
{
word_index:= Random(1, words_list.Length) ; 生成 1 到单词表长度之间的随机数
ToolTip(words_list[word_index])
}

为了区别于单词壁纸,让背单词的过程更接近于「背」而不是「看」,我们可以分两步显示词汇表的内容。一开始只显示单词本身,先想想自己是否认识这个单词,再单击鼠标左键后才显示对应的音标和释义。或者反过来,一开始只显示音标和释义,先猜猜这个单词如何拼写,再显示单词原文,这就相当于是各种背单词软件中的「测试模式」。

exam_flag:= false
^MButton:: ; Ctrl + 鼠标中键开启或关闭“测试模式”
{
global exam_flag
exam_flag:= !exam_flag
if exam_flag
ToolTip("测试模式")
else
ToolTip("普通模式")
SetTimer () => ToolTip(), -800
}

XButton1::
{
global words_list, word_index
word1:= StrSplit(words_list[word_index], " ", , 2)[1] ; StrSplit 分割空格前后的内容
word2:= StrSplit(words_list[word_index], " ", , 2)[2]
if exam_flag
ToolTip(word2)
else
ToolTip(word1)
KeyWait("LButton", "D") ; 不管一开始显示什么,单击鼠标左键,都会显示完整的单词+注释等内容
ToolTip(word1 . "`n" . word2)
}

其实到这一步为止,已经完成了我最初设想的背单词功能,如果不需要更多花里胡哨的功能,只需要在上面的内容里再加上两行单击关闭工具提示框的代码即可:

KeyWait("LButton", "D") 
ToolTip()

不过我还是为它适当添加了一些功能,包括查看词典,收藏和删掉单词。为此,我们需要再增加一个标记,仅在单词显示后这些功能才生效。

dict_flag:= false ; 标记现在是不是显示了单词内容
XButton1::
{
; ……
global dict_flag
dict_flag:= true
}

首先是调用其他词典软件查看释义,我这里设置设置的热键是键盘上的 D 键。

设置快捷键算是 AutoHotkey 本职工作,不存在难度。由于我下载过 GoldenDict,因此这里就使用这个软件。它可以读取剪贴板内的单词,并通过浮窗显示释义。虽然,在这台电脑上的反应不算快,但偶尔查一次,仍不失轻便。AutoHotkey 也提供了存放和恢复剪贴板内容的方法(当然某些特殊格式可能无法被识别),可以尽可能地减少对日常工作的影响。

GoldenDict 设置
d::
{
; ……
tmp:= ClipboardAll() ; 将剪贴板内容暂存下来
A_Clipboard:= StrSplit(words_list[word_index], " ", 2)[1] ; 复制单词到剪贴板
Send "^+c" ; 这是我设置的 GoldenDict 查单词快捷键
A_Clipboard:= tmp ; 恢复剪贴板
}

其次是收藏单词,我设置的热键是键盘上的 S 键。

收藏功能依靠保存至同目录下的「favourites.txt」文档来实现,你可以事先在文件夹内手动创建好文件,也可以像我一样在进入单词模式或首次收藏功能的时候创建这个文件。为了背收藏的单词,我们需要增加一个切换单词本的操作,我自己设置的快捷键是 Shift + 鼠标侧键。

; 创建 favourites.txt 的操作
if !FileExist(words_path . "favourites.txt")
FileOpen(words_path . "favourites.txt", "w", "UTF-8").Write(Chr(0xFEFF) "")

; 将单词收藏到 favourites.txt 的操作
s::
{
; ……
; 下面这个 if 的目的是判断单词在收藏里有没有出现过
if !Instr(FileRead(words_path . "favourites.txt"), words_list[word_index])
FileOpen(words_path . "favourites.txt", "a", "utf-8").Write(words_list[word_index] . "`n")
}

; 切换单词表为 favourites.txt 的操作
favourite_flag:= false
+XButton1::
{
global favourite_flag, words_list
words_list:= [] ; 清空单词表,切换为已收藏的单词表或原始单词本
favourite_flag:= !favourite_flag
if favourite_flag
{
Loop Read, words_path . "favourites.txt", "UTF-8-RAW"
words_list.Push(A_LoopReadLine)
ToolTip Format("复习模式")
}
else
{
Loop Read, words_path . "words.txt", "UTF-8-RAW"
words_list.Push(A_LoopReadLine)
ToolTip Format("结束复习")
}
SetTimer () => ToolTip(), -800
}

还有一种情况是这单词实在太简单了,我们不想要再次看到它。

那么实际操作就是从单词本里彻底删掉这一行,需要修改单词列表和单词文件。如果我们选择的单词表是相对全面或者基础的,那么删掉单词会是一个很常用的功能,因此我这里使用的热键是鼠标的右键(RButton)。也就是说,对于还想要再看到的单词,左键关闭显示,对于不想再见到的单词,右键删除。

RButton::
{
; ……
words_list.RemoveAt(word_index) ; 删除词汇表的这一行
new_list:= ""
for i in words_list
new_list.= i "`n"
; 覆盖 TXT 单词本内容,这里有可能是 favourite.txt,也可能是 words.txt,取决于在背哪个词汇表
FileOpen(相应的文件路径, "w", "utf-8").Write(new_list) 
}

在什么都不做只关闭工具提示框,以及收藏、删单词的场合,都应该关掉工具提示框,避免单词一直显示在桌面上,同时应将 dict_flag 设置为 false,避免反复触发以上操作。

global dict_flag
dict_flag:= false
ToolTip()

最后,由于这个背单词工具没有任何界面,全靠热键操作,如果忘记退出背单词模式,有可能出现一些尴尬场景。比如说,当在给其他人演示的时候,不小心点到热键,屏幕上突然蹦出一个单词。

因此,我们还是加一个自动关闭的设定为好。具体而言,就是设置一个计时器,超过一定时间没有使用单词模式下的功能,就自动退出单词模式。

words_last_press:= A_TickCount ; 记录最近一次打开或使用背单词功能的时间

words_check() {
if A_TickCount - words_last_press >= 600 * 1000 ; 检查上次使用时间距离现在是否超过了 600 秒
words_mode ; 改变单词模式的开关状态
}

; 进入单词模式的时候,启动计时器并更新最后使用时间
^XButton1:: words_mode() 
words_mode()
{
global words_last_press, words_flag
words_last_press:= A_TickCount
words_flag:= !words_flag

if words_flag
{
; ……
SetTimer words_check, 120 * 1000 ; 打开单词模式的同时打开计时器,每 120 秒检查一次
}
else
{
; ……
SetTimer words_check, 0 ; 关闭单词模式的同时,关闭计时器
}

; 显示单词的时候,更新最后使用时间
XButton1::
{
global words_last_press
words_last_press:= A_TickCount
; ……
}

第二项功能:读本书

写完背单词模式脚本,读书模式大部分内容都可以照搬。首先当然也需要准备好想要阅读的 TXT 文档,进入阅读模式后,脚本也需要逐行读取文件,定时关闭功能显得更为重要。不同的是,因为很多书籍存在大量空行,读取文件时要将空行排除在外。

if Trim(A_LoopReadLine) != "" ; 排除空行
book.Push(A_LoopReadLine) 

另外,由于书本内容一行可以非常长,ToolTip 显示无法设置宽度,默认宽度为一整个屏幕,这会阻碍我们阅读,最好还是把过长的内容手动分一下行。

这么多内容如果都放在一行的话就太长了
split_string(s, len:= 25) ; 处理文字,按照默认 25 个字换一次行
{
result:= ""
Loop Parse s
{
result.= A_LoopField
if (Mod(A_Index, len) = 0)
result.= "`n"
}
return result
}

看书和翻书可以都放在鼠标中键(滚轮)解决,这样就可以不动键盘实现翻书。

; 用鼠标中键打开或关闭显示书本内容
MButton::
{
; ……
global reading_flag ; 标记是否正在显示内容
reading_flag:= !reading_flag
if reading_flag
{
ToolTip(split_string(book[book_index])) ; 显示一行内容,其中 book_index 指示当前阅读到了哪一行
}
; ……
}

; 用鼠标滚轮上下翻页(行)
#HotIf reading_flag
WheelUp::
{
global book_index
book_index:= Max(1, book_index - 1) ; 这是为了避免在上翻时出现不存在的行号,比如第 0 行(AutoHotkey 的数组从 1 开始),下同
ToolTip(split_string(book[book_index]))
}
WheelDown::
{
global book_index
book_index:= Min(book.length, book_index + 1)
ToolTip(split_string(book[book_index]))
}
#HotIf

读书模式与背单词模式最大的区别在于,背单词是随机出现一个单词,每次背的东西可以是不一样的,但读书是要接着上文的,总不可能每次都从头开始。因此,我们就需要一个方法来记录上次读到哪里了。关于此,茴香豆有不止四种写法,我自己试了其中的三种。

第一种是阅后即焚,在关闭书籍显示或阅读结束的时候,读到哪里删到哪里,将书越读越薄的理念贯彻到底。好处是不用记录读到哪里了,每次都从第一行开始看,缺点是被删掉了的内容,是无法在下次点开的时候回看的。

book_index:= book_index - 1 ; 往上一行开始删
while book_index > 0
{
book.RemoveAt(book_index)
book_index -= 1
}
book_index:= 1
new:= ""
for i in book
new.= i "`n"
FileOpen(book_file, "w", "utf-8").Write(new) ; 删完了写回原 TXT 文档

第二种是在关闭书籍显示或阅读结束时,把行号加入读取的文本里最后一行存起来。为了避免书的最后一行本身是数字,需要在行号的前面加一些特殊符号(我使用的是「&&」)来避免误会。

Loop Read, book_file, "UTF-8" ; 读取书籍内容到 book 变量
{
if Trim(A_LoopReadLine) != ""
book.Push(A_LoopReadLine)
}

if SubStr(book[book.length], 1, 2) != "&&" ; 如果当前文档最后一行不是以“&&”开头,那么说明是首次打开,
{
FileOpen(book_file, "a", "utf-8-RAW").Write("&& 1") ; 在文档最后一行添加 && + 行号
book.Push("&& 1")
}
book_index:= StrSplit(book[book.length], " ")[-1] ; 分离出行号

; 下面是关闭书籍显示或结束阅读时加行号的操作
FileOpen(book_file, "a", "utf-8").Write(" " . book_index)

由于上面两种方法都会删改书本内容,我总觉得有点别扭。因此,目前我使用的是第三种方法——把行号放在 TXT 文档的文件名里。

; TXT 文档的地址和原始文件名 book.txt
book_path:= "D:\ahk"
book_file:= "D:\ahk\book.txt"

; 在文档所在位置匹配文件名带 book 的文档
Loop Files, book_path . "book*.txt", "R"
book_file:= A_LoopFileFullPath
if StrSplit(book_file, "")[-1] = "book.txt" ; 如果文件名没有包含数字,那么说明是首次打开
{
FileMove(book_file, book_path . "book1.txt") ; 将文档重命名为 book + 行号
book_file:= book_path . "book1.txt"
}
book_index:= SubStr(StrSplit(StrSplit(book_file, "")[-1], ".")[1], 5) ; 截取文件名中的数字,即行号

; 下面是关闭书籍显示或结束阅读时加行号的操作
FileMove(book_file, book_path . "book" . book_index . ".txt")

其他方法包括在同路径下新建一个文本,类似单词模式下的 favourites.txt,专门用来存放读到哪里,同样也可以避免反复删改原文档。唯一需要记得的是,在换书的时候,应该删掉或重置这个只记录行号的文件。

未来可能的扩展:画个饼

作为纯粹的业余人士,我愿意稍微花点时间(其实也花不了多少时间),来折腾一些小工具,提高自己的工作或学习效率,是因为比起别人设定好的软件,只有我才真正知晓自己的需求。

我目前写好的脚本,在实现背单词和看书的基本功能的同时,在界面整洁(根本没界面)、保护颈椎(减少低头看手机的时间)、防止焦虑(没有设置学习任务)、稳定情绪(不需要在同事凑过来的时候匆忙关闭软件或文档)等方面,还有着独特的优势。

而且,如果哪天我想要使用更多功能,不需要等待开发者,只要自己动手就可以将它改造为「威力增强版」,下面举几个我想到的栗子和实现方式。

对于背单词模式:

第一,我可以在测试模式里设置难度,或者说从更多维度考查自己记忆单词的程度。

例如,在困难模式下,将音标和释义进一步分离,每次只随机显示一项;而在简单模式下,显示单词的随机几个字母拼写,比如将「苹果」对应的单词显示为「a _ _ l _」。

这个操作的实现并不复杂,只需要结合使用随机数生成(Random)+ 截取字符串(StrSplit、SubStr)就可以实现,这样测试单词的效果就很接近于现实中的单词软件。

第二,我可以更集中地学习一批单词,有点类似于设置学习任务。

如果觉得单词表里的词汇太多,不利于集中学习怎么办。我们没有必要手动地去复制出 20个单词到原来的 words.txt 文档内,而是可以写脚本从单词表中随机挑选 20 个,存放到一个新文档中,然后将学习的词汇表切换至这个文档进行反复学习,并通过测试模式检验拼写。等到这 20 个单词学得差不多了,再自动或点击热键来补充到 20 个。

关于这部分内容的实现,可以参考上面提到的随机数生成,以及加入到收藏和删除单词的写法。

第三,我可以记录每个单词的学习次数,或单词最近一次出现的时间,随机但优先显示学习次数较少、上次出现时间较久远的词汇。

这一升级最好是结合上面的学习任务进行。我们需要相应记录词汇出现的频率表,用某种数据结构或算法来调整接下来出现的单词。比如,每次需要显示单词的时候,根据每个词汇出现次数和上次出现时间计算出一个权重,仅在权重前 50% 的单词中随机显示单词。显示单词的同时,需要相应更新该词汇的出现次数(+1),并记下它最后出现时间。

AutoHotkey 中提供了排序算法(Sort),也能够很精确地记录时间(年、月、日、时、分、秒,分别对应 A_YYYY、A_MM、A_DD、A_Hour、A_Min、A_Sec 这个内置变量),其他诸如写入和读取文档的方法,在上文中也已经出现过,实现起来确实稍复杂但也不算困难。主要难度可能在于权重计算公式的合理性,要简化的话,可以考虑只考虑出现的次数或最后出现时间的一项。

第四,计算权重完全也可以不依赖学习的次数,而是自己的主观感受。

我们可以把删除单词和收藏单词改造为「太简单」和「记不清」两个热键。给每个单词一个初始分数 0 分,如果单词「太简单」,则扣除一定分数,「记不清」则增加一定的分数,如果普通地过掉单词,则分数不增不减。根据单词分数的高低,决定其出现的概率,低分不出现,高分多出现。

第五,我们还可以做得再像背单词软件一点,对我们的学习成果做一个记录和分析,比如今天学会多少个单词,近一周学了多少个单词……

对于读书模式:

第一,如果想要同时读多本书,可以考虑添加一个书架

具体的操作上,也就是增加一个记录书籍文件名称的文档,并设置热键切换在读书籍。至于上面提到的行号记录,就可以放在这个文档中。

这个「书架」文档可以手动维护,也利用脚本自动维护,方法是读取目录下的全部 TXT 文档,添加至「书架」文档中。如何读取目录下名字符合一定条件的全部文档,以及如何维护文档内容,在本文中亦均有提及。

第二,如果读书模式读的是外文书籍,可以增加一个翻译热键。

利用各大词典或翻译工具、AI 工具的接口,翻译当前显示行内容为中文。这里同样可以利用剪贴板 + 其他软件的快捷键来使用。如果没有其他软件,那么可以参考我的上一篇文章,让 AutoHotkey 再辛苦兼职一下翻译功能。

不过,对我而言,现有的功能就已经十分足够。加上这些东西不是不好,也不是实现不了,可我总觉得,随着软件变得复杂,学习目标变得明确,可能会让我太过执着于「学到」点什么,失去了在工作间隙无痛地摄入零碎知识的初心。也许,这样一来,我将会逐渐地不想要使用这些功能了。

最后,是背单词和读书模式的完整代码:

#Requires AutoHotkey v2.0
; 文档的默认地址为“D:\ahk\”
; 单词本和书本的默认名字分别为“words.txt”和“book.txt”

; 背单词模式
words_list:= []
word_index:= 1
words_path:= "D:\ahk\"
words_flag:= false
dict_flag:= false
favourite_flag:= false
exam_flag:= false
words_last_press:= A_TickCount

^XButton1:: words_mode()
words_mode()
{
global
words_last_press:= A_TickCount
words_flag:= !words_flag
words_list:= []
if words_flag
{
if !FileExist(words_path . "favourites.txt")
FileOpen(words_path . "favourites.txt", "w", "UTF-8").Write(Chr(0xFEFF) "")
Loop Read, words_path . "words.txt", "UTF-8-RAW"
words_list.Push(A_LoopReadLine)
SetTimer words_check, 120 * 1000
ToolTip Format("开始")
SetTimer () => ToolTip(), -1200
}
else
{
dict_flag:= false
SetTimer words_check, 0
ToolTip ("结束")
SetTimer () => ToolTip(), -800
}
}

words_check() {
if A_TickCount - words_last_press >= 600 * 1000
words_mode
}

#HotIf words_flag
^MButton::
{
global exam_flag
exam_flag:= !exam_flag
if exam_flag
ToolTip("测试模式")
else
ToolTip("普通模式")
SetTimer () => ToolTip(), -800
}

+XButton1::
{
global favourite_flag, words_list
words_list:= []
favourite_flag:= !favourite_flag
if favourite_flag
{
Loop Read, words_path . "favourites.txt", "UTF-8-RAW"
words_list.Push(A_LoopReadLine)
ToolTip Format("复习模式")
}
else
{
Loop Read, words_path . "words.txt", "UTF-8-RAW"
words_list.Push(A_LoopReadLine)
ToolTip Format("结束复习")
}
SetTimer () => ToolTip(), -800
}

XButton1::
{
global words_list, dict_flag, word_index, words_last_press
words_last_press:= A_TickCount
word_index:= Random(1, words_list.Length)
try
{
word1:= StrSplit(words_list[word_index], " ", , 2)[1]
word2:= StrSplit(words_list[word_index], " ", , 2)[2]
if exam_flag
ToolTip(word2)
else
ToolTip(word1)
KeyWait("LButton", "D")
ToolTip(word1 . "`n" . word2)
dict_flag:= true
}
}
#HotIf

#HotIf dict_flag
LButton::
{
global dict_flag
dict_flag:= false
ToolTip()
}

d::
{
tmp:= ClipboardAll()
A_Clipboard:= StrSplit(words_list[word_index], " ", 2)[1]
Send "^+c"
A_Clipboard:= tmp
}

s::
{
global dict_flag
if !Instr(FileRead(words_path . "favourites.txt"), words_list[word_index])
FileOpen(words_path . "favourites.txt", "a", "utf-8").Write(words_list[word_index] . "`n") 
dict_flag:= false
ToolTip Format("加入收藏:{}", StrSplit(words_list[word_index], " ", 2)[1])
SetTimer () => ToolTip(), -1200
}

RButton::
{
global words_list, dict_flag
words_list.RemoveAt(word_index)
new_list:= ""
for i in words_list
new_list.= i "`n"
if favourite_flag
FileOpen(words_path . "favourites.txt", "w", "utf-8").Write(new_list)
else
FileOpen(words_path . "words.txt", "w", "utf-8").Write(new_list)
dict_flag:= false
ToolTip()
}
#HotIf

; 读书模式
book_index:= 1
book_path:= "D:\ahk\"
book_file:= "D:\ahk\book.txt"
book:= []
book_flag:= false
reading_flag:= false
book_last_press:= A_TickCount

^XButton2:: book_mode()
book_mode()
{
global book_flag, book, book_index, book_file, book_last_press, reading_flag
book_flag:= !book_flag
book_last_press:= A_TickCount
if book_flag
{
Loop Files, book_path . "book*.txt", "R"
book_file:= A_LoopFileFullPath
if StrSplit(book_file, "\")[-1] = "book.txt"
{
FileMove(book_file, book_path . "book1.txt")
book_file:= book_path . "book1.txt"
}
book_index:= SubStr(StrSplit(StrSplit(book_file, "\")[-1], ".")[1], 5)
Loop Read, book_file, "UTF-8"
{
if Trim(A_LoopReadLine) != ""
book.Push(A_LoopReadLine) 
}
SetTimer book_check, 120 * 1000
ToolTip("开始")
SetTimer () => ToolTip(), -800
}
else
{
SetTimer book_check, 0
reading_flag:= false
FileMove(book_file, book_path . "book" . book_index . ".txt")
ToolTip("结束")
SetTimer () => ToolTip(), -800
}
}

book_check() 
{
if A_TickCount - book_last_press >= 600 * 1000
book_mode
}

split_string(s, len:= 25)
{
result:= ""
Loop Parse s
{
result.= A_LoopField
if (Mod(A_Index, len) = 0)
result.= "`n"
}
return result
}

#HotIf book_flag
MButton::
{
global reading_flag, book_file, book_last_press
book_last_press:= A_TickCount
reading_flag:= !reading_flag
if reading_flag
{
try
ToolTip(split_string(book[book_index]))
}
else
{
FileMove(book_file, book_path . "book" . book_index . ".txt")
book_file:= book_path . "book" . book_index . ".txt"
ToolTip()
}
}
#HotIf

#HotIf reading_flag
WheelDown::
{
global book_index, book_last_press
book_last_press:= A_TickCount
book_index:= Min(book.length, book_index + 1)
ToolTip(split_string(book[book_index]))
}
WheelUp::
{
global book_index, book_last_press
book_last_press:= A_TickCount
book_index:= Max(1, book_index - 1)
ToolTip(split_string(book[book_index]))
}
#HotIf

> 关注 少数派公众号,解锁全新阅读体验 📰

> 实用、好用的 正版软件,少数派为你呈现 🚀

© 本文著作权归作者所有,并授权少数派独家使用,未经少数派许可,不得转载使用。


文章来源: https://sspai.com/post/101247
如有侵权请联系:admin#unsafe.sh