Updated accessible_output2. Add mac and a better linux support

This commit is contained in:
Manuel Cortez 2015-11-03 04:38:59 -06:00
parent 6a4a3cc94e
commit fe9f724673
22 changed files with 174 additions and 1261 deletions

View File

@ -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():

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

View 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

View File

@ -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."""

View File

@ -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()

View File

@ -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()

View File

@ -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__)

View File

@ -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("<", "&lt;"))
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:

View 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

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
SPD_SPAWN_CMD = "/usr/bin/speech-dispatcher"

View File

@ -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:

View File

@ -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

View File

@ -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):