用 pyqt4 編寫的一個翻譯小工具

  有時候咱們在開發時遇到一些陌生的英文單詞或者不容易看出某些長句的中文意思時該怎麼辦呢?打開桌面上的翻譯軟件?打開瀏覽器裏收藏着的翻譯網址或者直接貼上百度的搜索框去查?這些方法當然能夠,還很常見,但若是是 linux 系統的話,很難找到像 windows 上那些公司級別來開發的成熟的翻譯軟件,因此只能打開瀏覽器來查了。瀏覽器通常都會裝上一些翻譯插件,好比我經常使用的 chrome 的 劃詞翻譯,直接用這些插件來進行翻譯比起打開一個翻譯網站或者百度google搜索要更快,畢竟由於加載的內容更少,但這終究只是瀏覽器的插件,在瀏覽網頁時用還方便,對於在其它軟件上看到的陌生單詞若是也放到這兒來查的話多少就感到有些轉折了,因此少數的軟件也會有相應的翻譯插件,像優秀的編輯器 sublime text 等,就有人專門寫了一個翻譯的插件,但是像這類有翻譯插件的軟件畢竟仍是極少數,若是用其它編輯器或 IDE 來開發的話也本身寫一個翻譯插件的話就太折騰了(我以前用 codeblocks 來看代碼時遇到不懂的單詞也想着能不能自行開發一個翻譯插件,上網搜了一下發現開發 codeblocks 的插件須要的技術挺偏的,並且也不容易;有時常常在終端上 man 一個不熟悉的命令時也會遇到不少不懂的英文單詞,此時爲終端加一個翻譯插件感受就不太現實了吧)。python

  因此,我前幾天時就在想,能不能開發一個全局的翻譯插件呢?也就是在使用電腦上全部軟件時都能很方便調用這個插件去獲取翻譯的結果,由於我大多數時間是在使用 ubuntu,ubuntu 的任務欄默認是位於電腦的上方的,感受看着很顯眼,鼠標觸及也方便,因此想就把這個插件放到任務欄上吧,這樣子不論是在用 codeblocks / eclipse等 IDE 來進行開發,或者在使用終端進行各類命令的操做時,或者直接是在看網頁(用不一樣的瀏覽器)等等,都能經過點擊任務欄的小圖標就能當即進行翻譯了。linux

  我一開始的設計思想就是爲了用最少的操做(也就是最少的鼠標點擊次數或者鍵盤打字數目)來達到目的,因此個人這個全局插件的翻譯原理是這樣的:用 pyqt 構建好的 GUI 程序在後臺運行,任務欄上顯示,每當點擊它在任務欄上的圖標菜單裏的翻譯按鈕時,程序會獲取剪切版裏的內容,而後把這些內容經過有道翻譯的API獲得翻譯的 json 格式的結果,而後把 json 格式的結果處理一下,最後經過彈窗顯示或者系統氣泡消息的方式展示出來。由於我用的終端是 terminator,能設置成鼠標劃過的文本直接進入剪切板,因此當我想要翻譯一句英文時,只須要用鼠標選擇好這些文本,而後再點擊任務欄圖標的翻譯按鈕就能直接能到結果,整個過程不過兩次點擊鼠標的時間,無須用到鍵盤,比起用瀏覽器來查,能夠說是大大節省了時間,能顯著地提升平常學習/開發的效率。好了,廢話很少說了,先放上一張效果圖:git

  (我能說這個圖很是難截麼?由於用不了截圖軟件來截圖(鼠標一點擊截圖軟件時任務欄圖標的菜單列表就會馬上消失),因此只能用鍵盤的截圖鍵來截整個屏幕的圖,而後再裁剪圖片,但是在 ubuntu 下截取瞬間會有閃現的效果,致使這個菜單欄變得很模糊,因此我只能先換一張幾乎是純黑色的桌面背景,而後裁剪好後再做亮度調整等處理,先將就着看一下哦~)github

  完整代碼以下:web

  1 #!/usr/bin/env python
  2 # coding: utf-8
  3 
  4 from PyQt4.QtGui import *
  5 from PyQt4.QtCore import *
  6 import sys
  7 import requests
  8 # import redis
  9 import json
 10 
 11 
 12 class SystemTray(QMainWindow):
 13     """My SystemTray, includes translator and functions of getting commands quickly."""
 14 
 15     YDERRORCODE = {
 16         0: u'正常',
 17         20: u'要翻譯的文本過長',
 18         30: u'沒法進行有效的翻譯',
 19         40: u'不支持的語言類型',
 20         50: u'無效的key',
 21         60: u'無詞典結果,僅在獲取詞典結果生效'
 22     }
 23 
 24     def __init__(self, title="SystemTray", size=[600, 500]):
 25         super(SystemTray, self).__init__()
 26         self.initSelf(title, size)
 27         self.initUI()
 28 
 29     def initSelf(self, title, size):
 30         self.setWindowTitle(title)
 31         self.setWindowIcon(QIcon('./images/window_icon.jpg'))
 32         self.resize(size[0], size[1])
 33         self.showOnCenter()
 34 
 35         self.clipboard = QApplication.clipboard()
 36         self.translateUrl = "http://fanyi.youdao.com/openapi.do?keyfrom=myname&key=xxx&type=data&doctype=json&version=1.1&q="
 37 
 38     def initUI(self):
 39         self.initBoard()
 40         self.statusbar = self.statusBar()
 41         self.initAction()
 42         self.initTrayIcon()
 43 
 44     def initBoard(self):
 45         board = QWidget()
 46         mainLayout = QVBoxLayout()
 47 
 48         gbox = QGroupBox(u'翻譯設置')
 49         grid = QGridLayout()
 50         self.cbImmediatelyTranslate = QCheckBox(u'翻譯剪切板中的內容')
 51         self.cbImmediatelyTranslate.setChecked(True)
 52         self.cbImmediatelyTranslate.stateChanged.connect(self.translateOption)
 53         grid.addWidget(self.cbImmediatelyTranslate, 0, 0, 1, 2)
 54         label = QLabel(u'字數限制:')
 55         label.setAlignment(Qt.AlignRight)
 56         grid.addWidget(label, 0, 3)
 57         self.sbWordLimit = QSpinBox()
 58         self.sbWordLimit.setRange(1, 800)
 59         self.sbWordLimit.setValue(200)
 60         self.sbWordLimit.valueChanged.connect(lambda x: self.slotWordLimitChange(self.sbWordLimit.value()))
 61         grid.addWidget(self.sbWordLimit, 0, 4)
 62 
 63         self.label_1 = QLabel(u'輸入要翻譯的文本:')
 64         grid.addWidget(self.label_1, 1, 0)
 65         self.textTranslate = QTextEdit()
 66         grid.addWidget(self.textTranslate, 2, 0, 3, 6)
 67         self.btnTranslate = QPushButton(u'翻譯')
 68         self.btnTranslate.clicked.connect(self.btnClicked)
 69         grid.addWidget(self.btnTranslate, 6, 5)
 70         self.label_1.hide()
 71         self.textTranslate.hide()
 72         self.btnTranslate.hide()
 73 
 74         label = QLabel(u'顯示方式:')
 75         label.setAlignment(Qt.AlignRight)
 76         grid.addWidget(label, 6, 0)
 77         self.cbbTranslateShowType = QComboBox()
 78         self.cbbTranslateShowType.addItem(QIcon(u'./images/messagebox.png'), u'彈窗顯示')
 79         self.cbbTranslateShowType.addItem(self.style().standardIcon(QStyle.SP_MessageBoxInformation), u'系統通知')
 80         self.cbbTranslateShowType.currentIndexChanged.connect(lambda x: self.slotShowTypeChange(self.cbbTranslateShowType.currentIndex()))
 81         grid.addWidget(self.cbbTranslateShowType, 6, 1)
 82 
 83         self.cbShowTranslateDeatil = QCheckBox(u'顯示詳細的翻譯結果')
 84         self.cbShowTranslateDeatil.clicked.connect(lambda x: self.slotShowDetailChange(self.cbShowTranslateDeatil.isChecked()))
 85         self.cbShowTranslateDeatil.setChecked(True)
 86         grid.addWidget(self.cbShowTranslateDeatil, 6, 3)
 87 
 88         grid.setColumnStretch(1, 1)
 89         grid.setColumnStretch(2, 1)
 90         grid.setColumnStretch(4, 1)
 91         grid.setColumnStretch(5, 1)
 92         gbox.setLayout(grid)
 93         mainLayout.addWidget(gbox)
 94 
 95         gbox = QGroupBox(u'快速複製命令到剪切板')
 96         grid = QGridLayout()
 97         label = QLabel(u'歷史命令(history):')
 98         grid.addWidget(label, 0, 0)
 99         self.cbbHistoryCommands = QComboBox()
