基于pygtk的linux有道词典

一、桌面词典设计
想把Linux用作桌面系统,其中一部分障碍就是Linux上没有像有道一样简单易用的词典。其实我们完全可以自己开发一款桌面词典,而且开发一款桌面词典也没用我们想象的那么难。在这门项目课中,我们就将开发一款非常简单的桌面词典,其功能就是:当我们选中一个单词时,词典会将该单词的中文(英文)含义然后显示在新的窗口中。
1. 查询
那我们到哪儿去查询该单词呢?这里有两种方法:

  • 有道网站首页进行查询比如在http://dict.youdao.com/search?q=实验楼&keyfrom=dict.index 链接中我们查询了"实验楼"的英文翻译,但是这样的查询返回的是整个页面;
  • 通过有道API查询其实有道词典同时也提供了相关的API进行查询,查询结果是json数据,比如以下链接:http://fanyi.youdao.com/openapi.do?keyfrom=tinxing&key=1312427901&type=data&doctype=json&version=1.1&q=实验楼返回的数据如下:
  1. {
  2. translation: [
  3. "The lab building"
  4. ],
  5. basic: {
  6. phonetic: "shí yàn lóu",
  7. explains: [
  8. "laboratory building",
  9. "laboratory block"
  10. ]
  11. },
  12. query: "实验楼",
  13. errorCode: 0,
  14. web: [
  15. {
  16. value: [
  17. "Laboratory Building",
  18. "Experimental building"
  19. ],
  20. key: "实验楼"
  21. },
  22. {
  23. value: [
  24. "A experimental building",
  25. "A laboratory building"
  26. ],
  27. key: "一座实验楼"
  28. },
  29. {
  30. value: [
  31. "Three laboratory building",
  32. "Three experimental building"
  33. ],
  34. key: "三座实验楼"
  35. }
  36. ]
  37. }

虽然通过API查询的结果没有在首页上查询的结果丰富,但是对于解决一些阅读英文文档的需求完全足够了。感谢有道词典,提供了这么方便的API。
2. 图解界面设计
Linux上开发图形界面程序有很多选择,在这里我们选择使用GTK进行,使用webview来显示查询结果。下一章中,我们将学习一些简单的GTK和WEBVIEW的知识。
二. GTK 和 WEBVIEW
GTK最初是GIMP的专用开发库(GIMP Toolkit),后来发展为Unix-like系统下开发图形界面的应用程序的主流开发工具之一。GTK是自由软件,并且是GNU计划的一部分。GTK的许可协议是LGPL。GTK使用C语言开发,但是其设计者使用面向对象技术。 也提供了C++(gtkmm)、Perl、Ruby、Java和Python(PyGTK)绑定。在这门课程中,我们将使用pygtk进行开发。

1. GTK中的布局
GTK图形界面也像其他图形程序一样,由窗口,容器,控件,以及各种事件处理函数组成。其中窗口布局管理是很重要的一部分内容,因为这决定了我们的图形程序长什么样子。所谓布局管理就是在窗口中布置各种控件。各种控件可以放在一个“包”中进行统一显示处理,这种包就是GTK中的容器,其实它也是一个控件,只是不是可见的,它的作用就是用于包含其各种控件。
GTK中有各种各样的容器控件,为了更好理解GTK中的布局,我们创建一个计算器界面来学习下GTK中的容器,创建源文件calculator.py,输入以下源代码:

