Detected screen reader now show in the menu instead of generic phrase screen reader.
This commit is contained in:
@@ -741,7 +741,6 @@ class BookReader:
|
|||||||
else:
|
else:
|
||||||
message = f"Screen reader engine active: {self.readingEngine.get_active_reader_name()}"
|
message = f"Screen reader engine active: {self.readingEngine.get_active_reader_name()}"
|
||||||
print(message)
|
print(message)
|
||||||
self.speechEngine.speak(message)
|
|
||||||
else:
|
else:
|
||||||
# Reload piper-tts with new voice (check if available first)
|
# Reload piper-tts with new voice (check if available first)
|
||||||
if not TtsEngine.is_available():
|
if not TtsEngine.is_available():
|
||||||
|
|||||||
+42
-12
@@ -9,6 +9,7 @@ Inspired by soundstorm's hierarchical menu system.
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from src.tts_engine import TtsEngine
|
from src.tts_engine import TtsEngine
|
||||||
|
from src.screen_reader_engine import ScreenReaderEngine
|
||||||
|
|
||||||
|
|
||||||
class OptionsMenu:
|
class OptionsMenu:
|
||||||
@@ -32,6 +33,40 @@ class OptionsMenu:
|
|||||||
self.ttsReloadCallback = ttsReloadCallback
|
self.ttsReloadCallback = ttsReloadCallback
|
||||||
self.currentSelection = 0
|
self.currentSelection = 0
|
||||||
self.inMenu = False
|
self.inMenu = False
|
||||||
|
self.screenReaderName = None
|
||||||
|
self._refresh_screen_reader_target()
|
||||||
|
|
||||||
|
def _refresh_screen_reader_target(self):
|
||||||
|
"""Detect active supported screen reader for optional engine entry."""
|
||||||
|
self.screenReaderName = None
|
||||||
|
try:
|
||||||
|
detectionEngine = ScreenReaderEngine(quiet=True)
|
||||||
|
if detectionEngine.is_available():
|
||||||
|
activeName = detectionEngine.get_active_reader_name()
|
||||||
|
if activeName and activeName != "Unavailable":
|
||||||
|
self.screenReaderName = activeName
|
||||||
|
else:
|
||||||
|
self.screenReaderName = "Screen Reader"
|
||||||
|
detectionEngine.close()
|
||||||
|
except Exception:
|
||||||
|
self.screenReaderName = None
|
||||||
|
|
||||||
|
def _get_engine_labels(self):
|
||||||
|
"""Return user-facing labels for available engine values."""
|
||||||
|
engineLabels = {
|
||||||
|
'piper': 'Piper-TTS',
|
||||||
|
'speechd': 'Speech-Dispatcher'
|
||||||
|
}
|
||||||
|
if self.screenReaderName:
|
||||||
|
engineLabels['screenreader'] = self.screenReaderName
|
||||||
|
return engineLabels
|
||||||
|
|
||||||
|
def _get_engine_cycle_order(self):
|
||||||
|
"""Return engine cycle order, skipping screen-reader mode when unavailable."""
|
||||||
|
engineOrder = ['piper', 'speechd']
|
||||||
|
if self.screenReaderName:
|
||||||
|
engineOrder.append('screenreader')
|
||||||
|
return engineOrder
|
||||||
|
|
||||||
def show_main_menu(self):
|
def show_main_menu(self):
|
||||||
"""
|
"""
|
||||||
@@ -41,11 +76,7 @@ class OptionsMenu:
|
|||||||
Menu items as list of dicts
|
Menu items as list of dicts
|
||||||
"""
|
"""
|
||||||
readerEngine = self.config.get_reader_engine()
|
readerEngine = self.config.get_reader_engine()
|
||||||
engineLabels = {
|
engineLabels = self._get_engine_labels()
|
||||||
'piper': 'Piper-TTS',
|
|
||||||
'speechd': 'Speech-Dispatcher',
|
|
||||||
'screenreader': 'Screen Reader'
|
|
||||||
}
|
|
||||||
readerEngineText = engineLabels.get(readerEngine, 'Piper-TTS')
|
readerEngineText = engineLabels.get(readerEngine, 'Piper-TTS')
|
||||||
|
|
||||||
menuItems = [
|
menuItems = [
|
||||||
@@ -147,13 +178,10 @@ class OptionsMenu:
|
|||||||
|
|
||||||
def _toggle_reader_engine(self):
|
def _toggle_reader_engine(self):
|
||||||
"""Cycle reader engine: piper-tts, speech-dispatcher, screen reader."""
|
"""Cycle reader engine: piper-tts, speech-dispatcher, screen reader."""
|
||||||
|
self._refresh_screen_reader_target()
|
||||||
currentEngine = self.config.get_reader_engine()
|
currentEngine = self.config.get_reader_engine()
|
||||||
engineOrder = ['piper', 'speechd', 'screenreader']
|
engineOrder = self._get_engine_cycle_order()
|
||||||
engineLabels = {
|
engineLabels = self._get_engine_labels()
|
||||||
'piper': 'Piper-TTS',
|
|
||||||
'speechd': 'Speech-Dispatcher',
|
|
||||||
'screenreader': 'Screen Reader'
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentEngine not in engineOrder:
|
if currentEngine not in engineOrder:
|
||||||
currentEngine = 'piper'
|
currentEngine = 'piper'
|
||||||
@@ -203,7 +231,8 @@ class OptionsMenu:
|
|||||||
if readerEngine == 'speechd':
|
if readerEngine == 'speechd':
|
||||||
return self._select_speechd_voice()
|
return self._select_speechd_voice()
|
||||||
|
|
||||||
self.speechEngine.speak("Voice selection is managed by your active screen reader.")
|
readerName = self.screenReaderName if self.screenReaderName else "your active screen reader"
|
||||||
|
self.speechEngine.speak(f"Voice selection is managed by {readerName}.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _select_piper_voice(self):
|
def _select_piper_voice(self):
|
||||||
@@ -481,6 +510,7 @@ class OptionsMenu:
|
|||||||
|
|
||||||
def enter_menu(self):
|
def enter_menu(self):
|
||||||
"""Enter the options menu"""
|
"""Enter the options menu"""
|
||||||
|
self._refresh_screen_reader_target()
|
||||||
self.inMenu = True
|
self.inMenu = True
|
||||||
self.currentSelection = 0
|
self.currentSelection = 0
|
||||||
self.inVoiceMenu = False
|
self.inVoiceMenu = False
|
||||||
|
|||||||
@@ -167,11 +167,12 @@ class ScreenReaderRemoteController:
|
|||||||
class ScreenReaderEngine:
|
class ScreenReaderEngine:
|
||||||
"""Book reading engine that speaks via active screen reader D-Bus APIs."""
|
"""Book reading engine that speaks via active screen reader D-Bus APIs."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, quiet=False):
|
||||||
self.speechLock = threading.Lock()
|
self.speechLock = threading.Lock()
|
||||||
self.isAvailable = False
|
self.isAvailable = False
|
||||||
self.activeController = None
|
self.activeController = None
|
||||||
self.availableControllers = []
|
self.availableControllers = []
|
||||||
|
self.quiet = quiet
|
||||||
|
|
||||||
self.isReading = False
|
self.isReading = False
|
||||||
self.isPausedReading = False
|
self.isPausedReading = False
|
||||||
@@ -185,7 +186,8 @@ class ScreenReaderEngine:
|
|||||||
def _initialize_controllers(self):
|
def _initialize_controllers(self):
|
||||||
"""Discover available screen reader remote controllers."""
|
"""Discover available screen reader remote controllers."""
|
||||||
if not HAS_DASBUS:
|
if not HAS_DASBUS:
|
||||||
print("Warning: python-dasbus not installed. Screen reader engine unavailable.")
|
if not self.quiet:
|
||||||
|
print("Warning: python-dasbus not installed. Screen reader engine unavailable.")
|
||||||
return
|
return
|
||||||
|
|
||||||
controllerCandidates = [
|
controllerCandidates = [
|
||||||
@@ -207,10 +209,11 @@ class ScreenReaderEngine:
|
|||||||
self.activeController = self._select_active_controller()
|
self.activeController = self._select_active_controller()
|
||||||
self.isAvailable = self.activeController is not None
|
self.isAvailable = self.activeController is not None
|
||||||
|
|
||||||
if self.activeController:
|
if not self.quiet:
|
||||||
print(f"Screen reader engine connected to {self.activeController.displayName}.")
|
if self.activeController:
|
||||||
else:
|
print(f"Screen reader engine connected to {self.activeController.displayName}.")
|
||||||
print("Warning: No supported screen reader D-Bus service detected (Orca/Cthulhu).")
|
else:
|
||||||
|
print("Warning: No supported screen reader D-Bus service detected (Orca/Cthulhu).")
|
||||||
|
|
||||||
def _is_process_running(self, processName):
|
def _is_process_running(self, processName):
|
||||||
"""Return True when the process name is running."""
|
"""Return True when the process name is running."""
|
||||||
|
|||||||
Reference in New Issue
Block a user