100         grid.addWidget(self.cbbHistoryCommands, 1, 0)
101         gbox.setLayout(grid)
102         mainLayout.addWidget(gbox)
103 
104         mainLayout.addStretch(1)
105         board.setLayout(mainLayout)
106         self.setCentralWidget(board)
107 
108     def slotShowTypeChange(self, index):
109         self.cbbTranslateShowType.setCurrentIndex(index)
110         if index == 0:
111             self.actMessagebox.setChecked(True)
112         else:
113             self.actSystemMessage.setChecked(True)
114 
115     def slotShowDetailChange(self, bShowDeatil):
116         if bShowDeatil == True:
117             self.actShowDetail.setChecked(True)
118             self.cbShowTranslateDeatil.setChecked(True)
119         else:
120             self.actShowDetail.setChecked(False)
121             self.cbShowTranslateDeatil.setChecked(False)
122 
123     def slotWordLimitChange(self, wordLen):
124         if wordLen in [100, 200, 400]:
125             self.sbWordLimit.setValue(wordLen)
126         if wordLen == 100:
127             self.actWordLimit_1.setChecked(True)
128         elif wordLen == 200:
129             self.actWordLimit_2.setChecked(True)
130         elif wordLen == 400:
131             self.actWordLimit_3.setChecked(True)
132         else:
133             self.actWordLimit_4.setChecked(True)
134             self.actWordLimit_4.setText(('其它(' + str(wordLen) + ')').decode('utf8'))
135             self.show()
136 
137     def initAction(self):
138         self.actTranslate = QAction(QIcon('./images/youdao.jpg'), u'當即翻譯', self)
139         self.actTranslate.setShortcut('ctrl+alt+f')
140         self.actTranslate.triggered.connect(self.translate_clipboard)
141 
142         menuShowType = QMenu(u'顯示方式', self)
143         ag = QActionGroup(self, exclusive=True)
144         self.actMessagebox = QAction(QIcon(u'./images/messagebox.png'), u'彈窗顯示', menuShowType, checkable=True)
145         self.actMessagebox.triggered.connect(lambda x: self.slotShowTypeChange(0))
146         self.actMessagebox.setChecked(True)
147         self.actSystemMessage = QAction(self.style().standardIcon(QStyle.SP_MessageBoxInformation), u'系統通知', menuShowType, checkable=True)
148         self.actSystemMessage.triggered.connect(lambda x: self.slotShowTypeChange(1))
149         menuShowType.addActions([ag.addAction(self.actMessagebox), ag.addAction(self.actSystemMessage)])
150 
151         menuTranslateOption = QMenu(u'翻譯設置', self)
152         menuTranslateOption.setIcon(QIcon(u'./images/config.png'))
153         self.actShowDetail = QAction(u'顯示詳細結果', menuTranslateOption, checkable=True)
154         self.actShowDetail.triggered.connect(lambda x: self.slotShowDetailChange(self.actShowDetail.isChecked()))
155         self.actShowDetail.setChecked(True)
156 
157         menuWordLimit = QMenu(u'字數限制', self)
158         ag = QActionGroup(self, exclusive=True)
159         self.actWordLimit_1 = QAction(u'100', menuWordLimit, checkable=True)
160         self.actWordLimit_1.triggered.connect(lambda x: self.slotWordLimitChange(100))
161         self.actWordLimit_2 = QAction(u'200', menuWordLimit, checkable=True)
162         self.actWordLimit_2.triggered.connect(lambda x: self.slotWordLimitChange(200))
163         self.actWordLimit_2.setChecked(True)
164         self.actWordLimit_3 = QAction(u'400', menuWordLimit, checkable=True)
165         self.actWordLimit_3.triggered.connect(lambda x: self.slotWordLimitChange(400))
166         self.actWordLimit_4 = QAction(u'其它', menuWordLimit, checkable=True)
167         self.actWordLimit_4.triggered.connect(lambda x: self.slotWordLimitChange(1))
168         menuWordLimit.addActions([ag.addAction(self.actWordLimit_1), ag.addAction(self.actWordLimit_2), ag.addAction(self.actWordLimit_3), ag.addAction(self.actWordLimit_4)])
169 
170         menubar = self.menuBar()
171         menuTranslate = menubar.addMenu(u'翻譯')
172         menuTranslate.addAction(self.actTranslate)
173         menuTranslateOption.addMenu(menuShowType)
174         menuTranslateOption.addAction(self.actShowDetail)
175         menuTranslateOption.addMenu(menuWordLimit)
176         menuTranslate.addMenu(menuTranslateOption)
177 
178         commandsMenu = menubar.addMenu(u'commands設置')
179 
180     def initTrayIcon(self):
181         menuTrayIcon = QMenu(self)
182         menuTrayIcon.addAction(self.actTranslate)
183         
184         menuTranslateOption = QMenu(u'翻譯設置', self)
185         menuTranslateOption.setIcon(QIcon(u'./images/config.png'))
186         menuShowType = QMenu(u'顯示方式', self)
187         menuShowType.addActions([self.actMessagebox, self.actSystemMessage])
188         menuTranslateOption.addMenu(menuShowType)
189         menuTranslateOption.addAction(self.actShowDetail)
190         menuWordLimit = QMenu(u'字數限制', self)
191         menuWordLimit.addActions([self.actWordLimit_1, self.actWordLimit_2, self.actWordLimit_3, self.actWordLimit_4])
192         menuTranslateOption.addMenu(menuWordLimit)
193 
194         menuTrayIcon.addMenu(menuTranslateOption)
195         # menuTrayIcon.addAction(QAction("Minimize", self, triggered=self.showMinimized))
196         menuTrayIcon.addSeparator()
197         menuTrayIcon.addAction(QAction(QIcon('./images/qt.png'), u"打開主面板", self, triggered=self.showNormal))
198         menuTrayIcon.addAction(QAction(QIcon('./images/quit.png'), u"退出", self, triggered=qApp.quit))
199 
200         self.trayIcon = QSystemTrayIcon(QIcon('./images/tray_icon.jpg'), self)
201         self.trayIcon.setContextMenu(menuTrayIcon)
202         self.trayIcon.show()
203 
204     def btnClicked(self):
205         self.translateText(self.textTranslate.toPlainText())
206 
207     def translateOption(self):
208         if self.cbImmediatelyTranslate.isChecked():
209             self.label_1.hide()
210             self.textTranslate.hide()
211             self.btnTranslate.hide()
212         else:
213             self.label_1.show()
214             self.textTranslate.show()
215             self.btnTranslate.show()
216 
217     def translate(self, text):
218         if len(text) > self.sbWordLimit.value():
219             return (1, u'翻譯的文本長度超過字數限制!')
220         text = (str(text.toUtf8())).strip()
221         if text == '':
222             return (2, u'翻譯的字符串中沒有實際的字符(只包含空格/tab/換行等)')
223         req = self.translateUrl + text
224         r = requests.get(req)
225         if r.ok != True:
226             return (r.status_code, QString(u'網絡錯誤,url請求失敗'))
227         else:
228             u_dict = json.loads(r.text)
229             errorCode = u_dict['errorCode']
230             if errorCode != 0:
231                 return (errorCode, QString(self.YDERRORCODE[errorCode]))
232             else:
233                 res = QString(u'')
234                 if u_dict.has_key('translation'):
235                     res += u"<翻譯>:  "
236                     for x in u_dict['translation']:
237                         res = res + x + ", "
238                     res = (
239                         res[:-2] + "\n") if res[-2:] == ", " else (res + "\n")
240                 if u_dict.has_key('basic'):
241                     if u_dict['basic'].has_key('us-phonetic'):
242                         res = res + u"<美式發音>:  " + u_dict['basic']['us-phonetic'] + "\t"
243                     if u_dict['basic'].has_key('uk-phonetic'):
244                         res = res + u"<英式發音>:  " + u_dict['basic']['uk-phonetic'] + "\n"
245                     if u_dict['basic'].has_key('explains'):
246                         res += u"<解釋>:  "
247                         for x in u_dict['basic']['explains']:
248                             res = res + x + ", "
249                         res = (
250                             res[:-2] + "\n") if res[-2:] == ", " else (res + "\n")
251                 if self.cbShowTranslateDeatil.isChecked():
252                     if u_dict.has_key('web'):
253                         res += u"<網絡用語>:  \n"
254                         for d in u_dict['web']:
255                             if d.has_key('key'):
256                                 res = res + u"  <關鍵詞>:  " + d['key'] + "\n"
257                                 if d.has_key('value'):
258                                     res = res + u"  <意思>:  "
259                                     for v in d['value']:
260                                         res = res + v + ", "
261                                     res = (
262                                         res[:-2] + "\n") if res[-2:] == ", " else (res + "\n")
263                 return (errorCode, res)
264 
265     def translateText(self, text):
266         if type(text) == type(QString()):
267             text = text.simplified()
268         elif type(text) == str:
269             text = text.strip()
270         result = self.translate(text)
271         showType = self.cbbTranslateShowType.currentIndex()
272         if result[0] != 0:
273             title = QString(u'翻譯出錯')
274             self.showTranslateResult(title, result[1], QMessageBox.Warning)
275         else:
276             title = (text[0:20] + u'....' if len(text)
277                      >= 20 else text) + u" 的翻譯結果 "
278             self.showTranslateResult(title, result[1], QMessageBox.Information)
279 
280     def translate_clipboard(self):
281         clip_data = self.clipboard.mimeData()
282         if clip_data.hasText():
283             src = clip_data.text()
284             self.translateText(src)
285         else:
286             QMessageBox.information(self, u'提示', u'剪切板中的內容爲空,沒法翻譯!')
287 
288     def showTranslateResult(self, title, content, icon):
289         showType = self.cbbTranslateShowType.currentIndex()
290         if showType == 0:
291             # 相當重要的一句,不然當關閉messagebox的窗口時,整個程序自動退出!
292             QApplication.setQuitOnLastWindowClosed(False)
293             QMessageBox(QMessageBox.Icon(icon), title, content).exec_()
294         elif showType == 1:
295             self.trayIcon.showMessage(title, content, QSystemTrayIcon.MessageIcon(icon))
296         else:
297             pass
298 
299     def showOnCenter(self):
300         screen = QDesktopWidget().screenGeometry()
301         self.move((screen.width() - self.width()) / 2,
302                   (screen.height() - self.height()) / 2)
303 
304     def closeEvent(self, event):
305         if self.trayIcon.isVisible():
306             self.trayIcon.showMessage(u'隱藏', u'在任務欄按鈕可打開主窗口', QSystemTrayIcon.MessageIcon(QMessageBox.Information), 1000)
307             self.hide()
308             event.ignore()
309 
310 if __name__ == '__main__':
311     app = QApplication(sys.argv)
312     tray = SystemTray()
313     # tray.show()
314     sys.exit(app.exec_())

  只須要安裝好 pyqt4 便可。代碼寫得有些亂,由於一開始就是爲了快速達到目的,當複製好要翻譯的英文時,再點擊任務欄菜單當即翻譯的按鈕,就能看到翻譯的結果了:redis

或者系統的氣泡消息:chrome

  由於用到的外部庫只有 pyqt4 一個,其它都是 python2.7 的標準庫,而 pyqt4 在 windows 下能用 py2exe 打包成可執行程序,因此在windows下仍是能用的,並且由於 qt 對 windows 平臺的支持更好,因此我以爲在windows下的體驗應該比 ubuntu 要更好一些。json

  附上代碼中用到的圖片下載地址:beautiful_iconsubuntu

相關文章
相關標籤/搜索