Commit 0d2441bd authored by Manuel Cortez's avatar Manuel Cortez

Added accessible_output2 package

parent 278d6213
from __future__ import absolute_import
import ctypes
import os
import types
from platform_utils import paths
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)
if cdll:
return ctypes.cdll[libfile]
else:
return ctypes.windll[libfile]
def get_output_classes():
from . import outputs
module_type = types.ModuleType
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():
import os
import platform
from glob import glob
import accessible_output2
if platform.system() != 'Windows':
return []
path = os.path.join(accessible_output2.__path__[0], 'lib', '*.dll')
results = glob(path)
dest_dir = os.path.join('accessible_output2', 'lib')
return [(dest_dir, results)]
from __future__ import absolute_import
import platform
if platform.system() == 'Windows':
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
if platform.system() == 'Darwin':
from . import voiceover
if platform.system() == 'Linux':
from . import e_speak
from . import auto
from __future__ import absolute_import
import accessible_output2
from .base import Output, OutputError
class Auto(Output):
def __init__(self):
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:
if output.is_active():
return output
return None
def speak(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.speak(*args, **kwargs)
def braille(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.braille(*args, **kwargs)
def output(self, *args, **kwargs):
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()
from accessible_output2 import load_library
import platform
class OutputError(Exception):
pass
class Output(object):
name = "Unnamed Output"
lib32 = None
lib64 = None
argtypes = {}
cdll = False
priority = 100
system_output = False
def __init__(self):
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, 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 self.speak(text, **options):
output = True
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, text, **options):
return False
def braille(self, text, **options):
return False
from __future__ import absolute_import
import os
import ctypes
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(text, (len(text)*2)+2, 1)
def silence(self):
self.lib.DolAccess_Action(141)
def is_active(self):
try:
return self.lib.DolAccess_GetSystem() in (1, 4, 8)
except:
return False
output_class = Dolphin
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
from __future__ import absolute_import
import win32gui
from libloader.com import load_com
import pywintypes
from .base import Output, OutputError
class Jaws (Output):
"""Output supporting the Jaws for Windows screen reader."""
name = 'jaws'
def __init__(self, *args, **kwargs):
super (Jaws, self).__init__(*args, **kwargs)
try:
self.object = load_com("FreedomSci.JawsApi", "jfwapi")
except pywintypes.com_error:
raise OutputError
def braille(self, text, **options):
# HACK: replace " with ', Jaws doesn't seem to understand escaping them with \
text = text.replace('"', "'")
self.object.RunFunction("BrailleString(\"%s\")" % text)
def speak(self, text, interrupt=False):
self.object.SayString(' %s' % text, interrupt)
def is_active(self):
try:
return self.object.SayString('',0) == True or win32gui.FindWindow("JFWUI2", "JAWS") != 0
except:
return False
output_class = Jaws
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
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:
return self.lib.nvdaController_testIfRunning() == 0
except:
return False
def braille(self, text, **options):
self.lib.nvdaController_brailleMessage(text)
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.nvdaController_speakText(text)
def silence(self):
self.lib.nvdaController_cancelSpeech()
output_class = NVDA
from __future__ import absolute_import
import ctypes
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'), 0, 1)
def silence(self):
self.lib.PCTKVReset()
def is_active(self):
return self.lib.PCTKStatus() != 0
output_class = PCTalker
from __future__ import absolute_import
from builtins import range
from libloader.com import load_com
from .base import Output
import logging
log = logging.getLogger(__name__)
class Sapi4(Output):
name = 'sapi4'
priority = 102
def __init__(self):
sapi4 = load_com("{EEE78591-FE22-11D0-8BEF-0060081841DE}")
self._voiceNo = sapi4.Find(0)
sapi4.Select(self._voiceNo)
sapi4.Speak(" ")
self.__object = sapi4
self._voice_list = self._available_voices()
def _set_capabilities(self):
sapi4 = self.__object
try:
sapi4.Pitch = sapi4.Pitch
self._min_pitch = sapi4.MinPitch
self._max_pitch = sapi4.MaxPitch
self._has_pitch = True
except:
self._min_pitch = 0
self._max_pitch = 0
self._has_pitch = False
try:
sapi4.Speed = sapi4.Speed
self._min_rate = sapi4.MinSpeed
self._max_rate = sapi4.MaxSpeed
self._has_rate = True
except:
self._min_rate = 0
self._max_rate = 0
self._has_rate = False
try:
sapi4.VolumeLeft = sapi4.VolumeLeft
self._min_volume = sapi4.MinVolumeLeft
self._max_volume = sapi4.MaxVolumeLeft
self._has_volume = True
except:
self._min_volume = 0
self._max_volume = 0
self._has_volume = False
def _available_voices(self):
voice_list = []
for voice_no in range(1, self.__object.CountEngines):
voice_list.append(self.__object.ModeName(voice_no))
return voice_list
@property
def available_voices(self):
return self._voice_list
def list_voices(self):
return self.available_voices
def get_voice(self):
return self.__object.ModeName(self._voice_no)
def set_voice(self, value):
self._voice_no = self.list_voices().index(value) + 1
self.__object.Select(self._voice_no)
self.silence()
self.__object.Speak(" ")
self._set_capabilities()
def get_pitch(self):
if self.has_pitch:
return self.__object.Pitch
def set_pitch(self, value):
if self.has_pitch:
self.__object.Pitch = value
def get_rate(self):
if self.has_rate:
return self.__object.Speed
def set_rate(self, value):
if self.has_rate:
self.__object.Speed = value
def get_volume(self):
if self.has_volume:
return self.__object.VolumeLeft
def set_volume(self, value):
if self.has_volume:
self.__object.VolumeLeft = value
@property
def has_pitch(self):
return self._has_pitch
@property
def has_rate(self):
return self._has_rate
@property
def has_volume(self):
return self._has_volume
@property
def min_pitch(self):
return self._min_pitch
@property
def max_pitch(self):
return self._max_pitch
@property
def min_rate(self):
return self._min_rate
@property
def max_rate(self):
return self._max_rate
@property
def min_volume(self):
return self._min_volume
@property
def max_volume(self):
return self._max_volume
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.__object.Speak(text)
def silence(self):
self.__object.AudioReset()
output_class = Sapi4
from __future__ import absolute_import
from collections import OrderedDict
from libloader.com import load_com
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
has_pitch = True
min_pitch = -10
max_pitch = 10
min_rate = -10
max_rate = 10
min_volume = 0
max_volume = 100
name = "sapi5"
priority = 101
system_output = True
def __init__(self):
try:
self.object = load_com("SAPI.SPVoice")
self._voices = self._available_voices()
except pywintypes.com_error:
raise OutputError
self._pitch = 0
def _available_voices(self):
_voices = OrderedDict()
for v in self.object.GetVoices():
_voices[v.GetDescription()] = v
return _voices
def list_voices(self):
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._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:
# http://lists.nvaccess.org/pipermail/nvda-dev/2011-November/022464.html
self.object.AudioOutput = self.object.AudioOutput
def get_pitch(self):
return self._pitch
def set_pitch(self, value):
log.debug("Setting pitch to %d" % value)
self._pitch = value
def get_rate(self):
return self.object.Rate
def set_rate(self, value):
log.debug("Setting rate to %d" % value)
self.object.Rate = value
def get_volume(self):
return self.object.Volume
def set_volume(self, value):
self.object.Volume = value
def speak(self, text, interrupt=False):
if interrupt:
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, SVSFlagsAsync | SVSFIsXML)
def silence(self):
self.object.Speak("", SVSFlagsAsync | SVSFPurgeBeforeSpeak)
def is_active(self):
if self.object:
return True
return False
output_class = SAPI5
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(text)
def speak(self, text, interrupt=False):
if self.is_active():
self.dll.SA_SayW(str(text))
def is_active(self):
try:
return self.dll.SA_IsRunning()
except:
return False
output_class = SystemAccess
from __future__ import absolute_import
from .base import Output
import appscript
class VoiceOver(Output):
"""Speech output supporting the Apple VoiceOver screen reader."""
name = 'VoiceOver'
def __init__(self, *args, **kwargs):
self.app = appscript.app('voiceover')
def speak(self, text, interrupt=False):
self.app.output(text)
def silence (self):
self.app.output(u"")
def is_active(self):
return self.app.isrunning()
output_class = VoiceOver
from __future__ import absolute_import
import win32gui
from libloader.com import load_com
from .base import Output, OutputError
import pywintypes
class WindowEyes (Output):
"""Speech output supporting the WindowEyes screen reader"""
name = 'Window-Eyes'
def __init__(self, *args, **kwargs):
super(WindowEyes, self).__init__(*args, **kwargs)
try:
self.object = load_com("gwspeak.speak")
except pywintypes.com_error:
raise OutputError
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
self.object.SpeakString(text)
def silence (self):
self.object.Silence()
def is_active(self):
try:
return win32gui.FindWindow("GWMExternalControl", "External Control") != 0
except:
return False
output_class = WindowEyes
from setuptools import setup, find_packages
import io
from platform import system
_system = system()
install_requires = ['platform_utils']
if _system == 'Windows':
install_requires += [
'pypiwin32',
'libloader'
]
elif _system == 'Darwin':
install_requires += [
'appscript',
]
__doc__ = 'Library to provide speech and braille output to a variety of different screen readers and other accessibility solutions.',
with io.open('readme.md', encoding='UTF8') as readme:
long_description = readme.read()
setup(
name = 'accessible_output2',
author = 'Tyler Spivey',
author_email = 'tspivey@pcdesk.net',
version='0.13',
description = __doc__,
long_description = long_description,
package_dir = {'accessible_output2': 'accessible_output2'},
packages = find_packages(),
package_data = {"accessible_output2": ["lib/*"]},
zip_safe = False,
classifiers = [
'Development Status :: 5 - Stable',
'Intended Audience :: Developers',
'Operating System :: Microsoft :: Windows',
'Programming Language :: Python',
'License :: OSI Approved :: MIT License',
'Topic :: Adaptive Technologies',
'Topic :: Software Development :: Libraries'
],
install_requires = install_requires,
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment