Plugins/UiExtensionPlugins/Translator/TranslatorWidget.py

changeset 6018
1c858879d3d0
child 6048
82ad8ec9548c
equal deleted inserted replaced
6017:dab01678626d 6018:1c858879d3d0
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the translator widget.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import pyqtSlot, QTemporaryFile
13 from PyQt5.QtWidgets import QWidget
14 try:
15 from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
16 MULTIMEDIA_AVAILABLE = True
17 except ImportError:
18 MULTIMEDIA_AVAILABLE = False
19
20 from E5Gui import E5MessageBox
21
22 from .Ui_TranslatorWidget import Ui_TranslatorWidget
23
24 from .TranslatorLanguagesDb import TranslatorLanguagesDb
25 from . import TranslatorEngines
26
27 import UI.PixmapCache
28
29
30 class TranslatorWidget(QWidget, Ui_TranslatorWidget):
31 """
32 Class implementing the translator widget.
33 """
34 def __init__(self, plugin, translator, parent=None):
35 """
36 Constructor
37
38 @param plugin reference to the plugin object (TranslatorPlugin)
39 @param translator reference to the translator object (Translator)
40 @param parent reference to the parent widget (QWidget)
41 """
42 super(TranslatorWidget, self).__init__(parent)
43 self.setupUi(self)
44
45 self.__plugin = plugin
46 self.__translator = translator
47
48 self.__languages = TranslatorLanguagesDb(self)
49
50 self.__translatorRequest = None
51 self.__translationEngine = None
52
53 self.__mediaPlayer = None
54 self.__mediaFile = None
55
56 audioAvailable = (MULTIMEDIA_AVAILABLE and
57 bool(QMediaPlayer.hasSupport("audio/mpeg")))
58 self.pronounceOrigButton.setVisible(audioAvailable)
59 self.pronounceTransButton.setVisible(audioAvailable)
60
61 self.pronounceOrigButton.setIcon(
62 self.__translator.getAppIcon("pronounce.png"))
63 self.pronounceTransButton.setIcon(
64 self.__translator.getAppIcon("pronounce.png"))
65 self.swapButton.setIcon(
66 self.__translator.getAppIcon("swap.png"))
67 self.translateButton.setIcon(
68 self.__translator.getAppIcon("translate.png"))
69 self.clearButton.setIcon(UI.PixmapCache.getIcon("editDelete.png"))
70
71 self.translateButton.setEnabled(False)
72 self.clearButton.setEnabled(False)
73 self.pronounceOrigButton.setEnabled(False)
74 self.pronounceTransButton.setEnabled(False)
75
76 selectedEngine = self.__plugin.getPreferences("SelectedEngine")
77 origLanguage = self.__plugin.getPreferences("OriginalLanguage")
78 transLanguage = self.__plugin.getPreferences("TranslationLanguage")
79
80 self.__updateEngines()
81 engineIndex = self.engineComboBox.findData(selectedEngine)
82 self.engineComboBox.setCurrentIndex(engineIndex)
83 self.__engineComboBoxCurrentIndexChanged(engineIndex)
84 # this calls self.__updateLanguages() implicitly
85 self.origLanguageComboBox.setCurrentIndex(
86 self.origLanguageComboBox.findData(origLanguage))
87 self.transLanguageComboBox.setCurrentIndex(
88 self.transLanguageComboBox.findData(transLanguage))
89
90 self.engineComboBox.currentIndexChanged.connect(
91 self.__engineComboBoxCurrentIndexChanged)
92 self.__plugin.updateLanguages.connect(self.__updateLanguages)
93
94 def __updateLanguages(self):
95 """
96 Private slot to update the language combo boxes.
97 """
98 supportedCodes = self.__translationEngine.supportedLanguages()
99 origLanguage = self.origLanguageComboBox.itemData(
100 self.origLanguageComboBox.currentIndex())
101 transLanguage = self.transLanguageComboBox.itemData(
102 self.transLanguageComboBox.currentIndex())
103 self.origLanguageComboBox.clear()
104 self.transLanguageComboBox.clear()
105 for code in self.__plugin.getPreferences("EnabledLanguages"):
106 if code in supportedCodes:
107 language = self.__languages.getLanguage(code)
108 if language:
109 icon = self.__languages.getLanguageIcon(code)
110 self.origLanguageComboBox.addItem(icon, language, code)
111 self.transLanguageComboBox.addItem(icon, language, code)
112 self.origLanguageComboBox.model().sort(0)
113 self.transLanguageComboBox.model().sort(0)
114 self.origLanguageComboBox.setCurrentIndex(
115 self.origLanguageComboBox.findData(origLanguage))
116 self.transLanguageComboBox.setCurrentIndex(
117 self.transLanguageComboBox.findData(transLanguage))
118
119 def __updateEngines(self):
120 """
121 Private slot to update the engines combo box.
122 """
123 currentEngine = self.engineComboBox.itemData(
124 self.engineComboBox.currentIndex())
125 self.engineComboBox.clear()
126 for engineName in TranslatorEngines.supportedEngineNames():
127 icon = TranslatorEngines.getEngineIcon(engineName)
128 self.engineComboBox.addItem(
129 icon,
130 TranslatorEngines.engineDisplayName(engineName),
131 engineName)
132 self.engineComboBox.model().sort(0)
133 self.engineComboBox.setCurrentIndex(
134 self.engineComboBox.findData(currentEngine))
135
136 def __originalLanguage(self):
137 """
138 Private method to return the code of the selected original language.
139
140 @return code of the original language (string)
141 """
142 return self.origLanguageComboBox.itemData(
143 self.origLanguageComboBox.currentIndex())
144
145 def __translationLanguage(self):
146 """
147 Private method to return the code of the selected translation language.
148
149 @return code of the translation language (string)
150 """
151 return self.transLanguageComboBox.itemData(
152 self.transLanguageComboBox.currentIndex())
153
154 @pyqtSlot()
155 def on_translateButton_clicked(self):
156 """
157 Private slot to translate the entered text.
158 """
159 self.transEdit.clear()
160 result, ok = self.__translate(
161 self.origEdit.toPlainText(),
162 self.__originalLanguage(),
163 self.__translationLanguage())
164 if ok:
165 self.transEdit.setHtml(result)
166 else:
167 E5MessageBox.critical(
168 self,
169 self.tr("Translation Error"),
170 result)
171
172 @pyqtSlot()
173 def on_pronounceOrigButton_clicked(self):
174 """
175 Private slot to pronounce the original text.
176 """
177 self.__pronounce(
178 self.origEdit.toPlainText(), self.__originalLanguage())
179
180 @pyqtSlot()
181 def on_pronounceTransButton_clicked(self):
182 """
183 Private slot to pronounce the translated text.
184 """
185 self.__pronounce(
186 self.transEdit.toPlainText(), self.__translationLanguage())
187
188 @pyqtSlot()
189 def on_swapButton_clicked(self):
190 """
191 Private slot to swap the languages.
192 """
193 oIdx = self.origLanguageComboBox.currentIndex()
194 self.origLanguageComboBox.setCurrentIndex(
195 self.transLanguageComboBox.currentIndex())
196 self.transLanguageComboBox.setCurrentIndex(oIdx)
197
198 origText = self.origEdit.toPlainText()
199 self.origEdit.setPlainText(self.transEdit.toPlainText())
200 self.transEdit.setPlainText(origText)
201
202 @pyqtSlot()
203 def on_clearButton_clicked(self):
204 """
205 Private slot to clear the text fields.
206 """
207 self.origEdit.clear()
208 self.transEdit.clear()
209
210 @pyqtSlot()
211 def on_origEdit_textChanged(self):
212 """
213 Private slot to handle changes of the original text.
214 """
215 enable = bool(self.origEdit.toPlainText())
216 self.translateButton.setEnabled(enable)
217 self.__updatePronounceButtons()
218 self.__updateClearButton()
219
220 @pyqtSlot()
221 def on_transEdit_textChanged(self):
222 """
223 Private slot to handle changes of the translation text.
224 """
225 self.__updatePronounceButtons()
226 self.__updateClearButton()
227
228 @pyqtSlot(int)
229 def on_origLanguageComboBox_currentIndexChanged(self, index):
230 """
231 Private slot to handle the selection of the original language.
232
233 @param index current index (integer)
234 """
235 self.__plugin.setPreferences(
236 "OriginalLanguage", self.origLanguageComboBox.itemData(index))
237
238 @pyqtSlot(int)
239 def on_transLanguageComboBox_currentIndexChanged(self, index):
240 """
241 Private slot to handle the selection of the translation language.
242
243 @param index current index (integer)
244 """
245 self.__plugin.setPreferences(
246 "TranslationLanguage", self.transLanguageComboBox.itemData(index))
247
248 @pyqtSlot(int)
249 def __engineComboBoxCurrentIndexChanged(self, index):
250 """
251 Private slot to handle the selection of a translation service.
252
253 @param index current index (integer)
254 """
255 engineName = self.engineComboBox.itemData(index)
256 if self.__translationEngine and \
257 self.__translationEngine.engineName() != engineName:
258 self.__translationEngine.deleteLater()
259 self.__translationEngine = None
260
261 if self.__translationEngine is None:
262 self.__translationEngine = TranslatorEngines.getTranslationEngine(
263 engineName, self.__plugin, self)
264
265 self.__updatePronounceButtons()
266 self.__updateLanguages()
267
268 self.__plugin.setPreferences(
269 "SelectedEngine", self.engineComboBox.itemData(index))
270
271 def __updatePronounceButtons(self):
272 """
273 Private slot to set the state of the pronounce buttons.
274 """
275 hasTTS = self.__translationEngine.hasTTS()
276 self.pronounceOrigButton.setEnabled(
277 hasTTS and bool(self.origEdit.toPlainText()))
278 self.pronounceTransButton.setEnabled(
279 hasTTS and bool(self.transEdit.toPlainText()))
280
281 def __updateClearButton(self):
282 """
283 Private slot to set the state of the clear button.
284 """
285 enable = bool(self.origEdit.toPlainText()) or \
286 bool(self.transEdit.toPlainText())
287 self.clearButton.setEnabled(enable)
288
289 def __translate(self, text, originalLanguage, translationLanguage):
290 """
291 Private method to translate the given text.
292
293 @param text text to be translated (string)
294 @param originalLanguage language code of the original (string)
295 @param translationLanguage language code of the translation (string)
296 @return tuple of translated text (string) and flag indicating
297 success (boolean)
298 """
299 if self.__translatorRequest is None:
300 from .TranslatorRequest import TranslatorRequest
301 self.__translatorRequest = TranslatorRequest(self)
302
303 engineName = self.engineComboBox.itemData(
304 self.engineComboBox.currentIndex())
305 if self.__translationEngine and \
306 self.__translationEngine.engineName() != engineName:
307 self.__translationEngine.deleteLater()
308 self.__translationEngine = None
309
310 if self.__translationEngine is None:
311 self.__translationEngine = TranslatorEngines.getTranslationEngine(
312 engineName, self.__plugin, self)
313
314 result, ok = self.__translationEngine.getTranslation(
315 self.__translatorRequest, text, originalLanguage,
316 translationLanguage)
317
318 return result, ok
319
320 def __pronounce(self, text, language):
321 """
322 Private method to pronounce the given text.
323
324 @param text text to be pronounced (string)
325 @param language language code of the text (string)
326 """
327 if not text or not language:
328 return
329
330 if self.__translatorRequest is None:
331 from .TranslatorRequest import TranslatorRequest
332 self.__translatorRequest = TranslatorRequest(self)
333
334 if self.__mediaPlayer is None:
335 self.__mediaPlayer = QMediaPlayer(self)
336 self.__mediaPlayer.stateChanged.connect(
337 self.__mediaPlayerStateChanged)
338
339 if self.__mediaPlayer.state() == QMediaPlayer.PlayingState:
340 return
341
342 engineName = self.engineComboBox.itemData(
343 self.engineComboBox.currentIndex())
344 if self.__translationEngine and \
345 self.__translationEngine.engineName() != engineName:
346 self.__translationEngine.deleteLater()
347 self.__translationEngine = None
348
349 if self.__translationEngine is None:
350 self.__translationEngine = TranslatorEngines.getTranslationEngine(
351 engineName, self.__plugin, self)
352
353 if not self.__translationEngine.hasTTS():
354 E5MessageBox.critical(
355 self,
356 self.tr("Translation Error"),
357 self.tr("The selected translation service does not support"
358 " the Text-to-Speech function."))
359 return
360
361 data, ok = self.__translationEngine.getTextToSpeechData(
362 self.__translatorRequest, text, language)
363 if ok:
364 self.__mediaFile = QTemporaryFile(self)
365 self.__mediaFile.open()
366 self.__mediaFile.setAutoRemove(False)
367 self.__mediaFile.write(data)
368
369 self.__mediaPlayer.setMedia(QMediaContent(), self.__mediaFile)
370 self.__mediaPlayer.play()
371 else:
372 E5MessageBox.critical(
373 self,
374 self.tr("Translation Error"),
375 data)
376
377 def __mediaPlayerStateChanged(self, state):
378 """
379 Private slot handling changes of the media player state.
380
381 @param state media player state (QAudio.State)
382 """
383 if state == QMediaPlayer.StoppedState:
384 self.__mediaFile.close()
385 self.__mediaFile.remove()
386 self.__mediaFile = None

eric ide

mercurial