基于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=实验楼返回的数据如下:
- {
- translation: [
- "The lab building"
- ],
- basic: {
- phonetic: "shí yàn lóu",
- explains: [
- "laboratory building",
- "laboratory block"
- ]
- },
- query: "实验楼",
- errorCode: 0,
- web: [
- {
- value: [
- "Laboratory Building",
- "Experimental building"
- ],
- key: "实验楼"
- },
- {
- value: [
- "A experimental building",
- "A laboratory building"
- ],
- key: "一座实验楼"
- },
- {
- value: [
- "Three laboratory building",
- "Three experimental building"
- ],
- key: "三座实验楼"
- }
- ]
- }
虽然通过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 gtk class 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(""), 0, 1, 1, 2)
table.attach(gtk.Button(""), 1, 2, 1, 2)
table.attach(gtk.Button(""), 2, 3, 1, 2)
table.attach(gtk.Button("/"), 3, 4, 1, 2)
table.attach(gtk.Button(""), 0, 1, 2, 3)
table.attach(gtk.Button(""), 1, 2, 2, 3)
table.attach(gtk.Button(""), 2, 3, 2, 3)
table.attach(gtk.Button("*"), 3, 4, 2, 3)
table.attach(gtk.Button(""), 0, 1, 3, 4)
table.attach(gtk.Button(""), 1, 2, 3, 4)
table.attach(gtk.Button(""), 2, 3, 3, 4)
table.attach(gtk.Button("-"), 3, 4, 3, 4)
table.attach(gtk.Button(""), 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 gtk class 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")
return def 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 received
targets = selection_data.get_targets()
for target in targets:
name = str(target)
if name is not None:
print "%s" % name
else:
print "(bad target)"
else:
print "Selection was not returned as \"STRING\" or \"ATOM\"!" return False def main():
gtk.main()
return 0 if __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 json HOME = 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 = False
self.popuptime = 0
self.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() # 创建一个webview
self.view = webkit.WebView()
def title_changed(widget, frame, title):
logging.debug('title_changed to %s, will open webbrowser ' % title)
import webbrowser
webbrowser.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 False
text = text.decode('raw-unicode-escape')
if(len(text) > 20):
return False if (not text) or (text == self.last_selection):
return False logging.info("======== Selected String : %s" % text)
self.last_selection = text m = re.search(r'[a-zA-Z-]+', text.encode('utf8'))
if not m:
logging.info("Query nothing")
return False word = m.group(0).lower()
if self.ignore(word):
logging.info('Ignore Word: ' + word)
return False logging.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)
return x, 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="点击在浏览器中打开" onclick="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 True
return False def _on_mouse_enter(self, wid, event):
logging.debug('_on_mouse_enter')
self.mouse_in = True def _on_mouse_leave(self, *args):
logging.debug('_on_mouse_leave')
self.mouse_in = False
self.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有道词典的更多相关文章
- 【应用】_有道词典客户端一个后缀名为sql的数据库。
[缘起] 在清理电脑磁盘的时候,看一看各安装文件夹有占用了多大容量,发现有道词典居然达140MB了,于是进去看看. 发现个有趣的文件:XXX.sql. 首先我们看一看它的安装文件夹的结构: Dict ...
- ubuntu14.04 下安装有道词典
安装步骤 1.ubuntu14.04.1版本下是不能直接安装有道词典的,首先需要把14.04.版升级为14.04.2版. 在终端窗口中输入以下命令: sudo apt-get update sudo ...
- 在Ubuntu上安装有道词典
4月20日,由有道词典和Deepin团队共同完成的有道词典Linux版终于上线了,首先 推出Deepin和Ubuntu两个系统版本及其他版本的二进制包,估计以后还会有RPM 等版本.有道Linux版界 ...
- cef3和duilib简单仿有道词典学习
由于最近换工作的原因,也没啥事,就简单学习了一下cef3和duilib,楼主之前是做MFC框架下的windows开发的,对界面库和新的客户端开发模式也有所了解,现在的大部分客户端都是基本的客户端框架下 ...
- ubuntu16.04安装不上有道词典的解决办法
转自:http://www.linuxdiyf.com/linux/21143.html ubuntu16.04安装不上有道词典,提示: le@hu-pc:~/下载$ sudo dpkg -i you ...
- 有道词典 纯净版 - imsoft.cnblogs
软件名称:有道词典软件版本:5.4 正式版(5.4.46.5554)软件主页:http://cidian.youdao.com/软件定制:飞扬时空更新日期:2013年8月26日 软件简介: 有道词典是 ...
- Bing词典vs有道词典比对测试报告——体验篇之软件适应性
联网情况: 在联网情况下,针对每一次查询,有道词典的反应速度明显比必应词典快得多.据我推测有以下两个原因: 有道词典有本地词库而必应词典更多依赖联网. 有道词典的服务器在国内而必应的在国外. 断网情况 ...
- 在有道词典程序文件夹发现一个后缀名为sql的数据库(SQLite)
缘起 在清理电脑磁盘的时候,看一看各安装文件夹有占用了多大容量,发现有道词典居然达140MB了,于是进去看看. 发现个有趣的文件:XXX.sql. 首先我们看一看它的安装文件夹的结构: Dict └─ ...
- 有道词典 Andriod 版本数据格式分析
其实很简单无聊 基于版本 5.3 分析. 其实也简单分析了有道词典iOS版本,必应词典的各个版本,以及金山词典的各个版本,还有那个一直逍遥法外的林格斯词典. 由于在各个平台上的限制,同一词典的不同版本 ...
随机推荐
- T-SQL:毕业生出门需知系列(四)
第4课 过滤数据 4.1 使用 WHERE 子句:过滤 只检索所需数据需要指定搜索条件(即过滤条件). WHERE 子句在表名(FROM 子句)后给出,根据 WHERE 子句中指定的条件过滤. SEL ...
- golang获取程序运行路径
golang获取程序运行路径: /* 获取程序运行路径 */ func getCurrentDirectory() string { dir, err := filepath.Abs(filepath ...
- 微软消息分析器(Microsoft Message Analyzer )更新至1.2版-2015-1-20
就在刚才,收到了微软Connect的邮件推送,大名鼎鼎的微软消息分析器更新至1.2版,并且有公众下载链接,大家可以在这里进行下载. 这里简单摘录一下博客里面提到的新版所增加的功能与功能的改进方面. G ...
- Matrix Factorization SVD 矩阵分解
Today we have learned the Matrix Factorization, and I want to record my study notes. Some kownledge ...
- Oracle并行添加主键的方法
环境:Oracle 11.2.0.3 需求:生产一张表由于前期设计不当,没有主键.现需要添加主键,数据量很大,想并行建立. 1.直接添加,提示ora-3001:未实施的功能;只能单线程建立主键 S ...
- KVM的前世今生
1.虚拟化技术的演变过程:软件模拟.虚拟化层翻译.容器虚拟化三个阶段 (1)软件模拟的技术方式 软件模拟是通过软件完全模拟CPU.网卡.芯片组.磁盘等计算机硬件,因为是软件模拟,所以理论上可以模拟任何 ...
- jquery动画,基础以及我发现的新大陆
$.animate()在jquery官方介绍有2中方式,其实我发现的新大陆也是第二种方式的扩展! 一.$.animate( properties [, duration ] [, easing ] [ ...
- Windows Phone 的 TextBox 的实现 PropertyChanged
比如,View 的文本框 TextBox1 绑定了 ViewModel 的 Msg 属性, 当想把文本框输入的内容输入过程中实时更新到绑定的 Msg ,在Windows Phone 中是无法通过设置 ...
- nodejs学习笔记一——nodejs安装
a.nodejs安装 nodejs的安装没有什么说的默认安装即可.安装包官网下载即可:nodejs官网 本人用的是window的安装包node-v4.2.6-x64.msi 安装完成后打开命令行查看使 ...
- Xamarin.Android之引导页的简单制作
0x01 前言 对于现在大部分的APP,第一次打开刚安装或更新安装的APP都会有几个引导界面,通常这几个引导页是告诉用户 APP有些什么功能或者修改了什么bug.新增了什么功能等等等. 下面就用Xam ...