用 pyqt4 编写的一个翻译小工具
Posted Newdawn_ALM
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用 pyqt4 编写的一个翻译小工具相关的知识,希望对你有一定的参考价值。
有时候我们在开发时遇到一些陌生的英文单词或者不容易看出某些长句的中文意思时该怎么办呢?打开桌面上的翻译软件?打开浏览器里收藏着的翻译网址或者直接贴上百度的搜索框去查?这些方法固然可以,还很常见,但如果是 linux 系统的话,很难找到像 windows 上那些公司级别来开发的成熟的翻译软件,所以只能打开浏览器来查了。浏览器一般都会装上一些翻译插件,比如我常用的 chrome 的 划词翻译,直接用这些插件来进行翻译比起打开一个翻译网站或者百度google搜索要更快,毕竟因为加载的内容更少,但这终究只是浏览器的插件,在浏览网页时用还方便,对于在其它软件上看到的陌生单词如果也放到这儿来查的话多少就感到有些转折了,所以少数的软件也会有相应的翻译插件,像优秀的编辑器 sublime text 等,就有人专门写了一个翻译的插件,可是像这类有翻译插件的软件毕竟还是极少数,如果用其它编辑器或 IDE 来开发的话也自己写一个翻译插件的话就太折腾了(我之前用 codeblocks 来看代码时遇到不懂的单词也想着能不能自行开发一个翻译插件,上网搜了一下发现开发 codeblocks 的插件需要的技术挺偏的,而且也不容易;有时经常在终端上 man 一个不熟悉的命令时也会遇到很多不懂的英文单词,此时为终端加一个翻译插件感觉就不太现实了吧)。
所以,我前几天时就在想,能不能开发一个全局的翻译插件呢?也就是在使用电脑上所有软件时都能很方便调用这个插件去获取翻译的结果,因为我大多数时间是在使用 ubuntu,ubuntu 的任务栏默认是位于电脑的上方的,感觉看着很显眼,鼠标触及也方便,所以想就把这个插件放到任务栏上吧,这样子不管是在用 codeblocks / eclipse等 IDE 来进行开发,或者在使用终端进行各种命令的操作时,或者直接是在看网页(用不同的浏览器)等等,都能通过点击任务栏的小图标就能立即进行翻译了。
我一开始的设计思想就是为了用最少的操作(也就是最少的鼠标点击次数或者键盘打字数目)来达到目的,所以我的这个全局插件的翻译原理是这样的:用 pyqt 构建好的 GUI 程序在后台运行,任务栏上显示,每当点击它在任务栏上的图标菜单里的翻译按钮时,程序会获取剪切版里的内容,然后把这些内容通过有道翻译的API得到翻译的 json 格式的结果,然后把 json 格式的结果处理一下,最后通过弹窗显示或者系统气泡消息的方式展现出来。因为我用的终端是 terminator,能设置成鼠标划过的文本直接进入剪切板,所以当我想要翻译一句英文时,只需要用鼠标选择好这些文本,然后再点击任务栏图标的翻译按钮就能直接能到结果,整个过程不过两次点击鼠标的时间,无须用到键盘,比起用浏览器来查,可以说是大大节省了时间,能显著地提高日常学习/开发的效率。好了,废话不多说了,先放上一张效果图:
(我能说这个图非常难截么?因为用不了截图软件来截图(鼠标一点击截图软件时任务栏图标的菜单列表就会立刻消失),所以只能用键盘的截图键来截整个屏幕的图,然后再裁剪图片,可是在 ubuntu 下截取瞬间会有闪现的效果,导致这个菜单栏变得很模糊,所以我只能先换一张几乎是纯黑色的桌面背景,然后裁剪好后再作亮度调整等处理,先将就着看一下哦~)
完整代码如下:
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以上是关于用 pyqt4 编写的一个翻译小工具的主要内容,如果未能解决你的问题,请参考以下文章