基于pygtk的linux有道词典
生活随笔
收集整理的這篇文章主要介紹了
基于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 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
參考:
一、桌面詞典設計
想把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=實驗樓返回的數據如下:
雖然通過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有道词典的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Socket.IO聊天室~简单实用
- 下一篇: Linux学习路径(小白必看)