Package pydrizzle :: Package traits102 :: Module tktrait_sheet
[hide private]
[frames] | no frames]

Source Code for Module pydrizzle.traits102.tktrait_sheet

   1  #-------------------------------------------------------------------------------- 
   2  # 
   3  #  Define a Tkinter based trait sheet mechanism for visually editing the 
   4  #  values of traits. 
   5  # 
   6  #  Written by: David C. Morrill 
   7  # 
   8  #  Date: 10/15/2002 
   9  # 
  10  #  (c) Copyright 2002 by Enthought, Inc. 
  11  # 
  12  #-------------------------------------------------------------------------------- 
  13   
  14  #-------------------------------------------------------------------------------- 
  15  #  Imports: 
  16  #-------------------------------------------------------------------------------- 
  17  from __future__ import division # confidence medium 
  18   
  19  import sys 
  20  import os.path 
  21  import re 
  22   
  23  import Tkinter        as tk 
  24  import tkMessageBox   as mb 
  25  import tkSimpleDialog as sd 
  26  import tkColorChooser as cc 
  27  import Pmw 
  28  import tkFont 
  29   
  30  from traits      import Trait, HasTraits, TraitError, HasDynamicTraits, \ 
  31                          trait_editors 
  32  from trait_sheet import TraitEditor, TraitSheetHandler, TraitMonitor, \ 
  33                          TraitGroup, TraitGroupList, default_trait_sheet_handler 
  34  from types       import DictType, ListType, TupleType, ModuleType, \ 
  35                          StringType, FloatType 
  36   
  37  #------------------------------------------------------------------------------- 
  38  #  Module initialization: 
  39  #------------------------------------------------------------------------------- 
  40   
  41  trait_editors( __name__ ) 
  42   
  43  #------------------------------------------------------------------------------- 
  44  #  Constants: 
  45  #------------------------------------------------------------------------------- 
  46   
  47  # Boolean values: 
  48  TRUE  = 1 
  49  FALSE = 0 
  50   
  51  # Basic sequence types: 
  52  basic_sequence_types = [ ListType, TupleType ] 
  53   
  54  # Standard width of an image bitmap: 
  55  standard_bitmap_width = 120 
  56   
  57  # Standard colors: 
  58  WHITE = '#FFFFFF' 
  59   
  60  # Standard color samples: 
  61  color_choices = ( 0, 128, 192, 255 ) 
  62  color_samples = [ None ] * 48 
  63  i             = 0 
  64  for r in color_choices: 
  65      for g in color_choices: 
  66          for b in ( 0, 128, 255 ): 
  67              color_samples[i] = '#%02X%02X%02X' % ( r, g, b ) 
  68              i += 1 
  69   
  70  # List of available font facenames: 
  71  facenames = None 
  72   
  73  # Standard font point sizes: 
  74  point_sizes = [ 
  75     '8',  '9', '10', '11', '12', '14', '16', '18', 
  76    '20', '22', '24', '26', '28', '36', '48', '72' 
  77  ] 
  78   
  79  # Global switch governing whether or not tooltips are displayed in trait 
  80  # sheet dialogs: 
  81  tooltips_enabled = TRUE 
  82   
  83  # Pattern of all digits: 
  84  all_digits = re.compile( r'\d+' ) 
  85   
  86  #-------------------------------------------------------------------------------- 
  87  #  'TraitSheetDialog' class: 
  88  #-------------------------------------------------------------------------------- 
  89   
