有时候咱们在开发时遇到一些陌生的英文单词或者不容易看出某些长句的中文意思时该怎么办呢?打开桌面上的翻译软件?打开浏览器里收藏着的翻译网址或者直接贴上百度的搜索框去查?这些方法当然能够,还很常见,但若是是 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