mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Switch viewer to use new tts backend
This commit is contained in:
parent
a23944c5ca
commit
12a4dc6053
@ -112,6 +112,7 @@ class ResumeData:
|
||||
class TTSManager(QObject):
|
||||
|
||||
state_changed = pyqtSignal(QTextToSpeech.State)
|
||||
state_event = pyqtSignal(str)
|
||||
saying = pyqtSignal(int, int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@ -201,10 +202,22 @@ class TTSManager(QObject):
|
||||
self.tts.reload_after_configure()
|
||||
|
||||
def _state_changed(self, state: QTextToSpeech.State) -> None:
|
||||
self.state = state
|
||||
prev_state, self.state = self.state, state
|
||||
if state is QTextToSpeech.State.Error:
|
||||
error_dialog(self, _('Read aloud failed'), self.tts.error_message(), show=True)
|
||||
self.state_changed.emit(state)
|
||||
if state is QTextToSpeech.State.Paused:
|
||||
self.state_event.emit('pause')
|
||||
elif state is QTextToSpeech.State.Speaking:
|
||||
if prev_state is QTextToSpeech.State.Paused:
|
||||
self.state_event.emit('resume')
|
||||
elif prev_state is QTextToSpeech.State.Ready:
|
||||
self.state_event.emit('begin')
|
||||
elif state is QTextToSpeech.State.Ready:
|
||||
if prev_state in (QTextToSpeech.State.Paused, QTextToSpeech.State.Speaking):
|
||||
self.state_event.emit('end')
|
||||
elif state is QTextToSpeech.State.Error:
|
||||
self.state_event.emit('cancel')
|
||||
|
||||
def _saying(self, offset: int, length: int) -> None:
|
||||
self.tracker.boundary_reached(offset)
|
||||
|
@ -1,12 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from qt.core import QDialog, QDialogButtonBox, QObject, QVBoxLayout, pyqtSignal
|
||||
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.viewer.config import get_pref_group, vprefs
|
||||
from calibre.gui2.widgets2 import Dialog
|
||||
from calibre.utils.localization import _
|
||||
from qt.core import QObject, pyqtSignal
|
||||
|
||||
|
||||
def set_sync_override(allowed):
|
||||
@ -14,142 +9,62 @@ def set_sync_override(allowed):
|
||||
set_sync_override(allowed)
|
||||
|
||||
|
||||
class Config(Dialog):
|
||||
|
||||
def __init__(self, tts_client, ui_settings, backend_settings, parent):
|
||||
self.tts_client = tts_client
|
||||
self.ui_settings = ui_settings
|
||||
self.backend_settings = backend_settings
|
||||
Dialog.__init__(self, _('Configure Read aloud'), 'read-aloud-config', parent, prefs=vprefs)
|
||||
|
||||
def setup_ui(self):
|
||||
self.l = l = QVBoxLayout(self)
|
||||
self.config_widget = self.tts_client.config_widget(self.backend_settings, self)
|
||||
l.addWidget(self.config_widget)
|
||||
l.addWidget(self.bb)
|
||||
self.config_widget.restore_to_defaults
|
||||
b = self.bb.addButton(QDialogButtonBox.StandardButton.RestoreDefaults)
|
||||
b.clicked.connect(self.restore_to_defaults)
|
||||
self.config_widget.restore_state(vprefs)
|
||||
|
||||
def save_state(self):
|
||||
self.config_widget.save_state(vprefs)
|
||||
|
||||
def restore_to_defaults(self):
|
||||
self.config_widget.restore_to_defaults()
|
||||
|
||||
def accept(self):
|
||||
self.backend_settings = self.config_widget.backend_settings
|
||||
return super().accept()
|
||||
|
||||
|
||||
class TTS(QObject):
|
||||
|
||||
dispatch_on_main_thread_signal = pyqtSignal(object)
|
||||
event_received = pyqtSignal(object, object)
|
||||
settings_changed = pyqtSignal(object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QObject.__init__(self, parent)
|
||||
self._tts_client = None
|
||||
self.dispatch_on_main_thread_signal.connect(self.dispatch_on_main_thread)
|
||||
|
||||
def dispatch_on_main_thread(self, func):
|
||||
try:
|
||||
func()
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if getattr(e, 'display_to_user', False):
|
||||
error_dialog(self.parent(), _('Error in speech subsystem'), str(e), det_msg=traceback.format_exc(), show=True)
|
||||
super().__init__(parent)
|
||||
self._manager = None
|
||||
|
||||
@property
|
||||
def tts_client_class(self):
|
||||
from calibre.gui2.tts.implementation import Client
|
||||
return Client
|
||||
|
||||
@property
|
||||
def tts_client(self):
|
||||
if self._tts_client is None:
|
||||
settings = self.backend_settings
|
||||
self._tts_client = self.tts_client_class(settings, self.dispatch_on_main_thread_signal.emit)
|
||||
if self._tts_client.settings != settings:
|
||||
self.backend_settings = self._tts_client.settings
|
||||
return self._tts_client
|
||||
def manager(self):
|
||||
if self._manager is None:
|
||||
from calibre.gui2.tts2.manager import TTSManager
|
||||
self._manager = TTSManager(self)
|
||||
self._manager.saying.connect(self.saying)
|
||||
self._manager.state_event.connect(self.state_event)
|
||||
return self._manager
|
||||
|
||||
def shutdown(self):
|
||||
if self._tts_client is not None:
|
||||
self._tts_client.shutdown()
|
||||
self._tts_client = None
|
||||
if self._manager is not None:
|
||||
self._manager.stop()
|
||||
self._manager = None
|
||||
|
||||
def speak_simple_text(self, text):
|
||||
from calibre.gui2.tts.errors import TTSSystemUnavailable
|
||||
try:
|
||||
self.tts_client.speak_simple_text(text)
|
||||
except TTSSystemUnavailable as err:
|
||||
return error_dialog(self.parent(), _('Text-to-Speech unavailable'), str(err), show=True)
|
||||
self.manager.speak_simple_text(text)
|
||||
|
||||
def action(self, action, data):
|
||||
from calibre.gui2.tts.errors import TTSSystemUnavailable
|
||||
try:
|
||||
getattr(self, action)(data)
|
||||
except TTSSystemUnavailable as err:
|
||||
return error_dialog(self.parent(), _('Text-to-Speech unavailable'), str(err), show=True)
|
||||
getattr(self, action)(data)
|
||||
|
||||
def play(self, data):
|
||||
set_sync_override(False)
|
||||
self.tts_client.speak_marked_text(data['marked_text'], self.callback)
|
||||
self.manager.speak_marked_text(data['marked_text'])
|
||||
|
||||
def pause(self, data):
|
||||
set_sync_override(True)
|
||||
self.tts_client.pause()
|
||||
self.manager.pause()
|
||||
|
||||
def resume(self, data):
|
||||
set_sync_override(False)
|
||||
self.tts_client.resume()
|
||||
self.manager.resume()
|
||||
|
||||
def resume_after_configure(self, data):
|
||||
set_sync_override(False)
|
||||
self.tts_client.resume_after_configure()
|
||||
def saying(self, first: int, last: int) -> None:
|
||||
self.event_received.emit('mark', {'first': first, 'last': last})
|
||||
|
||||
def callback(self, event):
|
||||
data = event.data
|
||||
if event.type is event.type.mark:
|
||||
data = {'first': int(data), 'last': int(data)}
|
||||
self.event_received.emit(event.type.name, data)
|
||||
def state_event(self, name: str):
|
||||
self.event_received.emit(name, None)
|
||||
|
||||
def stop(self, data):
|
||||
set_sync_override(True)
|
||||
self.tts_client.stop()
|
||||
|
||||
@property
|
||||
def backend_settings(self):
|
||||
key = 'tts_' + self.tts_client_class.name
|
||||
return vprefs.get(key) or {}
|
||||
|
||||
@backend_settings.setter
|
||||
def backend_settings(self, val):
|
||||
key = 'tts_' + self.tts_client_class.name
|
||||
vprefs.set(key, val or {})
|
||||
self.manager.stop()
|
||||
|
||||
def configure(self, data):
|
||||
ui_settings = get_pref_group('tts').copy()
|
||||
d = Config(self.tts_client, ui_settings, self.backend_settings, parent=self.parent())
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
s = d.backend_settings
|
||||
self.backend_settings = s
|
||||
self.tts_client.apply_settings(s)
|
||||
self.settings_changed.emit(d.ui_settings)
|
||||
else:
|
||||
self.settings_changed.emit(None)
|
||||
d.save_state()
|
||||
self.manager.configure()
|
||||
|
||||
def slower(self, data):
|
||||
settings = self.tts_client.change_rate(steps=-1)
|
||||
if settings is not None:
|
||||
self.backend_settings = settings
|
||||
self.manager.change_rate(steps=-1)
|
||||
|
||||
def faster(self, data):
|
||||
settings = self.tts_client.change_rate(steps=1)
|
||||
if settings is not None:
|
||||
self.backend_settings = settings
|
||||
self.manager.change_rate(steps=1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user