1
2
3 """Parameter tables with type validation
4
5 Parameter table (aka worksheet) implementation. This module
6 provides a standard way to create task-specific parameter
7 tables to be filled out by the user and passed into running
8 tasks.
9
10 Validator function (is_int, is_pdf etc) are provided for
11 a number of common cases. If you want to write your own,
12 the validation function must be a function two args, eg:
13
14 def is_int(s, evaluate=None): ...
15
16 s is a string to validate -- when evaluate is None, then
17 the function simply decides whether or not s is in a valid
18 form and returns True or False. If evaluate is True, then
19 the validator function should return the actual valid of
20 the string -- ie, for is_int, return a validated integer
21 (not a string, but an actual integer).
22
23 Author -- James A. Mazer (mazerj@gmail.com)
24
25 """
26
27 from Tkinter import *
28 import Pmw
29 import string
30 import types
31 import re
32 import os
33 import posixpath
34 import cPickle
35 import ConfigParser
36 import numpy as np
37
38 import pype
39 import pype_aux
40 from guitools import *
41 from filebox import Open, SaveAs
42 from guitools import Logger
43
44 from pypedebug import keyboard
45
46
47
48 VALID = Pmw.OK
49 INVALID = Pmw.PARTIAL
50
51 _KEEPLOCKED = -1
52
54 (name, default, validate, descr, runlock) = [None] * 5
55 if len(slot) < 1:
56 raise FatalPypeError, 'ptable:_unpack_slot bad worksheet slot'
57 name = slot[0]
58 if len(slot) > 1: default = slot[1]
59 if len(slot) > 2: validate = slot[2]
60 if len(slot) > 3: descr = slot[3]
61 if len(slot) > 4: runlock = slot[4]
62
63 return (name, default, validate, descr, runlock)
64
66 _list = []
67
68 - def __init__(self, parent, table, file=None,
69 decorate=1, locks=1, allowalt=True, loadable=1):
70 """Parameter management table class"""
71
72 if parent is None and table is None: return
73
74 self._parent = parent
75 self._table = table
76 self._entries = {}
77 self._locks = {}
78 self._file = file
79 self._allowalt = allowalt
80
81
82
83 for slot in self._table:
84 (name, default, validate, descr, runlock) = _unpack_slot(slot)
85 if default is not None:
86 found = self.find(name, show=0)
87 for (duptab, dupname, duprow) in found:
88 if name == dupname:
89 Logger('ptable: ''%s'' dup in %s - priority '
90 'to %s\n' % (name, duptab.tablename, file))
91 ParamTable._list.append(self)
92
93 self.tablename = 'noname'
94
95 if file:
96 try:
97 self._file = pype.subjectrc(file)
98 except KeyError:
99 self._file = file
100 self.tablename = file
101 else:
102 self._file = None
103
104 self.balloon = Pmw.Balloon(parent, master=1, relmouse='both')
105
106 f = Frame(parent)
107 f.pack(expand=0, fill=X, side=TOP)
108 self.frame = f
109
110 if self._file:
111 Button(f, text='Save',
112 command=self.save).pack(expand=0, fill=X, side=LEFT)
113 if decorate:
114 Button(f, text="View",
115 command=self.view).pack(expand=0, fill=X, side=LEFT)
116
117 if loadable:
118 Button(f, text="Load (<-datafile)",
119 command=self.frompypefile).pack(expand=0, fill=X, side=LEFT)
120
121
122 f = Pmw.ScrolledFrame(parent)
123 f.pack(expand=1, fill=BOTH)
124
125 entries = []
126 self.names = []
127 nrow = 0
128 for slot in self._table:
129 nrow = nrow + 1
130 (name, default, validate, descr, runlock) = _unpack_slot(slot)
131
132 lf = Frame(f.interior())
133 lf.pack(expand=1, fill=X)
134 if default is None and validate is None:
135 e = Label(lf, text=name, bg='yellow', anchor=W)
136 e.pack(expand=1, fill=X)
137 elif isinstance(validate, types.TupleType):
138 tmpf = Frame(lf, height=12, width=12)
139 tmpf.pack(side=LEFT)
140 e = Pmw.RadioSelect(lf,
141 buttontype = 'radiobutton',
142 labelpos = 'w',
143 label_text = name + ':',
144 pady=0, padx=2)
145 e.pack(anchor=W)
146 for v in validate:
147 e.add(v)
148 e.invoke(default)
149 self._entries[name] = e
150 entries.append(e)
151 elif isinstance(validate, types.DictType):
152 tmpf = Frame(lf, height=12, width=12)
153 tmpf.pack(side=LEFT)
154 e = Pmw.RadioSelect(lf,
155 buttontype = 'radiobutton',
156 labelpos = 'w',
157 label_text = name + ':',
158 pady=0, padx=2)
159 if descr:
160 self.balloon.bind(e, '%d: %s' % (nrow, descr))
161 else:
162 self.balloon.bind(e, '%d: %s' % (nrow, '???'))
163 e.pack(anchor=W)
164 for v in validate:
165 e.add(v)
166 e.invoke(default)
167 self._entries[name] = e
168 self._entries[(name,'dict')] = validate
169 entries.append(e)
170 else:
171 e = Pmw.EntryField(lf,
172 labelpos = 'w',
173 label_text = name + ':',
174 validate = validate,
175 value = default)
176 if descr:
177 self.balloon.bind(e, '%d: %s' % (nrow, descr))
178 else:
179 self.balloon.bind(e, '%d: %s' % (nrow, '???'))
180 e.component('entry').configure(bg='white', width=75)
181 tmpf = Frame(lf, height=12, width=12)
182 tmpf.pack_propagate(0)
183 tmpf.pack(side=LEFT)
184 if locks:
185 if runlock == _KEEPLOCKED:
186 lockbut = Label(tmpf, text = '')
187 else:
188 lockbut = Button(tmpf, text = '',
189 command=lambda n=name:
190 self.lockfield(n, toggle=1))
191 lockbut.pack(fill=BOTH, expand=1)
192
193 if runlock == _KEEPLOCKED:
194
195 e.component('entry').configure(state=DISABLED)
196 e.component('label').configure(fg='red')
197 lockbut.configure(state=DISABLED)
198 self._entries[name] = e
199 entries.append(e)
200 self.names.append(e)
201 e.pack(expand=1, anchor=W, fill=X, side=RIGHT)
202 if descr and descr[0] == '*':
203
204 e.component('label').config(fg='blue')
205 Pmw.alignlabels(entries)
206
207 if self._file:
208 self.load()
209
213
227
228 - def find(self, name, show=1):
229 """Find parameter tables fields that (partial) match name.
230
231 :param name: (string) name of field find/match
232
233 :param show: (bool) popup table with found entries?
234
235 :return: list of matching tables [(table, slotname, index), ...]
236
237 """
238
239 intabs = []
240 for pt in ParamTable._list:
241 n = 0
242 for slot in pt._table:
243 n = n + 1
244 if string.find(slot[0], name) >= 0:
245 intabs.append((pt, slot[0], n,))
246 if show:
247 pt._parent._show()
248 return intabs
249
250 - def _get(self, evaluate=1, mergewith=None, readonly=1):
251 """Retrieve parameter table current state.
252
253 :param evaluate: (bool) if true, then table slots are 'evaluated'
254 before returned, otherwise the raw, unprocessed strings
255 are returned.
256
257 :param mergewith: (dict) optional dictionary to start with; values
258 from the table will be merged into this existing dictionary
259 and the merged dictionary returned.
260
261 :param readonly: (bool) validate 'readonly' fields?
262
263 :returns: (2-tuple) Returned valiue is a pair (valid, dict),
264 where dict contains all the values in the parameter
265 table. Dictionary keys are the slot names. Default is
266 to evaluate the parameters, which means they should
267 come back correctly) typed (ie, is_int's should come
268 back as Integers).
269
270 """
271
272 if mergewith:
273 d = mergewith
274 else:
275 d = {}
276
277 for slot in self._table:
278 (name, default, validate, descr, runlock) = _unpack_slot(slot)
279 if default is None:
280 continue
281 v = self.query(name)
282 if evaluate:
283
284
285
286
287 d[name+'_raw_'] = v
288 if validate and not (isinstance(validate, types.TupleType) or
289 isinstance(validate, types.DictType)):
290 (r, v) = apply(validate, (v,), {"evaluate": 1})
291 if (runlock == _KEEPLOCKED) and not readonly:
292 continue
293 elif r != VALID:
294 return (0, name)
295 d[name] = v
296 return (1, d)
297
298 - def lockfield(self, name, toggle=None, state=DISABLED):
299 """Lock specififed row of table.
300
301 :return: nothing
302
303 """
304 try:
305 w = self._entries[name].component('entry')
306 except KeyError:
307
308 return
309
310 if toggle:
311 if w.cget('state') == DISABLED:
312 state = NORMAL
313 else:
314 state = DISABLED
315 w.configure(state=state)
316 self._locks[name] = state
317
319 """Get list of keys (ie, row names) for this table/
320
321 """
322 return self._entries.keys()
323
325 """Retrieve single value from the parameter table by name
326 without validation.
327
328 :param qname: (string) slot name
329
330 :return: (string) current value
331
332 """
333 try:
334 v = self._entries[qname].get()
335 except AttributeError:
336 v = self._entries[qname].getvalue()
337
338 if (qname,'dict') in self._entries:
339 return self._entries[(qname,'dict')][v]
340 else:
341 return v
342
344 """Retrieve single value from the parameter table by name
345 with validation.
346
347 :param qname: (string) slot name
348
349 :return: (variable) validated current value -- might not be string!
350
351 """
352 for slot in self._table:
353 (name, default, validate, descr, runlock) = _unpack_slot(slot)
354 if default is None:
355 continue
356 if name is qname:
357 v = self.query(name)
358 if validate and not (isinstance(validate, types.TupleType) or
359 isinstance(validate, types.DictType)):
360 (r, v) = apply(validate, (v,), {"evaluate": 1})
361 if r != VALID:
362 (r, v) = apply(validate, (default,), {"evaluate": 1})
363 return v
364 warn('ptable:queryv',
365 'No value associated with "%s".' % qname)
366 return None
367
368 - def set(self, name, value):
369 """Set current value for named row.
370
371 :param name: (string) slot name
372
373 :param value: (string) new value
374
375 :return: nothing
376
377 Warning: this might not work for Dict and Tuple fields anymore..
378
379 """
380 self._entries[name].setentry(value)
381
382 - def save(self, file=None, remove=1):
383 """Save state for table to file.
384
385 :param file: (string) If specified, name of output file (goes in
386 subject-specific-dir).
387
388 :return: nothing
389
390 """
391 if file is None:
392 file = self._file
393
394 tmpfile = file + '.tmp'
395 f = open(tmpfile, 'w')
396
397 (ok, x) = self._get(evaluate=0)
398
399 c = ConfigParser.ConfigParser()
400 c.add_section('params')
401 c.add_section('locks')
402 for k in x.keys():
403 c.set('params', k, x[k])
404 if k in self._locks:
405 if self._locks[k] == DISABLED:
406 c.set('locks', k, 1)
407 lock = 1
408 c.write(f)
409 f.close()
410 os.rename(tmpfile, file)
411
412 if self in ParamTable._list:
413 ParamTable._list.remove(self)
414
415
416 - def load(self, file=None):
417 """
418 Load pickled table database - note that the pickled dictionary
419 will be unpickled, but only those values referenced in the table
420 will actually be used. The rest (ie, obsolete) are discarded.
421 This way you can safely inherit from previous modules w/o
422 accumulating excess garbage.
423 """
424 if file is None:
425 file = self._file
426
427 if self._load(file=file) == 1:
428 return
429
430 if not self._allowalt:
431 return
432
433 try:
434 initialdir = pype.subjectrc('')
435 except KeyError:
436 initialdir = os.getcwd()
437
438 while 1:
439 (file, mode) = Open(initialdir=initialdir,
440 initialfile=self.tablename,
441 text='No saved parameter\n'+
442 'Select task to inherit params or\n'+
443 'Cancel to accept defaults.')
444 if file is None:
445 return
446 if self._load(file=file) == 1:
447 sys.stderr.write("Loaded params from '%s'\n" % file)
448 return
449
450 - def _load(self, file=None):
451
452
453 for method in (self._load_cfg, self._load_txt, self._load_pickle):
454 if method(file=file):
455 if not method == self._load_cfg:
456 sys.stderr.write("Warning: updating '%s'\n" % file)
457 return 1
458 return 0
459
461 """Load config-file base ptable state file.
462
463 """
464
465 try:
466 f = open(file, 'r')
467 except IOError:
468 return 0
469
470 c = ConfigParser.ConfigParser()
471 try:
472 c.readfp(f)
473 except:
474 c = None
475 f.close()
476 if c is None:
477 return 0
478
479 for slot in self._table:
480 (name, default, validate, descr, runlock) = _unpack_slot(slot)
481 if default is None:
482 continue
483 try:
484 val = c.get('params', name)
485 except:
486 sys.stderr.write('WARNING: %s:params:%s missing/corrupt\n' %
487 (os.path.basename(file), name))
488 val = default
489
490 if isinstance(validate, types.TupleType):
491 try: self._entries[name].invoke(val)
492 except ValueError: pass
493 elif isinstance(validate, types.DictType):
494 for k in validate.keys():
495 if '%s'%validate[k] == val:
496 try: self._entries[name].invoke(k)
497 except ValueError: pass
498 else:
499 self._entries[name].setentry(val)
500
501 if c.has_section('locks'):
502 for (name, lockstate) in c.items('locks'):
503 if int(lockstate):
504 self.lockfield(name)
505
506 return 1
507
509 """Backward compatibility only.
510
511 """
512 try:
513 f = open(file, 'r')
514 x = cPickle.load(f)
515 try:
516 locks = cPickle.load(f)
517 except EOFError:
518 locks = {}
519 f.close()
520 for slot in self._table:
521 (name, default, validate, descr, runlock) = _unpack_slot(slot)
522 if default is None:
523 continue
524 if (isinstance(validate, types.TupleType) or
525 isinstance(validate, types.DictType)):
526 self._entries[name].invoke(x[name])
527 else:
528 try:
529 self._entries[name].setentry(x[name])
530 except KeyError:
531 pass
532
533 for k in locks.keys():
534 self.lockfield(k, state=locks[k])
535
536 return 1
537 except IOError:
538 return 0
539
541 """Backward compatibility only.
542
543 """
544 try:
545 x = {}
546 locks = {}
547
548 f = open(file, 'r')
549 while 1:
550 line = f.readline()
551 if not line: break
552 source, key, lockstate, value = line.split('!!')
553
554 x[key] = value[:-1]
555 locks[key] = lockstate
556 f.close()
557
558 for slot in self._table:
559 (name, default, validate, descr, runlock) = _unpack_slot(slot)
560 if default is None:
561 continue
562 if (isinstance(validate, types.TupleType) or
563 isinstance(validate, types.DictType)):
564 self._entries[name].invoke(x[name])
565 else:
566 try:
567 self._entries[name].setentry(x[name])
568 except KeyError:
569 pass
570 for k in locks.keys():
571 if len(locks[k]) > 0:
572 self.lockfield(k, state=locks[k])
573 return 1
574 except IOError:
575 return 0
576 except ValueError:
577 return 0
578
580 import filebox, pypedata
581
582 (file, mode) = filebox.Open(initialdir=os.getcwd(),
583 pattern='*.[0-9][0-9][0-9]',
584 initialfile='', datafiles=1)
585 if file:
586 pf = pypedata.PypeFile(file)
587 rec = pf.nth(0)
588 if rec:
589 s = ''
590 keys = rec.params.keys()
591 for k in self.keys():
592 kraw = k + '_raw_'
593 if kraw in keys:
594 s = s + '%s = %s --> %s\n' % (k, self.query(k),
595 rec.params[kraw])
596 try:
597 self.set(k, rec.params[kraw])
598 except:
599
600 self._entries[k].invoke(rec.params[kraw])
601 warn('Loaded values', s, astext=1)
602
603
605 """View current contents of table (in a popup dialog box).
606
607 :returns: nothing
608
609 """
610 (ok, x) = self._get(evaluate=0)
611 s = ''
612
613 for slot in self._table:
614 (name, default, validate, descr, runlock) = _unpack_slot(slot)
615 if default is None: continue
616 try:
617 s = s + '%20s: %s\n' % (name, x[name])
618 except KeyError:
619 s = s + '%20s: %s\n' % (name, '<undefined>')
620 warn('ptable:view', s, wait=None, astext=1)
621
622 - def check(self, mergewith=None):
623 """Validate parameter table.
624
625 NOTE: This forces the user to actually make all the slots valid!
626
627 :param mergewith: (dict) optional dictionary to start with; values
628 from the table will be merged into this existing dictionary
629 and the merged dictionary returned.
630
631 :return: (dict) validated dictionary of all params
632
633 """
634 while 1:
635 (ok, P) = self._get(mergewith=mergewith)
636 if self.tablename:
637 w = "Check parameter table '%s'.\n" % self.tablename
638 else:
639 w = "Check parameter tables.\n"
640 if not ok:
641 warn('ptable:check', w +
642 "Field '%s' contains invalid data.\n\n" % P +
643 "Please fix and then click Continue.",
644 wait=1, grab=None)
645 else:
646 break
647 return P
648
650 """Lock table (in preparation for a run.
651
652 """
653 for slot in self._table:
654 (name, default, validate, descr, runlock) = _unpack_slot(slot)
655 if default:
656 e = self._entries[name].component('label')
657 if runlock == 1:
658 if lock:
659 e.configure(state=DISABLED)
660 else:
661 try:
662 e.configure(state=self._locks[name])
663 except KeyError:
664 e.configure(state=NORMAL)
665
666
667
669 """Header for ParamTable (starts new section); same as ptitle"""
670 return (name, None, None, None, None)
671
673 """Header for ParamTable (starts new section); same as psection"""
674 return (name, None, None, None, None)
675
676 -def pslot(name, default=None, val=None, info='', lockonrun=None):
677 """Generic parameter ParamTable slot"""
678 return (name, default, val, info, lockonrun)
679
680 -def pslot_ro(name, default=None, val=None, info=''):
681 """Generic READONLY ParamTable slot"""
682 return (name, default, val, info, _KEEPLOCKED)
683
684 -def pchoice(name, default=None, choices=None, info='', lockonrun=None):
685 """Dropdown list of choices"""
686 return (name, default, choices, info, lockonrun)
687
688 -def pbool(name, default=1, info='', lockonrun=None):
689 """Yes/No box that (generates bool 0/1 value)"""
690 return (name, default, is_bool, info, lockonrun)
691
692 -def pyesno(name, default=1, info='', lockonrun=None):
693 """Yes/No box that (generates bool 0/1 value); save as pbool"""
694 return (name, 1-default, {'yes':1, 'no':0}, info, lockonrun)
695
696 -def pparam(name, default, info='', lockonrun=None):
697 """param slot -- see pype_aux.param_expand"""
698 return (name, default, is_iparam, info, lockonrun)
699
700 -def piparam(name, default, info='', lockonrun=None):
701 """integer param slot -- see pype_aux.param_expand"""
702 return (name, default, is_iparam, info, lockonrun)
703
704 -def pcolor(name, default, info='', lockonrun=None):
705 """Color spec (R,G,B,A)"""
706 return (name, default, is_color, info, lockonrun)
707
708 -def pint(name, default, info='', lockonrun=None):
709 """Integer"""
710 return (name, default, is_int, info, lockonrun)
711
712 -def pfloat(name, default, info='', lockonrun=None):
713 """Floating point number"""
714 return (name, default, is_float, info, lockonrun)
715
716 -def plist(name, default, info='', lockonrun=None):
717 """List of numbers (really should be pseq, for sequence)"""
718 return (name, default, is_list, info, lockonrun)
719
720 -def pany(name, default, info='', lockonrun=None):
721 """Anything -- basically no validation at all"""
722 return (name, default, is_any, info, lockonrun)
723
724
725
727 """Must be a directory.
728
729 """
730 if posixpath.isdir(s):
731 r = VALID
732 else:
733 r = INVALID
734 if evaluate:
735 return (r, s)
736 return r
737
739 """Must be name of an EXISTING file.
740
741 """
742 if posixpath.exists(s):
743 r = VALID
744 else:
745 r = INVALID
746 if evaluate:
747 return (r, s)
748 return r
749
751 """Must be name of NON-EXISTING file.
752
753 """
754 if posixpath.exists(s):
755 r = INVALID
756 else:
757 r = VALID
758 if evaluate:
759 return (r, s)
760 return r
761
763 """No type checking --> always returns true.
764
765 """
766 r = VALID
767 if evaluate:
768 return (r, s)
769 return r
770
772 """Must be some sort of boolean flag.
773
774 Try to do a bit of parsing:
775 - 1,yes,on,true -> 1
776 - 0,no,off,false -> 0
777
778 Use pyesno() or something like that for drop down.
779
780 """
781 try:
782 x = string.lower(s)
783 if (x == 'yes') or (x == 'on') or (x == 'true'):
784 r = VALID
785 i = 1
786 elif (x == 'no') or (x == 'off') or (x == 'false'):
787 r = VALID
788 i = 1
789 else:
790 i = int(s)
791 if (i == 0) or (i == 1):
792 r = VALID
793 else:
794 i = 0
795 r = INVALID
796 except ValueError:
797 i = 0
798 r = INVALID
799 if evaluate:
800 return (r, i)
801 return r
802
803
804 is_bool = is_boolean
805
807 """Must be simple integer (positive, negative or zero).
808
809 """
810
811 try:
812 i = int(s)
813 r = VALID
814 except ValueError:
815 i = 0
816 r = INVALID
817 if evaluate:
818 return (r, i)
819 return r
820
822 """Must be positive (> 0) integer.
823
824 """
825 try:
826 i = int(s)
827 if i > 0:
828 r = VALID
829 else:
830 r = INVALID
831 except ValueError:
832 i = 0
833 r = INVALID
834 if evaluate:
835 return (r, i)
836 return r
837
839 """Must be negative (< 0) integer.
840
841 """
842 try:
843 i = int(s)
844 if i > 0:
845 r = VALID
846 else:
847 r = INVALID
848 except ValueError:
849 i = 0
850 r = INVALID
851 if evaluate:
852 return (r, i)
853 return r
854
856 """Must be greater than or equal to zero.
857
858 """
859
860 try:
861 i = int(s)
862 if i >= 0:
863 r = VALID
864 else:
865 r = INVALID
866 except ValueError:
867 i = 0
868 r = INVALID
869 if evaluate:
870 return (r, i)
871 return r
872
874 """Must be less than or equal to zero.
875
876 """
877
878 try:
879 i = int(s)
880 if i <= 0:
881 r = VALID
882 else:
883 r = INVALID
884 except ValueError:
885 i = 0
886 r = INVALID
887 if evaluate:
888 return (r, i)
889 return r
890
892 """Must be a properly formed RGB color triple.
893
894 Form should be (R,G,B) were R G and B are all
895 in the range 0-255. Parens are required.
896
897 Note: is_color is an alias for this fn
898
899 """
900 l = None
901 try:
902 l = eval(s)
903 if (len(l) == 3 and
904 (l[0] >= 0 and l[0] < 256) and
905 (l[1] >= 0 and l[1] < 256) and
906 (l[2] >= 0 and l[2] < 256)):
907 r = VALID
908 elif len(l) == 1 and (l[0] >= 0 and l[0] < 256):
909 r = VALID
910 l = (l[0],l[0],l[0])
911 else:
912 r = INVALID
913 except:
914 r = INVALID
915 if evaluate:
916 return (r, l)
917 return r
918
919 is_color = is_rgb
920
922 """Must be proper RGBA tuple (RGB+alpha).
923
924 Like is_color()/is_rgb(), but allows for alpha channel
925 specification.
926 """
927 l = None
928 try:
929 l = eval(s)
930 if (len(l) == 3 and
931 (l[0] >= 0 and l[0] < 256) and (l[1] >= 0 and l[1] < 256) and
932 (l[2] >= 0 and l[2] < 256)):
933 r = VALID
934 elif (len(l) == 4 and
935 (l[0] >= 0 and l[0] < 256) and (l[1] >= 0 and l[1] < 256) and
936 (l[2] >= 0 and l[2] < 256) and (l[3] >= 0 and l[3] < 256)):
937 r = VALID
938 else:
939 r = INVALID
940 except:
941 r = INVALID
942 if evaluate:
943 return (r, l)
944 return r
945
947 """
948 entry must be a valid 8-bit grayscale value (0-255)
949 """
950 try:
951 i = int(s)
952 if (i >= 0) and (i <= 255):
953 r = VALID
954 else:
955 i = 0
956 r = INVALID
957 except ValueError:
958 i = 0
959 r = INVALID
960 if evaluate:
961 return (r, i)
962 return r
963
964 -def is_rgba2(s, evaluate=None, meanlum=(128,128,128)):
965 """RGBA triple where values are 0-1
966
967 """
968 l, r = None, INVALID
969 try:
970 l = tuple(eval(s))
971 if all(map(lambda x: x>=0 and x<=1, l)):
972 r = VALID
973 if len(l) == 3:
974 l = l + (1.0,)
975 except SyntaxError:
976 pass
977
978 l = map(lambda x: int(x[0] + (255 * (x[1] - 0.5))),
979 zip(meanlum, x)) + (l[3],)
980 if evaluate:
981 return (r, l)
982 return r
983
985 """Must be a valid floating point number
986
987 """
988 try:
989 f = float(s)
990 r = VALID
991 except ValueError:
992 f = 0.0
993 r = INVALID
994 if evaluate:
995 return (r, f)
996 return r
997
999 """Must be valid percentage (ie, float in range 0-1)
1000 (technically this is is_fraction!)
1001
1002 """
1003 try:
1004 f = float(s)
1005 if (f >= 0.0) and (f <= 1.0):
1006 r = VALID
1007 else:
1008 f = 0.0
1009 r = INVALID
1010 except ValueError:
1011 f = 0.0
1012 r = INVALID
1013 if evaluate:
1014 return (r, f)
1015 return r
1016
1018 """Must be valid angle in degrees (ie, float in range 0-360)
1019
1020 """
1021 try:
1022 f = float(s)
1023 if (f >= 0.0) and (f <= 360.0):
1024 r = VALID
1025 else:
1026 f = 0.0
1027 r = INVALID
1028 except ValueError:
1029 f = 0.0
1030 r = INVALID
1031 if evaluate:
1032 return (r, f)
1033 return r
1034
1036 """Is 'special parameter' string.
1037
1038 See the param_expand function (pype_aux.py) for
1039 full documentation on the valid forms of special
1040 parameter strings.
1041
1042 """
1043 try:
1044 x = pype_aux.param_expand(s)
1045 if x is None:
1046 r = INVALID
1047 else:
1048 r = VALID
1049 except ValueError:
1050 x = 0.0
1051 r = INVALID
1052 if evaluate:
1053 return (r, x)
1054 return r
1055
1057 """Like is_param, but integer values.
1058
1059 """
1060 try:
1061 x = pype_aux.param_expand(s)
1062 if x is None:
1063 r = INVALID
1064 else:
1065 r = VALID
1066 except ValueError:
1067 x = 0
1068 r = INVALID
1069 if evaluate:
1070 try:
1071 return (r, int(round(x)))
1072 except TypeError:
1073 return (INVALID, 0)
1074 return r
1075
1076 -def is_cdf(s, evaluate=None):
1077 """Must describe a cummulative distribution <-- NO REALLY, PDF...
1078
1079 This ensures that the specified value is an integer or a vector
1080 describing a valid cummulative PDF. If an integer, then return a
1081 n-length cdf of uniform prob (eg, [1/n, 1/n .. 1/n]) Otherwise,
1082 normalize the vector to have unity area.
1083
1084 IMPROVED: 23-mar-2002 JM --> changed so you don't have
1085 to make stuff add to 1.0, evaluating divides by the
1086 sum to force it to add up to 1.0...
1087
1088 """
1089 try:
1090 i = int(s)
1091 if evaluate:
1092 val = []
1093 for n in range(i):
1094 val.append(1.0 / float(i))
1095 r = VALID
1096 except ValueError:
1097 try:
1098 i = eval(s)
1099 val = []
1100 if isinstance(i, types.ListType):
1101 sum = 0.0
1102 for f in i:
1103 sum = sum + float(f)
1104 for f in i:
1105 val.append(float(f) / sum)
1106 r = VALID
1107 except:
1108 val = 0
1109 r = INVALID
1110 if evaluate:
1111 return (r, val)
1112 return r
1113
1115 try:
1116 return int(s)
1117 except ValueError:
1118 return None
1119
1120 -def is_pdf(s, evaluate=None):
1121 """Probability density function.
1122
1123 - If a list, then the list is normalized to sum=1.0 when retrieved.
1124
1125 - If an integeer, then a uniform distribution of specified length
1126 is used (ie, [1/n, 1/n .. 1/n]) is generated on retrieval
1127
1128 - If of the form 'hazard(N)', where N is an integer
1129
1130 """
1131
1132 try:
1133 i = int(s)
1134 if evaluate:
1135 val = []
1136 for n in range(i):
1137 val.append(1.0 / float(i))
1138 r = VALID
1139 except ValueError:
1140
1141 hparse = re.compile('^hazard\(([0-9]+)\)$').split(s)
1142 if len(hparse) == 3 and not toint(hparse[1]) is None:
1143 val = np.exp(-np.arange(toint(hparse[1])))
1144 r = VALID
1145 else:
1146 try:
1147 i = eval(s)
1148 val = []
1149 if isinstance(i, types.ListType):
1150 sum = 0.0
1151 for f in i:
1152 sum = sum + float(f)
1153 for f in i:
1154 val.append(float(f) / sum)
1155 r = VALID
1156 except:
1157 val = 0
1158 r = INVALID
1159 if evaluate:
1160 return (r, val)
1161 return r
1162
1164 """Must be a list/vector or range.
1165
1166 Lists are specified in []'s. Ranges can be either:
1167
1168 =start:stop:step (inclusive: both start and stop in list)
1169
1170 start:stop:step (stop is not included in the list)
1171
1172 """
1173
1174 try:
1175 v = s.split(':')
1176 if len(v) > 1:
1177 if s[0] == '=':
1178 inc = 1
1179 v = map(int, s[1:].split(':'))
1180 else:
1181 v = map(int, s.split(':'))
1182 inc = 0
1183 if len(v) < 3:
1184 stride = 1
1185 else:
1186 stride = v[2]
1187 r = VALID
1188 if inc:
1189 val = range(v[0], v[1]+1, stride);
1190 else:
1191 val = range(v[0], v[1], stride);
1192 if evaluate:
1193 return (r, val)
1194 return r
1195 except:
1196 pass
1197
1198 try:
1199 val = eval(s)
1200 if isinstance(val, types.ListType):
1201 r = VALID
1202 else:
1203 r = INVALID
1204 val = []
1205 except:
1206 r = INVALID
1207 val = []
1208
1209 if evaluate:
1210 return (r, val)
1211
1212 return r
1213
1214 if __name__ == '__main__':
1215 import sys
1216
1217 root = Tk()
1218 Pmw.initialise(root)
1219 exitButton = Button(root, text = 'Exit', command = root.destroy)
1220 exitButton.pack(side = 'bottom')
1221 p = ParamTable(root,
1222 (ptitle('test'),
1223 pslot('a', default='500+-10%', val=is_param),
1224 pslot('l', default='', val=is_list),
1225 pslot('b', default='3', val=None),
1226 pslot('choice', default=1, val=('true', 'false')),
1227 pslot('yn', default=1, val={'no':0, 'yes':1}),
1228 pslot('c', default='4', val=None)),
1229 file='foobar', allowalt=False)
1230
1231 Button(root, text = 'info',
1232 command = lambda p=p:
1233 sys.stdout.write('%s\n'%p.query('yn'))).pack(side='bottom')
1234
1235 p.load('foobar')
1236
1237 root.mainloop()
1238