有時候咱們在開發時遇到一些陌生的英文單詞或者不容易看出某些長句的中文意思時該怎麼辦呢?打開桌面上的翻譯軟件?打開瀏覽器裏收藏着的翻譯網址或者直接貼上百度的搜索框去查?這些方法當然能夠,還很常見,但若是是 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