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

Source Code for Module pydrizzle.traits102.wxtrait_sheet

   1  #------------------------------------------------------------------------------- 
   2  # 
   3  #  Define a wxPython based trait sheet mechanism for visually editing the 
   4  #  values of traits. 
   5  # 
   6  #  Written by: David C. Morrill 
   7  # 
   8  #  Date: 07/10/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  from string      import ascii_lowercase 
  24  from wxPython    import wx 
  25  from wxmenu      import MakeMenu 
  26  from traits      import Trait, HasTraits, TraitError, HasDynamicTraits, \ 
  27                          trait_editors 
  28  from trait_sheet import TraitEditor, TraitSheetHandler, TraitMonitor, \ 
  29                          TraitGroup, TraitGroupList, default_trait_sheet_handler 
  30  from types       import DictType, ListType, TupleType, ModuleType, \ 
  31                          StringType, FloatType 
  32  from math        import log10 
  33   
  34  #------------------------------------------------------------------------------- 
  35  #  Module initialization: 
  36  #------------------------------------------------------------------------------- 
  37   
  38  trait_editors( __name__ ) 
  39   
  40  #------------------------------------------------------------------------------- 
  41  #  Constants: 
  42  #------------------------------------------------------------------------------- 
  43   
  44  # Basic sequence types: 
  45  basic_sequence_types = [ ListType, TupleType ] 
  46   
  47  # Standard width of an image bitmap: 
  48  standard_bitmap_width = 120 
  49   
  50  # Standard color samples: 
  51  color_choices = ( 0, 128, 192, 255 ) 
  52  color_samples = [ None ] * 48 
  53  i             = 0 
  54  for r in color_choices: 
  55      for g in color_choices: 
  56          for b in ( 0, 128, 255 ): 
  57              color_samples[i] = wx.wxColour( r, g, b ) 
  58              i += 1 
  59   
  60  # List of available font facenames: 
  61  facenames = None 
  62   
  63  # Standard font point sizes: 
  64  point_sizes = [ 
  65     '8',  '9', '10', '11', '12', '14', '16', '18', 
  66    '20', '22', '24', '26', '28', '36', '48', '72' 
  67  ] 
  68   
  69  # Global switch governing whether or not tooltips are displayed in trait 
  70  # sheet dialogs: 
  71  tooltips_enabled = True 
  72   
  73  # Pattern of all digits: 
  74  all_digits = re.compile( r'\d+' ) 
  75   
  76  # Color used to highlight input errors: 
  77  error_color = wx.wxColour( 255, 192, 192 ) 
  78   
  79  # Width of a scrollbar: 
  80  scrollbar_dx = wx.wxSystemSettings_GetSystemMetric( wx.wxSYS_VSCROLL_X ) 
  81   
  82  # Screen size: 
  83  screen_dx    = wx.wxSystemSettings_GetSystemMetric( wx.wxSYS_SCREEN_X ) 
  84  screen_dy    = wx.wxSystemSettings_GetSystemMetric( wx.wxSYS_SCREEN_Y ) 
  85   
  86  #------------------------------------------------------------------------------- 
  87  #  Position one window near another: 
  88  #------------------------------------------------------------------------------- 
  89   
