Python 屏幕取词功能实现思路
前言
近期接到了一个项目,雇主需求是仿照【有道词典】
【欧路词典】【金山词霸】实现类似的屏幕取词功能。
当鼠标移到屏幕上任何一处显示的字符串时,有道词典将会自动将该词的释义展示出来。
起初我认为取词这个功能非常好实现,所以我便爽快地答应接下了这个项目。
买家提出的要求:不要按任何快捷键,鼠标悬停上去就显示该词的释义。
技术难点一
因为笔者只会熟练地使用Python以及Java。这两门语言对系统底层的开发支持的都不是很好。但是,相较于Java而言,Python拥有较多的库,可以在该方面实现更多的功能,所以我使用了Python来进行编写。
笔者准备使用Pyhook3来进行函数Hook。但是Pyhook3的使用以及安装都已经超出了我的能力范围。安装好之后,并不能实现屏幕取词的功能。所以笔者就另外构思了一种思路。
思路一:利用好剪贴板
既然没有直接取词的方法,那么我们可以就换个思路。
据资料显示,python有很多库是可以实现读取剪贴板信息的,最简洁好用的就是Pyperclip。
有了Pyperclip,我们就可以方便地操作剪贴板了。
加之Pyhook3的全局事件监听,我们监听鼠标双击事件,双击之后,电脑就会自动为我们选中单词,在这个时候,通过模拟按键Ctrl + C按下,实现复制将单词复制到剪贴板。此时,我们就可以通过剪贴板开心地读取了。
具体流程图如下:
但是,这样做也有问题:看似这个过程非常简单,但是实际操作便遇到了困难。
技术难点二
Pyhook3开启监听需要用到Pumpmessage函数,这个函数一旦运行就会阻塞程序的运行,也就是说监听必须要单独存在于一个线程。根据实测发现,这个线程必须要是主线程。
好巧不巧,笔者使用tkinter的mainloop函数,也必须要是主线程才能够正常运行。
所以,这两个库都需要主线程,那么这个问题究竟怎么解决呢?
我在国内外网站上查询了大量的资料,最终发现,国外网友自己实现了线程调度,解决了mainloop和Pumpmessage的线程冲突问题。然而,奈何笔者的程序体量太大,无法进行实际使用这种方法,会造成程序卡死,性能开销过大等一系列问题。
这种问题解决不了,就要尝试绕开它。
思路二:救星——Pynput
Tkinter or Pyhook3?
考虑一下把Tkinter换成PyQt的工作量,果断决定干Pyhook3。
Pyhook3的性能实在不高,对于很多基本组件也有冲突,再三考虑之下决定抛弃好不容易安装好的Pyhook3。o(╥﹏╥)o
寻找了良久之后,我最后选择了Pynput。
Pynput支持全局事件监听,还有模拟按键和鼠标。这个库主要分为两个模块——Keyboard和Mouse。
最最最重要的是,这个库对于多线程应用支持地很好
最终,这个问题终于得以解决。关于主线程的争夺之战,也终于在Pyhook3的让步之下,得以有一个最终的胜利。
技术难点三
虽然取词功能实现了,但是经过多次修改,代码已经不是碳基生物能读懂的了。
根据程序员原则:可运行、可实现功能的代码,不可再行修改。 最终笔者决定停止继续修改,反正也是个人开发,自己差不多能懂就可以了。
然而,我的战争并没有结束…
这个原理实现的大部分软件都可以正常取词,唯独客户最需要的microsoft word不可以。
本着探究的精神,我去尝试了一下对面的WPS OFFICE,发现在关闭悬浮工具栏的情况下是可以正常工作的,只不过没有完全正常工作。
出现的问题是,剪贴板老是读取到之前存储的内容。
直到最终,这个问题也没有得以解决。笔者猜测应该是本身这些软件就用到了鼠标钩子,导致hook之间会有冲突?
继续绕路
“程序员的天职是绕开问题” ——鲁迅
因为客户非常友好,很好说话,最后决定使用OCR方案。
具体原理很简单,屏幕框选截图,然后传入tesseract进行ocr识别,最终在特定位置输出查询结果。思路非常清晰,也比之前的解决方案稳定了很多。
tesseract OCR在大尺度识别下的准确率还是可圈可点的,加之后续的手动修正功能接口,其实正常使用的问题是不用担心了。
结语
本文章只限于给大家提供解决屏幕取词这个问题的思路。据了解,网上还有许多引用C++和C# dll库的,但是因为平台兼容性和稳定性等原因,笔者这里并没有采用。有兴趣的同学可以去尝试一下,应该在特定平台上效果会很好。
最后,希望大家都可以持之以恒地“绕开问题”,收获绕开问题的智慧。
最后的最后,希望读了本文的各位,遇到bug的几率大大减小,早日完成自己的项目!!!