mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-01-18 16:40:41 -06:00
Updated accessible_output2. Add mac and a better linux support
This commit is contained in:
parent
6a4a3cc94e
commit
fe9f724673
@ -1,19 +1,23 @@
|
||||
from __future__ import absolute_import
|
||||
import ctypes
|
||||
import os
|
||||
import types
|
||||
from platform_utils import paths
|
||||
|
||||
def load_library(libname):
|
||||
def load_library(libname, cdll=False):
|
||||
if paths.is_frozen():
|
||||
libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname)
|
||||
else:
|
||||
libfile = os.path.join(paths.module_path(), 'lib', libname)
|
||||
return ctypes.windll[libfile]
|
||||
if cdll:
|
||||
return ctypes.cdll[libfile]
|
||||
else:
|
||||
return ctypes.windll[libfile]
|
||||
|
||||
def get_output_classes():
|
||||
import outputs
|
||||
from . import outputs
|
||||
module_type = types.ModuleType
|
||||
classes = [m.output_class for m in outputs.__dict__.itervalues() if type(m) == module_type and hasattr(m, 'output_class')]
|
||||
classes = [m.output_class for m in outputs.__dict__.values() if type(m) == module_type and hasattr(m, 'output_class')]
|
||||
return sorted(classes, key=lambda c: c.priority)
|
||||
|
||||
def find_datafiles():
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,16 +1,20 @@
|
||||
from __future__ import absolute_import
|
||||
import platform
|
||||
if platform.system() == 'Windows':
|
||||
import nvda
|
||||
import jaws
|
||||
import sapi5
|
||||
import window_eyes
|
||||
import system_access
|
||||
import dolphin
|
||||
import pc_talker
|
||||
from . import nvda
|
||||
from . import jaws
|
||||
from . import sapi5
|
||||
from . import window_eyes
|
||||
from . import system_access
|
||||
from . import dolphin
|
||||
from . import pc_talker
|
||||
#import sapi4
|
||||
elif platform.system() == "Darwin":
|
||||
import voiceover
|
||||
elif platform.system() == "Linux":
|
||||
import speechDispatcher
|
||||
|
||||
import auto
|
||||
if platform.system() == 'Darwin':
|
||||
from . import voiceover
|
||||
from . import say
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
from . import e_speak
|
||||
|
||||
from . import auto
|
||||
|
@ -1,24 +1,17 @@
|
||||
import platform
|
||||
from __future__ import absolute_import
|
||||
import accessible_output2
|
||||
from base import Output, OutputError
|
||||
from .base import Output, OutputError
|
||||
|
||||
class Auto(Output):
|
||||
|
||||
def __init__(self):
|
||||
if platform.system() == "Darwin":
|
||||
import voiceover
|
||||
self.outputs = [voiceover.VoiceOver()]
|
||||
elif platform.system() == "Linux":
|
||||
import speechDispatcher
|
||||
self.outputs = [speechDispatcher.SpeechDispatcher()]
|
||||
elif platform.system() == "Windows":
|
||||
output_classes = accessible_output2.get_output_classes()
|
||||
self.outputs = []
|
||||
for output in output_classes:
|
||||
try:
|
||||
self.outputs.append(output())
|
||||
except OutputError:
|
||||
pass
|
||||
output_classes = accessible_output2.get_output_classes()
|
||||
self.outputs = []
|
||||
for output in output_classes:
|
||||
try:
|
||||
self.outputs.append(output())
|
||||
except OutputError:
|
||||
pass
|
||||
|
||||
def get_first_available_output(self):
|
||||
for output in self.outputs:
|
||||
@ -40,3 +33,8 @@ class Auto(Output):
|
||||
output = self.get_first_available_output()
|
||||
if output:
|
||||
output.speak(*args, **kwargs)
|
||||
|
||||
def is_system_output(self):
|
||||
output = self.get_first_available_output()
|
||||
if output:
|
||||
return output.is_system_output()
|
||||
|
@ -5,27 +5,43 @@ class OutputError(Exception):
|
||||
pass
|
||||
|
||||
class Output(object):
|
||||
name = "Unnamed Output" #The name of this output
|
||||
lib32 = None #name of 32-bit lib
|
||||
lib64 = None #name of 64-bit lib
|
||||
priority = 100 #Where to sort in the list of available outputs for automaticly speaking
|
||||
name = "Unnamed Output"
|
||||
lib32 = None
|
||||
lib64 = None
|
||||
argtypes = {}
|
||||
cdll = False
|
||||
priority = 100
|
||||
system_output = False
|
||||
|
||||
def __init__(self):
|
||||
is_32bit = platform.architecture()[0] == "32bit"
|
||||
if self.lib32 and is_32bit:
|
||||
self.lib = load_library(self.lib32)
|
||||
self.is_32bit = platform.architecture()[0] == "32bit"
|
||||
if self.lib32 and self.is_32bit:
|
||||
self.lib = load_library(self.lib32, cdll=self.cdll)
|
||||
elif self.lib64:
|
||||
self.lib = load_library(self.lib64)
|
||||
self.lib = load_library(self.lib64, cdll=self.cdll)
|
||||
else:
|
||||
self.lib = None
|
||||
if self.lib is not None:
|
||||
for func in self.argtypes:
|
||||
try:
|
||||
getattr(self.lib, func).argtypes = self.argtypes[func]
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def output(self, text, **options):
|
||||
output = False
|
||||
if hasattr(self, 'speak') and callable(self.speak):
|
||||
self.speak(text, **options)
|
||||
if self.speak(text, **options):
|
||||
output = True
|
||||
if hasattr(self, 'braille') and callable(self.braille):
|
||||
self.braille(text, **options)
|
||||
if self.braille(text, **options):
|
||||
output = True
|
||||
if not output:
|
||||
raise RuntimeError("Output %r does not have any method defined to output" % self)
|
||||
|
||||
def is_system_output(self):
|
||||
return self.system_output
|
||||
|
||||
def speak(self, **optiont):
|
||||
return False
|
||||
|
||||
def braille(self, **options):
|
||||
return False
|
||||
|
@ -1,19 +1,25 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import ctypes
|
||||
|
||||
from base import Output
|
||||
from .base import Output
|
||||
|
||||
class Dolphin (Output):
|
||||
"""Supports dolphin products."""
|
||||
|
||||
name = 'Dolphin'
|
||||
lib32 = 'dolapi.dll'
|
||||
argtypes = {
|
||||
'DolAccess_Command': (ctypes.c_wchar_p, ctypes.c_int, ctypes.c_int),
|
||||
'DolAccess_Action': (ctypes.c_int,),
|
||||
}
|
||||
|
||||
def speak(self, text, interrupt=0):
|
||||
if interrupt:
|
||||
self.silence()
|
||||
#If we don't call this, the API won't let us speak.
|
||||
if self.is_active():
|
||||
self.lib.DolAccess_Command(unicode(text), (len(text)*2)+2, 1)
|
||||
self.lib.DolAccess_Command(text, (len(text)*2)+2, 1)
|
||||
|
||||
def silence(self):
|
||||
self.lib.DolAccess_Action(141)
|
||||
|
31
src/accessible_output2/outputs/e_speak.py
Normal file
31
src/accessible_output2/outputs/e_speak.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import absolute_import
|
||||
from .base import Output
|
||||
|
||||
try:
|
||||
import espeak.core
|
||||
except:
|
||||
raise RuntimeError("Cannot find espeak.core. Please install python-espeak")
|
||||
|
||||
class ESpeak(Output):
|
||||
"""Speech output supporting ESpeak on Linux
|
||||
Note this requires python-espeak to be installed
|
||||
This can be done on Debian distros by using apt-get install python-espeak
|
||||
Or through this tarball: https://launchpad.net/python-espeak
|
||||
"""
|
||||
name = "Linux ESpeak"
|
||||
|
||||
def is_active(self):
|
||||
try:
|
||||
import espeak.core
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
def speak(self, text, interrupt = 0):
|
||||
if interrupt:
|
||||
self.silence()
|
||||
espeak.core.synth(text)
|
||||
def silence(self):
|
||||
espeak.core.cancel()
|
||||
|
||||
output_class = ESpeak
|
@ -1,8 +1,9 @@
|
||||
from __future__ import absolute_import
|
||||
import win32gui
|
||||
from libloader.com import load_com
|
||||
import pywintypes
|
||||
|
||||
from base import Output, OutputError
|
||||
from .base import Output, OutputError
|
||||
|
||||
class Jaws (Output):
|
||||
"""Output supporting the Jaws for Windows screen reader."""
|
||||
|
@ -1,15 +1,21 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import platform
|
||||
import ctypes
|
||||
|
||||
from platform_utils import paths
|
||||
from libloader import load_library
|
||||
from base import Output
|
||||
from .base import Output
|
||||
|
||||
class NVDA(Output):
|
||||
"""Supports The NVDA screen reader"""
|
||||
name = "NVDA"
|
||||
lib32 = 'nvdaControllerClient32.dll'
|
||||
lib64 = 'nvdaControllerClient64.dll'
|
||||
argtypes = {
|
||||
'nvdaController_brailleMessage': (ctypes.c_wchar_p,),
|
||||
'nvdaController_speakText': (ctypes.c_wchar_p,),
|
||||
}
|
||||
|
||||
def is_active(self):
|
||||
try:
|
||||
@ -18,12 +24,12 @@ class NVDA(Output):
|
||||
return False
|
||||
|
||||
def braille(self, text, **options):
|
||||
self.lib.nvdaController_brailleMessage(unicode(text))
|
||||
self.lib.nvdaController_brailleMessage(text)
|
||||
|
||||
def speak(self, text, interrupt=False):
|
||||
if interrupt:
|
||||
self.silence()
|
||||
self.lib.nvdaController_speakText(unicode(text))
|
||||
self.lib.nvdaController_speakText(text)
|
||||
|
||||
def silence(self):
|
||||
self.lib.nvdaController_cancelSpeech()
|
||||
|
@ -1,14 +1,19 @@
|
||||
from __future__ import absolute_import
|
||||
import ctypes
|
||||
from base import Output
|
||||
from .base import Output
|
||||
|
||||
class PCTalker(Output):
|
||||
lib32 = 'pctkusr.dll'
|
||||
lib64 = 'pctkusr64.dll'
|
||||
cdll = True
|
||||
argtypes = {
|
||||
'PCTKPRead': (ctypes.c_char_p, ctypes.c_int, ctypes.c_int)
|
||||
}
|
||||
|
||||
def speak(self, text, interrupt=False):
|
||||
if interrupt:
|
||||
self.silence()
|
||||
self.lib.PCTKPRead(text.encode('cp932', 'replace'))
|
||||
self.lib.PCTKPRead(text.encode('cp932', 'replace'), 0, 1)
|
||||
|
||||
def silence(self):
|
||||
self.lib.PCTKVReset()
|
||||
|
@ -1,5 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
from builtins import range
|
||||
from libloader.com import load_com
|
||||
from base import Output
|
||||
from .base import Output
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -1,12 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import config
|
||||
from __future__ import absolute_import
|
||||
from collections import OrderedDict
|
||||
from libloader.com import load_com
|
||||
from base import Output, OutputError
|
||||
from .base import Output, OutputError
|
||||
import pywintypes
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
SVSFDefault = 0
|
||||
SVSFlagsAsync = 1
|
||||
SVSFPurgeBeforeSpeak = 2
|
||||
SVSFIsFilename = 4
|
||||
SVSFIsXML = 8
|
||||
SVSFIsNotXML = 16
|
||||
SVSFPersistXML = 32
|
||||
|
||||
class SAPI5(Output):
|
||||
has_volume = True
|
||||
has_rate = True
|
||||
@ -19,9 +26,9 @@ class SAPI5(Output):
|
||||
max_volume = 100
|
||||
name = "sapi5"
|
||||
priority = 101
|
||||
system_output = True
|
||||
|
||||
def __init__(self):
|
||||
if config.app["app-settings"]["voice_enabled"] == False: raise OutputError
|
||||
try:
|
||||
self.object = load_com("SAPI.SPVoice")
|
||||
self._voices = self._available_voices()
|
||||
@ -36,14 +43,14 @@ class SAPI5(Output):
|
||||
return _voices
|
||||
|
||||
def list_voices(self):
|
||||
return self.available_voices.keys()
|
||||
return list(self._voices.keys())
|
||||
|
||||
def get_voice(self):
|
||||
return self.object.Voice.GetDescription()
|
||||
|
||||
def set_voice(self, value):
|
||||
log.debug("Setting SAPI5 voice to \"%s\"" % value)
|
||||
self.object.Voice = self.available_voices[value]
|
||||
self.object.Voice = self._voices[value]
|
||||
# For some reason SAPI5 does not reset audio after changing the voice
|
||||
# By setting the audio device after changing voices seems to fix this
|
||||
# This was noted from information at:
|
||||
@ -75,10 +82,10 @@ class SAPI5(Output):
|
||||
self.silence()
|
||||
# We need to do the pitch in XML here
|
||||
textOutput = "<pitch absmiddle=\"%d\">%s</pitch>" % (round(self._pitch), text.replace("<", "<"))
|
||||
self.object.Speak(textOutput, 1|8)
|
||||
self.object.Speak(textOutput, SVSFlagsAsync | SVSFIsXML)
|
||||
|
||||
def silence(self):
|
||||
self.object.Speak("", 3)
|
||||
self.object.Speak("", SVSFlagsAsync | SVSFPurgeBeforeSpeak)
|
||||
|
||||
def is_active(self):
|
||||
if self.object:
|
||||
|
21
src/accessible_output2/outputs/say.py
Normal file
21
src/accessible_output2/outputs/say.py
Normal file
@ -0,0 +1,21 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
from .base import Output
|
||||
|
||||
class AppleSay(Output):
|
||||
"""Speech output supporting the Apple Say subsystem."""
|
||||
name = 'Apple Say'
|
||||
def __init__(self, voice = 'Alex', rate = '300'):
|
||||
self.voice = voice
|
||||
self.rate = rate
|
||||
super(AppleSay, self).__init__()
|
||||
def is_active(self):
|
||||
return not os.system('which say')
|
||||
def speak(self, text, interrupt = 0):
|
||||
if interrupt:
|
||||
self.silence()
|
||||
os.system('say -v %s -r %s "%s" &' % (self.voice, self.rate, text))
|
||||
def silence(self):
|
||||
os.system('killall say')
|
||||
|
||||
output_class = AppleSay
|
@ -1,29 +0,0 @@
|
||||
from base import Output, OutputError
|
||||
import atexit
|
||||
import application
|
||||
class SpeechDispatcher(Output):
|
||||
"""Supports speech dispatcher on Linux.
|
||||
Note that this module will use the configuration of speech dispatcher, the user will need to configure the voice, language, punctuation and rate before using this module.
|
||||
"""
|
||||
name = 'SpeechDispatcher'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SpeechDispatcher, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
import speechd
|
||||
self.spd = speechd.SSIPClient(application.name)
|
||||
except ImportError:
|
||||
raise OutputError
|
||||
atexit.register(self.on_exit_event)
|
||||
|
||||
def speak(self, text, interupt=False):
|
||||
if interupt == True:
|
||||
self.spd.cancel()
|
||||
self.spd.speak(text)
|
||||
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
def on_exit_event(self):
|
||||
self.spd.close()
|
||||
del self.spd
|
@ -1,18 +0,0 @@
|
||||
# Copyright (C) 2001, 2002 Brailcom, o.p.s.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from .client import *
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
SPD_SPAWN_CMD = "/usr/bin/speech-dispatcher"
|
@ -1,18 +1,24 @@
|
||||
from base import Output
|
||||
from __future__ import absolute_import
|
||||
import ctypes
|
||||
from .base import Output
|
||||
|
||||
class SystemAccess (Output):
|
||||
"""Supports System Access and System Access Mobile"""
|
||||
|
||||
name = "System Access"
|
||||
lib32 = 'saapi32.dll'
|
||||
argtypes = {
|
||||
'SA_BrlShowTextW': (ctypes.c_wchar_p,),
|
||||
'SA_SayW': (ctypes.c_wchar_p,),
|
||||
}
|
||||
priority = 99
|
||||
|
||||
def braille(self, text, **options):
|
||||
self.lib.SA_BrlShowTextW(unicode(text))
|
||||
self.lib.SA_BrlShowTextW(text)
|
||||
|
||||
def speak(self, text, interrupt=False):
|
||||
if self.is_active():
|
||||
self.dll.SA_SayW(unicode(text))
|
||||
self.dll.SA_SayW(str(text))
|
||||
|
||||
def is_active(self):
|
||||
try:
|
||||
|
@ -1,23 +1 @@
|
||||
from base import Output, OutputError
|
||||
|
||||
class VoiceOver (Output):
|
||||
"""Supports the VoiceOver screenreader on the Mac.
|
||||
|
||||
Note that this will also output as a message to the braille display if VoiceOver is used with braille.
|
||||
Calling this module could cause VoiceOver to be started.
|
||||
"""
|
||||
name = 'VoiceOver'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(VoiceOver, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
from appscript import app
|
||||
self.app = app('VoiceOver')
|
||||
except ImportError:
|
||||
raise OutputError
|
||||
|
||||
def speak(self, text, interupt=False):
|
||||
self.app.output(text)
|
||||
|
||||
def is_active(self):
|
||||
return True
|
||||
from __future__ import absolute_import
from builtins import str
import subprocess, os
from .base import Output
class VoiceOver(Output):
"""Speech output supporting the Apple VoiceOver screen reader."""
def runAppleScript(self, command, process = 'voiceover'):
return subprocess.Popen(['osascript', '-e', 'tell application "' + process + '"\n' + command + '\nend tell'], stdout = subprocess.PIPE).communicate()[0]
name = 'VoiceOver'
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
os.system('osascript -e \"tell application \\\"voiceover\\\" to output \\\"%s\\\"\" &' % text)
def silence (self):
self.runAppleScript('output ""')
def is_active(self):
return self.runAppleScript('return (name of processes) contains "VoiceOver"', 'system events').startswith('true') and not self.runAppleScript('try\nreturn bounds of vo cursor\non error\nreturn false\nend try').startswith('false')
output_class = VoiceOver
|
@ -1,6 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
import win32gui
|
||||
from libloader.com import load_com
|
||||
from base import Output, OutputError
|
||||
from .base import Output, OutputError
|
||||
import pywintypes
|
||||
|
||||
class WindowEyes (Output):
|
||||
|
Loading…
x
Reference in New Issue
Block a user