Attachment 'ObjectAttrValidator2.py'
Download 1 # ObjectAttrValidator.py
2 #
3 # ------------------------------------------------------------
4 # Copyright 2004 by Samuel Reynolds. All rights reserved.
5 #
6 # Permission to use, copy, modify, and distribute this software and its
7 # documentation for any purpose and without fee is hereby granted,
8 # provided that the above copyright notice appear in all copies and that
9 # both that copyright notice and this permission notice appear in
10 # supporting documentation, and that the name of Samuel Reynolds
11 # not be used in advertising or publicity pertaining to distribution
12 # of the software without specific, written prior permission.
13 #
14 # SAMUEL REYNOLDS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
16 # EVENT SHALL SAMUEL REYNOLDS BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
17 # CONSEQUENTIAL DAMAGES, OR FOR ANY DAMAGES WHATSOEVER RESULTING FROM
18 # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
19 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
20 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 # ------------------------------------------------------------
22
23 import wx
24
25 class ObjectAttrValidator( wx.PyValidator ):
26 """
27 Base class for validators that validate *attributes* of
28 objects and provide two-way data transfer between those
29 attributes and UI controls.
30
31 ObjectAttrValidator has no knowledge of the specific
32 type of widget to be validated. Responsibility for
33 interacting with specific types of widgets is delegated
34 to subclasses.
35
36 Subclasses must implement the following methods:
37 * _setControlValue( self, value )
38 Set the value of control to the value provided.
39 * _getControlValue( self )
40 Return the value of control.
41 Remaining logic is implemented in ObjectAttrValidator.
42
43
44 CHAMELEON POWERS:
45
46 To simplify use in an object-editor context (in which
47 a single editor GUI is used to edit multiple objects
48 of the same type), ObjectAttrValidator allows changing
49 objects on the fly.
50
51 For example, if references to field widgets are stored
52 in a list self._fieldWidgets, the editor class could
53 implement the following methods:
54
55 def SetObject( self, editObject ):
56 'Set object for editing.'
57 self._editObject = editObject
58 self._updateValidators()
59 self.TransferDataToWindow()
60
61 def _updateValidators( self ):
62 for wgt in self._fieldWidgets:
63 validator = wgt.GetValidator()
64 validator.SetObject( self._editObject )
65
66
67 FORMATTERS
68
69 ObjectAttrValidator uses Formatters for validation and
70 two-way reformatting. A Formatter is aware of source data type,
71 and handles translation between presentation representation and
72 storage representation.
73
74 A Formatter must provide the following interface methods:
75 * format( stored_value )
76 Format a value for presentation.
77 * coerce( presentation_value )
78 Convert a presentation-formatted value
79 to a storage-formatted value (may include
80 type conversion such as string->integer).
81 * validate( presentation_value )
82 Validate a presentation-formatted value.
83 Return True if valid or False if invalid.
84
85 If a formatter is assigned, the ObjectAttrValidator
86 will call the formatter's format, coerce, and validate
87 methods at appropriate points in its processing.
88
89 With a formatter, there is no need of, for example,
90 integer-aware entry fields. The formatter handles
91 validation and conversion of the input value.
92 """
93
94 def __init__( self, obj, attrName, formatter=None, flRequired=True, validationCB=None ):
95 super(ObjectAttrValidator,self).__init__()
96
97 self.obj = obj
98 self.attrName = attrName
99 self.flRequired = flRequired
100 self.formatter = formatter
101 self.validationCB = validationCB
102
103
104 def Clone( self ):
105 """
106 Return a new validator for the same field of the same object.
107 """
108 return self.__class__( self.obj, self.attrName, self.formatter,
109 self.flRequired, self.validationCB )
110
111
112 def SetObject( self, obj ):
113 """
114 Set or change the object with which the validator interacts.
115
116 Useful for changing objects when a single panel is used
117 to edit a number of identical objects.
118 """
119 self.obj = obj
120
121
122 def TransferToWindow( self ):
123 """
124 Transfer data from validator to window.
125
126 Delegates actual writing to destination widget to subclass
127 via _setControlValue method.
128 """
129 if self.obj == None:
130 # Nothing to do
131 return True
132
133 # Copy object attribute value to widget
134 val = getattr( self.obj, self.attrName )
135 if val == None:
136 val = ''
137 if self.formatter:
138 val = self.formatter.format( val )
139 self._setControlValue( val )
140
141 return True
142
143
144 def TransferFromWindow( self ):
145 """
146 Transfer data from window to validator.
147
148 Delegates actual reading from destination widget to subclass
149 via _getControlValue method.
150
151 Only copies data if value is actually changed from attribute value.
152 """
153 if self.obj == None:
154 # Nothing to do
155 return True
156
157 # Get widget value
158 val = self._getControlValue()
159
160 # Check widget value against attribute value; only copy if changed
161 # Get object attribute value
162 oldVal = getattr( self.obj, self.attrName )
163 if self.formatter:
164 oldVal = self.formatter.format( oldVal )
165 if val != oldVal:
166 if self.formatter:
167 val = self.formatter.coerce( val )
168 setattr( self.obj, self.attrName, val )
169
170 return True
171
172
173 def Validate( self, win ):
174 """
175 Validate the contents of the given control.
176
177 Default behavior: Anything goes.
178 """
179 flValid = True
180 val = self._getControlValue()
181 if self.flRequired and val == '':
182 flValid = False
183 if flValid and self.formatter:
184 flValid = self.formatter.validate( val )
185 if self.validationCB:
186 self.validationCB( self.obj, self.attrName, val, self.flRequired, flValid )
187 return flValid
188
189
190 def _setControlValue( self, value ):
191 """
192 Set the value of the target control.
193
194 Subclass must implement.
195 """
196 raise NotImplementedError, 'Subclass must implement _setControlValue'
197
198
199 def _getControlValue( self ):
200 """
201 Return the value from the target control.
202
203 Subclass must implement.
204 """
205 raise NotImplementedError, 'Subclass must implement _getControlValue'
206
207
208 class ObjectAttrTextValidator( ObjectAttrValidator ):
209 """
210 Validator for TextCtrl widgets.
211 """
212 def __init__( self, *args, **kwargs ):
213 """ Standard constructor. """
214 super(ObjectAttrTextValidator,self).__init__( *args, **kwargs )
215
216
217 def TransferToWindow( self ):
218 """
219 Transfer data from validator to window.
220
221 Delegates actual writing to destination widget to subclass
222 via _setControlValue method.
223 """
224 if self.obj == None:
225 # Clear the widget
226 wgt = self.GetWindow()
227 wgt.Clear()
228 return True
229
230 # Default behavior otherwise
231 return super(ObjectAttrTextValidator,self).TransferToWindow()
232
233
234 def _setControlValue( self, value ):
235 """
236 Set the value of the TextCtrl.
237 """
238 wgt = self.GetWindow()
239 wgt.SetValue( value )
240
241
242 def _getControlValue( self ):
243 """
244 Return the value from the TextCtrl.
245 """
246 wgt = self.GetWindow()
247 return wgt.GetValue()
248
249
250 class ObjectAttrSelectorValidator( ObjectAttrValidator ):
251 """
252 Validator for ControlWithItems widgets (ListBox, Choice).
253
254 For wx.ListBox, assumes single-selection mode (wx.LB_SINGLE).
255 """
256 def __init__( self, obj, attrName, formatter, *args, **kwargs ):
257 """ Standard constructor. """
258 super(ObjectAttrSelectorValidator,self).__init__(
259 obj, attrName, formatter, *args, **kwargs )
260
261
262 def _getFieldOptions( self, name ):
263 """
264 Return list of (id,label) pairs.
265 """
266 return self.formatter.validValues()
267
268
269 def _setControlValue( self, value ):
270 """
271 Set the value *and the options* of the control.
272 By the time this is called, the value is already mapped for display.
273 """
274 wgt = self.GetWindow()
275
276 # Get options (list of (id,value) pairs)
277 options = self._getFieldOptions( self.attrName )
278 # Sort alphabetically
279 options = [ (opt[1], opt) for opt in options ]
280 options.sort()
281 options = [ opt[1] for opt in options ]
282 # Replace selector contents
283 wgt.Clear()
284 for id, label in options:
285 wgt.Append( label, id )
286
287 # Set selection
288 wgt.SetStringSelection( value )
289
290
291 def _getControlValue( self ):
292 """
293 Return the value from the TextCtrl.
294 """
295 wgt = self.GetWindow()
296 return wgt.GetStringSelection()
297
298
299 class ObjectAttrCheckListBoxValidator( ObjectAttrValidator ):
300 """
301 Validator for CheckListBox widgets.
302 """
303 def __init__( self, obj, attrName, formatter, *args, **kwargs ):
304 """ Standard constructor. """
305 super(ObjectAttrCheckListBoxValidator,self).__init__(
306 obj, attrName, formatter, *args, **kwargs )
307
308
309 ########## REQUIRED INTERFACE ##########
310
311 def _getFieldOptions( self, name ):
312 """
313 Return list of (id,label) pairs.
314 """
315 return self.formatter.validValues()
316
317
318 def _setControlValue( self, value ):
319 """
320 Set the value *and the options* of the control.
321 By the time this is called, the value is already mapped for display.
322
323 @param value: Sequence of (value, label) pairs.
324 """
325 wgt = self.GetWindow()
326
327 # Get options (list of (id,value) pairs)
328 options = self._getFieldOptions( self.attrName )
329 # Sort alphabetically
330 options = [ (opt[1], opt) for opt in options ]
331 options.sort()
332 options = [ opt[1] for opt in options ]
333 # Replace selector contents
334 self._setControlOptions( options )
335
336 # Set selection
337 wgt._setControlSelections( value )
338
339
340 def _getControlValue( self ):
341 """
342 Return the value from the TextCtrl.
343
344 Returns a list of client data values (not row indices).
345 """
346 wgt = self.GetWindow()
347 selections = wgt.GetStringSelections()
348 value = [ wgt.GetClientData( idx ) for idx in selections ]
349 return value
350
351
352 ########## END REQUIRED INTERFACE ##########
353
354
355 def _setControlOptions( self, options ):
356 """
357 Set up or update control options.
358
359 @param options: Sequence of (id,label) pairs.
360 """
361 wgt = self.GetWindow()
362 wgt.Clear()
363 for id, label in options:
364 wgt.Append( label, id )
365
366
367 def _setControlSelections( self, value ):
368 """
369 Select the specified items in the control, and unselect others.
370
371 @param value: Integer or sequence of integers representing
372 the data value(s) of item(s) to be selected.
373
374 If None or empty sequence, all currently-selected
375 items will be deselected.
376
377 Any items in value that are not found in the
378 control have no effect.
379 """
380 if value == None:
381 value = tuple()
382 elif not isinstance(value,(list,tuple)):
383 value = ( value, )
384
385 numItems = wgt.GetCount()
386 for idx in xrange( 0, numItems ):
387 itemData = wgt.GetClientData( idx )
388 if itemData in value:
389 if not wgt.IsChecked( idx ):
390 wgt.Check( idx, True )
391 else:
392 if wgt.IsChecked( idx ):
393 wgt.Check( idx, False )
394
395
396 class ObjectAttrRadioBoxValidator( ObjectAttrValidator ):
397 """
398 Validator for RadioBox widgets.
399 """
400 def __init__( self, obj, attrName, formatter, *args, **kwargs ):
401 """ Standard constructor. """
402 super(ObjectAttrRadioBoxValidator,self).__init__(
403 obj, attrName, formatter, *args, **kwargs )
404
405
406 def _setControlValue( self, value ):
407 """
408 Set the value *and the options* of the control.
409 By the time this is called, the value is already mapped for display.
410 """
411 wgt = self.GetWindow()
412 wgt.SetStringSelection( value )
413
414
415 def _getControlValue( self ):
416 """
417 Return the value from the TextCtrl.
418 """
419 wgt = self.GetWindow()
420 return wgt.GetStringSelection()
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.