1#!/usr/local/bin/python3.8 2# vim:fileencoding=utf-8 3# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net> 4 5from qt.core import QDialogButtonBox, QObject, QVBoxLayout, pyqtSignal, QDialog 6 7from calibre.gui2 import error_dialog 8from calibre.gui2.viewer.config import get_pref_group, vprefs 9from calibre.gui2.widgets2 import Dialog 10 11 12class Config(Dialog): 13 14 def __init__(self, tts_client, ui_settings, backend_settings, parent): 15 self.tts_client = tts_client 16 self.ui_settings = ui_settings 17 self.backend_settings = backend_settings 18 Dialog.__init__(self, _('Configure Read aloud'), 'read-aloud-config', parent, prefs=vprefs) 19 20 def setup_ui(self): 21 self.l = l = QVBoxLayout(self) 22 self.config_widget = self.tts_client.config_widget(self.backend_settings, self) 23 l.addWidget(self.config_widget) 24 l.addWidget(self.bb) 25 self.config_widget.restore_to_defaults 26 b = self.bb.addButton(QDialogButtonBox.StandardButton.RestoreDefaults) 27 b.clicked.connect(self.restore_to_defaults) 28 self.config_widget.restore_state(vprefs) 29 30 def save_state(self): 31 self.config_widget.save_state(vprefs) 32 33 def restore_to_defaults(self): 34 self.config_widget.restore_to_defaults() 35 36 def accept(self): 37 self.backend_settings = self.config_widget.backend_settings 38 return super().accept() 39 40 41class TTS(QObject): 42 43 dispatch_on_main_thread_signal = pyqtSignal(object) 44 event_received = pyqtSignal(object, object) 45 settings_changed = pyqtSignal(object) 46 47 def __init__(self, parent=None): 48 QObject.__init__(self, parent) 49 self._tts_client = None 50 self.dispatch_on_main_thread_signal.connect(self.dispatch_on_main_thread) 51 52 def dispatch_on_main_thread(self, func): 53 try: 54 func() 55 except Exception: 56 import traceback 57 traceback.print_exc() 58 59 @property 60 def tts_client_class(self): 61 from calibre.gui2.tts.implementation import Client 62 return Client 63 64 @property 65 def tts_client(self): 66 if self._tts_client is None: 67 settings = self.backend_settings 68 self._tts_client = self.tts_client_class(settings, self.dispatch_on_main_thread_signal.emit) 69 if self._tts_client.settings != settings: 70 self.backend_settings = self._tts_client.settings 71 return self._tts_client 72 73 def shutdown(self): 74 if self._tts_client is not None: 75 self._tts_client.shutdown() 76 self._tts_client = None 77 78 def speak_simple_text(self, text): 79 from calibre.gui2.tts.errors import TTSSystemUnavailable 80 try: 81 self.tts_client.speak_simple_text(text) 82 except TTSSystemUnavailable as err: 83 return error_dialog(self.parent(), _('Text-to-Speech unavailable'), str(err), show=True) 84 85 def action(self, action, data): 86 from calibre.gui2.tts.errors import TTSSystemUnavailable 87 try: 88 getattr(self, action)(data) 89 except TTSSystemUnavailable as err: 90 return error_dialog(self.parent(), _('Text-to-Speech unavailable'), str(err), show=True) 91 92 def play(self, data): 93 self.tts_client.speak_marked_text(data['marked_text'], self.callback) 94 95 def pause(self, data): 96 self.tts_client.pause() 97 98 def resume(self, data): 99 self.tts_client.resume() 100 101 def resume_after_configure(self, data): 102 self.tts_client.resume_after_configure() 103 104 def callback(self, event): 105 data = event.data 106 if event.type is event.type.mark: 107 data = int(data) 108 self.event_received.emit(event.type.name, data) 109 110 def stop(self, data): 111 self.tts_client.stop() 112 113 @property 114 def backend_settings(self): 115 key = 'tts_' + self.tts_client_class.name 116 return vprefs.get(key) or {} 117 118 @backend_settings.setter 119 def backend_settings(self, val): 120 key = 'tts_' + self.tts_client_class.name 121 vprefs.set(key, val or {}) 122 123 def configure(self, data): 124 ui_settings = get_pref_group('tts').copy() 125 d = Config(self.tts_client, ui_settings, self.backend_settings, parent=self.parent()) 126 if d.exec() == QDialog.DialogCode.Accepted: 127 s = d.backend_settings 128 self.backend_settings = s 129 self.tts_client.apply_settings(s) 130 self.settings_changed.emit(d.ui_settings) 131 else: 132 self.settings_changed.emit(None) 133 d.save_state() 134 135 def slower(self, data): 136 settings = self.tts_client.change_rate(steps=-1) 137 if settings is not None: 138 self.backend_settings = settings 139 140 def faster(self, data): 141 settings = self.tts_client.change_rate(steps=1) 142 if settings is not None: 143 self.backend_settings = settings 144