1 '''
2 Defines the base class for all L{AEOutput} devices.
3
4 @author: Larry Weiss
5 @author: Peter Parente
6 @author: Brett Clippingdale
7 @organization: IBM Corporation
8 @copyright: Copyright (c) 2005, 2007 IBM Corporation
9 @license: The BSD License
10
11 All rights reserved. This program and the accompanying materials are made
12 available under the terms of the BSD license which accompanies
13 this distribution, and is available at
14 U{http://www.opensource.org/licenses/bsd-license.php}
15 '''
16 import UIElement, Style, AEConstants
17
19 '''
20 Suggests the default L{AEOutput}s events to be monitored.
21
22 @return: Names of defaults to monitor
23 @rtype: list of string
24 '''
25 return AEConstants.OUTPUT_COMMAND_NAMES.values()
26
40
42 '''
43 Defines the base class for all output devices. All output devices used by
44 LSR should derive from this class and should implement the methods defined
45 here. All methods defined here raise NotImplementedError to ensure that
46 derived classes create appropriate implementions.
47
48 @cvar STYLE: Style class to use when constructing style objects for this
49 device. A subclass with configurable device settings should
50 override the default value (L{AEOutput.Style}) with a reference to
51 its own default class defining default values for all device properties.
52 @type STYLE: L{AEOutput.Style} class
53 @cvar USE_THREAD: Should this device use a separate thread to queue output?
54 Devices that block while doing output (e.g. serial Braille device) should
55 use a thread to keep the main thread unlocked and responsive. Defaults to
56 None so a subclass must override it with an explicit boolean value.
57 @type USE_THREAD: boolean
58 @cvar COMMAND_CHARS: String of characters that are treated as commands on the
59 device implementing this interface. These characters are replaced with
60 blanks in any string sent to the device.
61 @type COMMAND_CHARS: string
62 @ivar styles: Mapping from arbitrary, immutable keys to style objects
63 @type styles: dictionary
64 '''
65 USE_THREAD = None
66 COMMAND_CHARS = ''
67 STYLE = Style.Style
68
70 '''
71 Initializes the styles dictionary and default style object.
72 '''
73 self.styles = {}
74
75
76 self.default_style = self.STYLE()
77
79 '''
80 Gets a list of strings representing the capabilities of this device.
81 Typical output capabilities include "audio" and "braille" though others are
82 certainly possible.
83
84 The L{DeviceManager} will only load a device if another device doesn't
85 already provide all of its capabilities.
86
87 @return: Lowercase names of output capabilities
88 @rtype: list of string
89 '''
90 raise NotImplementedError
91
93 '''
94 Called after the L{init} method by the L{DeviceManager} to ensure that the
95 device is functioning before time is spent unserializing its style data.
96
97 Calls the init method on the default style object and provides it with a
98 reference to this initialized device. Then tries to load the persisted
99 setting values from disk. If that fails, the L{DeviceManager} will try to
100 call L{createDistinctStyles} instead.
101
102 @note: This method should not be overriden by a subclass. See L{postInit}
103 if you need to run code after the styles are created or restored from
104 disk.
105 @param sett_man: Instance of the settings manager
106 @type sett_man: L{SettingsManager}
107 @raise KeyError: When styles have not previously been persisted for this
108 device
109 @raise OSError: When the profile file cannot be opened or read
110 '''
111 self.default_style.init(self)
112
113 self.styles = sett_man.loadStyles(self, self.default_style, self.STYLE)
114
116 '''
117 Persists styles to disk. Called after the L{close} method by the
118 L{DeviceManager} to ensure the device is properly shutdown before
119 serializing its data.
120
121 @note: This method should not be overriden by a subclass.
122 @param sett_man: Instance of the settings manager
123 @type sett_man: L{SettingsManager}
124 @raise KeyError: When styles have not previously been persisted for this
125 device
126 @raise OSError: When the profile file cannot be opened or read
127 '''
128
129 sett_man.saveStyles(self, self.default_style, self.styles)
130
132 '''
133 Stores the style object under the given key. The style object should be
134 one previously generated by this device (e.g. using
135 L{createDistinctStyles}) but it is not an enforced requirement. Always
136 makes the style clean before storing it.
137
138 @param key: Any immutable object
139 @type key: immutable
140 @param style: L{AEOutput} subclass of L{AEState}
141 @type style: L{AEState}
142 '''
143 style.makeClean()
144 self.styles[key] = style
145
147 '''
148 Gets the style object stored under the given key. If the key is unknown,
149 returns an empty flyweight backed by the default style and stores the
150 new style in L{styles}.
151
152 @param key: Any immutable object
153 @type key: immutable
154 @return: L{AEOutput} subclass of L{AEState}
155 @rtype: L{AEState}
156 '''
157 try:
158 return self.styles[key]
159 except KeyError:
160
161 st = self.STYLE(self.default_style)
162
163 st.init(self)
164 self.styles[key] = st
165 return st
166
168 '''
169 Gets the default style of the device.
170
171 @return: Default style of the device
172 @rtype: L{AEOutput.Style}
173 '''
174 return self.default_style
175
177 '''
178 Creates a default set of distinct styles for this device. The device should
179 instantiate L{AEOutput.Style} objects having properties that reflect the
180 capabilities of this device, leaving any unsupported fields set to the
181 value of None. A total number of num_groups + num_layers style objects
182 should be returned.
183
184 The properties of the style object should be set so as to distinguish
185 content presented using the style. For instance, audio devices may
186 distinguish styles in the following recommended manner:
187
188 - Create a total of num_groups styles with different voices and pitches.
189 - If the device supports multiple channels,
190 - Duplicate the styles num_layers times assigning each set of
191 duplicates to the same channel.
192 - If the device does not support multiple channels,
193 - Duplicate the styles num_layers times.
194
195 For Braille and magnification devices, don't implement this method unless
196 you have some creative way of making the device respond differently to the
197 semantic tags defined in L{AEConstants}.
198
199 The total number of requested styles (num_groups * num_layers) must be
200 returned. If the device cannot honor the request for the number of distinct
201 styles it is asked to generate, it may duplicate styles it has already
202 created using L{AEState.Base.AEState.copy} to fulfill the quota.
203
204 The device is B{not} expected to create distinct styles across invocations
205 of this method. This method should only be called once by L{DeviceManager}
206 to create a default set of styles for the device if the manager cannot load
207 previously persisted settings from disk. If this method is not implemented,
208 calls to L{getStyle} during normal operation of the L{DeviceManager} will
209 end up making copies based on the default style.
210
211 @param num_groups: Number of sematic groups the requestor would like to
212 represent using distinct styles
213 @type num_groups: integer
214 @param num_layers: Number of content origins (e.g. output originating from
215 a background task versus the focus) the requestor would like to represent
216 using distinct styles
217 @type num_layers: integer
218 @return: New styles
219 @rtype: list of L{AEOutput.Style}
220 @raise NotImplementedError: When not overridden in a subclass
221 '''
222 raise NotImplementedError
223
225 '''
226 Called after the instance is created to initialize the device.
227 May be called to re-initialize the device after a call to L{close}.
228
229 @raise NotImplementedError: When not overridden in a subclass
230 @raise Error.InitError: When a communication or state problem exists for
231 the specific device
232 '''
233 raise NotImplementedError
234
235 - def postInit(self):
236 '''
237 Called after the L{init} method and after either L{loadStyles} or
238 L{createDistinctStyles}. Override this method to perform additional
239 initilization after the setting values are available.
240
241 @raise Error.InitError: When a communication or state problem exists for
242 the specific device
243 '''
244 pass
245
247 '''
248 Closes an initialized output device.
249
250 @raise NotImplementedError: When not overridden in a subclass
251 '''
252 raise NotImplementedError
253
255 '''
256 Gets the object that the L{DeviceManager} will use to communicate with this
257 device. The returned object may either be a proxy (e.g. a thread) or this
258 device itself.
259
260 @return: An output object that implements this class
261 @rtype: L{AEOutput}
262 @raise NotImplementedError: When not overridden in a subclass
263 '''
264 raise NotImplementedError
265
266 - def send(self, name, value, style=None):
267 '''
268 Sends arbitrary data to a device. The device must recognize the name in
269 order to decide what to do with the value data. If given, the style object
270 is used to decide how the data should be presented if it is content to be
271 rendered.
272
273 This is a generic method which receives all content and commands to be
274 rendered and executed on a device. Standard name identifiers should be
275 used whenever possible. For instance, L{AEConstants.Output.CMD_STOP} and
276 L{AEConstants.Output.CMD_TALK}. However, device specific names and values
277 are certainly possible.
278
279 @param name: Descriptor of the data value sent
280 @type name: object
281 @param value: Content value
282 @type value: object
283 @param style: Style with which this value should be output
284 @type style: L{AEOutput.Style}
285 @return: Return value specific to the given command
286 @rtype: object
287 @raise NotImplementedError: When not overridden in a subclass
288 '''
289 raise NotImplementedError
290
292 '''
293 Indicates whether the device is active (e.g. giving output, has buffered
294 content to output, is otherwise busy) or not.
295
296 @return: True when active, False when not
297 @rtype: boolean
298 @raise NotImplementedError: When not overriden in a subclass
299 '''
300 raise NotImplementedError
301