90 -class TraitSheetDialog ( tk.Toplevel ):
91 92 #----------------------------------------------------------------------------- 93 # Initialize the object: 94 #----------------------------------------------------------------------------- 95
96 - def __init__ ( self, object, 97 traits = None, 98 handler = default_trait_sheet_handler, 99 parent = None, 100 title = None ):
101 if title is None: 102 title = '%s Traits' % object.__class__.__name__ 103 tk.Toplevel.__init__( self, parent ) 104 self.title( title ) 105 self.bind( '<Destroy>', self.on_close_page ) 106 self.bind( '<Escape>', self.on_close_key ) 107 108 self.object = object 109 self.handler = handler 110 111 # Create the actual trait sheet panel: 112 TraitSheet( self, object, traits, handler ).grid( row = 0 ) 113 114 # Find a nice place on the screen to display the trait sheet so 115 # that it overlays the object as little as possible: 116 if not handler.position( self, object ): 117 #??? self.Centre( wx.wxBOTH ) 118 pass 119 120 self.resizable( FALSE, FALSE )
121 122 #----------------------------------------------------------------------------- 123 # Close the trait sheet window: 124 #----------------------------------------------------------------------------- 125
126 - def on_close_page ( self, event ):
127 self.handler.close( self, self.object )
128 129 #---------------------------------------------------------------------------- 130 # Handle the user hitting the 'Esc'ape key: 131 #---------------------------------------------------------------------------- 132
133 - def on_close_key ( self ):
134 self.on_close_page() 135 self.destroy()
136 137 #-------------------------------------------------------------------------------- 138 # 'TraitPanel' class: 139 #-------------------------------------------------------------------------------- 140
141 -class TraitPanel ( tk.Frame ):
142 143 #----------------------------------------------------------------------------- 144 # Initialize the object: 145 #----------------------------------------------------------------------------- 146
147 - def __init__ ( self, parent ):
148 tk.Frame.__init__( self, parent ) 149 pass ### NOT IMPLEMENTED YET
150 151 #---------------------------------------------------------------------------- 152 # Add a TraitSheet to the panel: 153 #---------------------------------------------------------------------------- 154
155 - def add ( self, sheet ):
156 pass ### NOT IMPLEMENTED YET
157 158 #---------------------------------------------------------------------------- 159 # Remove a TraitSheet from the panel: 160 #---------------------------------------------------------------------------- 161
162 - def remove ( self, sheet ):
163 pass ### NOT IMPLEMENTED YET
164 165 #---------------------------------------------------------------------------- 166 # Get the size of the panel: 167 #---------------------------------------------------------------------------- 168
169 - def size ( self ):
170 return ( 0, 0 ) ### NOT IMPLEMENTED YET
171 172 #---------------------------------------------------------------------------- 173 # Set the size and position of the panel: 174 #---------------------------------------------------------------------------- 175
176 - def position ( self, x, y, dx, dy ):
177 pass ### NOT IMPLEMENTED YET
178 179 #---------------------------------------------------------------------------- 180 # Destroy the panel: 181 #---------------------------------------------------------------------------- 182
183 - def destroy ( self ):
184 pass ### NOT IMPLEMENTED YET
185 186 #-------------------------------------------------------------------------------- 187 # 'TraitSheet' class: 188 #-------------------------------------------------------------------------------- 189
190 -class TraitSheet ( tk.Frame ):
191 192 #----------------------------------------------------------------------------- 193 # Initialize the object: 194 #----------------------------------------------------------------------------- 195
196 - def __init__ ( self, parent, 197 object, 198 traits = None, 199 handler = default_trait_sheet_handler ):
200 tk.Frame.__init__( self, parent ) 201 202 self.object = object 203 self.handler = handler 204 self.tooltip = None 205 206 # If no traits were specified: 207 if traits is None: 208 # Get them from the specified object: 209 traits = object.editable_traits() 210 211 # Try to make sure that we now have either a single TraitGroup, or 212 # a list of TraitGroups: 213 kind = type( traits ) 214 if kind == StringType: 215 # Convert the single trait name into a TraitGroup: 216 traits = TraitGroup( traits, show_border = FALSE ) 217 elif ((kind in basic_sequence_types) or 218 isinstance( traits, TraitGroupList )): 219 if len( traits ) == 0: 220 # Empty trait list, leave the panel empty: 221 return 222 if not isinstance( traits[0], TraitGroup ): 223 # Convert a simple list of trait elements into a single, 224 # TraitGroup, possibly containing multiple items: 225 traits = TraitGroup( show_border = FALSE, *traits ) 226 227 # Create the requested style of trait sheet editor: 228 if isinstance( traits, TraitGroup ): 229 # Single page dialog: 230 self.add_page( traits, self ) 231 else: 232 # Multi-tab notebook: 233 self.add_tabs( traits )
234 235 #----------------------------------------------------------------------------- 236 # Create a tab (i.e. notebook) style trait editor: 237 #----------------------------------------------------------------------------- 238
239 - def add_tabs ( self, traits ):
240 # Create the notebook: 241 nb = Pmw.NoteBook( self ) 242 nb.grid( row = 0, column = 0, sticky = 'new' ) 243 244 count = 0 245 for pg in traits: 246 # Create the new notebook page: 247 page_name = pg.label 248 if page_name is None: 249 count += 1 250 page_name = 'Page %d' % count 251 self.add_page( pg, nb.add( page_name ) ) 252 253 # Size the notebook to fit the pages it contains: 254 nb.setnaturalsize()
255 256 #----------------------------------------------------------------------------- 257 # Create a single trait editor page: 258 #----------------------------------------------------------------------------- 259
260 - def add_page ( self, pg, parent, default_style = 'simple' ):
261 default_style = pg.style or default_style 262 object = pg.object or self.object 263 row_incr = col_incr = 0 264 if pg.orientation == 'horizontal': 265 col_incr = 1 266 else: 267 row_incr = 1 268 show_labels = pg.show_labels_ 269 row = col = 0 270 cols = 1 + show_labels 271 272 for pge in pg.values: 273 if isinstance( pge, TraitGroup ): 274 if pge.show_border_: 275 box = Pmw.Group( parent, tag_text = pge.label or '' ) 276 box.grid( row = row, column = col, sticky = 'new', 277 padx = 4 ) 278 frame = tk.Frame( box.interior() ) 279 frame.grid( row = 0, column = 0, sticky = 'news', 280 padx = 4, pady = 3 ) 281 box.interior().columnconfigure( 0, weight = 1 ) 282 self.add_page( pge, frame, default_style ) 283 else: 284 self.add_page( pge, parent ) 285 if row_incr: 286 parent.columnconfigure( 0, weight = 1 ) 287 row += row_incr 288 col += col_incr 289 else: 290 name = pge.name or ' ' 291 292 if name == '-': 293 tk.Frame( parent, bg = '#A0A0A0' ).grid( 294 row = row, column = 0, columnspan = cols, sticky = 'ew' ) 295 parent.rowconfigure( row, minsize = 9 ) 296 row += 1 297 continue 298 299 if name == ' ': 300 name = '5' 301 if all_digits.match( name ): 302 parent.rowconfigure( row, minsize = int( name ) ) 303 row += 1 304 continue 305 306 editor = pge.editor 307 style = pge.style or default_style 308 pge_object = pge.object or object 309 if editor is None: 310 try: 311 editor = pge_object._base_trait( name ).get_editor() 312 except: 313 pass 314 315 if editor is None: 316 continue 317 label = None 318 if show_labels: 319 label = pge.label_for( object ) 320 self.add_trait( parent, row, object, name, label, editor, 321 style == 'simple' ) 322 parent.columnconfigure( 1, weight = 1 ) 323 324 # Advance to the next row in the grid: 325 row += 1 326 327 # Allocate any extra space to an imaginary row following last one used: 328 parent.rowconfigure( row, weight = 1 )
329 330 #----------------------------------------------------------------------------- 331 # Add a trait to the trait sheet: 332 #----------------------------------------------------------------------------- 333
334 - def add_trait ( self, parent, row, object, trait_name, description, editor, 335 is_simple ):
336 global tooltips_enabled 337 338 col = 0 339 if description is not None: 340 suffix = ':'[ description[-1:] == '?': ] 341 label = tk.Label( parent, 342 text = (description + suffix).capitalize(), 343 anchor = 'e' ) 344 label.grid( row = row, sticky = 'new', padx = 0, pady = 2 ) 345 col = 1 346 desc = object._base_trait( trait_name ).desc 347 if (desc is not None) and tooltips_enabled: 348 if self.tooltip is None: 349 self.tooltip = Pmw.Balloon( self, state = 'balloon' ) 350 self.tooltip.bind( label, 'Specifies ' + desc ) 351 label.bind( '<B2-ButtonRelease>', self.on_toggle_help ) 352 if is_simple: 353 control = editor.simple_editor( object, trait_name, description, 354 self.handler, parent ) 355 control.bind( '<B2-ButtonRelease>', editor.on_restore ) 356 else: 357 control = editor.custom_editor( object, trait_name, description, 358 self.handler, parent ) 359 control.grid( row = row, column = col, sticky = 'ew', padx = 4, pady = 2 ) 360 control.object = object 361 control.trait_name = trait_name 362 control.description = description 363 control.original_value = getattr( object, trait_name ) 364 control.handler = self.handler 365 return control
366 367 #----------------------------------------------------------------------------- 368 # Toggle whether user sees tooltips or not: 369 #----------------------------------------------------------------------------- 370
371 - def on_toggle_help ( self, event ):
372 global tooltips_enabled 373 tooltips_enabled = not tooltips_enabled 374 if self.tooltip is not None: 375 self.tooltip.configure( 376 state = ( 'none', 'balloon' )[ tooltips_enabled ] )
377 378 #-------------------------------------------------------------------------------- 379 # 'tkTraitEditor' class: 380 #-------------------------------------------------------------------------------- 381
382 -class tkTraitEditor ( TraitEditor ):
383 384 #----------------------------------------------------------------------------- 385 # Create a static view of the current value of the 'trait_name' 386 # trait of 'object': 387 #----------------------------------------------------------------------------- 388
389 - def simple_editor ( self, object, trait_name, description, handler, 390 parent ):
391 control = tk.Label( parent, text = self.str( object, trait_name ), 392 relief = tk.SUNKEN ) 393 control.object = object 394 control.trait_name = trait_name 395 control.description = description 396 control.handler = handler 397 control.original_value = getattr( object, trait_name ) 398 control.bind( '<B1-ButtonRelease>', self.on_popup ) 399 control.bind( '<3>', self.on_restore ) 400 return control
401 402 #----------------------------------------------------------------------------- 403 # Invoke the pop-up editor for an object trait: 404 #----------------------------------------------------------------------------- 405
406 - def on_popup ( self, event ):
407 control = event.widget 408 if hasattr( self, 'popup_editor' ): 409 self.popup_editor( control.object, control.trait_name, 410 control.description, control.handler, control )
411 412 #----------------------------------------------------------------------------- 413 # Restore the original value of the object's trait: 414 #----------------------------------------------------------------------------- 415
416 - def on_restore ( self, event ):
417 control = event.widget 418 object = control.object 419 trait_name = control.trait_name 420 old_value = getattr( object, trait_name ) 421 new_value = control.original_value 422 setattr( object, trait_name, new_value ) 423 control.handler.changed( object, trait_name, new_value, old_value, 424 FALSE ) 425 self.update( object, trait_name, control )
426 427 #----------------------------------------------------------------------------- 428 # Update the contents of a previously created viewer control with the new 429 # value of the 'trait_name' trait of 'object': 430 #----------------------------------------------------------------------------- 431
432 - def update ( self, object, trait_name, control ):
433 control.configure( text = self.str( object, trait_name ) )
434 435 #----------------------------------------------------------------------------- 436 # Handle a 'TraitError' exception: 437 #----------------------------------------------------------------------------- 438
439 - def error ( self, description, excp, parent ):
440 mb.showerror( description + ' value error', str( excp ) )
441 442 #-------------------------------------------------------------------------------- 443 # 'TraitEditorText' class: 444 #-------------------------------------------------------------------------------- 445
446 -class TraitEditorText ( tkTraitEditor ):
447 448 #----------------------------------------------------------------------------- 449 # Initialize the object: 450 #----------------------------------------------------------------------------- 451
452 - def __init__ ( self, dic = {}, auto_set = TRUE ):
453 self.dic = dic 454 self.auto_set = auto_set
455 456 #----------------------------------------------------------------------------- 457 # Interactively edit the 'trait_name' text trait of 'object': 458 #----------------------------------------------------------------------------- 459
460 - def popup_editor ( self, object, trait_name, description, handler, 461 control ):
462 while TRUE: 463 value = sd.askstring( 'Prompt', 464 'Enter the new %s value:' % trait_name, 465 initialvalue = getattr( object, trait_name ) ) 466 if value is None: 467 return 468 if self.dic.has_key( value ): 469 value = self.dic[ value ] 470 try: 471 self.set( object, trait_name, value, handler ) 472 self.update( object, trait_name, control ) 473 except TraitError, excp: 474 self.error( description, excp, object.window )
475 476 #----------------------------------------------------------------------------- 477 # Create an in-place editable view of the current value of the 478 # 'trait_name' trait of 'object': 479 #----------------------------------------------------------------------------- 480
481 - def simple_editor ( self, object, trait_name, description, handler, 482 parent ):
483 var = tk.StringVar() 484 var.set( self.str( object, trait_name ) ) 485 control = tk.Entry( parent, textvariable = var ) 486 control.var = var 487 control.value = self.get_value( control ) 488 control.bind( '<Return>', self.on_enter ) 489 control.bind( '<3>', self.on_restore ) 490 if self.auto_set: 491 control.bind( '<KeyRelease>', self.on_key ) 492 return control
493 494 #----------------------------------------------------------------------------- 495 # Update the contents of a previously created viewer control with the new 496 # value of the 'trait' trait of 'object': 497 #----------------------------------------------------------------------------- 498
499 - def update ( self, object, trait_name, control ):
500 control.var.set( self.str( object, trait_name ) ) 501 control.configure( bg = WHITE )
502 503 #----------------------------------------------------------------------------- 504 # Handle the user pressing the 'Enter' key in the edit control: 505 #----------------------------------------------------------------------------- 506
507 - def on_enter ( self, event ):
508 control = event.widget 509 try: 510 self.set( control.object, control.trait_name, 511 self.get_value( control ), control.handler ) 512 except TraitError, excp: 513 self.error( control.description, excp, control )
514 515 #----------------------------------------------------------------------------- 516 # Handle the user releasing a key in the edit control: 517 #----------------------------------------------------------------------------- 518
519 - def on_key ( self, event ):
520 control = event.widget 521 value = self.get_value( control ) 522 if value != control.value: 523 control.value = value 524 try: 525 setattr( control.object, control.trait_name, value ) 526 color = WHITE 527 except: 528 color = '#FFC0C0' 529 control.configure( bg = color )
530 531 #----------------------------------------------------------------------------- 532 # Get the actual value corresponding to what the user typed: 533 #----------------------------------------------------------------------------- 534
535 - def get_value ( self, control ):
536 value = control.var.get().strip() 537 if not self.dic.has_key( value ): 538 return value 539 return self.dic[ value ]
540 541 #-------------------------------------------------------------------------------- 542 # 'TraitEditorEnum' class: 543 #-------------------------------------------------------------------------------- 544
545 -class TraitEditorEnum ( tkTraitEditor ):
546 547 #----------------------------------------------------------------------------- 548 # Initialize the object: 549 #----------------------------------------------------------------------------- 550
551 - def __init__ ( self, values, cols = 1 ):
552 self.cols = cols 553 self.mapped = (type( values ) is DictType) 554 if self.mapped: 555 sorted = values.values() 556 sorted.sort() 557 col = sorted[0].find( ':' ) + 1 558 if col > 0: 559 self.sorted = [ x[ col: ] for x in sorted ] 560 for n, v in values.items(): 561 values[n] = v[ col: ] 562 else: 563 self.sorted = sorted 564 self.values = values 565 else: 566 if not type( values ) in basic_sequence_types: 567 handler = values 568 if isinstance( handler, Trait ): 569 handler = handler.setter 570 if hasattr( handler, 'map' ): 571 values = handler.map.keys() 572 values.sort() 573 else: 574 values = handler.values 575 self.values = [ str( x ) for x in values ]
576 577 #----------------------------------------------------------------------------- 578 # Create an in-place simple view of the current value of the 579 # 'trait_name' trait of 'object': 580 #----------------------------------------------------------------------------- 581
582 - def simple_editor ( self, object, trait_name, description, handler, 583 parent ):
584 delegate = tkDelegate( self.on_value_changed ) 585 values = self.all_values() 586 control = Pmw.ComboBox( parent, 587 dropdown = TRUE, 588 selectioncommand = delegate(), 589 scrolledlist_items = values, 590 listheight = min( 150, len( values ) * 24 ) ) 591 delegate.control = control 592 control.selectitem( self.current_value( object, trait_name ) ) 593 return control
594 595 #---------------------------------------------------------------------------- 596 # Create an in-place custom view of the current value of the 597 # 'trait_name' trait of 'object': 598 #---------------------------------------------------------------------------- 599
600 - def custom_editor ( self, object, trait_name, description, handler, 601 parent ):
602 # Create a panel to hold all of the radio buttons: 603 panel = tk.Frame( parent ) 604 605 # Get the current trait value: 606 cur_value = self.current_value( object, trait_name ) 607 608 # Initialize loop data: 609 values = self.all_values() 610 n = len( values ) 611 cols = self.cols 612 rows = (n + cols - 1) // cols 613 var = tk.StringVar() 614 delegate = tkDelegate( self.on_click, var = var, panel = panel ) 615 incr = [ n // cols ] * cols 616 rem = n % cols 617 for i in range( cols ): 618 incr[i] += (rem > i) 619 incr[-1] = -(reduce( lambda x, y: x + y, incr[:-1], 0 ) - 1) 620 621 # Add the set of all possible choices to the panel: 622 index = 0 623 for i in range( rows ): 624 for j in range( cols ): 625 value = values[ index ] 626 control = tk.Radiobutton( panel, 627 text = value.capitalize(), 628 value = value, 629 variable = var, 630 command = delegate() ) 631 control.grid( row = i, column = j, sticky = 'w' ) 632 if value == cur_value: 633 var.set( value ) 634 index += incr[j] 635 n -= 1 636 if n <= 0: 637 break 638 639 for j in range( cols ): 640 panel.columnconfigure( j, weight = 1 ) 641 642 # Return the panel as the result: 643 return panel
644 645 #---------------------------------------------------------------------------- 646 # Return the set of all possible values: 647 #---------------------------------------------------------------------------- 648
649 - def all_values ( self ):
650 if self.mapped: 651 return self.sorted 652 return self.values
653 654 #---------------------------------------------------------------------------- 655 # Return the current value of the object trait: 656 #---------------------------------------------------------------------------- 657
658 - def current_value ( self, object, trait_name ):
659 if self.mapped: 660 return self.values[ getattr( object, trait_name + '_' ) ] 661 return str( getattr( object, trait_name ) )
662 663 #----------------------------------------------------------------------------- 664 # Handle the user selecting a new value from the combo box: 665 #----------------------------------------------------------------------------- 666
667 - def on_value_changed ( self, delegate, value ):
668 control = delegate.control 669 try: 670 value = int( value ) 671 except: 672 pass 673 self.set( control.object, control.trait_name, value, control.handler )
674 675 #---------------------------------------------------------------------------- 676 # Handle the user clicking one of the 'custom' radio buttons: 677 #---------------------------------------------------------------------------- 678
679 - def on_click ( self, delegate ):
680 panel = delegate.panel 681 value = delegate.var.get() 682 try: 683 value = int( value ) 684 except: 685 pass 686 self.set( panel.object, panel.trait_name, value, panel.handler )
687 688 #-------------------------------------------------------------------------------- 689 # 'TraitEditorImageEnum' class: 690 #-------------------------------------------------------------------------------- 691
692 -class TraitEditorImageEnum ( TraitEditorEnum ):
693 694 #----------------------------------------------------------------------------- 695 # Initialize the object: 696 #----------------------------------------------------------------------------- 697
698 - def __init__ ( self, values, suffix = '', cols = 1, path = None ):
699 TraitEditorEnum.__init__( self, values ) 700 self.suffix = suffix 701 self.cols = cols 702 if type( path ) is ModuleType: 703 path = os.path.join( os.path.dirname( path.__file__ ), 'images' ) 704 self.path = path
705 706 #----------------------------------------------------------------------------- 707 # Interactively edit the 'trait_name' trait of 'object': 708 #----------------------------------------------------------------------------- 709
710 - def popup_editor ( self, object, trait_name, description, handler, 711 control ):
712 TraitEditorImageDialog( object, trait_name, description, 713 control, handler, self )
714 715 #----------------------------------------------------------------------------- 716 # Create an in-place editable view of the current value of the 717 # 'trait_name' trait of 'object': 718 #----------------------------------------------------------------------------- 719
720 - def simple_editor ( self, object, trait_name, description, handler, 721 parent ):
722 control = tk.Button( parent, 723 image = bitmap_cache( 724 self.current_value( object, trait_name ) + 725 self.suffix, self.path ), 726 bg = WHITE ) 727 control.configure( command = tkDelegate( self.on_popup, 728 widget = control )() ) 729 return control
730 731 #---------------------------------------------------------------------------- 732 # Create an in-place custom view of the current value of the 733 # 'trait_name' trait of 'object': 734 #---------------------------------------------------------------------------- 735
736 - def custom_editor ( self, object, trait_name, description, handler, 737 parent ):
738 # Create a panel to hold all of the radio buttons: 739 panel = tk.Frame( parent ) 740 741 # Save the information the event handler needs: 742 panel.object = object 743 panel.trait_name = trait_name 744 panel.handler = handler 745 746 # Add the image buttons to the panel: 747 self.create_image_grid( panel, self.on_click ) 748 749 # Return the panel as the result: 750 return panel
751 752 #---------------------------------------------------------------------------- 753 # Populate a specified window with a grid of image buttons: 754 #---------------------------------------------------------------------------- 755
756 - def create_image_grid ( self, parent, handler ):
757 # Add the set of all possible choices: 758 i = 0 759 cols = self.cols 760 for value in self.all_values(): 761 control = tk.Button( parent, 762 image = bitmap_cache( value + self.suffix, 763 self.path ), 764 command = tkDelegate( handler, 765 parent = parent, 766 value = value )() ) 767 control.grid( row = i // cols, column = i % cols, sticky = 'w', 768 padx = 2, pady = 2 ) 769 i += 1 770 771 parent.columnconfigure( cols, weight = 1 )
772 773 #----------------------------------------------------------------------------- 774 # Update the contents of a previously created viewer control with the new 775 # value of the 'trait' trait of 'object': 776 #----------------------------------------------------------------------------- 777
778 - def update ( self, object, trait_name, control ):
779 control.configure( image = bitmap_cache( 780 self.current_value( object, trait_name ) + 781 self.suffix, self.path ) )
782 783 #---------------------------------------------------------------------------- 784 # Handle the user clicking one of the image buttons: 785 #---------------------------------------------------------------------------- 786
787 - def on_click ( self, delegate ):
788 parent = delegate.parent 789 self.set( parent.object, parent.trait_name, delegate.value, 790 parent.handler )
791 792 #-------------------------------------------------------------------------------- 793 # 'TraitEditorCheckList' class: 794 #-------------------------------------------------------------------------------- 795
796 -class TraitEditorCheckList ( tkTraitEditor ):
797 798 #----------------------------------------------------------------------------- 799 # Initialize the object: 800 #----------------------------------------------------------------------------- 801
802 - def __init__ ( self, values, cols = 1 ):
803 self.cols = cols 804 self.values = values 805 if type( values[0] ) is StringType: 806 self.values = [ ( x, x.capitalize() ) for x in values ] 807 self.mapping = mapping = {} 808 for value, key in self.values: 809 mapping[ key ] = value
810 811 #----------------------------------------------------------------------------- 812 # Create an in-place simple view of the current value of the 813 # 'trait_name' trait of 'object': 814 #----------------------------------------------------------------------------- 815
816 - def simple_editor ( self, object, trait_name, description, handler, 817 parent ):
818 delegate = tkDelegate( self.on_value_changed ) 819 labels = self.all_labels() 820 control = Pmw.ComboBox( parent, 821 dropdown = TRUE, 822 selectioncommand = delegate(), 823 scrolledlist_items = labels, 824 listheight = min( 150, len( labels ) * 24 ) ) 825 delegate.control = control 826 try: 827 control.selectitem( self.all_values().index( 828 self.current_value( object, trait_name )[0] ) ) 829 except: 830 pass 831 return control
832 833 #---------------------------------------------------------------------------- 834 # Create an in-place custom view of the current value of the 835 # 'trait_name' trait of 'object': 836 #---------------------------------------------------------------------------- 837
838 - def custom_editor ( self, object, trait_name, description, handler, 839 parent ):
840 # Create a panel to hold all of the radio buttons: 841 panel = tk.Frame( parent ) 842 843 # Get the current trait value: 844 cur_value = self.current_value( object, trait_name ) 845 846 # Create a sizer to manage the radio buttons: 847 labels = self.all_labels() 848 values = self.all_values() 849 n = len( values ) 850 cols = self.cols 851 rows = (n + cols - 1) // cols 852 incr = [ n // cols ] * cols 853 rem = n % cols 854 for i in range( cols ): 855 incr[i] += (rem > i) 856 incr[-1] = -(reduce( lambda x, y: x + y, incr[:-1], 0 ) - 1) 857 858 # Add the set of all possible choices: 859 index = 0 860 for i in range( rows ): 861 for j in range( cols ): 862 if n > 0: 863 value = values[ index ] 864 var = tk.IntVar() 865 delegate = tkDelegate( self.on_click, 866 var = var, 867 panel = panel, 868 value = value ) 869 control = tk.Checkbutton( panel, 870 text = labels[ index ], 871 variable = var, 872 command = delegate() ) 873 control.grid( row = i, column = j, sticky = 'w' ) 874 var.set( value in cur_value ) 875 index += incr[j] 876 n -= 1 877 if n <= 0: 878 break 879 880 for j in range( cols ): 881 panel.columnconfigure( j, weight = 1 ) 882 883 # Return the panel as the result: 884 return panel
885 886 #---------------------------------------------------------------------------- 887 # Return the set of all possible labels: 888 #---------------------------------------------------------------------------- 889
890 - def all_labels ( self ):
891 return [ x[1] for x in self.values ]
892 893 #---------------------------------------------------------------------------- 894 # Return the set of all possible values: 895 #---------------------------------------------------------------------------- 896
897 - def all_values ( self ):
898 return [ x[0] for x in self.values ]
899 900 #---------------------------------------------------------------------------- 901 # Return whether or not the current value is a string or not: 902 #---------------------------------------------------------------------------- 903
904 - def is_string ( self, object, trait_name ):
905 return (type( getattr( object, trait_name ) ) is StringType)
906 907 #---------------------------------------------------------------------------- 908 # Return the current value of the object trait: 909 #---------------------------------------------------------------------------- 910
911 - def current_value ( self, object, trait_name ):
912 value = getattr( object, trait_name ) 913 if value is None: 914 return [] 915 if type( value ) is not StringType: 916 return value 917 return [ x.strip() for x in value.split( ',' ) ]
918 919 #----------------------------------------------------------------------------- 920 # Handle the user selecting a new value from the combo box: 921 #----------------------------------------------------------------------------- 922
923 - def on_value_changed ( self, delegate, value ):
924 control = delegate.control 925 value = self.mapping[ value ] 926 if not self.is_string( control.object, control.trait_name ): 927 value = [ value ] 928 self.set( control.object, control.trait_name, value, control.handler )
929 930 #---------------------------------------------------------------------------- 931 # Handle the user clicking one of the 'custom' radio buttons: 932 #---------------------------------------------------------------------------- 933
934 - def on_click ( self, delegate ):
935 panel = delegate.panel 936 value = delegate.value 937 cur_value = self.current_value( panel.object, panel.trait_name ) 938 if delegate.var.get(): 939 cur_value.append( value ) 940 else: 941 cur_value.remove( value ) 942 if self.is_string( panel.object, panel.trait_name ): 943 cur_value = ','.join( cur_value ) 944 self.set( panel.object, panel.trait_name, cur_value, panel.handler )
945 946 #-------------------------------------------------------------------------------- 947 # 'TraitEditorBoolean' class: 948 #-------------------------------------------------------------------------------- 949
950 -class TraitEditorBoolean ( tkTraitEditor ):
951 952 #----------------------------------------------------------------------------- 953 # Create an in-place editable view of the current value of the 954 # 'trait_name' trait of 'object': 955 #----------------------------------------------------------------------------- 956
957 - def simple_editor ( self, object, trait_name, description, handler, 958 parent ):
959 var = tk.IntVar() 960 control = tk.Checkbutton( parent, 961 text = '', 962 variable = var, 963 anchor = 'w' ) 964 control.configure( command = tkDelegate( self.on_value_changed, 965 control = control, 966 var = var )() ) 967 try: 968 value = getattr( object, trait_name + '_' ) 969 except: 970 value = getattr( object, trait_name ) 971 var.set( value ) 972 return control
973 974 #----------------------------------------------------------------------------- 975 # Handle the user clicking on the checkbox: 976 #----------------------------------------------------------------------------- 977
978 - def on_value_changed ( self, delegate ):
979 control = delegate.control 980 self.set( control.object, control.trait_name, delegate.var.get(), 981 control.handler )
982 983 #-------------------------------------------------------------------------------- 984 # 'TraitEditorRange class: 985 #-------------------------------------------------------------------------------- 986
987 -class TraitEditorRange ( TraitEditorEnum ):
988 989 #----------------------------------------------------------------------------- 990 # Initialize the object: 991 #----------------------------------------------------------------------------- 992
993 - def __init__ ( self, handler, cols = 1 , auto_set = True):
994 if isinstance( handler, Trait ): 995 handler = handler.setter 996 self.low = handler.low 997 self.high = handler.high 998 self.cols = cols 999 self.auto_set = auto_set 1000 self.is_float = (type( self.low ) is FloatType)
1001 1002 #----------------------------------------------------------------------------- 1003 # Create an in-place editable view of the current value of the 1004 # 'trait_name' trait of 'object': 1005 #----------------------------------------------------------------------------- 1006
1007 - def simple_editor ( self, object, trait_name, description, handler, 1008 parent ):
1009 value = self.str( object, trait_name ) 1010 var = tk.StringVar() 1011 var.set(value) 1012 1013 if self.is_float or abs( self.high - self.low ) > 1000: 1014 control = tk.Entry( parent, textvariable = var ) 1015 control.var = var 1016 control.value = value 1017 control.bind( '<Return>', self.on_enter ) 1018 control.bind( '<KeyRelease>', self.on_key ) 1019 control.bind( '<3>', self.on_restore ) 1020 else: 1021 control = Pmw.Counter( parent ) 1022 control.configure( datatype = { 1023 'counter': self.on_value_changed, 1024 'control': control, 1025 'min_value': self.low, 1026 'max_value': self.high } 1027 ) 1028 control._counterEntry.setentry( str( value ) ) 1029 1030 return control
1031 1032 #---------------------------------------------------------------------------- 1033 # Create an in-place custom view of the current value of the 1034 # 'trait_name' trait of 'object': 1035 #---------------------------------------------------------------------------- 1036
1037 - def custom_editor ( self, object, trait_name, description, handler, 1038 parent ):
1039 if abs( self.high - self.low ) > 15: 1040 return self.simple_editor( object, trait_name, description, 1041 handler, parent ) 1042 return TraitEditorEnum.custom_editor( self, object, trait_name, 1043 description, handler, parent )
1044 1045 #---------------------------------------------------------------------------- 1046 # Return the set of all possible values: 1047 #---------------------------------------------------------------------------- 1048
1049 - def all_values ( self ):
1050 return [ str( x ) for x in xrange( self.low, self.high + 1 ) ]
1051 1052 #---------------------------------------------------------------------------- 1053 # Return the current value of the object trait: 1054 #---------------------------------------------------------------------------- 1055
1056 - def current_value ( self, object, trait_name ):
1057 return str( getattr( object, trait_name ) )
1058 1059 #----------------------------------------------------------------------------- 1060 # Update the contents of a previously created viewer control with the new 1061 # value of the 'trait' trait of 'object': 1062 #----------------------------------------------------------------------------- 1063
1064 - def update ( self, object, trait_name, control ):
1065 if isinstance( control, tk.Entry ): 1066 control.var.set( self.str( object, trait_name ) ) 1067 control.configure( bg = WHITE ) 1068 else: 1069 control.entryfield.configure( 1070 text = str( getattr( object, trait_name ) ) )
1071 1072 #----------------------------------------------------------------------------- 1073 # Handle the user selecting a new value from the spin control: 1074 #----------------------------------------------------------------------------- 1075
1076 - def on_value_changed ( self, value, factor, incr, 1077 control = None, 1078 min_value = None, 1079 max_value = None ):
1080 value = min( max_value, 1081 max( min_value, int( value ) + factor * incr ) ) 1082 try: 1083 self.set( control.object, control.trait_name, value, 1084 control.handler ) 1085 return str( value ) 1086 except: 1087 raise ValueError
1088 1089 #----------------------------------------------------------------------------- 1090 # Handle the user pressing the 'Enter' key in thfvalue = getattr( object, trait_name )e edit control: 1091 #----------------------------------------------------------------------------- 1092
1093 - def on_enter ( self, event ):
1094 control = event.widget 1095 try: 1096 self.set( control.object, control.trait_name, 1097 control.var.get().strip(), control.handler ) 1098 except TraitError, excp: 1099 self.error( control.description, excp, control )
1100 1101 #----------------------------------------------------------------------------- 1102 # Handle the user releasing a key in the edit control: 1103 #----------------------------------------------------------------------------- 1104
1105 - def on_key ( self, event ):
1106 control = event.widget 1107 value = control.var.get() 1108 if value != control.value: 1109 control.value = value 1110 try: 1111 setattr( control.object, control.trait_name, value ) 1112 color = WHITE 1113 except: 1114 color = '#FFC0C0' 1115 control.configure( bg = color )
1116 1117 #------------------------------------------------------------------------------- 1118 # 'TraitEditorImageDialog' class: 1119 #------------------------------------------------------------------------------- 1120
1121 -class TraitEditorImageDialog ( tk.Toplevel ):
1122 1123 #----------------------------------------------------------------------------- 1124 # Initialize the object: 1125 #----------------------------------------------------------------------------- 1126
1127 - def __init__ ( self, object, trait_name, description, control, handler, 1128 editor ):
1129 tk.Toplevel.__init__( self, control ) 1130 self.title( 'Choose ' + description ) 1131 self.bind( '<Escape>', self.on_close_key ) 1132 1133 # Initialize instance data: 1134 self.object = object 1135 self.trait_name = trait_name 1136 self.control = control 1137 self.handler = handler 1138 self.editor = editor 1139 1140 # Create the grid of image buttons: 1141 editor.create_image_grid( self, self.on_click )
1142 1143 #---------------------------------------------------------------------------- 1144 # Handle the user hitting the 'Esc'ape key: 1145 #---------------------------------------------------------------------------- 1146
1147 - def on_close_key ( self ):
1148 self.destroy()
1149 1150 #---------------------------------------------------------------------------- 1151 # Handle the user clicking one of the choice buttons: 1152 #---------------------------------------------------------------------------- 1153
1154 - def on_click ( self, delegate ):
1155 self.editor.set( self.object, self.trait_name, 1156 delegate.value, self.handler ) 1157 self.editor.update( self.object, self.trait_name, self.control ) 1158 self.destroy()
1159 1160 #-------------------------------------------------------------------------------- 1161 # Convert an image file name to a cached bitmap: 1162 #-------------------------------------------------------------------------------- 1163 1164 # Bitmap cache dictionary (indexed by filename): 1165 _bitmap_cache = {} 1166 1167 ### NOTE: This needs major improvements: 1168 app_path = None 1169 traits_path = None 1170
1171 -def bitmap_cache ( name, path = None ):
1172 global app_path, traits_path 1173 if path is None: 1174 if traits_path is None: 1175 import traits 1176 traits_path = os.path.join( 1177 os.path.dirname( traits.__file__ ), 'images' ) 1178 path = traits_path 1179 elif path == '': 1180 if app_path is None: 1181 app_path = os.path.join( os.path.dirname( sys.argv[0] ), 1182 '..', 'images' ) 1183 path = app_path 1184 filename = os.path.abspath( os.path.join( path, 1185 name.replace( ' ', '_' ).lower() + '.gif' ) ) 1186 bitmap = _bitmap_cache.get( filename ) 1187 if bitmap is None: 1188 bitmap = _bitmap_cache[ filename ] = tk.PhotoImage( file = filename ) 1189 return bitmap
1190 1191 #------------------------------------------------------------------------------- 1192 # Standard colors: 1193 #------------------------------------------------------------------------------- 1194 1195 standard_colors = { 1196 'aquamarine': '#70DB93', 1197 'black': '#000000', 1198 'blue': '#0000FF', 1199 'blue violet': '#9F5F9F', 1200 'brown': '#A52A2A', 1201 'cadet blue': '#5F9F9F', 1202 'coral': '#FF7F00', 1203 'cornflower blue': '#42426F', 1204 'cyan': '#00FFFF', 1205 'dark green': '#2F4F2F', 1206 'dark grey': '#2F2F2F', 1207 'dark olive green': '#4F4F2F', 1208 'dark orchid': '#9932CC', 1209 'dark slate blue': '#6B238E', 1210 'dark slate grey': '#2F4F4F', 1211 'dark turquoise': '#7093DB', 1212 'dim grey': '#545454', 1213 'firebrick': '#8E2323', 1214 'forest green': '#238E23', 1215 'gold': '#CC7F32', 1216 'goldenrod': '#DBDB70', 1217 'green': '#00FF00', 1218 'green yellow': '#93DB70', 1219 'grey': '#808080', 1220 'indian red': '#4F2F2F', 1221 'khaki': '#9F9F5F', 1222 'light blue': '#BFD8D8', 1223 'light grey': '#C0C0C0', 1224 'light steel': '#000000', 1225 'lime green': '#32CC32', 1226 'magenta': '#FF00FF', 1227 'maroon': '#8E236B', 1228 'medium aquamarine': '#32CC99', 1229 'medium blue': '#3232CC', 1230 'medium forest green': '#6B8E23', 1231 'medium goldenrod': '#EAEAAD', 1232 'medium orchid': '#9370DB', 1233 'medium sea green': '#426F42', 1234 'medium slate blue': '#7F00FF', 1235 'medium spring green': '#7FFF00', 1236 'medium turquoise': '#70DBDB', 1237 'medium violet red': '#DB7093', 1238 'midnight blue': '#2F2F4F', 1239 'navy': '#23238E', 1240 'orange': '#CC3232', 1241 'orange red': '#FF007F', 1242 'orchid': '#DB70DB', 1243 'pale green': '#8FBC8F', 1244 'pink': '#BC8FEA', 1245 'plum': '#EAADEA', 1246 'purple': '#B000FF', 1247 'red': '#FF0000', 1248 'salmon': '#6F4242', 1249 'sea green': '#238E6B', 1250 'sienna': '#8E6B23', 1251 'sky blue': '#3299CC', 1252 'slate blue': '#007FFF', 1253 'spring green': '#00FF7F', 1254 'steel blue': '#236B8E', 1255 'tan': '#DB9370', 1256 'thistle': '#D8BFD8', 1257 'turquoise': '#ADEAEA', 1258 'violet': '#4F2F4F', 1259 'violet red': '#CC3299', 1260 'wheat': '#D8D8BF', 1261 'white': '#FFFFFF', 1262 'yellow': '#FFFF00', 1263 'yellow green': '#99CC32' 1264 } 1265 1266 #-------------------------------------------------------------------------------- 1267 # Convert a number into a Tkinter color string: 1268 #-------------------------------------------------------------------------------- 1269
1270 -def num_to_color ( object, name, value ):
1271 if type( value ) is StringType: 1272 if ((len( value ) == 7) and (value[0] == '#') and 1273 (eval( '0x' + value[1:] ) >= 0)): 1274 return value 1275 raise TraitError 1276 return '#%06X' % int( value )
1277 1278 num_to_color.info = ("a string of the form '#RRGGBB' or a number, which in " 1279 "hex is of the form 0xRRGGBB, where RR is red, GG is " 1280 "green, and BB is blue") 1281 1282 #-------------------------------------------------------------------------------- 1283 # 'TraitEditorColor' class: 1284 #-------------------------------------------------------------------------------- 1285
1286 -class TraitEditorColor ( tkTraitEditor ):
1287 1288 #----------------------------------------------------------------------------- 1289 # Interactively edit the 'trait_name' color trait of 'object': 1290 #----------------------------------------------------------------------------- 1291
1292 - def popup_editor ( self, object, trait_name, description, handler, 1293 control ):
1294 color = cc.askcolor( self.to_tk_color( object, 1295 trait_name ) )[1].upper() 1296 if color is not None: 1297 self.set( object, trait_name, self.from_tk_color( color ), 1298 handler ) 1299 self.update( object, trait_name, control )
1300 1301 #----------------------------------------------------------------------------- 1302 # Create a static view of the current value of the 'trait_name' 1303 # trait of 'object': 1304 #----------------------------------------------------------------------------- 1305
1306 - def simple_editor ( self, object, trait_name, description, handler, 1307 parent ):
1308 control = tkTraitEditor.simple_editor( self, object, trait_name, 1309 description, handler, parent ) 1310 self.update_color( object, trait_name, control ) 1311 return control
1312 1313 #---------------------------------------------------------------------------- 1314 # Create an in-place custom view of the current value of the 1315 # 'trait_name' trait of 'object': 1316 #---------------------------------------------------------------------------- 1317
1318 - def custom_editor ( self, object, trait_name, description, handler, 1319 parent ):
1320 # Create a panel to hold all of the buttons: 1321 panel = tk.Frame( parent ) 1322 panel.color = self.simple_editor( object, trait_name, description, 1323 handler, panel ) 1324 panel.color.grid( row = 0, column = 0, sticky = 'nesw' ) 1325 1326 # Add all of the color choice buttons: 1327 panel2 = tk.Frame( panel ) 1328 for i in range( len( color_samples ) ): 1329 tk.Button( panel2, 1330 bg = color_samples[i], 1331 font = 'Helvetica 1', 1332 command = tkDelegate( self.on_click, 1333 panel = panel, 1334 color = color_samples[i] )() ).grid( 1335 row = i // 12, column = i % 12, sticky = 'ew' ) 1336 for i in range( 12 ): 1337 panel2.columnconfigure( i, weight = 1 ) 1338 1339 panel2.grid( row = 0, column = 1, sticky = 'nesw', padx = 4 ) 1340 panel.columnconfigure( 0, minsize = 70 ) 1341 panel.columnconfigure( 1, weight = 1 ) 1342 1343 # Return the panel as the result: 1344 return panel
1345 1346 #---------------------------------------------------------------------------- 1347 # Handle the user clicking one of the 'custom' color buttons: 1348 #---------------------------------------------------------------------------- 1349
1350 - def on_click ( self, delegate ):
1351 panel = delegate.panel 1352 self.set( panel.object, panel.trait_name, 1353 self.from_tk_color( delegate.color ), 1354 panel.handler ) 1355 self.update( panel.object, panel.trait_name, panel.color )
1356 1357 #----------------------------------------------------------------------------- 1358 # Update the contents of a previously created viewer control with the new 1359 # value of the 'trait' trait of 'object': 1360 #----------------------------------------------------------------------------- 1361
1362 - def update ( self, object, trait_name, control ):
1363 tkTraitEditor.update( self, object, trait_name, control ) 1364 self.update_color( object, trait_name, control )
1365 1366 #------------------------------------------------------------------------------- 1367 # Update the foreground/background colors of the control widget: 1368 #------------------------------------------------------------------------------- 1369
1370 - def update_color ( self, object, trait_name, control ):
1371 bg_color = self.to_tk_color( object, trait_name ) 1372 red = eval( '0x%s' % bg_color[1:3] ) 1373 green = eval( '0x%s' % bg_color[3:5] ) 1374 blue = eval( '0x%s' % bg_color[5:7] ) 1375 fg_color = ( '#FFFFFF', '#000000' )[ 1376 (red > 192) or(green > 192) or(blue > 192) ] 1377 control.configure( bg = bg_color, fg = fg_color )
1378 1379 #------------------------------------------------------------------------------- 1380 # Get the Tkinter color equivalent of the object trait: 1381 #------------------------------------------------------------------------------- 1382
1383 - def to_tk_color ( self, object, trait_name ):
1384 try: 1385 cur_color = getattr( object, trait_name + '_' ) 1386 except: 1387 cur_color = getattr( object, trait_name ) 1388 if cur_color is None: 1389 return WHITE 1390 return cur_color
1391 1392 #------------------------------------------------------------------------------- 1393 # Get the application equivalent of a Tkinter value: 1394 #------------------------------------------------------------------------------- 1395
1396 - def from_tk_color ( self, color ):
1397 return color
1398 1399 #------------------------------------------------------------------------------- 1400 # Define Tkinter specific color traits: 1401 #------------------------------------------------------------------------------- 1402 1403 # Create a singleton color editor: 1404 color_editor = TraitEditorColor() 1405 1406 # Color traits: 1407 color_trait = Trait( 'white', standard_colors, num_to_color, 1408 editor = color_editor ) 1409 clear_color_trait = Trait( 'clear', None, standard_colors, 1410 { 'clear': None }, num_to_color, 1411 editor = color_editor ) 1412 1413 #-------------------------------------------------------------------------------- 1414 # Convert a string into a valid Tkinter font (if possible): 1415 #-------------------------------------------------------------------------------- 1416 1417 slant_types = [ 'roman', 'italic' ] 1418 weight_types = [ 'bold' ] 1419 font_noise = [ 'pt', 'point', 'family' ] 1420
1421 -def str_to_font ( object, name, value ):
1422 try: 1423 size = 10 1424 family = [] 1425 slant = 'roman' 1426 weight = 'normal' 1427 underline = overstrike = 0 1428 for word in value.split(): 1429 lword = word.lower() 1430 if lword in slant_types: 1431 slant = lword 1432 elif lword in weight_types: 1433 weight = lword 1434 elif lword == 'underline': 1435 underline = 1 1436 elif lword == 'overstrike': 1437 overstrike = 1 1438 elif lword not in font_noise: 1439 try: 1440 size = int( lword ) 1441 except: 1442 family.append( word ) 1443 if len( family ) == 0: 1444 family = [ 'Helvetica' ] 1445 return tkFont.Font( family = ' '.join( family ), 1446 size = size, 1447 weight = weight, 1448 slant = slant, 1449 underline = underline, 1450 overstrike = overstrike ) 1451 except: 1452 pass 1453 raise TraitError
1454 1455 str_to_font.info = ( "a string describing a font (e.g. '12 pt bold italic' " 1456 "or 'Arial bold 14 point')" ) 1457 1458 #-------------------------------------------------------------------------------- 1459 # 'TraitEditorFont' class: 1460 #-------------------------------------------------------------------------------- 1461
1462 -class TraitEditorFont ( tkTraitEditor ):
1463 1464 #----------------------------------------------------------------------------- 1465 # Create a static view of the current value of the 'trait_name' 1466 # trait of 'object': 1467 #----------------------------------------------------------------------------- 1468
1469 - def simple_editor ( self, object, trait_name, description, handler, 1470 parent ):
1471 control = tkTraitEditor.simple_editor( self, object, trait_name, 1472 description, handler, parent ) 1473 control.is_custom = FALSE 1474 self.update_font( object, trait_name, control ) 1475 return control
1476 1477 #---------------------------------------------------------------------------- 1478 # Create an in-place custom view of the current value of the 1479 # 'trait_name' trait of 'object': 1480 #---------------------------------------------------------------------------- 1481
1482 - def custom_editor ( self, object, trait_name, description, handler, 1483 parent ):
1484 # Create a panel to hold all of the buttons: 1485 panel = tk.Frame( parent ) 1486 1487 # Add the standard font control: 1488 font = panel.font = self.simple_editor( object, trait_name, 1489 description, handler, panel ) 1490 font.configure( anchor = 'w' ) 1491 font.is_custom = TRUE 1492 font.grid( row = 0, column = 0, columnspan = 2, sticky = 'ew', pady = 3 ) 1493 1494 # Add all of the font choice controls: 1495 delegate = tkDelegate( self.on_value_changed, panel = panel ) 1496 values = self.all_facenames() 1497 panel.facename = control = Pmw.ComboBox( panel, 1498 dropdown = TRUE, 1499 selectioncommand = delegate(), 1500 scrolledlist_items = values, 1501 listheight = min( 150, len( values ) * 24 ) ) 1502 control.grid( row = 1, column = 0 ) 1503 1504 panel.pointsize = control = Pmw.ComboBox( panel, 1505 dropdown = TRUE, 1506 selectioncommand = delegate(), 1507 scrolledlist_items = point_sizes ) 1508 control.grid( row = 1, column = 1, padx = 4 ) 1509 1510 # Initialize the control's with the object's current trait value: 1511 self.update_font( object, trait_name, font ) 1512 1513 # Return the panel as the result: 1514 return panel
1515 1516 #----------------------------------------------------------------------------- 1517 # Update the contents of a previously created viewer control with the new 1518 # value of the 'trait' trait of 'object': 1519 #----------------------------------------------------------------------------- 1520
1521 - def update ( self, object, trait_name, control ):
1522 tkTraitEditor.update( self, object, trait_name, control ) 1523 self.update_font( object, trait_name, control )
1524 1525 #------------------------------------------------------------------------------- 1526 # Update the font of the control widget: 1527 #------------------------------------------------------------------------------- 1528
1529 - def update_font ( self, object, trait_name, control ):
1530 cur_font = self.to_tk_font( object, trait_name ) 1531 size = cur_font.cget( 'size' ) 1532 if control.is_custom: 1533 panel = control.master 1534 try: 1535 panel.facename.selectitem( cur_font.cget( 'family' ) ) 1536 except: 1537 panel.facename.selectitem( 0 ) 1538 try: 1539 panel.pointsize.selectitem( size ) 1540 except: 1541 panel.pointsize.selectitem( '10' ) 1542 cur_font.configure( size = min( 10, size ) ) 1543 control.configure( font = cur_font )
1544 1545 #----------------------------------------------------------------------------- 1546 # Return the text representation of the 'trait' trait of 'object': 1547 #----------------------------------------------------------------------------- 1548
1549 - def str ( self, object, trait_name ):
1550 font = getattr( object, trait_name ) 1551 size = font.cget( 'size' ) 1552 family = font.cget( 'family' ) 1553 slant = font.cget( 'slant' ) 1554 weight = font.cget( 'weight' ) 1555 return '%s point %s %s %s' % ( size, family, slant.capitalize(), 1556 weight.capitalize() )
1557 1558 #---------------------------------------------------------------------------- 1559 # Return a list of all available font facenames: 1560 #---------------------------------------------------------------------------- 1561
1562 - def all_facenames ( self ):
1563 return ( 'Courier', 1564 'Times', 1565 'Helvetica', 1566 'Arial', 1567 'Verdana' )
1568 1569 #---------------------------------------------------------------------------- 1570 # Handle the user selecting a new facename or point size: 1571 #---------------------------------------------------------------------------- 1572
1573 - def on_value_changed ( self, delegate, unused ):
1574 panel = delegate.panel 1575 size = panel.pointsize.get() 1576 family = panel.facename.get() 1577 font = tkFont.Font( family = family, size = size ) 1578 self.set( panel.object, panel.trait_name, 1579 self.from_tk_font( font ), panel.handler ) 1580 self.update( panel.object, panel.trait_name, panel.font )
1581 1582 #------------------------------------------------------------------------------- 1583 # Return a Tkinter Font object corresponding to a specified object's font 1584 # trait: 1585 #------------------------------------------------------------------------------- 1586
1587 - def to_tk_font ( self, object, trait_name ):
1588 return getattr( object, trait_name )
1589 1590 #------------------------------------------------------------------------------- 1591 # Get the application equivalent of a Tkinter Font value: 1592 #------------------------------------------------------------------------------- 1593
1594 - def from_tk_font ( self, font ):
1595 return font
1596 1597 #------------------------------------------------------------------------------- 1598 # Define a Tkinter specific font trait: 1599 #------------------------------------------------------------------------------- 1600 1601 font_trait = Trait( 'Arial 10', tkFont.Font, str_to_font, 1602 editor = TraitEditorFont() ) 1603 1604 #------------------------------------------------------------------------------- 1605 # 'tkDelegate' class: 1606 #------------------------------------------------------------------------------- 1607
1608 -class tkDelegate:
1609 1610 #---------------------------------------------------------------------------- 1611 # Initialize the object: 1612 #---------------------------------------------------------------------------- 1613
1614 - def __init__ ( self, delegate = None, **kw ):
1615 self.delegate = delegate 1616 for name, value in kw.items(): 1617 setattr( self, name, value )
1618 1619 #---------------------------------------------------------------------------- 1620 # Return the handle method for the delegate: 1621 #---------------------------------------------------------------------------- 1622
1623 - def __call__ ( self ):
1624 return self.on_event
1625 1626 #---------------------------------------------------------------------------- 1627 # Handle an event: 1628 #---------------------------------------------------------------------------- 1629
1630 - def on_event ( self, *args ):
1631 self.delegate( self, *args )
1632