基于pygtk的linux有道词典

基于pygtk的linux有道词典


一、桌面词典设计

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

1. 查询

那我们到哪儿去查询该单词呢?这里有两种方法:

{
    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 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 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 = ‘
 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‘)            
            f(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

参考:

时间: 2024-11-06 07:21:34

基于pygtk的linux有道词典的相关文章

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

外语翻译是用户在日常工作和生活中经常遇到的,而linux桌面系统中始终缺乏一个用户在应用中得心应手的翻译软件.近日,有道词典linux版正式上线,此版本是国内Linux系统发行版团队deepin(即深度操作系统)与网易有道联合开发,专门针对Linux用户的需求与使用习惯进行设计,Linux桌面用户再也不用担心外语该如何翻译了. deepin是一个致力于为全球用户提供美观易用.安全可靠系统环境的Linux发行版,非常注重易用的体验和美观的设计,因此在有道词典的设计开发过程中,使其完美的继承了win

必应词典桌面版 --- 基于大学生用户群体的软件评测与分析(与有道词典对比版 2 体验篇)

2.2体验 本篇评测的体验指使用词典时用户对整款软件的整体感受.评测包括各功能是否初次使用易学.长期使用好用,产品界面是否友好美观,与用户交互是否简单直观.良好体验的目的是将功能更完美的发挥出来,是功能的“外衣”,因此相对功能的总分100分,我们给体验的总分是50分. 2.2.1核心功能单词释义的使用 词典软件的功能简单明了,而必应词典也没有将这一简单的功能做的太过复杂. 必应词典主界面 用户只需在上方的单词栏输入单词即会显示相应的单词释义,简单明确,功能实现的很棒. 但是我们在使用过程中还是发

必应词典桌面版 --- 基于大学生用户群体的软件分析(与有道词典对比版 1功能篇)

1.概述 这篇博客会从大学生的角度来评测必应词典桌面版,以大学生的使用习惯来评判必应词典桌面版各项功能的优劣,并与同类软件进行横向分析,最终给出我们的评分.本次分析评测的主要评测员为博主本人,相关分析评测人员包括软件工程团队人员与他们的来自各个学校各个专业的同学们,人数共计23个,具有较好的普遍性. 2.软件分析与评测 本次评测我们会从功能.体验两大方面来对必应词典桌面版进行分析.必应词典版本:V3.5.0 for windows 桌面版     有道词典版本:PC版6.2 这篇博客是功能篇.

在Ubuntu上安装有道词典

4月20日,由有道词典和Deepin团队共同完成的有道词典Linux版终于上线了,首先 推出Deepin和Ubuntu两个系统版本及其他版本的二进制包,估计以后还会有RPM 等版本.有道Linux版界面精美,实现多语言精准翻译,有原声发音和图解词典, 支持屏幕取词划词.下面是有道官网 http://cidian.youdao.com/index-linux.html 可以根据自己的linux版本和机器位数自行选择下载安装包,deb包下载完成后双击即可直接安装.

有道词典配置

(Linux)有道词典命令行版配置教程: 先看效果: 使用命令: #youdao 要查询的单词 就可以获取该单词的释义 教程开始: (1)去现在有道词典的开源版本(仅110行代码,不要惊讶)下载地址:http://sourceforge.net/projects/yodao-free/ (2)安装python.这个软件是用py开发的. #yum install python (3)解压刚才下载的文件得到dict.py 输入: #python dict.py hello 是不是能查询单词了? (4

archlinux 安装有道词典

非常高兴有道出了Linux版本的词典. 下载二进制包,解压然后就是安装依赖环境. 基本安装环境使用pacman依次安装即可, 如果找不到使用 -Ss 选项搜索一下. 除了readme中声明的依赖,还有lxml,webob这两个python包. 其中xlib可能需要yaourt搞定. 后面三个包 1054 sudo pacman -S tesseract-data-eng 1055 sudo pacman -S tesseract-data-chi-tra 1056 sudo pacman -S

Bing词典vs有道词典比对测试报告——体验篇之软件适应性

联网情况: 在联网情况下,针对每一次查询,有道词典的反应速度明显比必应词典快得多.据我推测有以下两个原因: 有道词典有本地词库而必应词典更多依赖联网. 有道词典的服务器在国内而必应的在国外. 断网情况: 有道词典和必应词典都能较快地识别当前网络连接已经中断的状况,并且都能显示词汇的基本释义. 有无鼠标及多平台 没有鼠标就用触摸板,区别不大 因为测试的是PC端软件,不存在平台问题.如果ms能开发一个Linux端的安装版必应词典就太赞了!

ubuntu下python脚本调用有道词典API实现命令行查词

#!/usr/bin/env python #coding=utf-8 '''   python使用有道词典的API来实现命令行查词 ''' import urllib2 import json import sys  reload(sys) sys.setdefaultencoding('utf-8') key = '1096888977' keyfrom = 'bloketest' doctype = 'json' u = 'http://fanyi.youdao.com/openapi.d

基于int的Linux的经典系统调用实现

 先说明两个概念:中断和系统调用 一 系统调用: 是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口,它决定了应用程序是如何和内核打交道的. 1,  Linux系统调用:2.6.19版内核提供了319个系统调用.比如 exit fork read open close …… 2,  对Windows来说,操作系统提供给应用程序的接口不是系统调用,而是API.比如:ReadFile.我们暂时把API和系统调用等同起来 3,  Linux中,每个系统调用对应一个系统调用号,内核维护了一