Package pype :: Module ptable
[frames] | no frames]

Source Code for Module pype.ptable

   1  # -*- Mode: Python; tab-width: 4; py-indent-offset: 4; -*- 
   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  # (custom) validator functions must return one of these: 
  48  VALID = Pmw.OK 
  49  INVALID = Pmw.PARTIAL 
  50   
  51  _KEEPLOCKED = -1 
  52   
53 -def _unpack_slot(slot):
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
65 -class ParamTable(object):
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 # got to check for duplicates before this table's added 82 # into the global list. 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 # usr can NEVER set this value! 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 # self-proclaimed override param (ie, hides another) 204 e.component('label').config(fg='blue') 205 Pmw.alignlabels(entries) 206 207 if self._file: 208 self.load()
209
210 - def __del__(self):
211 if self in ParamTable._list: 212 ParamTable._list.remove(self)
213
214 - def addbutton(self, text='userdef', command=None):
215 """Add user-defined button to param table. 216 217 :param text: (string) text of button label 218 219 :param command: (function) function to call when clicked 220 221 :return: Button 222 223 """ 224 b = Button(self.frame, text=text, fg='green', command=command) 225 b.pack(expand=0, fill=X, side=LEFT) 226 return b
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 # Wed Mar 11 11:08:13 2009 mazer 284 # store raw string version of param in dictionary in addition 285 # to the evaluated version for future reference.. only do 286 # this if evaluating 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 # obsolete field... 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
318 - def keys(self):
319 """Get list of keys (ie, row names) for this table/ 320 321 """ 322 return self._entries.keys()
323
324 - def query(self, qname):
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
343 - def queryv(self, qname):
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 # try all the load methods in order until one works.. 452 # or if none work, return 0.. 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
460 - def _load_cfg(self, file=None):
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
508 - def _load_pickle(self, file=None):
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
540 - def _load_txt(self, file=None):
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 # value has trailing \n 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
579 - def frompypefile(self):
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 # radio buttons etc.. 600 self._entries[k].invoke(rec.params[kraw]) 601 warn('Loaded values', s, astext=1)
602 603
604 - def view(self):
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
649 - def runlock(self, lock=1):
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 # helper functions for creating rows in the parameter table: 667
668 -def psection(name):
669 """Header for ParamTable (starts new section); same as ptitle""" 670 return (name, None, None, None, None)
671
672 -def ptitle(name):
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 # validator functions 725
726 -def is_dir(s, evaluate=None):
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
738 -def is_file(s, evaluate=None):
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
750 -def is_newfile(s, evaluate=None):
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
762 -def is_any(s, evaluate=None):
763 """No type checking --> always returns true. 764 765 """ 766 r = VALID 767 if evaluate: 768 return (r, s) 769 return r
770
771 -def is_boolean(s, evaluate=None):
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 # alias for is_boolean: 804 is_bool = is_boolean 805
806 -def is_int(s, evaluate=None):
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
821 -def is_posint(s, evaluate=None):
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
838 -def is_negint(s, evaluate=None):
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
855 -def is_gteq_zero(s, evaluate=None):
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
873 -def is_lteq_zero(s, evaluate=None):
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
891 -def is_rgb(s, evaluate=None):
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
921 -def is_rgba(s, evaluate=None):
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
946 -def is_gray(s, evaluate=None):
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
984 -def is_float(s, evaluate=None):
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
998 -def is_percent(s, evaluate=None):
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
1017 -def is_angle_degree(s, evaluate=None):
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
1035 -def is_param(s, evaluate=None):
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
1056 -def is_iparam(s, evaluate=None):
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
1114 -def _toint(s):
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 # this re pulls out the argument for the hazard function 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
1163 -def is_list(s, evaluate=None):
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