1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 from __future__ import division
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
36
37
38 trait_editors( __name__ )
39
40
41
42
43
44
45 basic_sequence_types = [ ListType, TupleType ]
46
47
48 standard_bitmap_width = 120
49
50
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
61 facenames = None
62
63
64 point_sizes = [
65 '8', '9', '10', '11', '12', '14', '16', '18',
66 '20', '22', '24', '26', '28', '36', '48', '72'
67 ]
68
69
70
71 tooltips_enabled = True
72
73
74 all_digits = re.compile( r'\d+' )
75
76
77 error_color = wx.wxColour( 255, 192, 192 )
78
79
80 scrollbar_dx = wx.wxSystemSettings_GetSystemMetric( wx.wxSYS_VSCROLL_X )
81
82
83 screen_dx = wx.wxSystemSettings_GetSystemMetric( wx.wxSYS_SCREEN_X )
84 screen_dy = wx.wxSystemSettings_GetSystemMetric( wx.wxSYS_SCREEN_Y )
85
86
87
88
89
91 x, y = origin.ClientToScreenXY( 0, 0 )
92 y -= 30
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
106
107
109
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
123
124
126
127
128
129
130
132 self.app = app
133 self.modified = False
134
135
136
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
152
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
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
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
191
192
194
195
196
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
209
210
215
216
217
218
219
221
222
223
224
225
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
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
264
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
275
276
277 - def close_page ( self, event = None ):
278 if self.handler.close( self, self.object ):
279 self.Destroy()
280
281
282
283
284
286 if event.GetKeyCode() == 0x1B:
287 self.on_close_page( event )
288
289
290
291
292
294
295
296
297
298
300
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
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
317
318
320 sheet.Destroy()
321 self._size = None
322 self._need_layout = True
323
324
325
326
327
329 if self._size is None:
330 self._size = self.GetSizer().GetMinSize()
331 return self._size
332
333
334
335
336
338 self.SetDimensions( x, y, dx, dy )
339 if self._need_layout:
340 self._need_layout = False
341 self.Layout()
342
343
344
345
346
349
350
351
352
353
355
356
357
358
359
364 wx.wxPanel.__init__( self, parent, -1 )
365
366 self.object = object
367 self.handler = handler
368
369
370 if traits is None:
371
372 traits = object.editable_traits()
373
374
375
376 kind = type( traits )
377 if kind is StringType:
378
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
384 return
385 if not isinstance( traits[0], TraitGroup ):
386
387
388 traits = TraitGroup( show_border = False,
389 style = 'custom', *traits )
390
391
392 if isinstance( traits, TraitGroup ):
393
394 sizer = self.add_page( traits, self )
395 self.SetAutoLayout( True )
396 self.SetSizer( sizer )
397 sizer.Fit( self )
398 else:
399
400 self.add_tabs( traits )
401
402
403
404
405
407
408 nb = wx.wxNotebook( self, -1 )
409 nbs = wx.wxNotebookSizer( nb )
410 count = 0
411
412 for pg in traits:
413
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
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
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
541
542
547
548
549
550
551
553
554
555
556
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
568
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
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
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
606
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
619
620
623
625 if hasattr( self, 'popup_editor' ):
626 self.popup_editor( control.object, control.trait_name,
627 control.description, control.handler, control )
628
629
630
631
632
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
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
656
657
660
661
662
663
664
666
667
668
669
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
686
687
703
704
705
706
707
708 -class TraitEditorText ( wxTraitEditor ):
709
710
711
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
721
722
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
741
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
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
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
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
800
801
803
804
805
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
836
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
854
855
856
857 - def custom_editor ( self, object, trait_name, description, handler, parent ):
858
859 panel = wx.wxPanel( parent, -1 )
860
861
862 cur_value = self.current_value( object, trait_name )
863
864
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
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
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
911 return panel
912
913
914
915
916
918 if self.mapped:
919 return self.sorted
920 return self.values
921
922
923
924
925
927 if self.mapped:
928 return self.values[ getattr( object, trait_name + '_' ) ]
929 return str( getattr( object, trait_name ) )
930
931
932
933
934
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
946
947
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
960
961
963 try:
964 control.SetStringSelection( str( new ) )
965 except:
966 pass
967
969 for button in control.GetChildren():
970 button.SetValue( button.value == new )
971
972
973
974
975
977
978
979
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
992
993
998
999
1000
1001
1002
1003
1004 - def readonly_editor ( self, object, trait_name, description, handler,
1005 parent ):
1012
1013
1014
1015
1016
1017
1018 - def simple_editor ( self, object, trait_name, description, handler,
1019 parent ):
1025
1026
1027
1028
1029
1030
1031 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1032
1033 panel = wx.wxPanel( parent, -1 )
1034
1035
1036 self.create_image_grid( panel, getattr( object, trait_name ),
1037 self.on_click )
1038
1039
1040 return panel
1041
1042
1043
1044
1045
1047
1048 if self.cols > 1:
1049 sizer = wx.wxGridSizer( 0, self.cols, 0, 0 )
1050 else:
1051 sizer = wx.wxBoxSizer( wx.wxVERTICAL )
1052
1053
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
1063 parent.SetAutoLayout( True )
1064 parent.SetSizer( sizer )
1065 sizer.Fit( parent )
1066
1067
1068
1069
1070
1073
1074
1075
1076
1077
1080
1081
1082
1083
1084
1086 parent= control.GetParent()
1087 self.set( parent.object, parent.trait_name, control.value,
1088 parent.handler )
1089
1090
1091
1092
1093
1095
1096
1097
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
1111
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
1130
1131
1132
1133 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1134
1135 panel = wx.wxPanel( parent, -1 )
1136
1137
1138 cur_value = self.current_value( object, trait_name )
1139
1140
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
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
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
1180 return panel
1181
1182
1183
1184
1185
1187 return [ x[1] for x in self.values ]
1188
1189
1190
1191
1192
1194 return [ x[0] for x in self.values ]
1195
1196
1197
1198
1199
1200 - def is_string ( self, object, trait_name ):
1201 return (type( getattr( object, trait_name ) ) is StringType)
1202
1203
1204
1205
1206
1209
1210
1211
1212
1213
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
1223
1224
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
1234
1235
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
1251
1252
1254 try:
1255 control.SetSelection( self.all_values().index(
1256 self.parse_value( new )[0] ) )
1257 except:
1258 pass
1259
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
1268
1269
1271
1272
1273
1274
1275
1276
1277 - def readonly_editor ( self, object, trait_name, description, handler,
1278 parent ):
1289
1290
1291
1292
1293
1295 if new:
1296 control.SetValue( 'True' )
1297 else:
1298 control.SetValue( 'False' )
1299
1300
1301
1302
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
1320
1321
1323 control = event.GetEventObject()
1324 self.set( control.object, control.trait_name, control.GetValue(),
1325 control.handler )
1326
1327
1328
1329
1330
1332 control.SetValue( new )
1333
1334
1335
1336
1337
1339
1340
1341
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
1356
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
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
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
1421
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
1436
1437
1439 return [ str( x ) for x in xrange( self.low, self.high + 1 ) ]
1440
1441
1442
1443
1444
1447
1448
1449
1450
1451
1453 control = event.GetEventObject()
1454 self.set( control.object, control.trait_name, control.GetValue(),
1455 control.handler )
1456
1457
1458
1459
1460
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
1479
1480
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
1494
1495
1506
1507
1508
1509
1510
1512 try:
1513 control.SetValue( int( new ) )
1514 except:
1515 pass
1516
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
1530
1531
1533
1534
1535
1536
1537
1538 - def __init__ ( self, editors, auto_set = True ):
1541
1542
1543
1544
1545
1546
1547 - def custom_editor ( self, object, trait_name, description, handler, parent ):
1548
1549 panel = wx.wxPanel( parent, -1 )
1550 sizer = wx.wxBoxSizer( wx.wxVERTICAL )
1551
1552
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
1560 panel.SetAutoLayout( True )
1561 panel.SetSizer( sizer )
1562 sizer.Fit( panel )
1563
1564
1565 return panel
1566
1567
1568
1569
1570
1572
1573
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
1587 empty_list_menu = """
1588 Add: self.add_empty()
1589 """
1590
1591
1592
1593
1594
1595 - def __init__ ( self, trait_handler, rows = 5 ):
1596 self.trait_handler = trait_handler
1597 self.rows = rows
1598
1599
1600
1601
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
1611
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
1621
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
1631
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
1640
1641
1642
1643 - def list_editor ( self, object, trait_name, description, handler, parent,
1644 type ):
1645
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
1705
1706
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
1730
1731
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
1745
1746
1748 self.cur_control = control
1749 control.PopupMenuXY( MakeMenu( self.empty_list_menu, self, True,
1750 control ).menu, 0, 0 )
1751
1752
1753
1754
1755
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
1774
1775
1778
1779
1780
1781
1782
1785
1786
1787
1788
1789
1793
1794
1795
1796
1797
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
1816
1817
1824
1825
1826
1827
1828
1835
1836
1837
1838
1839
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
1853
1854
1865
1866
1867
1868
1869
1871 cur_control = self.cur_control
1872 proxy = self.cur_control.pcontrol.object
1873 return ( proxy.list(), proxy.index )
1874
1875
1876
1877
1878
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
1890
1891
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
1908
1909
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
1924 self.list()[ self.index ] = new_value
1925
1926
1927
1928
1929
1931
1932
1933
1934
1935
1939
1940
1941
1942
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
1969
1970
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
1987
1988
1990
1991
1992
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
2001 self.object = object
2002 self.trait_name = trait_name
2003 self.handler = handler
2004 self.editor = editor
2005
2006
2007 if editor.mapped:
2008 trait_name == '_'
2009 editor.create_image_grid( self, getattr( object, trait_name ),
2010 self.on_click )
2011
2012
2013 position_near( parent, self )
2014
2015
2016
2017
2018
2020 self.EndModal( rc )
2021 self.Destroy()
2022
2023
2024
2025
2026
2028 self.editor.set( self.object, self.trait_name, control.value,
2029 self.handler )
2030
2031
2032
2033
2034
2035
2036 _bitmap_cache = {}
2037
2038
2039 app_path = None
2040 traits_path = None
2041
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
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
2157
2158
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
2168
2169
2171
2172
2173
2174
2175
2177 self.color_data = wx.wxColourData()
2178
2179
2180
2181
2182
2185 if not hasattr( parent, 'is_custom' ):
2186
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
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
2225
2226
2227
2228 - def readonly_editor ( self, object, trait_name, description, handler,
2229 parent ):
2232
2233
2234
2235
2236
2237
2238 - def simple_editor ( self, object, trait_name, description, handler,
2239 parent ):
2242
2243
2244
2245
2246
2247
2248 - def custom_editor ( self, object, trait_name, description, handler, parent ):
2249
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
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
2272 panel.SetAutoLayout( True )
2273 panel.SetSizer( sizer )
2274 sizer.Fit( panel )
2275
2276
2277 return panel
2278
2279
2280
2281
2282
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
2292
2293
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
2305
2306
2309
2313
2314
2315
2316
2317
2319 try:
2320 return getattr( object, trait_name + '_' )
2321 except:
2322 return getattr( object, trait_name )
2323
2324
2325
2326
2327
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
2339
2340
2343
2344
2345
2346
2347
2348
2349 color_editor = TraitEditorColor()
2350
2351
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
2362
2363
2365
2366
2367
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
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
2391
2392
2394 self.EndModal( rc )
2395 self.Destroy()
2396
2397
2398
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
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
2457
2458
2460
2461
2462
2463
2464
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
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
2491
2492
2493
2494 - def readonly_editor ( self, object, trait_name, description, handler,
2495 parent ):
2498
2499
2500
2501
2502
2503
2504 - def simple_editor ( self, object, trait_name, description, handler,
2505 parent ):
2508
2509
2510
2511
2512
2513
2514 - def custom_editor ( self, object, trait_name, description, handler, parent ):
2515
2516 panel = wx.wxPanel( parent, -1 )
2517 sizer = wx.wxBoxSizer( wx.wxVERTICAL )
2518
2519
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
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
2540 self.on_trait_change_custom( font, getattr( object, trait_name ) )
2541
2542
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
2550 return panel
2551
2552
2553
2554
2555
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
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
2579
2580
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
2591
2592
2599
2600
2601
2602
2603
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
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
2624
2625
2628
2629
2630
2631
2632
2633 font_trait = Trait( 'Arial 10', wx.wxFont, wx.wxFontPtr, str_to_font,
2634 editor = TraitEditorFont() )
2635
2636
2637
2638
2639
2641
2642
2643
2644
2645
2647 self._facenames = []
2648 self.EnumerateFacenames()
2649 return self._facenames
2650
2651
2652
2653
2654
2656 self._facenames.append( facename )
2657 return True
2658
2659
2660
2661
2662
2664
2665
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
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
2688 wx.EVT_PAINT( self, self._on_paint )
2689
2690
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
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
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
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
2738
2739
2741 if self._selected is not None:
2742 self._mouse_over = True
2743 self.Refresh()
2744
2745
2746
2747
2748
2750 if self._mouse_over:
2751 self._mouse_over = False
2752 self.Refresh()
2753
2754
2755
2756
2757
2759 if self._selected is not None:
2760 self.CaptureMouse()
2761 self._button_down = True
2762 self.Refresh()
2763
2764
2765
2766
2767
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
2790
2791
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