import pygtk
pygtk.require('2.0')
import gtkclass Calculator(gtk.Window):def __init__(self):super(Calculator, self).__init__()self.set_title("Calculator")self.set_size_request(250, 230)self.set_position(gtk.WIN_POS_CENTER)vbox = gtk.VBox(False, 2)table = gtk.Table(5, 4, True)table.attach(gtk.Button("Cls"), 0, 1, 0, 1)table.attach(gtk.Button("Bck"), 1, 2, 0, 1)table.attach(gtk.Label(), 2, 3, 0, 1)table.attach(gtk.Button("Close"), 3, 4, 0, 1)table.attach(gtk.Button("7"), 0, 1, 1, 2)table.attach(gtk.Button("8"), 1, 2, 1, 2)table.attach(gtk.Button("9"), 2, 3, 1, 2)table.attach(gtk.Button("/"), 3, 4, 1, 2)table.attach(gtk.Button("4"), 0, 1, 2, 3)table.attach(gtk.Button("5"), 1, 2, 2, 3)table.attach(gtk.Button("6"), 2, 3, 2, 3)table.attach(gtk.Button("*"), 3, 4, 2, 3)table.attach(gtk.Button("1"), 0, 1, 3, 4)table.attach(gtk.Button("2"), 1, 2, 3, 4)table.attach(gtk.Button("3"), 2, 3, 3, 4)table.attach(gtk.Button("-"), 3, 4, 3, 4)table.attach(gtk.Button("0"), 0, 1, 4, 5)table.attach(gtk.Button("."), 1, 2, 4, 5)table.attach(gtk.Button("="), 2, 3, 4, 5)table.attach(gtk.Button("+"), 3, 4, 4, 5)vbox.pack_start(gtk.Entry(), False, False, 0)vbox.pack_end(table, True, True, 0)self.add(vbox)self.connect("destroy", gtk.main_quit)self.show_all()Calculator()
gtk.main()

以上程序中,我们首先设置了窗口的一些属性:title,大小和位置。然后我们使用vbox = gtk.VBox(False, 2) 我们创建了一个垂直的容器( vertical container box),其中参数False指明了该容器中的控件不会是均匀大小的,参数2指明了该容器子部件之间的距离,单位是像素。
然后我们使用 Table 容器部件创建了一个计算器的框架。table = gtk.Table(5, 4, True)我们创建了一个 5 行 4 列的 table 容器部件。第三个参数是同质参数,如果被设置为 ture,table 中所有的部件将是相同的尺寸。而所有部件的尺寸与 table 容器中最大部件的尺寸相同。
table.attach(gtk.Button("Cls"), 0, 1, 0, 1)我们附加了一个按钮到 table 容器中,其位置在表格的左上单元(cell)。前面两个参数代表这个单元的左侧和右侧,后两个参数代表这个单元的上部和下部。Table中的单元是依靠这个单元的四个点的位置来确定的。
vbox.pack_end(table, True, True, 0)我们将table 容器部件放置到垂直箱子容器中。最后我们使用窗口的shwo_all()方法,显示了所有的控件。使用以下命令执行该代码:

$ python calculator.py

可以看到以上程序输出了以下画面:


2. GTK中的事件
GTK中有各种各样的事件,比如按钮点击事件,选择事件等。又由于GTK中的控件没有X window,所以这些控件本身不具有接收事件的功能。在GTK中如果要让控件接收到事件,必须要先生成一个事件容器控件,然后让控件附加到这个事件容器中。我们开发的词典程序,会翻译我们选择到的单词,那程序是如何检测到选择到的单词的呢?这就需要selection_received事件了,同时获取选择事件是一个异步过程,所以要获取选择事件,需要先执行widget.selection_convert()方法。下面让我们练下,创建源文件selection_received.py,输入以下代码:

#-*- coding: utf-8 -*-
import pygtk
pygtk.require('2.0')
import gtkclass GetSelectionExample(object):def __init__(self):# 创建窗口window = gtk.Window(gtk.WINDOW_TOPLEVEL)window.set_title("Get Selection")window.set_border_width(10)window.connect("destroy", lambda w: gtk.main_quit())# 创建一个垂直容器vbox = gtk.VBox(False, 0)window.add(vbox)vbox.show()# 创建了一个按钮,当用点击按钮的时候,触发self.get_stringtarget函数button = gtk.Button(u"输出选择字符串")eventbox = gtk.EventBox()eventbox.add(button)button.connect_object("clicked", self.get_stringtarget, eventbox)eventbox.connect("selection_received", self.selection_received)vbox.pack_start(eventbox)eventbox.show()button.show()window.show()def get_stringtarget(self, widget):# 开始获取选择的字符串widget.selection_convert("PRIMARY", "STRING")returndef selection_received(self, widget, selection_data, data):# 开始解析出获取到的字符串if str(selection_data.type) == "STRING":# 打印获取到的字符串print u"被选择的字符串: " + selection_data.get_text()elif str(selection_data.type) == "ATOM":# Print out the target list we receivedtargets = selection_data.get_targets()for target in targets:name = str(target)if name is not None:print "%s" % nameelse:print "(bad target)"else:print "Selection was not returned as \"STRING\" or \"ATOM\"!"return Falsedef main():gtk.main()return 0if __name__ == "__main__":GetSelectionExample()main()