90 -def position_near ( origin, target ):
91 x, y = origin.ClientToScreenXY( 0, 0 ) 92 y -= 30 # Approximate adjustment for window title bar 93 dx, dy = target.GetSizeTuple() 94 if (x + dx) > screen_dx: 95 x = screen_dx - dx 96 if x < 0: 97 x = 0 98 if (y + dy) > screen_dy: 99 y = screen_dy - dy 100 if y < 0: 101 y = 0 102 target.SetPosition( wx.wxPoint( x, y ) )
103 104 #------------------------------------------------------------------------------- 105 # Initialize an editor control: 106 #------------------------------------------------------------------------------- 107
108 -class Undefined: pass
109
110 -def init_control ( control, object, trait_name, description, handler, 111 original_value = Undefined ):
112 control.object = object 113 control.trait_name = trait_name 114 control.description = description 115 control.handler = handler 116 if original_value is Undefined: 117 control.original_value = getattr( object, trait_name ) 118 else: 119 control.original_value = original_value
120 121 #------------------------------------------------------------------------------- 122 # 'TraitSheetAppHandler' class: 123 #------------------------------------------------------------------------------- 124
125 -class TraitSheetAppHandler ( TraitSheetHandler ):
126 127 #---------------------------------------------------------------------------- 128 # Initialize the object: 129 #---------------------------------------------------------------------------- 130
131 - def __init__ ( self, app ):
132 self.app = app 133 self.modified = False
134 135 #---------------------------------------------------------------------------- 136 # Notification that a trait sheet has been closed: 137 #---------------------------------------------------------------------------- 138
139 - def close ( self, trait_sheet, object ):
140 rc = True 141 if self.modified: 142 dlg = wx.wxMessageDialog( trait_sheet, 143 'Changes have been made.\nDiscard changes?', 144 '%s Traits' % object.__class__.__name__ ) 145 result = dlg.ShowModal() 146 dlg.Destroy() 147 rc = (result == wx.wxID_OK) 148 return rc
149 150 #---------------------------------------------------------------------------- 151 # Notification that a trait sheet has modified a trait of its 152 # associated object: 153 #---------------------------------------------------------------------------- 154
155 - def changed ( self, object, trait_name, new_value, old_value, is_set ):
156 self.modified = True 157 self.save_button.Enable( True )
158 159 #---------------------------------------------------------------------------- 160 # Create extra content to add to the trait sheet: 161 #---------------------------------------------------------------------------- 162
163 - def init ( self, trait_sheet, object ):
164 self.sheet = trait_sheet 165 vsizer = wx.wxBoxSizer( wx.wxVERTICAL ) 166 hsizer = wx.wxBoxSizer( wx.wxHORIZONTAL ) 167 vsizer.Add( wx.wxStaticLine( trait_sheet, -1 ), 1, 168 wx.wxEXPAND | wx.wxTOP, 4 ) 169 vsizer.Add( hsizer, 0, wx.wxALIGN_RIGHT ) 170 self.save_button = button = wx.wxButton( trait_sheet, -1, 171 'Save changes' ) 172 hsizer.Add( button, 0, wx.wxALL, 4 ) 173 wx.EVT_BUTTON( trait_sheet, button.GetId(), self.save ) 174 button.Enable( False ) 175 button = wx.wxButton( trait_sheet, -1, 'Cancel' ) 176 hsizer.Add( button, 0, wx.wxALL, 4 ) 177 wx.EVT_BUTTON( trait_sheet, button.GetId(), trait_sheet.close_page ) 178 return vsizer
179 180 #---------------------------------------------------------------------------- 181 # Handle the user requesting that all changes to the object be saved: 182 #---------------------------------------------------------------------------- 183
184 - def save ( self, event ):
185 self.modified = False 186 self.app.save_ok = True 187 self.sheet.close_page()
188 189 #------------------------------------------------------------------------------- 190 # 'TraitSheetApp' class: 191 #------------------------------------------------------------------------------- 192
193 -class TraitSheetApp ( wx.wxApp ):
194 195 #---------------------------------------------------------------------------- 196 # Initialize the object: 197 #---------------------------------------------------------------------------- 198
199 - def __init__ ( self, object, traits = None ):
200 self.object = object 201 self.traits = traits 202 self.save_ok = False 203 wx.wxInitAllImageHandlers() 204 wx.wxApp.__init__( self, 1, 'debug.log' ) 205 self.MainLoop()
206 207 #---------------------------------------------------------------------------- 208 # Handle application initialization: 209 #---------------------------------------------------------------------------- 210
211 - def OnInit ( self ):
212 self.SetTopWindow( TraitSheetDialog( self.object, self.traits, 213 TraitSheetAppHandler( self ) ) ) 214 return True
215 216 #------------------------------------------------------------------------------- 217 # 'TraitSheetDialog' class: 218 #------------------------------------------------------------------------------- 219
220 -class TraitSheetDialog ( wx.wxDialog ):
221 222 #---------------------------------------------------------------------------- 223 # Initialize the object: 224 #---------------------------------------------------------------------------- 225
226 - def __init__ ( self, object, 227 traits = None, 228 handler = default_trait_sheet_handler, 229 parent = None, 230 title = None ):
231 if title is None: 232 title = '%s Traits' % object.__class__.__name__ 233 wx.wxDialog.__init__( self, parent, -1, title ) 234 wx.EVT_CLOSE( self, self.close_page ) 235 wx.EVT_CHAR( self, self.on_key ) 236 237 self.object = object 238 self.handler = handler 239 240 # Create the actual trait sheet panel: 241 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 242 sw = wx.wxScrolledWindow( self ) 243 trait_sheet = TraitSheet( sw, object, traits, handler ) 244 sizer.Add( trait_sheet, 0, wx.wxALL, 4 ) 245 tsdx, tsdy = trait_sheet.GetSizeTuple() 246 tsdx += 8 247 tsdy += 8 248 extra = handler.init( self, object ) 249 if extra is not None: 250 sizer.Add( extra, 1, wx.wxEXPAND ) 251 252 max_dy = (2 * screen_dy) // 3 253 sw.SetAutoLayout( True ) 254 sw.SetSizer( sizer ) 255 sw.SetSize( wx.wxSize( tsdx + ((tsdy > max_dy) * scrollbar_dx), 256 min( tsdy, max_dy ) ) ) 257 sw.SetScrollRate( 0, 1 ) 258 259 sw_sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 260 sw_sizer.Add( sw ) 261 sw_sizer.Fit( self ) 262 263 # Find a nice place on the screen to display the trait sheet so 264 # that it overlays the object as little as possible: 265 if not handler.position( self, object ): 266 if parent is None: 267 self.Centre( wx.wxBOTH ) 268 else: 269 position_near( parent, self ) 270 271 self.Show( True )
272 273 #---------------------------------------------------------------------------- 274 # Close the trait sheet window: 275 #---------------------------------------------------------------------------- 276
277 - def close_page ( self, event = None ):
278 if self.handler.close( self, self.object ): 279 self.Destroy()
280 281 #---------------------------------------------------------------------------- 282 # Handle the user hitting the 'Esc'ape key: 283 #---------------------------------------------------------------------------- 284
285 - def on_key ( self, event ):
286 if event.GetKeyCode() == 0x1B: 287 self.on_close_page( event )
288 289 #------------------------------------------------------------------------------- 290 # 'TraitPanel' class: 291 #------------------------------------------------------------------------------- 292
293 -class TraitPanel ( wx.wxPanel ):
294 295 #---------------------------------------------------------------------------- 296 # Initialize the object: 297 #---------------------------------------------------------------------------- 298
299 - def __init__ ( self, parent ):
300 # wx.wxPanel.__init__( self, parent, -1, style = wx.wxSUNKEN_BORDER ) 301 wx.wxPanel.__init__( self, parent, -1 ) 302 self.SetSizer( wx.wxBoxSizer( wx.wxVERTICAL ) ) 303 self._size = None 304 self._need_layout = True
305 306 #---------------------------------------------------------------------------- 307 # Add a TraitSheet to the panel: 308 #---------------------------------------------------------------------------- 309
310 - def add ( self, sheet ):
311 self.GetSizer().Add( sheet, 0, wx.wxEXPAND ) 312 self._size = None 313 self._need_layout = True
314 315 #---------------------------------------------------------------------------- 316 # Remove a TraitSheet from the panel: 317 #---------------------------------------------------------------------------- 318
319 - def remove ( self, sheet ):
320 sheet.Destroy() 321 self._size = None 322 self._need_layout = True
323 324 #---------------------------------------------------------------------------- 325 # Get the size of the panel: 326 #---------------------------------------------------------------------------- 327
328 - def size ( self ):
329 if self._size is None: 330 self._size = self.GetSizer().GetMinSize() 331 return self._size
332 333 #---------------------------------------------------------------------------- 334 # Set the size and position of the panel: 335 #---------------------------------------------------------------------------- 336
337 - def position ( self, x, y, dx, dy ):
338 self.SetDimensions( x, y, dx, dy ) 339 if self._need_layout: 340 self._need_layout = False 341 self.Layout()
342 343 #---------------------------------------------------------------------------- 344 # Destroy the panel: 345 #---------------------------------------------------------------------------- 346
347 - def destroy ( self ):
348 self.Destroy()
349 350 #------------------------------------------------------------------------------- 351 # 'TraitSheet' class: 352 #------------------------------------------------------------------------------- 353
354 -class TraitSheet ( wx.wxPanel ):
355 356 #---------------------------------------------------------------------------- 357 # Initialize the object: 358 #---------------------------------------------------------------------------- 359
360 - def __init__ ( self, parent, 361 object, 362 traits = None, 363 handler = default_trait_sheet_handler ):
364 wx.wxPanel.__init__( self, parent, -1 ) 365 366 self.object = object 367 self.handler = handler 368 369 # If no traits were specified: 370 if traits is None: 371 # Get them from the specified object: 372 traits = object.editable_traits() 373 374 # Try to make sure that we now have either a single TraitGroup, or 375 # a list of TraitGroups: 376 kind = type( traits ) 377 if kind is StringType: 378 # Convert the single trait name into a TraitGroup: 379 traits = TraitGroup( traits, show_border = False, style = 'custom' ) 380 elif ((kind in basic_sequence_types) or 381 isinstance( traits, TraitGroupList )): 382 if len( traits ) == 0: 383 # Empty trait list, leave the panel empty: 384 return 385 if not isinstance( traits[0], TraitGroup ): 386 # Convert a simple list of trait elements into a single, 387 # TraitGroup, possibly containing multiple items: 388 traits = TraitGroup( show_border = False, 389 style = 'custom', *traits ) 390 391 # Create the requested style of trait sheet editor: 392 if isinstance( traits, TraitGroup ): 393 # Single page dialog: 394 sizer = self.add_page( traits, self ) 395 self.SetAutoLayout( True ) 396 self.SetSizer( sizer ) 397 sizer.Fit( self ) 398 else: 399 # Multi-tab notebook: 400 self.add_tabs( traits )
401 402 #---------------------------------------------------------------------------- 403 # Create a tab (i.e. notebook) style trait editor: 404 #---------------------------------------------------------------------------- 405
406 - def add_tabs ( self, traits ):
407 # Create the notebook, add it to the the dialog's sizer: 408 nb = wx.wxNotebook( self, -1 ) 409 nbs = wx.wxNotebookSizer( nb ) 410 count = 0 411 412 for pg in traits: 413 # Create the new notebook page and add it to the notebook: 414 panel = wx.wxPanel( nb, -1 ) 415 page_name = pg.label 416 if page_name is None: 417 count += 1 418 page_name = 'Page %d' % count 419 nb.AddPage( panel, page_name ) 420 sizer = self.add_page( pg, panel ) 421 panel.SetAutoLayout( True ) 422 panel.SetSizer( sizer ) 423 424 nbs.Fit( nb ) 425 dx, dy = nb.GetSizeTuple() 426 size = wx.wxSize( max( len( traits ) * 54, 260, dx ), dy ) 427 nb.SetSize( size ) 428 self.SetSize( size )
429 430 #---------------------------------------------------------------------------- 431 # Create a single trait editor page: 432 #---------------------------------------------------------------------------- 433
434 - def add_page ( self, pg, parent, box = None, default_style = 'simple' ):
435 default_style = pg.style or default_style 436 object = pg.object or self.object 437 if pg.orientation == 'horizontal': 438 rsizer = wx.wxBoxSizer( wx.wxVERTICAL ) 439 if box is not None: 440 psizer = wx.wxStaticBoxSizer( box, wx.wxHORIZONTAL ) 441 else: 442 psizer = wx.wxBoxSizer( wx.wxHORIZONTAL ) 443 rsizer.Add( psizer, 0, wx.wxEXPAND ) 444 elif box is not None: 445 rsizer = psizer = wx.wxStaticBoxSizer( box, wx.wxVERTICAL ) 446 else: 447 rsizer = psizer = wx.wxBoxSizer( wx.wxVERTICAL ) 448 sizer = None 449 show_labels = pg.show_labels_ 450 for pge in pg.values: 451 if isinstance( pge, TraitGroup ): 452 box = None 453 if pge.show_border_: 454 box = wx.wxStaticBox( parent, -1, pge.label or '' ) 455 psizer.Add( self.add_page( pge, parent, box, default_style ), 0, 456 wx.wxEXPAND | wx.wxALL, 2 ) 457 else: 458 if sizer is None: 459 cols = 1 + show_labels 460 sizer = wx.wxFlexGridSizer( 0, cols, 2, 4 ) 461 if show_labels: 462 sizer.AddGrowableCol( 1 ) 463 464 name = pge.name or ' ' 465 466 if name == '-': 467 for i in range( cols ): 468 sizer.Add( wx.wxStaticLine( parent, -1 ), 0, 469 wx.wxTOP | wx.wxBOTTOM | wx.wxEXPAND, 2 ) 470 continue 471 472 if name == ' ': 473 name = '5' 474 if all_digits.match( name ): 475 n = int( name ) 476 for i in range( cols ): 477 sizer.Add( n, n ) 478 continue 479 480 editor = pge.editor 481 style = pge.style or default_style 482 pge_object = pge.object or object 483 if editor is None: 484 try: 485 editor = pge_object._base_trait( name ).get_editor() 486 except: 487 pass 488 if editor is None: 489 continue 490 label = None 491 if show_labels: 492 label = pge.label_for( object ) 493 self.add_trait( parent, sizer, pge_object, name, label, editor, 494 style ) 495 496 if sizer is not None: 497 psizer.Add( sizer, 1, wx.wxALL | wx.wxEXPAND, 1 ) 498 499 if rsizer is not psizer: 500 rsizer.Add( wx.wxPanel( parent, -1 ), 1, wx.wxEXPAND ) 501 502 return rsizer
503 504 #---------------------------------------------------------------------------- 505 # Add a trait to the trait sheet: 506 #---------------------------------------------------------------------------- 507
508 - def add_trait ( self, parent, sizer, object, trait_name, description, editor, 509 style ):
510 if description is not None: 511 suffix = ':'[ description[-1:] == '?': ] 512 label = wx.wxStaticText( parent, -1, 513 description + suffix, 514 style = wx.wxALIGN_RIGHT ) 515 sizer.Add( label, 0, wx.wxEXPAND | wx.wxALIGN_CENTER ) 516 desc = object._base_trait( trait_name ).desc 517 if desc is not None: 518 label.SetToolTip( wx.wxToolTip( 'Specifies ' + desc ) ) 519 wx.EVT_RIGHT_UP( label, self.on_toggle_help ) 520 if style == 'custom': 521 control = editor.custom_editor( object, trait_name, description, 522 self.handler, parent ) 523 elif style == 'readonly': 524 control = editor.readonly_editor( object, trait_name, description, 525 self.handler, parent ) 526 else: 527 if style == 'simple': 528 control = editor.simple_editor( object, trait_name, description, 529 self.handler, parent ) 530 else: 531 control = editor.text_editor( object, trait_name, description, 532 self.handler, parent ) 533 wx.EVT_RIGHT_UP( control, editor.on_restore ) 534 535 init_control( control, object, trait_name, description, self.handler ) 536 sizer.Add( control, 1, editor.layout_style() ) 537 return control
538 539 #---------------------------------------------------------------------------- 540 # Display a help message to the user indicating the purpose of a trait: 541 #---------------------------------------------------------------------------- 542
543 - def on_toggle_help ( self, event ):
544 global tooltips_enabled 545 tooltips_enabled = not tooltips_enabled 546 wx.wxToolTip_Enable( tooltips_enabled )
547 548 #------------------------------------------------------------------------------- 549 # 'wxTraitEditor' class: 550 #------------------------------------------------------------------------------- 551
552 -class wxTraitEditor ( TraitEditor ):
553 554 #---------------------------------------------------------------------------- 555 # Create an in-place read only view of the current value of the 556 # 'trait_name' trait of 'object': 557 #---------------------------------------------------------------------------- 558
559 - def readonly_editor ( self, object, trait_name, description, handler, 560 parent ):
561 control = wx.wxTextCtrl( parent, -1, self.str( object, trait_name ), 562 style = wx.wxTE_READONLY ) 563 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 564 return control
565 566 #---------------------------------------------------------------------------- 567 # Create an in-place text editable view of the current value of the 568 # 'trait_name' trait of 'object': 569 #---------------------------------------------------------------------------- 570
571 - def text_editor ( self, object, trait_name, description, handler, 572 parent ):
573 control = wx.wxTextCtrl( parent, -1, self.str( object, trait_name ), 574 style = wx.wxTE_PROCESS_ENTER ) 575 wx.EVT_KILL_FOCUS( control, self.on_text_enter ) 576 wx.EVT_TEXT_ENTER( parent, control.GetId(), self.on_text_enter ) 577 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 578 return control
579 580 #---------------------------------------------------------------------------- 581 # Handle the user pressing the 'Enter' key in the edit control: 582 #---------------------------------------------------------------------------- 583
584 - def on_text_enter ( self, event ):
585 control = event.GetEventObject() 586 try: 587 self.set( control.object, control.trait_name, 588 control.GetValue(), control.handler ) 589 return True 590 except TraitError, excp: 591 self.error( control.description, excp, control ) 592 return False
593 594 #---------------------------------------------------------------------------- 595 # Handle the object trait changing value outside the editor: 596 #---------------------------------------------------------------------------- 597
598 - def on_trait_change_text ( self, control, new ):
599 if not hasattr( control, 'updating' ): 600 new_value = self.str_value( new ) 601 if control.GetValue() != new_value: 602 control.SetValue( new_value )
603 604 #---------------------------------------------------------------------------- 605 # Create a static view of the current value of the 'trait_name' 606 # trait of 'object': 607 #---------------------------------------------------------------------------- 608
609 - def simple_editor ( self, object, trait_name, description, handler, 610 parent ):
611 control = wx.wxTextCtrl( parent, -1, self.str( object, trait_name ), 612 style = wx.wxTE_READONLY ) 613 wx.EVT_LEFT_UP( control, self.on_popup ) 614 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 615 return control
616 617 #---------------------------------------------------------------------------- 618 # Invoke the pop-up editor for an object trait: 619 #---------------------------------------------------------------------------- 620
621 - def on_popup ( self, event ):
622 self.on_popup_control( event.GetEventObject() )
623
624 - def on_popup_control ( self, control ):
625 if hasattr( self, 'popup_editor' ): 626 self.popup_editor( control.object, control.trait_name, 627 control.description, control.handler, control )
628 629 #---------------------------------------------------------------------------- 630 # Restore the original value of the object's trait: 631 #---------------------------------------------------------------------------- 632
633 - def on_restore ( self, event ):
634 control = event.GetEventObject() 635 object = control.object 636 trait_name = control.trait_name 637 old_value = getattr( object, trait_name ) 638 new_value = control.original_value 639 setattr( object, trait_name, new_value ) 640 control.handler.changed( object, trait_name, new_value, old_value, 641 False )
642 643 #---------------------------------------------------------------------------- 644 # Handle a 'TraitError' exception: 645 #---------------------------------------------------------------------------- 646
647 - def error ( self, description, excp, parent ):
648 dlg = wx.wxMessageDialog( parent, str( excp ), 649 description + ' value error', 650 wx.wxOK | wx.wxICON_INFORMATION ) 651 dlg.ShowModal() 652 dlg.Destroy()
653 654 #---------------------------------------------------------------------------- 655 # Return the layout style for this trait editor's control: 656 #---------------------------------------------------------------------------- 657
658 - def layout_style ( self ):
659 return wx.wxEXPAND
660 661 #------------------------------------------------------------------------------- 662 # 'TraitEditorInstance' class: 663 #------------------------------------------------------------------------------- 664
665 -class TraitEditorInstance ( wxTraitEditor ):
666 667 #---------------------------------------------------------------------------- 668 # Create a static view of the current value of the 'trait_name' 669 # trait of 'object': 670 #---------------------------------------------------------------------------- 671
672 - def simple_editor ( self, object, trait_name, description, handler, 673 parent ):
674 value = getattr( object, trait_name ) 675 if value is None: 676 control = wx.wxButton( parent, -1, 'None' ) 677 control.Enable( False ) 678 else: 679 control = wx.wxButton( parent, -1, value.__class__.__name__ ) 680 wx.EVT_BUTTON( parent, control.GetId(), self.on_popup ) 681 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 682 return control
683 684 #---------------------------------------------------------------------------- 685 # Invoke the pop-up editor for an object trait: 686 #---------------------------------------------------------------------------- 687
688 - def on_popup_control ( self, control ):
689 object = getattr( control.object, control.trait_name ) 690 if isinstance( object, HasTraits ): 691 traits = object.editable_traits() 692 try: 693 wrap = (not isinstance( traits, TraitGroup )) 694 if wrap: 695 wrap = (not isinstance( traits[0], TraitGroup )) 696 except: 697 wrap = True 698 if wrap: 699 traits = TraitGroup( show_border = False, 700 style = 'simple', 701 *traits ) 702 TraitSheetDialog( object, traits, control.handler, control )
703 704 #------------------------------------------------------------------------------- 705 # 'TraitEditorText' class: 706 #------------------------------------------------------------------------------- 707
708 -class TraitEditorText ( wxTraitEditor ):
709 710 #---------------------------------------------------------------------------- 711 # Initialize the object: 712 #---------------------------------------------------------------------------- 713
714 - def __init__ ( self, dic = {}, auto_set = True, evaluate = False ):
715 self.dic = dic 716 self.auto_set = auto_set 717 self.evaluate = evaluate
718 719 #---------------------------------------------------------------------------- 720 # Interactively edit the 'trait_name' text trait of 'object': 721 #---------------------------------------------------------------------------- 722
723 - def popup_editor ( self, object, trait_name, description, handler, 724 parent = None ):
725 while True: 726 dialog = wx.wxTextEntryDialog( parent or object.window, 727 'Enter the new %s value:' % trait_name, 728 defaultValue = getattr( object, trait_name ) ) 729 if dialog.ShowModal() != wx.wxID_OK: 730 return 731 try: 732 self.set( object, trait_name, self.get_value( dialog ), 733 handler ) 734 return True 735 except TraitError, excp: 736 self.error( description, excp, object.window ) 737 return False
738 739 #---------------------------------------------------------------------------- 740 # Create an in-place editable view of the current value of the 741 # 'trait_name' trait of 'object': 742 #---------------------------------------------------------------------------- 743
744 - def simple_editor ( self, object, trait_name, description, handler, 745 parent ):
746 control = wx.wxTextCtrl( parent, -1, self.str( object, trait_name ), 747 style = wx.wxTE_PROCESS_ENTER ) 748 wx.EVT_KILL_FOCUS( control, self.on_enter ) 749 wx.EVT_TEXT_ENTER( parent, control.GetId(), self.on_enter ) 750 if self.auto_set: 751 wx.EVT_TEXT( parent, control.GetId(), self.on_key ) 752 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 753 return control
754 755 #---------------------------------------------------------------------------- 756 # Handle the user pressing the 'Enter' key in the edit control: 757 #---------------------------------------------------------------------------- 758
759 - def on_enter ( self, event ):
760 control = event.GetEventObject() 761 try: 762 self.set( control.object, control.trait_name, 763 self.get_value( control ), control.handler ) 764 except TraitError, excp: 765 self.error( control.description, excp, control )
766 767 #---------------------------------------------------------------------------- 768 # Handle the user changing the contents of the edit control: 769 #---------------------------------------------------------------------------- 770
771 - def on_key ( self, event ):
772 owner = control = event.GetEventObject() 773 control.updating = True 774 if not hasattr( owner, 'object' ): 775 owner = control.GetParent() 776 try: 777 self.set( owner.object, owner.trait_name, 778 self.get_value( control ), owner.handler ) 779 color = wx.wxWHITE 780 except: 781 color = error_color 782 del control.updating 783 control.SetBackgroundColour( color ) 784 control.Refresh()
785 786 #---------------------------------------------------------------------------- 787 # Get the actual value corresponding to what the user typed: 788 #---------------------------------------------------------------------------- 789
790 - def get_value ( self, control ):
791 value = control.GetValue().strip() 792 if self.evaluate: 793 value = eval( value ) 794 if not self.dic.has_key( value ): 795 return value 796 return self.dic[ value ]
797 798 #------------------------------------------------------------------------------- 799 # 'TraitEditorEnum' class: 800 #------------------------------------------------------------------------------- 801
802 -class TraitEditorEnum ( wxTraitEditor ):
803 804 #---------------------------------------------------------------------------- 805 # Initialize the object: 806 #---------------------------------------------------------------------------- 807
808 - def __init__ ( self, values, cols = 1 ):
809 self.cols = cols 810 self.mapped = (type( values ) is DictType) 811 if self.mapped: 812 sorted = values.values() 813 sorted.sort() 814 col = sorted[0].find( ':' ) + 1 815 if col > 0: 816 self.sorted = [ x[ col: ] for x in sorted ] 817 for n, v in values.items(): 818 values[n] = v[ col: ] 819 else: 820 self.sorted = sorted 821 self.values = values 822 else: 823 if not type( values ) in basic_sequence_types: 824 handler = values 825 if isinstance( handler, Trait ): 826 handler = handler.setter 827 if hasattr( handler, 'map' ): 828 values = handler.map.keys() 829 values.sort() 830 else: 831 values = handler.values 832 self.values = [ str( x ) for x in values ]
833 834 #---------------------------------------------------------------------------- 835 # Create an in-place simple view of the current value of the 836 # 'trait_name' trait of 'object': 837 #---------------------------------------------------------------------------- 838
839 - def simple_editor ( self, object, trait_name, description, handler, 840 parent ):
841 control = wx.wxChoice( parent, -1, 842 wx.wxPoint( 0, 0 ), wx.wxSize( 100, 20 ), 843 self.all_values() ) 844 try: 845 control.SetStringSelection( self.current_value( object, trait_name ) ) 846 except: 847 pass 848 wx.EVT_CHOICE( parent, control.GetId(), self.on_value_changed ) 849 TraitMonitor( object, trait_name, control, self.on_trait_change_choice ) 850 return control
851 852 #---------------------------------------------------------------------------- 853 # Create an in-place custom view of the current value of the 854 # 'trait_name' trait of 'object': 855 #---------------------------------------------------------------------------- 856
857 - def custom_editor ( self, object, trait_name, description, handler, parent ):
858 # Create a panel to hold all of the radio buttons: 859 panel = wx.wxPanel( parent, -1 ) 860 861 # Get the current trait value: 862 cur_value = self.current_value( object, trait_name ) 863 864 # Create a sizer to manage the radio buttons: 865 values = self.all_values() 866 n = len( values ) 867 cols = self.cols 868 rows = (n + cols - 1) // cols 869 incr = [ n // cols ] * cols 870 rem = n % cols 871 for i in range( cols ): 872 incr[i] += (rem > i) 873 incr[-1] = -(reduce( lambda x, y: x + y, incr[:-1], 0 ) - 1) 874 if cols > 1: 875 sizer = wx.wxGridSizer( 0, cols, 2, 4 ) 876 else: 877 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 878 879 # Add the set of all possible choices: 880 style = wx.wxRB_GROUP 881 index = 0 882 for i in range( rows ): 883 for j in range( cols ): 884 if n > 0: 885 value = label = values[ index ] 886 if label[:1] in ascii_lowercase: 887 label = label.capitalize() 888 control = wx.wxRadioButton( panel, -1, label, style = style ) 889 control.value = value 890 style = 0 891 control.SetValue( value == cur_value ) 892 wx.EVT_RADIOBUTTON( panel, control.GetId(), self.on_click ) 893 index += incr[j] 894 n -= 1 895 else: 896 control = wx.wxRadioButton( panel, -1, '' ) 897 control.value = '' 898 control.Show( False ) 899 sizer.Add( control, 0, wx.wxNORTH, 5 ) 900 901 # Set-up the layout: 902 panel.SetAutoLayout( True ) 903 panel.SetSizer( sizer ) 904 sizer.Fit( panel ) 905 906 if self.mapped: 907 trait_name += '_' 908 TraitMonitor( object, trait_name, panel, self.on_trait_change_radio ) 909 910 # Return the panel as the result: 911 return panel
912 913 #---------------------------------------------------------------------------- 914 # Return the set of all possible values: 915 #---------------------------------------------------------------------------- 916
917 - def all_values ( self ):
918 if self.mapped: 919 return self.sorted 920 return self.values
921 922 #---------------------------------------------------------------------------- 923 # Return the current value of the object trait: 924 #---------------------------------------------------------------------------- 925
926 - def current_value ( self, object, trait_name ):
927 if self.mapped: 928 return self.values[ getattr( object, trait_name + '_' ) ] 929 return str( getattr( object, trait_name ) )
930 931 #---------------------------------------------------------------------------- 932 # Handle the user selecting a new value from the combo box: 933 #---------------------------------------------------------------------------- 934
935 - def on_value_changed ( self, event ):
936 control = event.GetEventObject() 937 value = event.GetString() 938 try: 939 value = int( value ) 940 except: 941 pass 942 self.set( control.object, control.trait_name, value, control.handler )
943 944 #---------------------------------------------------------------------------- 945 # Handle the user clicking one of the 'custom' radio buttons: 946 #---------------------------------------------------------------------------- 947
948 - def on_click ( self, event ):
949 control = event.GetEventObject() 950 parent = control.GetParent() 951 value = control.value 952 try: 953 value = int( value ) 954 except: 955 pass 956 self.set( parent.object, parent.trait_name, value, parent.handler )
957 958 #---------------------------------------------------------------------------- 959 # Handle the object trait changing value outside the editor: 960 #---------------------------------------------------------------------------- 961
962 - def on_trait_change_choice ( self, control, new ):
963 try: 964 control.SetStringSelection( str( new ) ) 965 except: 966 pass
967
968 - def on_trait_change_radio ( self, control, new ):
969 for button in control.GetChildren(): 970 button.SetValue( button.value == new )
971 972 #------------------------------------------------------------------------------- 973 # 'TraitEditorImageEnum' class: 974 #------------------------------------------------------------------------------- 975
976 -class TraitEditorImageEnum ( TraitEditorEnum ):
977 978 #---------------------------------------------------------------------------- 979 # Initialize the object: 980 #---------------------------------------------------------------------------- 981
982 - def __init__ ( self, values, suffix = '', cols = 1, path = None ):
983 TraitEditorEnum.__init__( self, values ) 984 self.suffix = suffix 985 self.cols = cols 986 if type( path ) is ModuleType: 987 path = os.path.join( os.path.dirname( path.__file__ ), 'images' ) 988 self.path = path
989 990 #---------------------------------------------------------------------------- 991 # Interactively edit the 'trait_name' trait of 'object': 992 #---------------------------------------------------------------------------- 993
994 - def popup_editor ( self, object, trait_name, description, handler, 995 parent = None ):
996 return TraitEditorImageDialog( object, trait_name, description, 997 parent, handler, self ).ShowModal()
998 999 #---------------------------------------------------------------------------- 1000 # Create an in-place read only view of the current value of the 1001 # 'trait_name' trait of 'object': 1002 #---------------------------------------------------------------------------- 1003
1004 - def readonly_editor ( self, object, trait_name, description, handler, 1005 parent ):
1006 control = ImageControl( parent, 1007 bitmap_cache( self.current_value( object, trait_name ) + 1008 self.suffix, False, self.path ) ) 1009 #control.SetBackgroundColour( wx.wxWHITE ) 1010 TraitMonitor( object, trait_name, control, self.on_trait_change_image ) 1011 return control
1012 1013 #---------------------------------------------------------------------------- 1014 # Create an in-place editable view of the current value of the 1015 # 'trait_name' trait of 'object': 1016 #---------------------------------------------------------------------------- 1017
1018 - def simple_editor ( self, object, trait_name, description, handler, 1019 parent ):
1020 control = self.readonly_editor( object, trait_name, description, handler, 1021 parent ) 1022 control.Selected( True ) 1023 control.Handler( self.on_popup_control ) 1024 return control
1025 1026 #---------------------------------------------------------------------------- 1027 # Create an in-place custom view of the current value of the 1028 # 'trait_name' trait of 'object': 1029 #---------------------------------------------------------------------------- 1030
1031 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1032 # Create a panel to hold all of the radio buttons: 1033 panel = wx.wxPanel( parent, -1 ) 1034 1035 # Add the image buttons to the panel: 1036 self.create_image_grid( panel, getattr( object, trait_name ), 1037 self.on_click ) 1038 1039 # Return the panel as the result: 1040 return panel
1041 1042 #---------------------------------------------------------------------------- 1043 # Populate a specified window with a grid of image buttons: 1044 #---------------------------------------------------------------------------- 1045
1046 - def create_image_grid ( self, parent, cur_value, handler ):
1047 # Create the main sizer: 1048 if self.cols > 1: 1049 sizer = wx.wxGridSizer( 0, self.cols, 0, 0 ) 1050 else: 1051 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 1052 1053 # Add the set of all possible choices: 1054 cur_value = str( cur_value ) 1055 for value in self.all_values(): 1056 control = ImageControl( parent, 1057 bitmap_cache( value + self.suffix, False, self.path ), 1058 value == cur_value, handler ) 1059 control.value = value 1060 sizer.Add( control, 0, wx.wxALL, 2 ) 1061 1062 # Finish setting up the control layout: 1063 parent.SetAutoLayout( True ) 1064 parent.SetSizer( sizer ) 1065 sizer.Fit( parent )
1066 1067 #---------------------------------------------------------------------------- 1068 # Handle the object trait changing value outside the editor: 1069 #---------------------------------------------------------------------------- 1070
1071 - def on_trait_change_image ( self, control, new ):
1072 control.Bitmap( bitmap_cache( new + self.suffix, False, self.path ) )
1073 1074 #---------------------------------------------------------------------------- 1075 # Return the layout style for this trait editor's control: 1076 #---------------------------------------------------------------------------- 1077
1078 - def layout_style ( self ):
1079 return 0
1080 1081 #---------------------------------------------------------------------------- 1082 # Handle the user clicking one of the image buttons: 1083 #---------------------------------------------------------------------------- 1084
1085 - def on_click ( self, control ):
1086 parent= control.GetParent() 1087 self.set( parent.object, parent.trait_name, control.value, 1088 parent.handler )
1089 1090 #------------------------------------------------------------------------------- 1091 # 'TraitEditorCheckList' class: 1092 #------------------------------------------------------------------------------- 1093
1094 -class TraitEditorCheckList ( wxTraitEditor ):
1095 1096 #---------------------------------------------------------------------------- 1097 # Initialize the object: 1098 #---------------------------------------------------------------------------- 1099
1100 - def __init__ ( self, values, cols = 1 ):
1101 self.cols = cols 1102 self.values = values 1103 if type( values[0] ) is StringType: 1104 self.values = [ ( x, x.capitalize() ) for x in values ] 1105 self.mapping = mapping = {} 1106 for value, key in self.values: 1107 mapping[ key ] = value
1108 1109 #---------------------------------------------------------------------------- 1110 # Create an in-place simple view of the current value of the 1111 # 'trait_name' trait of 'object': 1112 #---------------------------------------------------------------------------- 1113
1114 - def simple_editor ( self, object, trait_name, description, handler, 1115 parent ):
1116 control = wx.wxChoice( parent, -1, 1117 wx.wxPoint( 0, 0 ), wx.wxSize( 100, 20 ), 1118 self.all_labels() ) 1119 try: 1120 control.SetSelection( self.all_values().index( 1121 self.current_value( object, trait_name )[0] ) ) 1122 except: 1123 pass 1124 wx.EVT_CHOICE( parent, control.GetId(), self.on_value_changed ) 1125 TraitMonitor( object, trait_name, control, on_trait_change_choice ) 1126 return control
1127 1128 #---------------------------------------------------------------------------- 1129 # Create an in-place custom view of the current value of the 1130 # 'trait_name' trait of 'object': 1131 #---------------------------------------------------------------------------- 1132
1133 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1134 # Create a panel to hold all of the radio buttons: 1135 panel = wx.wxPanel( parent, -1 ) 1136 1137 # Get the current trait value: 1138 cur_value = self.current_value( object, trait_name ) 1139 1140 # Create a sizer to manage the radio buttons: 1141 labels = self.all_labels() 1142 values = self.all_values() 1143 n = len( values ) 1144 cols = self.cols 1145 rows = (n + cols - 1) // cols 1146 incr = [ n // cols ] * cols 1147 rem = n % cols 1148 for i in range( cols ): 1149 incr[i] += (rem > i) 1150 incr[-1] = -(reduce( lambda x, y: x + y, incr[:-1], 0 ) - 1) 1151 if cols > 1: 1152 sizer = wx.wxGridSizer( 0, cols, 2, 4 ) 1153 else: 1154 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 1155 1156 # Add the set of all possible choices: 1157 index = 0 1158 for i in range( rows ): 1159 for j in range( cols ): 1160 if n > 0: 1161 control = wx.wxCheckBox( panel, -1, labels[ index ] ) 1162 control.value = value = values[ index ] 1163 control.SetValue( value in cur_value ) 1164 wx.EVT_CHECKBOX( panel, control.GetId(), self.on_click ) 1165 index += incr[j] 1166 n -= 1 1167 else: 1168 control = wx.wxCheckBox( panel, -1, '' ) 1169 control.Show( False ) 1170 sizer.Add( control, 0, wx.wxNORTH, 5 ) 1171 1172 # Set-up the layout: 1173 panel.SetAutoLayout( True ) 1174 panel.SetSizer( sizer ) 1175 sizer.Fit( panel ) 1176 1177 TraitMonitor( object, trait_name, panel, self.on_trait_change_list ) 1178 1179 # Return the panel as the result: 1180 return panel
1181 1182 #---------------------------------------------------------------------------- 1183 # Return the set of all possible labels: 1184 #---------------------------------------------------------------------------- 1185
1186 - def all_labels ( self ):
1187 return [ x[1] for x in self.values ]
1188 1189 #---------------------------------------------------------------------------- 1190 # Return the set of all possible values: 1191 #---------------------------------------------------------------------------- 1192
1193 - def all_values ( self ):
1194 return [ x[0] for x in self.values ]
1195 1196 #---------------------------------------------------------------------------- 1197 # Return whether or not the current value is a string or not: 1198 #---------------------------------------------------------------------------- 1199
1200 - def is_string ( self, object, trait_name ):
1201 return (type( getattr( object, trait_name ) ) is StringType)
1202 1203 #---------------------------------------------------------------------------- 1204 # Return the current value of the object trait: 1205 #---------------------------------------------------------------------------- 1206
1207 - def current_value ( self, object, trait_name ):
1208 return self.parse_value( getattr( object, trait_name ) )
1209 1210 #---------------------------------------------------------------------------- 1211 # Parse a value into a list: 1212 #---------------------------------------------------------------------------- 1213
1214 - def parse_value ( self, value ):
1215 if value is None: 1216 return [] 1217 if type( value ) is not StringType: 1218 return value 1219 return [ x.strip() for x in value.split( ',' ) ]
1220 1221 #---------------------------------------------------------------------------- 1222 # Handle the user selecting a new value from the combo box: 1223 #---------------------------------------------------------------------------- 1224
1225 - def on_value_changed ( self, event ):
1226 control = event.GetEventObject() 1227 value = self.mapping[ event.GetString() ] 1228 if not self.is_string( control.object, control.trait_name ): 1229 value = [ value ] 1230 self.set( control.object, control.trait_name, value, control.handler )
1231 1232 #---------------------------------------------------------------------------- 1233 # Handle the user clicking one of the 'custom' check boxes: 1234 #---------------------------------------------------------------------------- 1235
1236 - def on_click ( self, event ):
1237 control = event.GetEventObject() 1238 parent = control.GetParent() 1239 value = control.value 1240 cur_value = self.current_value( parent.object, parent.trait_name ) 1241 if control.GetValue(): 1242 cur_value.append( value ) 1243 else: 1244 cur_value.remove( value ) 1245 if self.is_string( parent.object, parent.trait_name ): 1246 cur_value = ','.join( cur_value ) 1247 self.set( parent.object, parent.trait_name, cur_value, parent.handler )
1248 1249 #---------------------------------------------------------------------------- 1250 # Handle the object trait changing value outside the editor: 1251 #---------------------------------------------------------------------------- 1252
1253 - def on_trait_change_choice ( self, control, new ):
1254 try: 1255 control.SetSelection( self.all_values().index( 1256 self.parse_value( new )[0] ) ) 1257 except: 1258 pass
1259
1260 - def on_trait_change_list ( self, panel, new ):
1261 new_values = self.parse_value( new ) 1262 for control in panel.GetChildren(): 1263 if control.IsShown(): 1264 control.SetValue( control.value in new_values )
1265 1266 #------------------------------------------------------------------------------- 1267 # 'TraitEditorBoolean' class: 1268 #------------------------------------------------------------------------------- 1269
1270 -class TraitEditorBoolean ( wxTraitEditor ):
1271 1272 #---------------------------------------------------------------------------- 1273 # Create an in-place read only view of the current value of the 1274 # 'trait_name' trait of 'object': 1275 #---------------------------------------------------------------------------- 1276
1277 - def readonly_editor ( self, object, trait_name, description, handler, 1278 parent ):
1279 control = wx.wxTextCtrl( parent, -1, '', style = wx.wxTE_READONLY ) 1280 try: 1281 value = getattr( object, trait_name + '_' ) 1282 trait_name += '_' 1283 except: 1284 value = getattr( object, trait_name ) 1285 TraitMonitor( object, trait_name, control, 1286 self.on_trait_change_readonly ) 1287 self.on_trait_change_readonly( control, value ) 1288 return control
1289 1290 #---------------------------------------------------------------------------- 1291 # Handle the object trait changing value outside the editor: 1292 #---------------------------------------------------------------------------- 1293
1294 - def on_trait_change_readonly ( self, control, new ):
1295 if new: 1296 control.SetValue( 'True' ) 1297 else: 1298 control.SetValue( 'False' )
1299 1300 #---------------------------------------------------------------------------- 1301 # Create an in-place editable view of the current value of the 1302 # 'trait_name' trait of 'object': 1303 #---------------------------------------------------------------------------- 1304
1305 - def simple_editor ( self, object, trait_name, description, handler, 1306 parent ):
1307 control = wx.wxCheckBox( parent, -1, '' ) 1308 try: 1309 value = getattr( object, trait_name + '_' ) 1310 trait_name += '_' 1311 except: 1312 value = getattr( object, trait_name ) 1313 control.SetValue( value ) 1314 wx.EVT_CHECKBOX( parent, control.GetId(), self.on_value_changed ) 1315 TraitMonitor( object, trait_name, control, self.on_trait_change_check ) 1316 return control
1317 1318 #---------------------------------------------------------------------------- 1319 # Handle the user clicking on the checkbox: 1320 #---------------------------------------------------------------------------- 1321
1322 - def on_value_changed ( self, event ):
1323 control = event.GetEventObject() 1324 self.set( control.object, control.trait_name, control.GetValue(), 1325 control.handler )
1326 1327 #---------------------------------------------------------------------------- 1328 # Handle the object trait changing value outside the editor: 1329 #---------------------------------------------------------------------------- 1330
1331 - def on_trait_change_check ( self, control, new ):
1332 control.SetValue( new )
1333 1334 #------------------------------------------------------------------------------- 1335 # 'TraitEditorRange class: 1336 #------------------------------------------------------------------------------- 1337
1338 -class TraitEditorRange ( TraitEditorEnum ):
1339 1340 #---------------------------------------------------------------------------- 1341 # Initialize the object: 1342 #---------------------------------------------------------------------------- 1343
1344 - def __init__ ( self, handler, cols = 1, auto_set = True ):
1345 if isinstance( handler, Trait ): 1346 handler = handler.setter 1347 self.low = handler.low 1348 self.high = handler.high 1349 self.is_float = (type( self.low ) is FloatType) 1350 self.cols = cols 1351 self.auto_set = auto_set 1352 self.mapped = False
1353 1354 #---------------------------------------------------------------------------- 1355 # Create an in-place editable view of the current value of the 1356 # 'trait_name' trait of 'object': 1357 #---------------------------------------------------------------------------- 1358
1359 - def simple_editor ( self, object, trait_name, description, handler, 1360 parent ):
1361 if self.is_float or (abs( self.high - self.low ) > 100): 1362 self.format = '%d' 1363 if self.is_float: 1364 self.format = '%%.%df' % max( 0, 1365 4 - int( log10( self.high - self.low ) ) ) 1366 panel = wx.wxPanel( parent, -1 ) 1367 sizer = wx.wxBoxSizer( wx.wxHORIZONTAL ) 1368 fvalue = getattr( object, trait_name ) 1369 try: 1370 fvalue_text = self.format % fvalue 1371 1 / (self.low <= fvalue <= self.high) 1372 except: 1373 fvalue_text = '' 1374 fvalue = self.low 1375 ivalue = int( (float( fvalue - self.low ) / (self.high - self.low)) 1376 * 10000 ) 1377 label_lo = wx.wxStaticText( panel, -1, '999.999', 1378 style = wx.wxALIGN_RIGHT | wx.wxST_NO_AUTORESIZE ) 1379 sizer.Add( label_lo, 0, wx.wxALIGN_CENTER ) 1380 panel.slider = slider = wx.wxSlider( panel, -1, ivalue, 0, 10000, 1381 size = wx.wxSize( 100, 20 ), 1382 style = wx.wxSL_HORIZONTAL | wx.wxSL_AUTOTICKS ) 1383 slider.SetTickFreq( 1000, 1 ) 1384 slider.SetPageSize( 1000 ) 1385 slider.SetLineSize( 100 ) 1386 wx.EVT_SCROLL( slider, self.on_scroll ) 1387 sizer.Add( slider, 1, wx.wxEXPAND ) 1388 label_hi = wx.wxStaticText( panel, -1, '999.999' ) 1389 sizer.Add( label_hi, 0, wx.wxALIGN_CENTER ) 1390 panel.text = text = wx.wxTextCtrl( panel, -1, fvalue_text, 1391 size = wx.wxSize( 60, 20 ), 1392 style = wx.wxTE_PROCESS_ENTER ) 1393 wx.EVT_KILL_FOCUS( text, self.on_enter ) 1394 wx.EVT_TEXT_ENTER( panel, text.GetId(), self.on_enter ) 1395 sizer.Add( text, 0, wx.wxLEFT | wx.wxEXPAND, 8 ) 1396 1397 # Set-up the layout: 1398 panel.SetAutoLayout( True ) 1399 panel.SetSizer( sizer ) 1400 sizer.Fit( panel ) 1401 label_lo.SetLabel( str( self.low ) ) 1402 label_hi.SetLabel( str( self.high ) ) 1403 panel.is_enum = False 1404 TraitMonitor( object, trait_name, panel, self.on_trait_change_slider ) 1405 1406 # Return the panel as the result: 1407 return panel 1408 else: 1409 value = getattr( object, trait_name ) 1410 control = wx.wxSpinCtrl( parent, -1, str( value ), 1411 min = self.low, 1412 max = self.high, 1413 initial = value ) 1414 wx.EVT_SPINCTRL( parent, control.GetId(), self.on_value_changed ) 1415 TraitMonitor( object, trait_name, control, 1416 self.on_trait_change_spinner ) 1417 return control
1418 1419 #---------------------------------------------------------------------------- 1420 # Create an in-place custom view of the current value of the 1421 # 'trait_name' trait of 'object': 1422 #---------------------------------------------------------------------------- 1423
1424 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1425 if ((type( self.low ) is FloatType) or 1426 (abs( self.high - self.low ) > 15)): 1427 return self.simple_editor( object, trait_name, description, 1428 handler, parent ) 1429 control = TraitEditorEnum.custom_editor( self, object, trait_name, 1430 description, handler, parent ) 1431 control.is_enum = True 1432 return control
1433 1434 #---------------------------------------------------------------------------- 1435 # Return the set of all possible values: 1436 #---------------------------------------------------------------------------- 1437
1438 - def all_values ( self ):
1439 return [ str( x ) for x in xrange( self.low, self.high + 1 ) ]
1440 1441 #---------------------------------------------------------------------------- 1442 # Return the current value of the object trait: 1443 #---------------------------------------------------------------------------- 1444
1445 - def current_value ( self, object, trait_name ):
1446 return str( getattr( object, trait_name ) )
1447 1448 #---------------------------------------------------------------------------- 1449 # Handle the user selecting a new value from the spin control: 1450 #---------------------------------------------------------------------------- 1451
1452 - def on_value_changed ( self, event ):
1453 control = event.GetEventObject() 1454 self.set( control.object, control.trait_name, control.GetValue(), 1455 control.handler )
1456 1457 #---------------------------------------------------------------------------- 1458 # Handle the user pressing the 'Enter' key in the edit control: 1459 #---------------------------------------------------------------------------- 1460
1461 - def on_enter ( self, event ):
1462 control = text = event.GetEventObject() 1463 slider = None 1464 if not hasattr( control, 'object' ): 1465 control = control.GetParent() 1466 slider = control.slider 1467 try: 1468 value = text.GetValue().strip() 1469 self.set( control.object, control.trait_name, value, 1470 control.handler ) 1471 if slider is not None: 1472 slider.SetValue( int( ((float( value ) - self.low) / 1473 (self.high - self.low)) * 10000 ) ) 1474 except TraitError, excp: 1475 self.error( control.description, excp, control )
1476 1477 #---------------------------------------------------------------------------- 1478 # Handle the user changing the contents of the edit control: 1479 #---------------------------------------------------------------------------- 1480
1481 - def on_key ( self, event ):
1482 control = event.GetEventObject() 1483 try: 1484 setattr( control.object, control.trait_name, 1485 control.GetValue().strip() ) 1486 color = wx.wxWHITE 1487 except: 1488 color = error_color 1489 control.SetBackgroundColour( color ) 1490 control.Refresh()
1491 1492 #---------------------------------------------------------------------------- 1493 # Handle the user changing the current slider value: 1494 #---------------------------------------------------------------------------- 1495
1496 - def on_scroll ( self, event ):
1497 control = event.GetEventObject().GetParent() 1498 value = self.low + ((float( event.GetPosition() ) / 10000.0) * 1499 (self.high - self.low)) 1500 control.text.SetValue( self.format % value ) 1501 event_type = event.GetEventType() 1502 if ((event_type == wx.wxEVT_SCROLL_ENDSCROLL) or 1503 (self.auto_set and (event_type == wx.wxEVT_SCROLL_THUMBTRACK))): 1504 self.set( control.object, control.trait_name, value, 1505 control.handler )
1506 1507 #---------------------------------------------------------------------------- 1508 # Handle the object trait changing value outside the editor: 1509 #---------------------------------------------------------------------------- 1510
1511 - def on_trait_change_spinner ( self, control, new ):
1512 try: 1513 control.SetValue( int( new ) ) 1514 except: 1515 pass
1516
1517 - def on_trait_change_slider ( self, control, new ):
1518 try: 1519 new_text = self.format % new 1520 1 / (self.low <= new <= self.high) 1521 except: 1522 new_text = '' 1523 new = self.low 1524 ivalue = int( (float(new - self.low) / (self.high - self.low)) * 10000.0) 1525 control.text.SetValue( new_text ) 1526 control.slider.SetValue( ivalue )
1527 1528 #------------------------------------------------------------------------------- 1529 # 'TraitEditorComplex class: 1530 #------------------------------------------------------------------------------- 1531
1532 -class TraitEditorComplex ( TraitEditorText ):
1533 1534 #---------------------------------------------------------------------------- 1535 # Initialize the object: 1536 #---------------------------------------------------------------------------- 1537
1538 - def __init__ ( self, editors, auto_set = True ):
1539 TraitEditorText.__init__( self, auto_set = auto_set ) 1540 self.editors = editors
1541 1542 #---------------------------------------------------------------------------- 1543 # Create an in-place custom view of the current value of the 1544 # 'trait_name' trait of 'object': 1545 #---------------------------------------------------------------------------- 1546
1547 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1548 # Create a panel to hold all of the component trait editors: 1549 panel = wx.wxPanel( parent, -1 ) 1550 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 1551 1552 # Add all of the component trait editors: 1553 for editor in self.editors: 1554 control = editor.custom_editor( object, trait_name, description, 1555 handler, panel ) 1556 init_control( control, object, trait_name, description, handler ) 1557 sizer.Add( control, 1, editor.layout_style() ) 1558 1559 # Set-up the layout: 1560 panel.SetAutoLayout( True ) 1561 panel.SetSizer( sizer ) 1562 sizer.Fit( panel ) 1563 1564 # Return the panel as the result: 1565 return panel
1566 1567 #------------------------------------------------------------------------------- 1568 # 'TraitEditorList class: 1569 #------------------------------------------------------------------------------- 1570
1571 -class TraitEditorList ( wxTraitEditor ):
1572 1573 # Normal list item menu: 1574 list_menu = """ 1575 Add before [menu_before]: self.add_before() 1576 Add after [menu_after]: self.add_after() 1577 --- 1578 Delete [menu_delete]: self.delete_item() 1579 --- 1580 Move up [menu_up]: self.move_up() 1581 Move down [menu_down]: self.move_down() 1582 Move to top [menu_top]: self.move_top() 1583 Move to bottom [menu_bottom]: self.move_bottom() 1584 """ 1585 1586 # Empty list item menu: 1587 empty_list_menu = """ 1588 Add: self.add_empty() 1589 """ 1590 1591 #---------------------------------------------------------------------------- 1592 # Initialize the object: 1593 #---------------------------------------------------------------------------- 1594
1595 - def __init__ ( self, trait_handler, rows = 5 ):
1596 self.trait_handler = trait_handler 1597 self.rows = rows
1598 1599 #---------------------------------------------------------------------------- 1600 # Create an in-place read only view of the current value of the 1601 # 'trait_name' trait of 'object': 1602 #---------------------------------------------------------------------------- 1603
1604 - def readonly_editor ( self, object, trait_name, description, handler, 1605 parent ):
1606 return self.list_editor( object, trait_name, description, handler, 1607 parent, 'readonly_editor' )
1608 1609 #---------------------------------------------------------------------------- 1610 # Create an in-place text editable view of the current value of the 1611 # 'trait_name' trait of 'object': 1612 #---------------------------------------------------------------------------- 1613
1614 - def text_editor ( self, object, trait_name, description, handler, 1615 parent ):
1616 return self.list_editor( object, trait_name, description, handler, 1617 parent, 'text_editor' )
1618 1619 #---------------------------------------------------------------------------- 1620 # Create an in-place editable view of the current value of the 1621 # 'trait_name' trait of 'object': 1622 #---------------------------------------------------------------------------- 1623
1624 - def simple_editor ( self, object, trait_name, description, handler, 1625 parent ):
1626 return self.list_editor( object, trait_name, description, handler, 1627 parent, 'simple_editor' )
1628 1629 #---------------------------------------------------------------------------- 1630 # Create an in-place custom view of the current value of the 1631 # 'trait_name' trait of 'object': 1632 #---------------------------------------------------------------------------- 1633
1634 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1635 return self.list_editor( object, trait_name, description, handler, 1636 parent, 'custom_editor' )
1637 1638 #---------------------------------------------------------------------------- 1639 # Create an editable list view of the current value of the 'trait_name' 1640 # trait of 'object': 1641 #---------------------------------------------------------------------------- 1642
1643 - def list_editor ( self, object, trait_name, description, handler, parent, 1644 type ):
1645 # Create all of the list item trait editors: 1646 trait_handler = self.trait_handler 1647 resizable = ((trait_handler.min_items != trait_handler.max_items) and 1648 (type != 'readonly_editor')) 1649 item_trait = trait_handler.item_trait 1650 list_pane = wx.wxScrolledWindow( parent, -1 ) 1651 init_control( list_pane, object, trait_name, description, handler, None ) 1652 list_pane.editor = editor = getattr( item_trait.get_editor(), type ) 1653 list_sizer = wx.wxFlexGridSizer( 0, 1 + resizable, 0, 0 ) 1654 list_sizer.AddGrowableCol( resizable ) 1655 values = getattr( object, trait_name ) 1656 index = 0 1657 width, height = 100, 18 1658 is_fake = (resizable and (len( values ) == 0)) 1659 if is_fake: 1660 values = [ item_trait.default_value ] 1661 for value in values: 1662 width = height = 0 1663 if resizable: 1664 control = ImageControl( list_pane, 1665 bitmap_cache( 'list_editor', False ), 1666 -1, self.popup_menu ) 1667 width, height = control.GetSize() 1668 width += 4 1669 try: 1670 proxy = ListItemProxy( object, trait_name, index, 1671 item_trait, value ) 1672 pcontrol = editor( proxy, 'value', description, handler, 1673 list_pane ) 1674 if resizable: 1675 control.pcontrol = pcontrol 1676 init_control( pcontrol, proxy, 'value', description, handler, 1677 value ) 1678 except: 1679 if not is_fake: 1680 raise 1681 pcontrol = wx.wxButton( list_pane, -1, 'sample' ) 1682 width2, height2 = pcontrol.GetSize() 1683 width += width2 1684 height = max( height, height2 ) 1685 if resizable: 1686 list_sizer.Add( control, 0, wx.wxLEFT | wx.wxRIGHT, 2 ) 1687 list_sizer.Add( pcontrol, 1, wx.wxEXPAND ) 1688 index += 1 1689 list_pane.SetAutoLayout( True ) 1690 list_pane.SetSizer( list_sizer ) 1691 if is_fake: 1692 self.cur_control = control 1693 self.empty_list( list_pane ) 1694 control.Destroy() 1695 pcontrol.Destroy() 1696 rows = [ self.rows, 1 ][ type == 'simple_editor' ] 1697 list_pane.SetSize( wx.wxSize( 1698 width + ((trait_handler.max_items > rows) * scrollbar_dx), 1699 height * rows ) ) 1700 list_pane.SetScrollRate( 0, height ) 1701 return list_pane
1702 1703 #---------------------------------------------------------------------------- 1704 # Add a new value at the specified list index: 1705 #---------------------------------------------------------------------------- 1706
1707 - def add_item ( self, offset ):
1708 controls = self.get_controls() 1709 list, index = self.get_info() 1710 index += offset 1711 item_trait = self.trait_handler.item_trait 1712 value = item_trait.default_value 1713 list[ index: index ] = [ value ] 1714 list_pane = self.cur_control.GetParent() 1715 control = ImageControl( list_pane, 1716 bitmap_cache( 'list_editor', False ), 1717 -1, self.popup_menu ) 1718 proxy = ListItemProxy( list_pane.object, list_pane.trait_name, index, 1719 item_trait, value ) 1720 pcontrol = list_pane.editor( proxy, 'value', list_pane.description, 1721 list_pane.handler, list_pane ) 1722 control.pcontrol = pcontrol 1723 init_control( pcontrol, proxy, 'value', list_pane.description, 1724 list_pane.handler, value ) 1725 controls[ index: index ] = [ ( control, pcontrol ) ] 1726 self.reload_sizer( controls, -2 )
1727 1728 #---------------------------------------------------------------------------- 1729 # Create an empty list entry (so the user can add a new item): 1730 #---------------------------------------------------------------------------- 1731
1732 - def empty_list ( self, list_pane ):
1733 control = ImageControl( list_pane, bitmap_cache( 'list_editor', False ), 1734 -1, self.popup_empty_menu ) 1735 control.is_empty = True 1736 proxy = ListItemProxy( list_pane.object, list_pane.trait_name, -1, 1737 None, None ) 1738 pcontrol = wx.wxStaticText( list_pane, -1, ' (Empty List)' ) 1739 control.pcontrol = pcontrol 1740 pcontrol.object = proxy 1741 self.reload_sizer( [ ( control, pcontrol ) ] )
1742 1743 #---------------------------------------------------------------------------- 1744 # Display the empty list editor popup menu: 1745 #---------------------------------------------------------------------------- 1746
1747 - def popup_empty_menu ( self, control ):
1748 self.cur_control = control 1749 control.PopupMenuXY( MakeMenu( self.empty_list_menu, self, True, 1750 control ).menu, 0, 0 )
1751 1752 #---------------------------------------------------------------------------- 1753 # Display the list editor popup menu: 1754 #---------------------------------------------------------------------------- 1755
1756 - def popup_menu ( self, control ):
1757 self.cur_control = control 1758 proxy = control.pcontrol.object 1759 index = proxy.index 1760 menu = MakeMenu( self.list_menu, self, True, control ).menu 1761 len_list = len( proxy.list() ) 1762 not_full = (len_list < self.trait_handler.max_items) 1763 self.menu_before.enabled( not_full ) 1764 self.menu_after.enabled( not_full ) 1765 self.menu_delete.enabled( len_list > self.trait_handler.min_items ) 1766 self.menu_up.enabled( index > 0 ) 1767 self.menu_top.enabled( index > 0 ) 1768 self.menu_down.enabled( index < (len_list - 1) ) 1769 self.menu_bottom.enabled( index < (len_list - 1) ) 1770 control.PopupMenuXY( menu, 0, 0 )
1771 1772 #---------------------------------------------------------------------------- 1773 # Insert a new item before the current item: 1774 #---------------------------------------------------------------------------- 1775
1776 - def add_before ( self ):
1777 self.add_item( 0 )
1778 1779 #---------------------------------------------------------------------------- 1780 # Insert a new item after the current item: 1781 #---------------------------------------------------------------------------- 1782
1783 - def add_after ( self ):
1784 self.add_item( 1 )
1785 1786 #---------------------------------------------------------------------------- 1787 # Add a new item when the list is empty: 1788 #---------------------------------------------------------------------------- 1789
1790 - def add_empty ( self ):
1791 self.add_item( 0 ) 1792 self.delete_item()
1793 1794 #---------------------------------------------------------------------------- 1795 # Delete the current item: 1796 #---------------------------------------------------------------------------- 1797
1798 - def delete_item ( self ):
1799 controls = self.get_controls() 1800 list, index = self.get_info() 1801 control, pcontrol = controls[ index ] 1802 del controls[ index ] 1803 if hasattr( control, 'is_empty' ): 1804 self.reload_sizer( controls, 2 ) 1805 else: 1806 del list[ index ] 1807 if len( list ) == 0: 1808 self.empty_list( control.GetParent() ) 1809 else: 1810 self.reload_sizer( controls, 2 ) 1811 control.Destroy() 1812 pcontrol.Destroy()
1813 1814 #---------------------------------------------------------------------------- 1815 # Move the current item up one in the list: 1816 #---------------------------------------------------------------------------- 1817
1818 - def move_up ( self ):
1819 controls = self.get_controls() 1820 list, index = self.get_info() 1821 controls[index-1], controls[index] = controls[index], controls[index-1] 1822 list[index-1], list[index] = list[index], list[index-1] 1823 self.reload_sizer( controls )
1824 1825 #---------------------------------------------------------------------------- 1826 # Move the current item down one in the list: 1827 #---------------------------------------------------------------------------- 1828
1829 - def move_down ( self ):
1830 controls = self.get_controls() 1831 list, index = self.get_info() 1832 controls[index+1], controls[index] = controls[index], controls[index+1] 1833 list[index+1], list[index] = list[index], list[index+1] 1834 self.reload_sizer( controls )
1835 1836 #---------------------------------------------------------------------------- 1837 # Move the current item to the top of the list: 1838 #---------------------------------------------------------------------------- 1839
1840 - def move_top ( self ):
1841 controls = self.get_controls() 1842 list, index = self.get_info() 1843 control = [ controls[ index ] ] 1844 item = [ list[ index ] ] 1845 del controls[ index ] 1846 del list[ index ] 1847 controls[0:0] = control 1848 list[0:0] = item 1849 self.reload_sizer( controls )
1850 1851 #---------------------------------------------------------------------------- 1852 # Move the current item to the bottom of the list: 1853 #---------------------------------------------------------------------------- 1854
1855 - def move_bottom ( self ):
1856 controls = self.get_controls() 1857 list, index = self.get_info() 1858 control = controls[ index ] 1859 item = list[ index ] 1860 del controls[ index ] 1861 del list[ index ] 1862 controls.append( control ) 1863 list.append( item ) 1864 self.reload_sizer( controls )
1865 1866 #---------------------------------------------------------------------------- 1867 # Return the associated object list and current item index: 1868 #---------------------------------------------------------------------------- 1869
1870 - def get_info ( self ):
1871 cur_control = self.cur_control 1872 proxy = self.cur_control.pcontrol.object 1873 return ( proxy.list(), proxy.index )
1874 1875 #---------------------------------------------------------------------------- 1876 # Return all controls in the correct index order as ( button, proxy ) pairs: 1877 #---------------------------------------------------------------------------- 1878
1879 - def get_controls ( self ):
1880 cur_control = self.cur_control 1881 result = [] 1882 controls = cur_control.GetParent().GetChildren() 1883 for i in xrange( 0, len( controls ), 2 ): 1884 result.append( ( controls[i], controls[i+1] ) ) 1885 result.sort( lambda x, y: cmp( x[1].object.index, y[1].object.index ) ) 1886 return result
1887 1888 #---------------------------------------------------------------------------- 1889 # Reload the layout from the specified list of ( button, proxy ) pairs: 1890 #---------------------------------------------------------------------------- 1891
1892 - def reload_sizer ( self, controls, extra = 0 ):
1893 list = self.cur_control.GetParent() 1894 sizer = list.GetSizer() 1895 for i in xrange( 2 * len( controls ) + extra ): 1896 sizer.Remove( 0 ) 1897 index = 0 1898 for control, pcontrol in controls: 1899 sizer.Add( control, 0, wx.wxLEFT | wx.wxRIGHT, 2 ) 1900 sizer.Add( pcontrol, 1, wx.wxEXPAND ) 1901 pcontrol.object.index = index 1902 index += 1 1903 sizer.Layout() 1904 list.SetVirtualSize( sizer.GetMinSize() )
1905 1906 #------------------------------------------------------------------------------- 1907 # 'ListItemProxy' class: 1908 #------------------------------------------------------------------------------- 1909
1910 -class ListItemProxy ( HasDynamicTraits ):
1911
1912 - def __init__ ( self, object, trait_name, index, trait, value ):
1913 HasDynamicTraits.__init__( self ) 1914 self.add_trait( 'value', trait ) 1915 self.object = object 1916 self.trait_name = trait_name 1917 self.index = index 1918 self.value = value
1919
1920 - def list ( self ):
1921 return getattr( self.object, self.trait_name )
1922
1923 - def value_changed ( self, old_value, new_value ):
1924 self.list()[ self.index ] = new_value
1925 1926 #------------------------------------------------------------------------------- 1927 # 'TraitEditorFile' class: 1928 #------------------------------------------------------------------------------- 1929
1930 -class TraitEditorFile ( TraitEditorText ):
1931 1932 #---------------------------------------------------------------------------- 1933 # Initialize the object: 1934 #---------------------------------------------------------------------------- 1935
1936 - def __init__ ( self, filter = None ):
1937 TraitEditorText.__init__( self ) 1938 self.filter = filter
1939 1940 #---------------------------------------------------------------------------- 1941 # Create an in-place simple view of the current value of the 1942 # 'trait_name' trait of 'object': 1943 #---------------------------------------------------------------------------- 1944
1945 - def simple_editor ( self, object, trait_name, description, handler, 1946 parent ):
1947 panel = wx.wxPanel( parent, -1 ) 1948 sizer = wx.wxBoxSizer( wx.wxHORIZONTAL ) 1949 control = wx.wxTextCtrl( panel, -1, 1950 self.str( object, trait_name ), 1951 style = wx.wxTE_PROCESS_ENTER ) 1952 init_control( control, object, trait_name, description, handler ) 1953 wx.EVT_KILL_FOCUS( control, self.on_enter ) 1954 wx.EVT_TEXT_ENTER( panel, control.GetId(), self.on_enter ) 1955 wx.EVT_TEXT( panel, control.GetId(), self.on_key ) 1956 sizer.Add( control, 1, wx.wxEXPAND | wx.wxALIGN_CENTER ) 1957 button = wx.wxButton( panel, -1, 'Browse...' ) 1958 button.filename = control 1959 sizer.Add( button, 0, wx.wxLEFT | wx.wxALIGN_CENTER, 8 ) 1960 wx.EVT_BUTTON( panel, button.GetId(), self.on_browse ) 1961 panel.SetAutoLayout( True ) 1962 panel.SetSizer( sizer ) 1963 sizer.Fit( panel ) 1964 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 1965 return panel
1966 1967 #---------------------------------------------------------------------------- 1968 # Interactively edit the 'trait_name' color trait of 'object': 1969 #---------------------------------------------------------------------------- 1970
1971 - def on_browse ( self, event ):
1972 control = event.GetEventObject() 1973 parent = control.GetParent() 1974 dlg = wx.wxFileDialog( control, message = 'Select a file' ) 1975 dlg.SetFilename( control.filename.GetValue() ) 1976 if self.filter is not None: 1977 dlg.SetWildcard( self.filter ) 1978 rc = (dlg.ShowModal() == wx.wxID_OK) 1979 filename = os.path.abspath( dlg.GetPath() ) 1980 dlg.Destroy() 1981 if rc: 1982 control.filename.SetValue( filename ) 1983 self.set( parent.object, parent.trait_name, filename, parent.handler )
1984 1985 #------------------------------------------------------------------------------- 1986 # 'TraitEditorImageDialog' class: 1987 #------------------------------------------------------------------------------- 1988
1989 -class TraitEditorImageDialog ( wx.wxDialog ):
1990 1991 #---------------------------------------------------------------------------- 1992 # Initialize the object: 1993 #---------------------------------------------------------------------------- 1994
1995 - def __init__ ( self, object, trait_name, description, parent, handler, 1996 editor ):
1997 wx.wxDialog.__init__( self, parent, -1, 'Choose ' + description ) 1998 wx.EVT_CLOSE( self, self.on_close_dialog ) 1999 2000 # Initialize instance data: 2001 self.object = object 2002 self.trait_name = trait_name 2003 self.handler = handler 2004 self.editor = editor 2005 2006 # Create the grid of image buttons: 2007 if editor.mapped: 2008 trait_name == '_' 2009 editor.create_image_grid( self, getattr( object, trait_name ), 2010 self.on_click ) 2011 2012 # Position the dialog: 2013 position_near( parent, self )
2014 2015 #---------------------------------------------------------------------------- 2016 # Close the dialog: 2017 #---------------------------------------------------------------------------- 2018
2019 - def on_close_dialog ( self, event, rc = False ):
2020 self.EndModal( rc ) 2021 self.Destroy()
2022 2023 #---------------------------------------------------------------------------- 2024 # Handle the user clicking one of the choice buttons: 2025 #---------------------------------------------------------------------------- 2026
2027 - def on_click ( self, control ):
2028 self.editor.set( self.object, self.trait_name, control.value, 2029 self.handler )
2030 2031 #------------------------------------------------------------------------------- 2032 # Convert an image file name to a cached bitmap: 2033 #------------------------------------------------------------------------------- 2034 2035 # Bitmap cache dictionary (indexed by filename): 2036 _bitmap_cache = {} 2037 2038 ### NOTE: This needs major improvements: 2039 app_path = None 2040 traits_path = None 2041
2042 -def bitmap_cache ( name, standard_size, path = None ):
2043 global app_path, traits_path 2044 if path is None: 2045 if traits_path is None: 2046 import traits 2047 traits_path = os.path.join( 2048 os.path.dirname( traits.__file__ ), 'images' ) 2049 path = traits_path 2050 elif path == '': 2051 if app_path is None: 2052 app_path = os.path.join( os.path.dirname( sys.argv[0] ), 2053 '..', 'images' ) 2054 path = app_path 2055 filename = os.path.abspath( os.path.join( path, 2056 name.replace( ' ', '_' ).lower() + '.gif' ) ) 2057 bitmap = _bitmap_cache.get( filename + ('*'[ not standard_size: ]) ) 2058 if bitmap is not None: 2059 return bitmap 2060 std_bitmap = bitmap = wx.wxBitmapFromImage( wx.wxImage( filename ) ) 2061 _bitmap_cache[ filename ] = bitmap 2062 dx = bitmap.GetWidth() 2063 if dx < standard_bitmap_width: 2064 dy = bitmap.GetHeight() 2065 std_bitmap = wx.wxEmptyBitmap( standard_bitmap_width, dy ) 2066 dc1 = wx.wxMemoryDC() 2067 dc2 = wx.wxMemoryDC() 2068 dc1.SelectObject( std_bitmap ) 2069 dc2.SelectObject( bitmap ) 2070 dc1.SetPen( wx.wxTRANSPARENT_PEN ) 2071 dc1.SetBrush( wx.wxWHITE_BRUSH ) 2072 dc1.DrawRectangle( 0, 0, standard_bitmap_width, dy ) 2073 dc1.Blit( (standard_bitmap_width - dx) // 2, 0, dx, dy, dc2, 0, 0 ) 2074 _bitmap_cache[ filename + '*' ] = std_bitmap 2075 if standard_size: 2076 return std_bitmap 2077 return bitmap
2078 2079 #------------------------------------------------------------------------------- 2080 # Standard colors: 2081 #------------------------------------------------------------------------------- 2082 2083 standard_colors = { 2084 'aquamarine': wx.wxNamedColour( 'aquamarine' ), 2085 'black': wx.wxNamedColour( 'black' ), 2086 'blue': wx.wxNamedColour( 'blue' ), 2087 'blue violet': wx.wxNamedColour( 'blue violet' ), 2088 'brown': wx.wxNamedColour( 'brown' ), 2089 'cadet blue': wx.wxNamedColour( 'cadet blue' ), 2090 'coral': wx.wxNamedColour( 'coral' ), 2091 'cornflower blue': wx.wxNamedColour( 'cornflower blue' ), 2092 'cyan': wx.wxNamedColour( 'cyan' ), 2093 'dark grey': wx.wxNamedColour( 'dark grey' ), 2094 'dark green': wx.wxNamedColour( 'dark green' ), 2095 'dark olive green': wx.wxNamedColour( 'dark olive green' ), 2096 'dark orchid': wx.wxNamedColour( 'dark orchid' ), 2097 'dark slate blue': wx.wxNamedColour( 'dark slate blue' ), 2098 'dark slate grey': wx.wxNamedColour( 'dark slate grey' ), 2099 'dark turquoise': wx.wxNamedColour( 'dark turquoise' ), 2100 'dim grey': wx.wxNamedColour( 'dim grey' ), 2101 'firebrick': wx.wxNamedColour( 'firebrick' ), 2102 'forest green': wx.wxNamedColour( 'forest green' ), 2103 'gold': wx.wxNamedColour( 'gold' ), 2104 'goldenrod': wx.wxNamedColour( 'goldenrod' ), 2105 'grey': wx.wxNamedColour( 'grey' ), 2106 'green': wx.wxNamedColour( 'green' ), 2107 'green yellow': wx.wxNamedColour( 'green yellow' ), 2108 'indian red': wx.wxNamedColour( 'indian red' ), 2109 'khaki': wx.wxNamedColour( 'khaki' ), 2110 'light blue': wx.wxNamedColour( 'light blue' ), 2111 'light grey': wx.wxNamedColour( 'light grey' ), 2112 'light steel': wx.wxNamedColour( 'light steel' ), 2113 'blue': wx.wxNamedColour( 'blue' ), 2114 'lime green': wx.wxNamedColour( 'lime green' ), 2115 'magenta': wx.wxNamedColour( 'magenta' ), 2116 'maroon': wx.wxNamedColour( 'maroon' ), 2117 'medium aquamarine': wx.wxNamedColour( 'medium aquamarine' ), 2118 'medium blue': wx.wxNamedColour( 'medium blue' ), 2119 'medium forest green': wx.wxNamedColour( 'medium forest green' ), 2120 'medium goldenrod': wx.wxNamedColour( 'medium goldenrod' ), 2121 'medium orchid': wx.wxNamedColour( 'medium orchid' ), 2122 'medium sea green': wx.wxNamedColour( 'medium sea green' ), 2123 'medium slate blue': wx.wxNamedColour( 'medium slate blue' ), 2124 'medium spring green': wx.wxNamedColour( 'medium spring green' ), 2125 'medium turquoise': wx.wxNamedColour( 'medium turquoise' ), 2126 'medium violet red': wx.wxNamedColour( 'medium violet red' ), 2127 'midnight blue': wx.wxNamedColour( 'midnight blue' ), 2128 'navy': wx.wxNamedColour( 'navy' ), 2129 'orange': wx.wxNamedColour( 'orange' ), 2130 'orange red': wx.wxNamedColour( 'orange red' ), 2131 'orchid': wx.wxNamedColour( 'orchid' ), 2132 'pale green': wx.wxNamedColour( 'pale green' ), 2133 'pink': wx.wxNamedColour( 'pink' ), 2134 'plum': wx.wxNamedColour( 'plum' ), 2135 'purple': wx.wxNamedColour( 'purple' ), 2136 'red': wx.wxNamedColour( 'red' ), 2137 'salmon': wx.wxNamedColour( 'salmon' ), 2138 'sea green': wx.wxNamedColour( 'sea green' ), 2139 'sienna': wx.wxNamedColour( 'sienna' ), 2140 'sky blue': wx.wxNamedColour( 'sky blue' ), 2141 'slate blue': wx.wxNamedColour( 'slate blue' ), 2142 'spring green': wx.wxNamedColour( 'spring green' ), 2143 'steel blue': wx.wxNamedColour( 'steel blue' ), 2144 'tan': wx.wxNamedColour( 'tan' ), 2145 'thistle': wx.wxNamedColour( 'thistle' ), 2146 'turquoise': wx.wxNamedColour( 'turquoise' ), 2147 'violet': wx.wxNamedColour( 'violet' ), 2148 'violet red': wx.wxNamedColour( 'violet red' ), 2149 'wheat': wx.wxNamedColour( 'wheat' ), 2150 'white': wx.wxNamedColour( 'white' ), 2151 'yellow': wx.wxNamedColour( 'yellow' ), 2152 'yellow green': wx.wxNamedColour( 'yellow green' ), 2153 } 2154 2155 #------------------------------------------------------------------------------- 2156 # Convert a number into a wxColour object: 2157 #------------------------------------------------------------------------------- 2158
2159 -def num_to_color ( object, name, value ):
2160 num = int( value ) 2161 return wx.wxColour( num // 0x10000, (num // 0x100) & 0xFF, num & 0xFF )
2162 2163 num_to_color.info = ('a number, which in hex is of the form 0xRRGGBB, where ' 2164 'RR is red, GG is green, and BB is blue') 2165 2166 #------------------------------------------------------------------------------- 2167 # 'TraitEditorColor' class: 2168 #------------------------------------------------------------------------------- 2169
2170 -class TraitEditorColor ( wxTraitEditor ):
2171 2172 #---------------------------------------------------------------------------- 2173 # Initialize the object: 2174 #---------------------------------------------------------------------------- 2175
2176 - def __init__ ( self ):
2177 self.color_data = wx.wxColourData()
2178 2179 #---------------------------------------------------------------------------- 2180 # Interactively edit the 'trait_name' color trait of 'object': 2181 #---------------------------------------------------------------------------- 2182
2183 - def popup_editor ( self, object, trait_name, description, handler, 2184 parent = None ):
2185 if not hasattr( parent, 'is_custom' ): 2186 # Fixes a problem with the edit field having the focus: 2187 parent.ReleaseMouse() 2188 return TraitEditorColorDialog( object, trait_name, description, 2189 parent, handler, self ).ShowModal() 2190 2191 self.color_data.SetColour( self.to_wx_color( object, trait_name ) ) 2192 self.color_data.SetChooseFull( True ) 2193 dialog = wx.wxColourDialog( parent or object.window, self.color_data ) 2194 if dialog.ShowModal() == wx.wxID_OK: 2195 panel = parent 2196 if panel is not None: 2197 panel = panel.GetParent() 2198 self.set( object, trait_name, 2199 self.from_wx_color( dialog.GetColourData().GetColour(), 2200 panel ), 2201 handler ) 2202 return True 2203 return False
2204 2205 #---------------------------------------------------------------------------- 2206 # Create a view of the current value of the 'trait_name' trait of 'object': 2207 #---------------------------------------------------------------------------- 2208
2209 - def create_editor ( self, object, trait_name, description, handler, 2210 parent, factory ):
2211 control = factory( self, object, trait_name, description, handler, 2212 parent ) 2213 self.set_color( control, self.to_wx_color( object, trait_name ) ) 2214 try: 2215 getattr( object, trait_name + '_' ) 2216 TraitMonitor( object, trait_name, control, self.on_trait_change_text ) 2217 TraitMonitor( object, trait_name + '_', control, 2218 self.on_trait_change_color ) 2219 except: 2220 TraitMonitor( object, trait_name, control, self.on_trait_change_both ) 2221 return control
2222 2223 #---------------------------------------------------------------------------- 2224 # Create a read only view of the current value of the 'trait_name' 2225 # trait of 'object': 2226 #---------------------------------------------------------------------------- 2227
2228 - def readonly_editor ( self, object, trait_name, description, handler, 2229 parent ):
2230 return self.create_editor( object, trait_name, description, handler, 2231 parent, wxTraitEditor.readonly_editor )
2232 2233 #---------------------------------------------------------------------------- 2234 # Create a static view of the current value of the 'trait_name' 2235 # trait of 'object': 2236 #---------------------------------------------------------------------------- 2237
2238 - def simple_editor ( self, object, trait_name, description, handler, 2239 parent ):
2240 return self.create_editor( object, trait_name, description, handler, 2241 parent, wxTraitEditor.simple_editor )
2242 2243 #---------------------------------------------------------------------------- 2244 # Create an in-place custom view of the current value of the 2245 # 'trait_name' trait of 'object': 2246 #---------------------------------------------------------------------------- 2247
2248 - def custom_editor ( self, object, trait_name, description, handler, parent ):
2249 # Create a panel to hold all of the buttons: 2250 panel = wx.wxPanel( parent, -1 ) 2251 sizer = wx.wxBoxSizer( wx.wxHORIZONTAL ) 2252 control = panel.color = self.simple_editor( object, trait_name, 2253 description, handler, panel ) 2254 control.is_custom = True 2255 init_control( control, object, trait_name, description, handler, None ) 2256 control.SetSize( wx.wxSize( 72, 72 ) ) 2257 sizer.Add( control, 0, wx.wxEXPAND | wx.wxRIGHT, 4 ) 2258 2259 # Add all of the color choice buttons: 2260 sizer2 = wx.wxGridSizer( 0, 12, 0, 0 ) 2261 2262 for i in range( len( color_samples ) ): 2263 control = wx.wxButton( panel, -1, '', 2264 size = wx.wxSize( 18, 18 ) ) 2265 control.SetBackgroundColour( color_samples[i] ) 2266 wx.EVT_BUTTON( panel, control.GetId(), self.on_click ) 2267 sizer2.Add( control ) 2268 2269 sizer.Add( sizer2 ) 2270 2271 # Set-up the layout: 2272 panel.SetAutoLayout( True ) 2273 panel.SetSizer( sizer ) 2274 sizer.Fit( panel ) 2275 2276 # Return the panel as the result: 2277 return panel
2278 2279 #---------------------------------------------------------------------------- 2280 # Handle the user clicking one of the 'custom' radio buttons: 2281 #---------------------------------------------------------------------------- 2282
2283 - def on_click ( self, event ):
2284 control = event.GetEventObject() 2285 parent = control.GetParent() 2286 self.set( parent.object, parent.trait_name, 2287 self.from_wx_color( control.GetBackgroundColour(), parent ), 2288 parent.handler )
2289 2290 #---------------------------------------------------------------------------- 2291 # Set the color of the 'current color' control: 2292 #---------------------------------------------------------------------------- 2293
2294 - def set_color ( self, control, color ):
2295 control.SetBackgroundColour( color ) 2296 if ((color.Red() > 192) or 2297 (color.Blue() > 192) or 2298 (color.Green() > 192)): 2299 control.SetForegroundColour( wx.wxBLACK ) 2300 else: 2301 control.SetForegroundColour( wx.wxWHITE )
2302 2303 #---------------------------------------------------------------------------- 2304 # Handle the object trait changing value outside the editor: 2305 #---------------------------------------------------------------------------- 2306
2307 - def on_trait_change_color ( self, control, new ):
2308 self.set_color( control, self.to_wx_color( new ) )
2309
2310 - def on_trait_change_both ( self, control, new ):
2311 self.on_trait_change_text( control, new ) 2312 self.on_trait_change_color( control, new )
2313 2314 #---------------------------------------------------------------------------- 2315 # Get the current object trait color: 2316 #---------------------------------------------------------------------------- 2317
2318 - def get_cur_color ( self, object, trait_name ):
2319 try: 2320 return getattr( object, trait_name + '_' ) 2321 except: 2322 return getattr( object, trait_name )
2323 2324 #---------------------------------------------------------------------------- 2325 # Get the wxPython color equivalent of the object trait: 2326 #---------------------------------------------------------------------------- 2327
2328 - def to_wx_color ( self, object, trait_name = None ):
2329 if trait_name is None: 2330 color = object 2331 else: 2332 color = self.get_cur_color( object, trait_name ) 2333 if color is None: 2334 color = wx.wxWHITE 2335 return color
2336 2337 #---------------------------------------------------------------------------- 2338 # Get the application equivalent of a wxPython value: 2339 #---------------------------------------------------------------------------- 2340
2341 - def from_wx_color ( self, color, panel = None ):
2342 return color
2343 2344 #------------------------------------------------------------------------------- 2345 # Define wxPython specific color traits: 2346 #------------------------------------------------------------------------------- 2347 2348 # Create a singleton color editor: 2349 color_editor = TraitEditorColor() 2350 2351 # Color traits: 2352 color_trait = Trait( 'white', wx.wxColour, wx.wxColourPtr, 2353 standard_colors, num_to_color, 2354 editor = color_editor ) 2355 clear_color_trait = Trait( 'clear', None, wx.wxColour, 2356 wx.wxColourPtr, standard_colors, 2357 { 'clear': None }, num_to_color, 2358 editor = color_editor ) 2359 2360 #------------------------------------------------------------------------------- 2361 # 'TraitEditorColorDialog' class: 2362 #------------------------------------------------------------------------------- 2363
2364 -class TraitEditorColorDialog ( wx.wxDialog ):
2365 2366 #---------------------------------------------------------------------------- 2367 # Initialize the object: 2368 #---------------------------------------------------------------------------- 2369
2370 - def __init__ ( self, object, trait_name, description, parent, handler, 2371 editor ):
2372 wx.wxDialog.__init__( self, parent, -1, 'Choose ' + description ) 2373 wx.EVT_CLOSE( self, self.on_close_dialog ) 2374 panel = editor.custom_editor( object, trait_name, description, handler, 2375 self ) 2376 # Initialize instance data: 2377 panel.object = object 2378 panel.trait_name = trait_name 2379 panel.handler = handler 2380 panel.editor = editor 2381 2382 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 2383 sizer.Add( panel ) 2384 self.SetAutoLayout( True ) 2385 self.SetSizer( sizer ) 2386 sizer.Fit( self ) 2387 position_near( parent, self )
2388 2389 #---------------------------------------------------------------------------- 2390 # Close the dialog: 2391 #---------------------------------------------------------------------------- 2392
2393 - def on_close_dialog ( self, event, rc = False ):
2394 self.EndModal( rc ) 2395 self.Destroy()
2396 2397 #------------------------------------------------------------------------------- 2398 # Convert a string into a valid 'wxFont' object (if possible): 2399 #------------------------------------------------------------------------------- 2400 2401 font_families = { 2402 'default': wx.wxDEFAULT, 2403 'decorative': wx.wxDECORATIVE, 2404 'roman': wx.wxROMAN, 2405 'script': wx.wxSCRIPT, 2406 'swiss': wx.wxSWISS, 2407 'modern': wx.wxMODERN 2408 } 2409 2410 font_styles = { 2411 'slant': wx.wxSLANT, 2412 'italic': wx.wxITALIC 2413 } 2414 2415 font_weights = { 2416 'light': wx.wxLIGHT, 2417 'bold': wx.wxBOLD 2418 } 2419 2420 font_noise = [ 'pt', 'point', 'family' ] 2421
2422 -def str_to_font ( object, name, value ):
2423 try: 2424 point_size = 10 2425 family = wx.wxDEFAULT 2426 style = wx.wxNORMAL 2427 weight = wx.wxNORMAL 2428 underline = 0 2429 facename = [] 2430 for word in value.split(): 2431 lword = word.lower() 2432 if font_families.has_key( lword ): 2433 family = font_families[ lword ] 2434 elif font_styles.has_key( lword ): 2435 style = font_styles[ lword ] 2436 elif font_weights.has_key( lword ): 2437 weight = font_weights[ lword ] 2438 elif lword == 'underline': 2439 underline = 1 2440 elif lword not in font_noise: 2441 try: 2442 point_size = int( lword ) 2443 except: 2444 facename.append( word ) 2445 return wx.wxFont( point_size, family, style, weight, underline, 2446 ' '.join( facename ) ) 2447 except: 2448 pass 2449 raise TraitError, ( object, name, 'a font descriptor string', 2450 repr( value ) )
2451 2452 str_to_font.info = ( "a string describing a font (e.g. '12 pt bold italic " 2453 "swiss family Arial' or 'default 12')" ) 2454 2455 #------------------------------------------------------------------------------- 2456 # 'TraitEditorFont' class: 2457 #------------------------------------------------------------------------------- 2458
2459 -class TraitEditorFont ( wxTraitEditor ):
2460 2461 #---------------------------------------------------------------------------- 2462 # Interactively edit the 'trait_name' font trait of 'object': 2463 #---------------------------------------------------------------------------- 2464
2465 - def popup_editor ( self, object, trait_name, description, handler, 2466 parent = None ):
2467 font_data = wx.wxFontData() 2468 font_data.SetInitialFont( self.to_wx_font( object, trait_name ) ) 2469 dialog = wx.wxFontDialog( parent or object.window, font_data ) 2470 if dialog.ShowModal() == wx.wxID_OK: 2471 self.set( object, trait_name, 2472 self.from_wx_font( dialog.GetFontData().GetChosenFont() ), 2473 handler ) 2474 return True 2475 return False
2476 2477 #---------------------------------------------------------------------------- 2478 # Create a view of the current value of the 'trait_name' trait of 'object': 2479 #---------------------------------------------------------------------------- 2480
2481 - def create_editor ( self, object, trait_name, description, handler, 2482 parent, factory ):
2483 control = factory( self, object, trait_name, description, handler, 2484 parent ) 2485 self.on_trait_change_simple( control, getattr( object, trait_name ) ) 2486 TraitMonitor( object, trait_name, control, self.on_trait_change_simple ) 2487 return control
2488 2489 #---------------------------------------------------------------------------- 2490 # Create a read only view of the current value of the 'trait_name' 2491 # trait of 'object': 2492 #---------------------------------------------------------------------------- 2493
2494 - def readonly_editor ( self, object, trait_name, description, handler, 2495 parent ):
2496 return self.create_editor( object, trait_name, description, handler, 2497 parent, wxTraitEditor.readonly_editor )
2498 2499 #---------------------------------------------------------------------------- 2500 # Create a static view of the current value of the 'trait_name' 2501 # trait of 'object': 2502 #---------------------------------------------------------------------------- 2503
2504 - def simple_editor ( self, object, trait_name, description, handler, 2505 parent ):
2506 return self.create_editor( object, trait_name, description, handler, 2507 parent, wxTraitEditor.simple_editor )
2508 2509 #---------------------------------------------------------------------------- 2510 # Create an in-place custom view of the current value of the 2511 # 'trait_name' trait of 'object': 2512 #---------------------------------------------------------------------------- 2513
2514 - def custom_editor ( self, object, trait_name, description, handler, parent ):
2515 # Create a panel to hold all of the buttons: 2516 panel = wx.wxPanel( parent, -1 ) 2517 sizer = wx.wxBoxSizer( wx.wxVERTICAL ) 2518 2519 # Add the standard font control: 2520 font = panel.font = self.simple_editor( object, trait_name, 2521 description, handler, panel ) 2522 init_control( font, object, trait_name, description, handler, None ) 2523 sizer.Add( font, 0, wx.wxEXPAND | wx.wxBOTTOM, 3 ) 2524 2525 # Add all of the font choice controls: 2526 sizer2 = wx.wxBoxSizer( wx.wxHORIZONTAL ) 2527 control = panel.facename = wx.wxChoice( panel, -1, wx.wxPoint( 0, 0 ), 2528 wx.wxSize( 100, 20 ), self.all_facenames() ) 2529 sizer2.Add( control, 2, wx.wxEXPAND ) 2530 wx.EVT_CHOICE( panel, control.GetId(), self.on_value_changed ) 2531 2532 control = panel.pointsize = wx.wxChoice( panel, -1, wx.wxPoint( 0, 0 ), 2533 wx.wxSize( 30, 20 ), point_sizes ) 2534 sizer2.Add( control, 1, wx.wxEXPAND | wx.wxRIGHT, 3 ) 2535 wx.EVT_CHOICE( panel, control.GetId(), self.on_value_changed ) 2536 2537 sizer.Add( sizer2, 0, wx.wxEXPAND ) 2538 2539 # Initialize the control's with the object's current trait value: 2540 self.on_trait_change_custom( font, getattr( object, trait_name ) ) 2541 2542 # Set-up the layout: 2543 panel.SetAutoLayout( True ) 2544 panel.SetSizer( sizer ) 2545 sizer.Fit( panel ) 2546 2547 TraitMonitor( object, trait_name, font, self.on_trait_change_custom ) 2548 2549 # Return the panel as the result: 2550 return panel
2551 2552 #---------------------------------------------------------------------------- 2553 # Handle the object trait changing value outside the editor: 2554 #---------------------------------------------------------------------------- 2555
2556 - def on_trait_change_simple ( self, control, new ):
2557 self.on_trait_change_text( control, new ) 2558 font = self.to_wx_font( new ) 2559 font.SetPointSize( min( 10, font.GetPointSize() ) ) 2560 control.SetFont( font )
2561
2562 - def on_trait_change_custom ( self, control, new ):
2563 self.on_trait_change_text( control, new ) 2564 font = self.to_wx_font( new ) 2565 panel = control.GetParent() 2566 try: 2567 panel.facename.SetStringSelection( font.GetFaceName() ) 2568 except: 2569 panel.facename.SetSelection( 0 ) 2570 try: 2571 panel.pointsize.SetStringSelection( str( font.GetPointSize() ) ) 2572 except: 2573 panel.pointsize.SetSelection( 0 ) 2574 font.SetPointSize( min( 10, font.GetPointSize() ) ) 2575 control.SetFont( font )
2576 2577 #---------------------------------------------------------------------------- 2578 # Return the text representation of the specified object trait value: 2579 #---------------------------------------------------------------------------- 2580
2581 - def str_value ( self, font ):
2582 weight = { wx.wxLIGHT: ' Light', 2583 wx.wxBOLD: ' Bold' }.get( font.GetWeight(), '' ) 2584 style = { wx.wxSLANT: ' Slant', 2585 wx.wxITALIC:' Italic' }.get( font.GetStyle(), '' ) 2586 return '%s point %s%s%s' % ( 2587 font.GetPointSize(), font.GetFaceName(), style, weight )
2588 2589 #---------------------------------------------------------------------------- 2590 # Return a list of all available font facenames: 2591 #---------------------------------------------------------------------------- 2592
2593 - def all_facenames ( self ):
2594 global facenames 2595 if facenames is None: 2596 facenames = FontEnumerator().facenames() 2597 facenames.sort() 2598 return facenames
2599 2600 #---------------------------------------------------------------------------- 2601 # Handle the user selecting a new facename or point size: 2602 #---------------------------------------------------------------------------- 2603
2604 - def on_value_changed ( self, event ):
2605 control = event.GetEventObject().GetParent() 2606 pointsize = int( control.pointsize.GetLabel() ) 2607 facename = control.facename.GetLabel() 2608 font = wx.wxFont( pointsize, wx.wxDEFAULT, wx.wxNORMAL, wx.wxNORMAL, 2609 faceName = facename ) 2610 self.set( control.object, control.trait_name, 2611 self.from_wx_font( font ), control.handler )
2612 2613 #---------------------------------------------------------------------------- 2614 # Return a wxFont object corresponding to a specified object's font trait: 2615 #---------------------------------------------------------------------------- 2616
2617 - def to_wx_font ( self, object, trait_name = None ):
2618 if trait_name is None: 2619 return object 2620 return getattr( object, trait_name )
2621 2622 #---------------------------------------------------------------------------- 2623 # Get the application equivalent of a wxPython value: 2624 #---------------------------------------------------------------------------- 2625
2626 - def from_wx_font ( self, font ):
2627 return font
2628 2629 #------------------------------------------------------------------------------- 2630 # Define a wxPython specific font trait: 2631 #------------------------------------------------------------------------------- 2632 2633 font_trait = Trait( 'Arial 10', wx.wxFont, wx.wxFontPtr, str_to_font, 2634 editor = TraitEditorFont() ) 2635 2636 #------------------------------------------------------------------------------- 2637 # 'FontEnumerator' class: 2638 #------------------------------------------------------------------------------- 2639
2640 -class FontEnumerator ( wx.wxFontEnumerator ):
2641 2642 #---------------------------------------------------------------------------- 2643 # Return a list of all available font facenames: 2644 #---------------------------------------------------------------------------- 2645
2646 - def facenames ( self ):
2647 self._facenames = [] 2648 self.EnumerateFacenames() 2649 return self._facenames
2650 2651 #---------------------------------------------------------------------------- 2652 # Add a facename to the list of facenames: 2653 #---------------------------------------------------------------------------- 2654
2655 - def OnFacename ( self, facename ):
2656 self._facenames.append( facename ) 2657 return True
2658 2659 #------------------------------------------------------------------------------- 2660 # 'ImageControl' class: 2661 #------------------------------------------------------------------------------- 2662
2663 -class ImageControl ( wx.wxWindow ):
2664 2665 # Pens used to draw the 'selection' marker: 2666 _selectedPenDark = wx.wxPen( 2667 wx.wxSystemSettings_GetColour( wx.wxSYS_COLOUR_3DSHADOW ), 1, 2668 wx.wxSOLID ) 2669 _selectedPenLight = wx.wxPen( 2670 wx.wxSystemSettings_GetColour( wx.wxSYS_COLOUR_3DHIGHLIGHT ), 1, 2671 wx.wxSOLID ) 2672 2673 #---------------------------------------------------------------------------- 2674 # Initialize the object: 2675 #---------------------------------------------------------------------------- 2676
2677 - def __init__ ( self, parent, bitmap, selected = None, handler = None ):
2678 wx.wxWindow.__init__( self, parent, -1, 2679 size = wx.wxSize( bitmap.GetWidth() + 10, 2680 bitmap.GetHeight() + 10 ) ) 2681 self._bitmap = bitmap 2682 self._selected = selected 2683 self._handler = handler 2684 self._mouse_over = False 2685 self._button_down = False 2686 2687 # Set up the 'paint' event handler: 2688 wx.EVT_PAINT( self, self._on_paint ) 2689 2690 # Set up mouse event handlers: 2691 wx.EVT_LEFT_DOWN( self, self._on_left_down ) 2692 wx.EVT_LEFT_UP( self, self._on_left_up ) 2693 wx.EVT_ENTER_WINDOW( self, self._on_enter ) 2694 wx.EVT_LEAVE_WINDOW( self, self._on_leave )
2695 2696 #---------------------------------------------------------------------------- 2697 # Get/Set the current selection state of the image: 2698 #---------------------------------------------------------------------------- 2699
2700 - def Selected ( self, selected = None ):
2701 if selected is not None: 2702 selected = (selected != 0) 2703 if selected != self._selected: 2704 if selected: 2705 for control in self.GetParent().GetChildren(): 2706 if (isinstance( control, ImageControl ) and 2707 control.Selected()): 2708 control.Selected( False ) 2709 break 2710 self._selected = selected 2711 self.Refresh() 2712 return self._selected
2713 2714 #---------------------------------------------------------------------------- 2715 # Get/Set the current bitmap image: 2716 #---------------------------------------------------------------------------- 2717
2718 - def Bitmap ( self, bitmap = None ):
2719 if bitmap is not None: 2720 if bitmap != self._bitmap: 2721 self._bitmap = bitmap 2722 self.Refresh() 2723 return self._bitmap
2724 2725 #---------------------------------------------------------------------------- 2726 # Get/Set the current click handler: 2727 #---------------------------------------------------------------------------- 2728
2729 - def Handler ( self, handler = None ):
2730 if handler is not None: 2731 if handler != self._handler: 2732 self._handler = handler 2733 self.Refresh() 2734 return self._handler
2735 2736 #---------------------------------------------------------------------------- 2737 # Handle the mouse entering the control: 2738 #---------------------------------------------------------------------------- 2739
2740 - def _on_enter ( self, event = None ):
2741 if self._selected is not None: 2742 self._mouse_over = True 2743 self.Refresh()
2744 2745 #---------------------------------------------------------------------------- 2746 # Handle the mouse leaving the control: 2747 #---------------------------------------------------------------------------- 2748
2749 - def _on_leave ( self, event = None ):
2750 if self._mouse_over: 2751 self._mouse_over = False 2752 self.Refresh()
2753 2754 #---------------------------------------------------------------------------- 2755 # Handle the user pressing the mouse button: 2756 #---------------------------------------------------------------------------- 2757
2758 - def _on_left_down ( self, event = None ):
2759 if self._selected is not None: 2760 self.CaptureMouse() 2761 self._button_down = True 2762 self.Refresh()
2763 2764 #---------------------------------------------------------------------------- 2765 # Handle the user clicking the control: 2766 #---------------------------------------------------------------------------- 2767
2768 - def _on_left_up ( self, event = None ):
2769 need_refresh = self._button_down 2770 if need_refresh: 2771 self.ReleaseMouse() 2772 self._button_down = False 2773 2774 if self._selected is not None: 2775 wdx, wdy = self.GetClientSizeTuple() 2776 x = event.GetX() 2777 y = event.GetY() 2778 if (0 <= x < wdx) and (0 <= y < wdy): 2779 if self._selected != -1: 2780 self.Selected( True ) 2781 need_refresh = False 2782 if self._handler is not None: 2783 self._handler( self ) 2784 2785 if need_refresh: 2786 self.Refresh()
2787 2788 #---------------------------------------------------------------------------- 2789 # Handle the control being re-painted: 2790 #---------------------------------------------------------------------------- 2791
2792 - def _on_paint ( self, event = None ):
2793 wdc = wx.wxPaintDC( self ) 2794 wdx, wdy = self.GetClientSizeTuple() 2795 bitmap = self._bitmap 2796 bdx = bitmap.GetWidth() 2797 bdy = bitmap.GetHeight() 2798 wdc.DrawBitmap( bitmap, (wdx - bdx) // 2, (wdy - bdy