eric6/Plugins/UiExtensionPlugins/Translator/TranslatorWidget.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 - 2019 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 from E5Gui.E5Application import e5App
22
23 from .Ui_TranslatorWidget import Ui_TranslatorWidget
24
25 from .TranslatorLanguagesDb import TranslatorLanguagesDb
26 from . import TranslatorEngines
27
28 import UI.PixmapCache
29
30
31 class TranslatorWidget(QWidget, Ui_TranslatorWidget):
32 """
33 Class implementing the translator widget.
34 """
35 def __init__(self, plugin, translator, parent=None):
36 """
37 Constructor
38
39 @param plugin reference to the plugin object (TranslatorPlugin)
40 @param translator reference to the translator object (Translator)
41 @param parent reference to the parent widget (QWidget)
42 """
43 super(TranslatorWidget, self).__init__(parent)
44 self.setupUi(self)
45
46 self.__plugin = plugin
47 self.__translator = translator
48
49 self.__languages = TranslatorLanguagesDb(self)
50
51 self.__translatorRequest = None
52 self.__translationEngine = None
53
54 self.__mediaPlayer = None
55 self.__mediaFile = None
56
57 audioAvailable = (MULTIMEDIA_AVAILABLE and
58 bool(QMediaPlayer.hasSupport("audio/mpeg")))
59 self.pronounceOrigButton.setVisible(audioAvailable)
60 self.pronounceTransButton.setVisible(audioAvailable)
61
62 self.pronounceOrigButton.setIcon(
63 self.__translator.getAppIcon("pronounce.png"))
64 self.pronounceTransButton.setIcon(
65 self.__translator.getAppIcon("pronounce.png"))
66 self.swapButton.setIcon(
67 self.__translator.getAppIcon("swap.png"))
68 self.translateButton.setIcon(
69 self.__translator.getAppIcon("translate.png"))
70 self.clearButton.setIcon(UI.PixmapCache.getIcon("editDelete.png"))
71 self.preferencesButton.setIcon(UI.PixmapCache.getIcon("configure.png"))
72
73 self.translateButton.setEnabled(False)
74 self.clearButton.setEnabled(False)
75 self.pronounceOrigButton.setEnabled(False)
76 self.pronounceTransButton.setEnabled(False)
77
78 selectedEngine = self.__plugin.getPreferences("SelectedEngine")
79
80 self.__updateEngines()
81 engineIndex = self.engineComboBox.findData(selectedEngine)
82 self.engineComboBox.setCurrentIndex(engineIndex)
83 self.__engineComboBoxCurrentIndexChanged(engineIndex)
84
85 self.engineComboBox.currentIndexChanged.connect(
86 self.__engineComboBoxCurrentIndexChanged)
87 self.__plugin.updateLanguages.connect(self.__updateLanguages)
88
89 def __updateLanguages(self):
90 """
91 Private slot to update the language combo boxes.
92 """
93 self.__ensureTranslationEngineReady()
94 if self.__translationEngine is not None:
95 supportedCodes = self.__translationEngine.supportedLanguages()
96 enabledCodes = self.__plugin.getPreferences("EnabledLanguages")
97
98 # 1. save current selections
99 origLanguage = self.origLanguageComboBox.itemData(
100 self.origLanguageComboBox.currentIndex())
101
102 # 2. reload the original language combo box
103 self.origLanguageComboBox.blockSignals(True)
104 self.origLanguageComboBox.clear()
105 for code in enabledCodes:
106 if code in supportedCodes:
107 language = self.__languages.getLanguage(code)
108 if language:
109 icon = self.__languages.getLanguageIcon(code)
110 self.origLanguageComboBox.addItem(
111 icon, language, code)
112 self.origLanguageComboBox.model().sort(0)
113 origIndex = self.origLanguageComboBox.findData(origLanguage)
114 if origIndex == -1:
115 origIndex = 0
116 self.origLanguageComboBox.blockSignals(False)
117 self.origLanguageComboBox.setCurrentIndex(origIndex)
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 # save selected language codes
194 oLanguage = self.origLanguageComboBox.itemData(
195 self.origLanguageComboBox.currentIndex())
196
197 tLanguage = self.transLanguageComboBox.itemData(
198 self.transLanguageComboBox.currentIndex())
199
200 oIdx = self.origLanguageComboBox.findData(tLanguage)
201 if oIdx < 0:
202 oIdx = 0
203 self.origLanguageComboBox.setCurrentIndex(oIdx)
204
205 tIdx = self.transLanguageComboBox.findData(oLanguage)
206 if tIdx < 0:
207 tIdx = 0
208 self.transLanguageComboBox.setCurrentIndex(tIdx)
209
210 origText = self.origEdit.toPlainText()
211 self.origEdit.setPlainText(self.transEdit.toPlainText())
212 self.transEdit.setPlainText(origText)
213
214 @pyqtSlot()
215 def on_clearButton_clicked(self):
216 """
217 Private slot to clear the text fields.
218 """
219 self.origEdit.clear()
220 self.transEdit.clear()
221
222 @pyqtSlot()
223 def on_origEdit_textChanged(self):
224 """
225 Private slot to handle changes of the original text.
226 """
227 self.__updatePronounceButtons()
228 self.__updateClearButton()
229 self.__updateTranslateButton()
230
231 @pyqtSlot()
232 def on_transEdit_textChanged(self):
233 """
234 Private slot to handle changes of the translation text.
235 """
236 self.__updatePronounceButtons()
237 self.__updateClearButton()
238
239 @pyqtSlot(int)
240 def on_origLanguageComboBox_currentIndexChanged(self, index):
241 """
242 Private slot to handle the selection of the original language.
243
244 @param index current index (integer)
245 """
246 self.__plugin.setPreferences(
247 "OriginalLanguage", self.origLanguageComboBox.itemData(index))
248
249 supportedTargetCodes = \
250 self.__translationEngine.supportedTargetLanguages(
251 self.origLanguageComboBox.itemData(index)
252 )
253 if supportedTargetCodes is not None:
254 enabledCodes = self.__plugin.getPreferences("EnabledLanguages")
255 transLanguage = self.transLanguageComboBox.itemData(
256 self.transLanguageComboBox.currentIndex())
257 self.transLanguageComboBox.clear()
258 if len(supportedTargetCodes) > 0:
259 for code in enabledCodes:
260 if code in supportedTargetCodes:
261 language = self.__languages.getLanguage(code)
262 if language:
263 icon = self.__languages.getLanguageIcon(code)
264 self.transLanguageComboBox.addItem(
265 icon, language, code)
266 self.transLanguageComboBox.model().sort(0)
267 index = self.transLanguageComboBox.findData(transLanguage)
268 if index == -1:
269 index = 0
270 self.transLanguageComboBox.setCurrentIndex(index)
271
272 self.__updateTranslateButton()
273
274 @pyqtSlot(int)
275 def on_transLanguageComboBox_currentIndexChanged(self, index):
276 """
277 Private slot to handle the selection of the translation language.
278
279 @param index current index (integer)
280 """
281 self.__plugin.setPreferences(
282 "TranslationLanguage", self.transLanguageComboBox.itemData(index))
283
284 @pyqtSlot()
285 def __availableTranslationsLoaded(self):
286 """
287 Private slot to handle the availability of translations.
288 """
289 origLanguage = self.__plugin.getPreferences("OriginalLanguage")
290 transLanguage = self.__plugin.getPreferences("TranslationLanguage")
291
292 self.__updateLanguages()
293
294 origIndex = self.origLanguageComboBox.findData(origLanguage)
295 self.origLanguageComboBox.setCurrentIndex(origIndex)
296 self.on_origLanguageComboBox_currentIndexChanged(origIndex)
297 self.transLanguageComboBox.setCurrentIndex(
298 self.transLanguageComboBox.findData(transLanguage))
299
300 def __ensureTranslationEngineReady(self):
301 """
302 Private slot to ensure, that the currently selected translation engine
303 is ready.
304 """
305 engineName = self.engineComboBox.itemData(
306 self.engineComboBox.currentIndex())
307 if self.__translationEngine is not None and \
308 self.__translationEngine.engineName() != engineName:
309 self.__translationEngine.availableTranslationsLoaded.disconnect(
310 self.__availableTranslationsLoaded)
311 self.__translationEngine.deleteLater()
312 self.__translationEngine = None
313
314 if self.__translationEngine is None:
315 self.__translationEngine = TranslatorEngines.getTranslationEngine(
316 engineName, self.__plugin, self)
317 if self.__translationEngine is not None:
318 self.__translationEngine.availableTranslationsLoaded.connect(
319 self.__availableTranslationsLoaded)
320
321 @pyqtSlot(int)
322 def __engineComboBoxCurrentIndexChanged(self, index):
323 """
324 Private slot to handle the selection of a translation service.
325
326 @param index current index
327 @type int
328 """
329 self.__ensureTranslationEngineReady()
330 if self.__translationEngine is not None:
331 self.__updateTranslateButton()
332 self.__updatePronounceButtons()
333
334 self.__plugin.setPreferences(
335 "SelectedEngine", self.engineComboBox.itemData(index))
336
337 def __updatePronounceButtons(self):
338 """
339 Private slot to set the state of the pronounce buttons.
340 """
341 if self.__translationEngine is not None:
342 hasTTS = self.__translationEngine.hasTTS()
343 else:
344 hasTTS = False
345 self.pronounceOrigButton.setEnabled(
346 hasTTS and bool(self.origEdit.toPlainText()))
347 self.pronounceTransButton.setEnabled(
348 hasTTS and bool(self.transEdit.toPlainText()))
349
350 def __updateClearButton(self):
351 """
352 Private slot to set the state of the clear button.
353 """
354 enable = bool(self.origEdit.toPlainText()) or \
355 bool(self.transEdit.toPlainText())
356 self.clearButton.setEnabled(enable)
357
358 def __updateTranslateButton(self):
359 """
360 Private slot to set the state of the translate button.
361 """
362 enable = bool(self.origEdit.toPlainText())
363 enable &= bool(self.__translationLanguage())
364 enable &= bool(self.__originalLanguage())
365 self.translateButton.setEnabled(enable)
366
367 def __translate(self, text, originalLanguage, translationLanguage):
368 """
369 Private method to translate the given text.
370
371 @param text text to be translated (string)
372 @param originalLanguage language code of the original (string)
373 @param translationLanguage language code of the translation (string)
374 @return tuple of translated text (string) and flag indicating
375 success (boolean)
376 """
377 if self.__translatorRequest is None:
378 from .TranslatorRequest import TranslatorRequest
379 self.__translatorRequest = TranslatorRequest(self)
380
381 self.__ensureTranslationEngineReady()
382 if self.__translationEngine is None:
383 return "", False
384 else:
385 result, ok = self.__translationEngine.getTranslation(
386 self.__translatorRequest, text, originalLanguage,
387 translationLanguage)
388
389 return result, ok
390
391 def __pronounce(self, text, language):
392 """
393 Private method to pronounce the given text.
394
395 @param text text to be pronounced (string)
396 @param language language code of the text (string)
397 """
398 if not text or not language:
399 return
400
401 if self.__translatorRequest is None:
402 from .TranslatorRequest import TranslatorRequest
403 self.__translatorRequest = TranslatorRequest(self)
404
405 if self.__mediaPlayer is None:
406 self.__mediaPlayer = QMediaPlayer(self)
407 self.__mediaPlayer.stateChanged.connect(
408 self.__mediaPlayerStateChanged)
409
410 if self.__mediaPlayer.state() == QMediaPlayer.PlayingState:
411 return
412
413 self.__ensureTranslationEngineReady()
414 if self.__translationEngine is not None:
415 if not self.__translationEngine.hasTTS():
416 E5MessageBox.critical(
417 self,
418 self.tr("Translation Error"),
419 self.tr("The selected translation service does not"
420 " support the Text-to-Speech function."))
421 return
422
423 data, ok = self.__translationEngine.getTextToSpeechData(
424 self.__translatorRequest, text, language)
425 if ok:
426 self.__mediaFile = QTemporaryFile(self)
427 self.__mediaFile.open()
428 self.__mediaFile.setAutoRemove(False)
429 self.__mediaFile.write(data)
430
431 self.__mediaPlayer.setMedia(QMediaContent(), self.__mediaFile)
432 self.__mediaPlayer.play()
433 else:
434 E5MessageBox.critical(
435 self,
436 self.tr("Translation Error"),
437 data)
438
439 def __mediaPlayerStateChanged(self, state):
440 """
441 Private slot handling changes of the media player state.
442
443 @param state media player state (QAudio.State)
444 """
445 if state == QMediaPlayer.StoppedState:
446 self.__mediaFile.close()
447 self.__mediaFile.remove()
448 self.__mediaFile = None
449
450 @pyqtSlot()
451 def on_preferencesButton_clicked(self):
452 """
453 Private slot to open the Translator configuration page.
454 """
455 e5App().getObject("UserInterface").showPreferences("translatorPage")

eric ide

mercurial