以上代码中的逻辑非常清晰,我们一次创建了窗口,垂直容器,事件容器以及按钮,并将get_stringtarget()函数注册到了按钮的clicked事件上,然后将selection_received()函数注册打了事件容器的selection_received事件上。在这个例子中,一定要注意是clicked事件,触发了selection_convert函数,然后该函数检查成功后触发了selection_received事件。
让我们来执行以上代码:

$ python selection_received.py

要测试该程序,我们首先应该在任意界面选择字符串,然后点击程序界面上的按钮,这个时候在console就可以看到被选择的字符串了。如下图:
3. WEBVIEW
webview其实就是浏览器控件,所谓浏览器控件是指这个控件可以用来解析html字符串,就像网页一样显示。还是直接从练习学习吧,创建文件webview.py,输入以下代码:

#-*- coding:utf-8 -*-
import gtk
import webkit view = webkit.WebView() sw = gtk.ScrolledWindow()
sw.add(view) win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.add(sw)
win.set_title("shiyanlou")
win.show_all() view.open("http://www.shiyanlou.com")
gtk.main()

以上代码中,我们创建了一个webview,并在具有滚动条的窗口中显示,然后该veiw直接打开了http://www.shiyanlou.com网站。使用以下命令执行该程序:

python webview.py

可以看到以下输出:


三. 词典程序的实现
到这里程序的整个逻辑已经非常清晰啦。我们可以让selection_convert()方法周期性的执行检查选择事件,然后促发selection_received事件,接着执行相应的查询函数,将选择到的单词的含义查询显示到webview上。那么还有最后一个问题,我们怎么样周期性的执行selection_convert函数呢?在GTK中,我们可以方便的使用gobject.timeout_add(interval, function, ...)函数注册需要周期性执行的函数,其中interval为周期,单位是毫秒。整个程序的源代码相当清晰,就不再详细描述了。创建源文件pyoudao.py,输入以下源码:

#-*- coding: utf-8 -*-import os
import re
import time
import fcntl
import logging
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import webkit
import requests
import jsonHOME = os.getenv("HOME") + '/.youdao-dict/'
LOG = HOME + '/pyoudao.log'
LOCK = HOME +  '/pyoudao.lock'
QUERY_URL = 'http://fanyi.youdao.com/openapi.do?keyfrom=tinxing&key=1312427901&type=data&doctype=json&version=1.1&q='if not os.path.exists(HOME):os.mkdir(HOME)logging.basicConfig(filename=LOG, level=logging.DEBUG)class Dict:def __init__(self):self.mouse_in = Falseself.popuptime = 0self.last_selection = ''# 初始化窗口self.window = gtk.Window(gtk.WINDOW_POPUP)self.window.set_title("pyoudao")self.window.set_border_width(3)self.window.connect("destroy", lambda w: gtk.main_quit())self.window.resize(360, 200)# 初始化垂直容器vbox = gtk.VBox(False, 0)vbox.show()# 创建一个事件容器, 并注册selection_recevied事件函数eventbox = gtk.EventBox()eventbox.connect("selection_received", self._on_selection_received)eventbox.connect('enter-notify-event', self._on_mouse_enter)eventbox.connect('leave-notify-event', self._on_mouse_leave)# 注册周期函数_on_timer,每隔500毫秒执行一次gobject.timeout_add(500, self._on_timer, eventbox)eventbox.show()# 创建一个webviewself.view = webkit.WebView()def title_changed(widget, frame, title):logging.debug('title_changed to %s, will open webbrowser ' % title)import webbrowserwebbrowser.open('http://dict.youdao.com/search?le=eng&q=' + title )self.view.connect('title-changed', title_changed)self.view.show()# 打包各种控件self.window.add(vbox)vbox.pack_start(eventbox)eventbox.add(self.view)def _on_timer(self, widget):# 开始检查选择事件widget.selection_convert("PRIMARY", "STRING")if self.window.get_property('visible') and not self.mouse_in:x, y = self.window.get_position()px, py, mods = self.window.get_screen().get_root_window().get_pointer()if (px-x)*(px-x) + (py-y)*(py-y) > 400:logging.debug('distance big enough, hide window')self.window.hide();if(time.time() - self.popuptime > 3):logging.debug('time long enough, hide window')self.window.hide();return True# 如果有字符串被选择,则执行该函数def _on_selection_received(self, widget, selection_data, data):if str(selection_data.type) == "STRING":text = selection_data.get_text()if not text:return Falsetext = text.decode('raw-unicode-escape')if(len(text) > 20):return Falseif (not text) or (text == self.last_selection):return Falselogging.info("======== Selected String : %s" % text)self.last_selection = textm = re.search(r'[a-zA-Z-]+', text.encode('utf8'))if not m:logging.info("Query nothing")return Falseword = m.group(0).lower()if self.ignore(word):logging.info('Ignore Word: ' + word)return Falselogging.info('QueryWord: ' + word)self.query_word(word)return False# 查询单词def query_word(self, word):query_url = QUERY_URL + word# 使用requests模块获取json字符串js= json.loads(requests.get(query_url).text)if 'basic' not in js:logging.info('IgnoreWord: ' + word)returnx, y, mods = self.window.get_screen().get_root_window().get_pointer()self.window.move(x+15, y+10)self.window.present()translation = '<br/>'.join(js['translation'])if 'phonetic' in js['basic']:phonetic = js['basic']['phonetic']else:phonetic = ''explains = '<br/>'.join(js['basic']['explains'])web = '<br/>'.join( ['<a href="javascript:void(0);">%s</a>: %s'%(i['key'], ' '.join(i['value'])) for i in js['web'][:3] ] )html = '''
<style>
.add_to_wordbook {background: url(http://bs.baidu.com/yanglin/add.png) no-repeat;vertical-align: middle;overflow: hidden;display: inline-block;vertical-align: top;width: 24px;padding-top: 26px;height: 0;margin-left: .5em;
}
</style><h2>%(translation)s<span style="color: #0B6121; font-size: 12px">< %(phonetic)s > </span><a href="javascript:void(0);" id="wordbook" class="add_to_wordbook" title="点击在浏览器中打开" οnclick="document.title='%(word)s'"></a> <br/></h2><span style="color: #A0A0A0; font-size: 15px">[ %(word)s ] </span><b>基本翻译:</b><p> %(explains)s </p><span style="color: #A0A0A0; font-size: 15px">[ %(word)s ] </span><b>网络释意:</b><p> %(web)s </p>''' % locals()# 通过webview显示html字符串self.view.load_html_string(html, '')self.view.reload()self.popuptime = time.time()def ignore(self, word):if len(word)<=3:return Truereturn Falsedef _on_mouse_enter(self, wid, event):logging.debug('_on_mouse_enter')self.mouse_in = Truedef _on_mouse_leave(self, *args):logging.debug('_on_mouse_leave')self.mouse_in = Falseself.window.hide()def main():Dict()gtk.main()if __name__ == "__main__":f=open(LOCK, 'w')try:fcntl.flock(f.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)except:print 'a process is already running!!!'exit(0)main()

执行程序:

$ python pyoudao.py

下面是该程序的效果图:

总的来说这门项目课相对简单,我们只用不到300行的代码就实现了一个有道桌面词典,虽然其功能非常简陋。更进一步,我们可以实现单词白名单、查询缓存、多个源查询等功能,更多的功能还需要你更进一步努力哦。
最后的最后~小编祝大家新年快乐~大家来年再见哈~还有,天天开心,笑口常开,啦啦啦~

如果还有疑问或者不解的地方,欢迎登陆实验楼官方网站 http://www.shiyanlou.com
查看该项目课的详细步骤和内容: http://www.shiyanlou.com/courses/47
与大家交流分享学习心得: http://forum.shiyanlou.com/forum.php?mod=guide&view=newthread

参考:

  • pygtk2 官方文档
  • [url=]pygtk2 教程[/url]
  • 有道词典 Linux客户端

基于pygtk的linux有道词典相关推荐

  1. 基于PyQT5的翻译小程序(支持百度翻译和有道词典)

    基于PyQT5制作的翻译小程序 通过requests获取翻译结果,使用PyQT5设计界面,使用SystemHotkey设置全局快捷键 1. 实现基本翻译功能 2. 实现截图(支持快捷键F1启动)或拖入 ...

  2. Linux Mint (应用软件— 翻译工具:有道词典)

    使用Mint已经有一段时间了,在阅读英文资料时或多或少会遇到一些生僻的单词,这时候就想起了翻译软件,不过Mint没有安装翻译软件,需要我们自己去安装. 在Linux上有一个老牌的翻译软件:星际译王(S ...

  3. 翻译信手拈来:有道词典linux版正式上线

    外语翻译是用户在日常工作和生活中经常遇到的,而linux桌面系统中始终缺乏一个用户在应用中得心应手的翻译软件.近日,有道词典linux版正式上线,此版本是国内Linux系统发行版团队deepin(即深 ...

  4. Linux下Python实现有道词典

    最近发现,用Linux系统比Window的工作和学习效率高多了,做任何事情都更直接有效, 而且现在绝大部分应用都是基于WEB的:所以,以后尽量用Linux了. 以下是用Python脚本实现的有道词典. ...

  5. Ubuntu 14.04 64bit上安装有道词典Linux版本

    4月20日,由有道词典和Deepin团队共同完成的有道词典Linux版终于上线了,首先 推出Deepin和Ubuntu两个系统版本及其他版本的二进制包,估计以后还会有RPM 等版本.有道Linux版界 ...

  6. 有道 linux 安装路径,「Linux」- 安装有道词典

    「Linux」- 安装有道词典 更新日期:2019年07月10日 @IGNORECHANGE 系统环境 系统环境:Debian 安装依赖 #!/bin/bash apt-get install pyt ...

  7. ubuntu18.04安装linux版的有道词典

    一直在ubuntu上安装有道词典,因为有道还是很好用的,但是发现有道linux版有多个版本,一时不知道如何选择: https://cidian.youdao.com/multi.html 我的ubun ...

  8. Linux中安装有道词典

    Linux安装有道词典 (http://cidian.youdao.com/index-linux.html)附上安装报的下载网址 (http://cidian.youdao.com/index-li ...

  9. linux下如何使用有道词典

    原文地址:http://blog.chinaunix.net/uid-28806348-id-3591914.html 第一步,安装python, 好多系统都自带的了, 输入python, 没有这个命 ...

最新文章

  1. Python 文件 close() 方法
  2. [转载] 中华典故故事(孙刚)——02 半路杀出个程咬金
  3. Lucene 学习笔记(一)
  4. STL:使用string、vector、complex和limits
  5. r语言r-shiny_使用Shiny和R构建您的第一个Web应用程序仪表板
  6. php mysql 中文_PHP连接MySQL查询结果中文显示乱码解决方法
  7. 解决虚拟机安装64位系统“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题...
  8. 静态内部类 java 1614958017
  9. 从零开始学Pytorch(六)之梯度消失、梯度爆炸
  10. Spring Boot学习总结(3)——SpringBoot魅力所在
  11. ie6、ie7下overflow失效
  12. 会场安排问题(贪心算法) Comparator类排序的学习
  13. Java项目:校园二手交易平台(java+SSM+Thymeleaf+Html+jQuery+mysql)
  14. 随机对偶动态规划 SDDP,报童模型的一个 python 例子
  15. Nuxt.js mini聊天室代码
  16. pdf照片显示正常打印时被翻转_现场确认完没事了?准考证打印要注意哪些细节!...
  17. linux下unison安装配置
  18. Eloquen模型的具体使用方法
  19. 2022年指数与指数公司行业研究报告
  20. 数学建模----LaTex排版使用速成

热门文章

  1. swiper轮播后hover无效问题解决案例
  2. js array 的理解
  3. redux中的小bug
  4. 2013计算机视觉代码合集一
  5. 数据处理程序的一点经验
  6. (021) Linux之正则表达式
  7. 博客新家(agiledon.github.com)
  8. 初学者适用的最新Java学习路线
  9. C# 读取Excel文件,并写入word模板文档
  10. STL vector 容器介绍