有时候我们在开发时遇到一些陌生的英文单词或者不容易看出某些长句的中文意思时该怎么办呢?打开桌面上的翻译软件?打开浏览器里收藏着的翻译网址或者直接贴上百度的搜索框去查?这些方法固然可以,还很常见,但如果是 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. class SystemTray(QMainWindow):
  12. """My SystemTray, includes translator and functions of getting commands quickly."""
  13.  
  14. YDERRORCODE = {
  15. 0: u'正常',
  16. 20: u'要翻译的文本过长',
  17. 30: u'无法进行有效的翻译',
  18. 40: u'不支持的语言类型',
  19. 50: u'无效的key',
  20. 60: u'无词典结果,仅在获取词典结果生效'
  21. }
  22.  
  23. def __init__(self, title="SystemTray", size=[600, 500]):
  24. super(SystemTray, self).__init__()
  25. self.initSelf(title, size)
  26. self.initUI()
  27.  
  28. def initSelf(self, title, size):
  29. self.setWindowTitle(title)
  30. self.setWindowIcon(QIcon('./images/window_icon.jpg'))
  31. self.resize(size[0], size[1])
  32. self.showOnCenter()
  33.  
  34. self.clipboard = QApplication.clipboard()
  35. self.translateUrl = "http://fanyi.youdao.com/openapi.do?keyfrom=myname&key=xxx&type=data&doctype=json&version=1.1&q="
  36.  
  37. def initUI(self):
  38. self.initBoard()
  39. self.statusbar = self.statusBar()
  40. self.initAction()
  41. self.initTrayIcon()
  42.  
  43. def initBoard(self):
  44. board = QWidget()
  45. mainLayout = QVBoxLayout()
  46.  
  47. gbox = QGroupBox(u'翻译设置')
  48. grid = QGridLayout()
  49. self.cbImmediatelyTranslate = QCheckBox(u'翻译剪切板中的内容')
  50. self.cbImmediatelyTranslate.setChecked(True)
  51. self.cbImmediatelyTranslate.stateChanged.connect(self.translateOption)
  52. grid.addWidget(self.cbImmediatelyTranslate, 0, 0, 1, 2)
  53. label = QLabel(u'字数限制:')
  54. label.setAlignment(Qt.AlignRight)
  55. grid.addWidget(label, 0, 3)
  56. self.sbWordLimit = QSpinBox()
  57. self.sbWordLimit.setRange(1, 800)
  58. self.sbWordLimit.setValue(200)
  59. self.sbWordLimit.valueChanged.connect(lambda x: self.slotWordLimitChange(self.sbWordLimit.value()))
  60. grid.addWidget(self.sbWordLimit, 0, 4)
  61.  
  62. self.label_1 = QLabel(u'输入要翻译的文本:')
  63. grid.addWidget(self.label_1, 1, 0)
  64. self.textTranslate = QTextEdit()
  65. grid.addWidget(self.textTranslate, 2, 0, 3, 6)
  66. self.btnTranslate = QPushButton(u'翻译')
  67. self.btnTranslate.clicked.connect(self.btnClicked)
  68. grid.addWidget(self.btnTranslate, 6, 5)
  69. self.label_1.hide()
  70. self.textTranslate.hide()
  71. self.btnTranslate.hide()
  72.  
  73. label = QLabel(u'显示方式:')
  74. label.setAlignment(Qt.AlignRight)
  75. grid.addWidget(label, 6, 0)
  76. self.cbbTranslateShowType = QComboBox()
  77. self.cbbTranslateShowType.addItem(QIcon(u'./images/messagebox.png'), u'弹窗显示')
  78. self.cbbTranslateShowType.addItem(self.style().standardIcon(QStyle.SP_MessageBoxInformation), u'系统通知')
  79. self.cbbTranslateShowType.currentIndexChanged.connect(lambda x: self.slotShowTypeChange(self.cbbTranslateShowType.currentIndex()))
  80. grid.addWidget(self.cbbTranslateShowType, 6, 1)
  81.  
  82. self.cbShowTranslateDeatil = QCheckBox(u'显示详细的翻译结果')
  83. self.cbShowTranslateDeatil.clicked.connect(lambda x: self.slotShowDetailChange(self.cbShowTranslateDeatil.isChecked()))
  84. self.cbShowTranslateDeatil.setChecked(True)
  85. grid.addWidget(self.cbShowTranslateDeatil, 6, 3)
  86.  
  87. grid.setColumnStretch(1, 1)
  88. grid.setColumnStretch(2, 1)
  89. grid.setColumnStretch(4, 1)
  90. grid.setColumnStretch(5, 1)
  91. gbox.setLayout(grid)
  92. mainLayout.addWidget(gbox)
  93.  
  94. gbox = QGroupBox(u'快速复制命令到剪切板')
  95. grid = QGridLayout()
  96. label = QLabel(u'历史命令(history):')
  97. grid.addWidget(label, 0, 0)
  98. self.cbbHistoryCommands = QComboBox()
  99. grid.addWidget(self.cbbHistoryCommands, 1, 0)
  100. gbox.setLayout(grid)
  101. mainLayout.addWidget(gbox)
  102.  
  103. mainLayout.addStretch(1)
  104. board.setLayout(mainLayout)
  105. self.setCentralWidget(board)
  106.  
  107. def slotShowTypeChange(self, index):
  108. self.cbbTranslateShowType.setCurrentIndex(index)
  109. if index == 0:
  110. self.actMessagebox.setChecked(True)
  111. else:
  112. self.actSystemMessage.setChecked(True)
  113.  
  114. def slotShowDetailChange(self, bShowDeatil):
  115. if bShowDeatil == True:
  116. self.actShowDetail.setChecked(True)
  117. self.cbShowTranslateDeatil.setChecked(True)
  118. else:
  119. self.actShowDetail.setChecked(False)
  120. self.cbShowTranslateDeatil.setChecked(False)
  121.  
  122. def slotWordLimitChange(self, wordLen):
  123. if wordLen in [100, 200, 400]:
  124. self.sbWordLimit.setValue(wordLen)
  125. if wordLen == 100:
  126. self.actWordLimit_1.setChecked(True)
  127. elif wordLen == 200:
  128. self.actWordLimit_2.setChecked(True)
  129. elif wordLen == 400:
  130. self.actWordLimit_3.setChecked(True)
  131. else:
  132. self.actWordLimit_4.setChecked(True)
  133. self.actWordLimit_4.setText(('其它(' + str(wordLen) + ')').decode('utf8'))
  134. self.show()
  135.  
  136. def initAction(self):
  137. self.actTranslate = QAction(QIcon('./images/youdao.jpg'), u'立即翻译', self)
  138. self.actTranslate.setShortcut('ctrl+alt+f')
  139. self.actTranslate.triggered.connect(self.translate_clipboard)
  140.  
  141. menuShowType = QMenu(u'显示方式', self)
  142. ag = QActionGroup(self, exclusive=True)
  143. self.actMessagebox = QAction(QIcon(u'./images/messagebox.png'), u'弹窗显示', menuShowType, checkable=True)
  144. self.actMessagebox.triggered.connect(lambda x: self.slotShowTypeChange(0))
  145. self.actMessagebox.setChecked(True)
  146. self.actSystemMessage = QAction(self.style().standardIcon(QStyle.SP_MessageBoxInformation), u'系统通知', menuShowType, checkable=True)
  147. self.actSystemMessage.triggered.connect(lambda x: self.slotShowTypeChange(1))
  148. menuShowType.addActions([ag.addAction(self.actMessagebox), ag.addAction(self.actSystemMessage)])
  149.  
  150. menuTranslateOption = QMenu(u'翻译设置', self)
  151. menuTranslateOption.setIcon(QIcon(u'./images/config.png'))
  152. self.actShowDetail = QAction(u'显示详细结果', menuTranslateOption, checkable=True)
  153. self.actShowDetail.triggered.connect(lambda x: self.slotShowDetailChange(self.actShowDetail.isChecked()))
  154. self.actShowDetail.setChecked(True)
  155.  
  156. menuWordLimit = QMenu(u'字数限制', self)
  157. ag = QActionGroup(self, exclusive=True)
  158. self.actWordLimit_1 = QAction(u'', menuWordLimit, checkable=True)
  159. self.actWordLimit_1.triggered.connect(lambda x: self.slotWordLimitChange(100))
  160. self.actWordLimit_2 = QAction(u'', menuWordLimit, checkable=True)
  161. self.actWordLimit_2.triggered.connect(lambda x: self.slotWordLimitChange(200))
  162. self.actWordLimit_2.setChecked(True)
  163. self.actWordLimit_3 = QAction(u'', menuWordLimit, checkable=True)
  164. self.actWordLimit_3.triggered.connect(lambda x: self.slotWordLimitChange(400))
  165. self.actWordLimit_4 = QAction(u'其它', menuWordLimit, checkable=True)
  166. self.actWordLimit_4.triggered.connect(lambda x: self.slotWordLimitChange(1))
  167. menuWordLimit.addActions([ag.addAction(self.actWordLimit_1), ag.addAction(self.actWordLimit_2), ag.addAction(self.actWordLimit_3), ag.addAction(self.actWordLimit_4)])
  168.  
  169. menubar = self.menuBar()
  170. menuTranslate = menubar.addMenu(u'翻译')
  171. menuTranslate.addAction(self.actTranslate)
  172. menuTranslateOption.addMenu(menuShowType)
  173. menuTranslateOption.addAction(self.actShowDetail)
  174. menuTranslateOption.addMenu(menuWordLimit)
  175. menuTranslate.addMenu(menuTranslateOption)
  176.  
  177. commandsMenu = menubar.addMenu(u'commands设置')
  178.  
  179. def initTrayIcon(self):
  180. menuTrayIcon = QMenu(self)
  181. menuTrayIcon.addAction(self.actTranslate)
  182.  
  183. menuTranslateOption = QMenu(u'翻译设置', self)
  184. menuTranslateOption.setIcon(QIcon(u'./images/config.png'))
  185. menuShowType = QMenu(u'显示方式', self)
  186. menuShowType.addActions([self.actMessagebox, self.actSystemMessage])
  187. menuTranslateOption.addMenu(menuShowType)
  188. menuTranslateOption.addAction(self.actShowDetail)
  189. menuWordLimit = QMenu(u'字数限制', self)
  190. menuWordLimit.addActions([self.actWordLimit_1, self.actWordLimit_2, self.actWordLimit_3, self.actWordLimit_4])
  191. menuTranslateOption.addMenu(menuWordLimit)
  192.  
  193. menuTrayIcon.addMenu(menuTranslateOption)
  194. # menuTrayIcon.addAction(QAction("Minimize", self, triggered=self.showMinimized))
  195. menuTrayIcon.addSeparator()
  196. menuTrayIcon.addAction(QAction(QIcon('./images/qt.png'), u"打开主面板", self, triggered=self.showNormal))
  197. menuTrayIcon.addAction(QAction(QIcon('./images/quit.png'), u"退出", self, triggered=qApp.quit))
  198.  
  199. self.trayIcon = QSystemTrayIcon(QIcon('./images/tray_icon.jpg'), self)
  200. self.trayIcon.setContextMenu(menuTrayIcon)
  201. self.trayIcon.show()
  202.  
  203. def btnClicked(self):
  204. self.translateText(self.textTranslate.toPlainText())
  205.  
  206. def translateOption(self):
  207. if self.cbImmediatelyTranslate.isChecked():
  208. self.label_1.hide()
  209. self.textTranslate.hide()
  210. self.btnTranslate.hide()
  211. else:
  212. self.label_1.show()
  213. self.textTranslate.show()
  214. self.btnTranslate.show()
  215.  
  216. def translate(self, text):
  217. if len(text) > self.sbWordLimit.value():
  218. return (1, u'翻译的文本长度超过字数限制!')
  219. text = (str(text.toUtf8())).strip()
  220. if text == '':
  221. return (2, u'翻译的字符串中没有实际的字符(只包含空格/tab/换行等)')
  222. req = self.translateUrl + text
  223. r = requests.get(req)
  224. if r.ok != True:
  225. return (r.status_code, QString(u'网络错误,url请求失败'))
  226. else:
  227. u_dict = json.loads(r.text)
  228. errorCode = u_dict['errorCode']
  229. if errorCode != 0:
  230. return (errorCode, QString(self.YDERRORCODE[errorCode]))
  231. else:
  232. res = QString(u'')
  233. if u_dict.has_key('translation'):
  234. res += u"<翻译>: "
  235. for x in u_dict['translation']:
  236. res = res + x + ", "
  237. res = (
  238. res[:-2] + "\n") if res[-2:] == ", " else (res + "\n")
  239. if u_dict.has_key('basic'):
  240. if u_dict['basic'].has_key('us-phonetic'):
  241. res = res + u"<美式发音>: " + u_dict['basic']['us-phonetic'] + "\t"
  242. if u_dict['basic'].has_key('uk-phonetic'):
  243. res = res + u"<英式发音>: " + u_dict['basic']['uk-phonetic'] + "\n"
  244. if u_dict['basic'].has_key('explains'):
  245. res += u"<解释>: "
  246. for x in u_dict['basic']['explains']:
  247. res = res + x + ", "
  248. res = (
  249. res[:-2] + "\n") if res[-2:] == ", " else (res + "\n")
  250. if self.cbShowTranslateDeatil.isChecked():
  251. if u_dict.has_key('web'):
  252. res += u"<网络用语>: \n"
  253. for d in u_dict['web']:
  254. if d.has_key('key'):
  255. res = res + u" <关键词>: " + d['key'] + "\n"
  256. if d.has_key('value'):
  257. res = res + u" <意思>: "
  258. for v in d['value']:
  259. res = res + v + ", "
  260. res = (
  261. res[:-2] + "\n") if res[-2:] == ", " else (res + "\n")
  262. return (errorCode, res)
  263.  
  264. def translateText(self, text):
  265. if type(text) == type(QString()):
  266. text = text.simplified()
  267. elif type(text) == str:
  268. text = text.strip()
  269. result = self.translate(text)
  270. showType = self.cbbTranslateShowType.currentIndex()
  271. if result[0] != 0:
  272. title = QString(u'翻译出错')
  273. self.showTranslateResult(title, result[1], QMessageBox.Warning)
  274. else:
  275. title = (text[0:20] + u'....' if len(text)
  276. >= 20 else text) + u" 的翻译结果 "
  277. self.showTranslateResult(title, result[1], QMessageBox.Information)
  278.  
  279. def translate_clipboard(self):
  280. clip_data = self.clipboard.mimeData()
  281. if clip_data.hasText():
  282. src = clip_data.text()
  283. self.translateText(src)
  284. else:
  285. QMessageBox.information(self, u'提示', u'剪切板中的内容为空,无法翻译!')
  286.  
  287. def showTranslateResult(self, title, content, icon):
  288. showType = self.cbbTranslateShowType.currentIndex()
  289. if showType == 0:
  290. # 至关重要的一句,否则当关闭messagebox的窗口时,整个程序自动退出!
  291. QApplication.setQuitOnLastWindowClosed(False)
  292. QMessageBox(QMessageBox.Icon(icon), title, content).exec_()
  293. elif showType == 1:
  294. self.trayIcon.showMessage(title, content, QSystemTrayIcon.MessageIcon(icon))
  295. else:
  296. pass
  297.  
  298. def showOnCenter(self):
  299. screen = QDesktopWidget().screenGeometry()
  300. self.move((screen.width() - self.width()) / 2,
  301. (screen.height() - self.height()) / 2)
  302.  
  303. def closeEvent(self, event):
  304. if self.trayIcon.isVisible():
  305. self.trayIcon.showMessage(u'隐藏', u'在任务栏按钮可打开主窗口', QSystemTrayIcon.MessageIcon(QMessageBox.Information), 1000)
  306. self.hide()
  307. event.ignore()
  308.  
  309. if __name__ == '__main__':
  310. app = QApplication(sys.argv)
  311. tray = SystemTray()
  312. # tray.show()
  313. sys.exit(app.exec_())

  只需要安装好 pyqt4 即可。代码写得有些乱,因为一开始就是为了快速达到目的,当复制好要翻译的英文时,再点击任务栏菜单立即翻译的按钮,就能看到翻译的结果了:

或者系统的气泡消息:

  因为用到的外部库只有 pyqt4 一个,其它都是 python2.7 的标准库,而 pyqt4 在 windows 下能用 py2exe 打包成可执行程序,所以在windows下还是能用的,而且因为 qt 对 windows 平台的支持更好,所以我觉得在windows下的体验应该比 ubuntu 要更好一些。

  附上代码中用到的图片下载地址:beautiful_icons

用 pyqt4 编写的一个翻译小工具的更多相关文章

  1. 基于百度通用翻译API的一个翻译小工具

    前几天写了一个简单的翻译小工具,是基于有道翻译的,不过那个翻译接口有访问限制,超过一定次数后会提示访问过于频繁,偶然发现百度翻译API如果月翻译字符少于200万是不收取费用的,所以就注册了一个百度开发 ...

  2. (win环境)使用Electron打造一个桌面应用翻译小工具

    初始化项目 npm init 修改package.json {"name": "trans","version": "1.0.0& ...

  3. C#借助谷歌翻译实现翻译小工具(一)基本功能实现

    软件效果: 实现原理很简单,就是封装谷歌翻译网站:http://translate.google.cn/,一个WebBrowser"肢解"谷歌翻译网站的HtmlElement元素, ...

  4. 搭建Spring开发环境并编写第一个Spring小程序

    搭建Spring开发环境并编写第一个Spring小程序 2015-05-27      0个评论    来源:茕夜   收藏    我要投稿 一.前面,我写了一篇Spring框架的基础知识文章,里面没 ...

  5. JDK9版本以上Java独有的一个轻量级小工具,你知道吗?jshell

    jshell,是JavaJDK9这个大版本更新以来,带来的一个轻量级小工具.我们再也不用进入Java目录,编写一个Java文件,然后再去编译,最后才能执行它. 这里,你可以直接写一个小功能,就能去实现 ...

  6. 练习-99乘法表 token生成器 翻译小工具

    一.99乘法表 1.1 技术点 记住: for 循环的使用,以及for的嵌套使用 range()的使用,掌握sep为负数的使用的使用. print() 函数的使用,默认的结尾的换行符 替换 end= ...

  7. 访问github太慢?我写了一个开源小工具一键变快

    前言 GitHub应该是广大开发者最常去的站点,这里面有大量的优秀项目,是广大开发者寻找资源,交友学习的好地方.尤其是前段时间GitHub公布了一项代码存档计划--Arctic Code Vault, ...

  8. 调用百度API写了一个js翻译小工具

    目前还未完成的功能有:textarea高度自适应,移动端与pc端都写了.效果如图: html: <!DOCTYPE html> <html lang="en"&g ...

  9. C#借助谷歌翻译实现翻译小工具(二)添加托盘图标

    接上一节完善小翻译工具 设置Form的ShowInTaskbar属性为False,取消任务栏显示 设置Form的MaximizeBox属性为False,取消最大化显示 窗口添加两个控件 分别是:Con ...

随机推荐

  1. 复制mysql数据库的步骤

    Navicat 转存sql文件 然后命令 mysql -uroot -p123456 dbname < e:/backup/20141014.sql

  2. bzoj 3119: Book

    Description Wayne喜欢看书,更喜欢买书.某天Wayne在当当网上买书,买了很多很多书.Wayne有一个奇怪的癖好,就是第一本书的价格必须恰为X,而之后买的每一本书,若是比上一本更昂贵, ...

  3. generatorConfig.xml

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration ...

  4. MAMP pro mac 本地集成环境 php sal apache等集成软件

    http://www.sdifen.com/mamppro411.html 已存在我的百度云盘 安装后,打开  MAMP 第一步:配置启动和停止选项 默认启动项.默认停止项,只需要勾选: 1.Star ...

  5. PHP数组键值使用单引号和双引号和无符号的区别

    PHP数组键值使用单引号和双引号和无符号的区别 方法/步骤 1 第一种:$array['key']此单引号键值模式可以直接被解析为一个数组即$array 第二种:$array["key&qu ...

  6. SpringBoot入门(1)

    一.初始 ①.首先还是要创建一个maven工程 ②.然后编写Controller 让SpringBoot跑起来并不需要太多的代码,就能实现了我们平时要配置很多的功能,这是怎么做到的呢?我们就下面一个入 ...

  7. IPv4正则表达式匹配

    IP地址的长度为32位,分为4段,每段8位.用十进制数字表示,每段数字范围为0~255,段与段之间用英文句点“.”隔开.例如:某台计算机IP地址为111.22.33.4. 分析IP地址的组成特点:25 ...

  8. django-DIL模板自定义过滤器,自定义标签,自定义包含标签

    自定义过滤器 DTL模板语言生来只是为了方便的展示信息,所以与编程语言相比显得有点薄弱,有时候不能满足我们的需求.因此django提供了一个接口,让开发者能自定义标签和过滤器. 首先,你需要添加一个t ...

  9. 分享一个jquery插件,弥补一下hover事件的小小不足

    hover事件有一个缺点:当你的鼠标无意划过一个dom元素(瞬间划过,这个时候用户可能不想触发hover事件),会触发hover事件 应该设置一个时差来控制hover事件的触发 比如jd左边的菜单 你 ...

  10. linux下mysql的安装配置

    http://blog.csdn.net/xiagege3/article/details/41852895   (实战用的此文,要求mysql源码必须是未编译的,需依赖cmake编译) http:/ ...