~ubuntu-branches/ubuntu/trusty/python-traitsui/trusty

« back to all changes in this revision

Viewing changes to traitsui/wx/instance_editor.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-09 13:57:39 UTC
  • Revision ID: james.westby@ubuntu.com-20110709135739-x5u20q86huissmn1
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#------------------------------------------------------------------------------
 
2
#
 
3
#  Copyright (c) 2005, Enthought, Inc.
 
4
#  All rights reserved.
 
5
#
 
6
#  This software is provided without warranty under the terms of the BSD
 
7
#  license included in enthought/LICENSE.txt and may be redistributed only
 
8
#  under the conditions described in the aforementioned license.  The license
 
9
#  is also available online at http://www.enthought.com/licenses/BSD.txt
 
10
#
 
11
#  Thanks for using Enthought open source!
 
12
#
 
13
#  Author: David C. Morrill
 
14
#  Date:   10/21/2004
 
15
#
 
16
#------------------------------------------------------------------------------
 
17
 
 
18
""" Defines the various instance editors for the wxPython user interface
 
19
toolkit.
 
20
"""
 
21
 
 
22
#-------------------------------------------------------------------------------
 
23
#  Imports:
 
24
#-------------------------------------------------------------------------------
 
25
 
 
26
import wx
 
27
 
 
28
from traits.api \
 
29
    import HasTraits, Property
 
30
 
 
31
# FIXME: ToolkitEditorFactory is a proxy class defined here just for backward
 
32
# compatibility. The class has been moved to the
 
33
# traitsui.editors.instance_editor file.
 
34
from traitsui.editors.instance_editor \
 
35
    import ToolkitEditorFactory
 
36
 
 
37
from traitsui.ui_traits \
 
38
    import AView
 
39
 
 
40
from traitsui.helper \
 
41
    import user_name_for
 
42
 
 
43
from traitsui.handler \
 
44
    import Handler
 
45
 
 
46
from traitsui.instance_choice \
 
47
    import InstanceChoiceItem
 
48
 
 
49
from editor \
 
50
    import Editor
 
51
 
 
52
from constants \
 
53
    import DropColor, is_wx26
 
54
 
 
55
from helper \
 
56
    import TraitsUIPanel, position_window
 
57
 
 
58
from pyface.wx.drag_and_drop \
 
59
    import PythonDropTarget
 
60
 
 
61
#-------------------------------------------------------------------------------
 
62
#  Constants:
 
63
#-------------------------------------------------------------------------------
 
64
 
 
65
OrientationMap = {
 
66
    'default':    None,
 
67
    'horizontal': wx.HORIZONTAL,
 
68
    'vertical':   wx.VERTICAL
 
69
}
 
70
 
 
71
#-------------------------------------------------------------------------------
 
72
#  'CustomEditor' class:
 
73
#-------------------------------------------------------------------------------
 
74
 
 
75
class CustomEditor ( Editor ):
 
76
    """ Custom style of editor for instances. If selection among instances is
 
77
    allowed, the editor displays a combo box listing instances that can be
 
78
    selected. If the current instance is editable, the editor displays a panel
 
79
    containing trait editors for all the instance's traits.
 
80
    """
 
81
 
 
82
    # Background color when an item can be dropped on the editor:
 
83
    ok_color = DropColor
 
84
 
 
85
    # The orientation of the instance editor relative to the instance selector:
 
86
    orientation = wx.VERTICAL
 
87
 
 
88
    # Class constant:
 
89
    extra = 0
 
90
 
 
91
    #---------------------------------------------------------------------------
 
92
    #  Trait definitions:
 
93
    #---------------------------------------------------------------------------
 
94
 
 
95
    # List of InstanceChoiceItem objects used by the editor
 
96
    items = Property
 
97
 
 
98
    # The maximum extra padding that should be allowed around the editor:
 
99
    # (Override of the Editor base class trait)
 
100
    border_size = 0
 
101
 
 
102
    # The view to use for displaying the instance:
 
103
    view = AView
 
104
 
 
105
    #---------------------------------------------------------------------------
 
106
    #  Finishes initializing the editor by creating the underlying toolkit
 
107
    #  widget:
 
108
    #---------------------------------------------------------------------------
 
109
 
 
110
    def init ( self, parent ):
 
111
        """ Finishes initializing the editor by creating the underlying toolkit
 
112
            widget.
 
113
        """
 
114
        factory = self.factory
 
115
        if factory.name != '':
 
116
            self._object, self._name, self._value = \
 
117
                self.parse_extended_name( factory.name )
 
118
 
 
119
        # Create a panel to hold the object trait's view:
 
120
        if factory.editable:
 
121
            self.control = self._panel = parent = TraitsUIPanel( parent, -1 )
 
122
 
 
123
        # Build the instance selector if needed:
 
124
        selectable = factory.selectable
 
125
        droppable  = factory.droppable
 
126
        items      = self.items
 
127
        for item in items:
 
128
            droppable  |= item.is_droppable()
 
129
            selectable |= item.is_selectable()
 
130
 
 
131
        if selectable:
 
132
            self._object_cache = {}
 
133
            item = self.item_for( self.value )
 
134
            if item is not None:
 
135
                self._object_cache[ id( item ) ] = self.value
 
136
 
 
137
            self._choice = choice = wx.Choice( parent, -1, wx.Point( 0, 0 ),
 
138
                                        wx.Size( -1, -1 ), [] )
 
139
            wx.EVT_CHOICE( choice, choice.GetId(), self.update_object )
 
140
            if droppable:
 
141
                self._choice.SetBackgroundColour( self.ok_color )
 
142
 
 
143
            self.set_tooltip( self._choice )
 
144
 
 
145
            if factory.name != '':
 
146
                self._object.on_trait_change( self.rebuild_items,
 
147
                                              self._name, dispatch = 'ui' )
 
148
                self._object.on_trait_change( self.rebuild_items,
 
149
                                 self._name + '_items', dispatch = 'ui' )
 
150
 
 
151
            factory.on_trait_change( self.rebuild_items, 'values',
 
152
                                     dispatch = 'ui' )
 
153
            factory.on_trait_change( self.rebuild_items, 'values_items',
 
154
                                     dispatch = 'ui' )
 
155
 
 
156
            self.rebuild_items()
 
157
 
 
158
        elif droppable:
 
159
            self._choice = wx.TextCtrl( parent, -1, '',
 
160
                                        style = wx.TE_READONLY )
 
161
            self._choice.SetBackgroundColour( self.ok_color )
 
162
            self.set_tooltip( self._choice )
 
163
 
 
164
        if droppable:
 
165
            self._choice.SetDropTarget( PythonDropTarget( self ) )
 
166
 
 
167
        orientation = OrientationMap[ factory.orientation ]
 
168
        if orientation is None:
 
169
            orientation = self.orientation
 
170
 
 
171
        if (selectable or droppable) and factory.editable:
 
172
            sizer = wx.BoxSizer( orientation )
 
173
            sizer.Add( self._choice, self.extra, wx.EXPAND )
 
174
            if orientation == wx.VERTICAL:
 
175
                sizer.Add(
 
176
                    wx.StaticLine( parent, -1, style = wx.LI_HORIZONTAL ), 0,
 
177
                    wx.EXPAND | wx.TOP | wx.BOTTOM, 5 )
 
178
            self.create_editor( parent, sizer )
 
179
            parent.SetSizer( sizer )
 
180
        elif self.control is None:
 
181
            if self._choice is None:
 
182
                self._choice = choice = wx.Choice( parent, -1, wx.Point( 0, 0 ),
 
183
                                            wx.Size( -1, -1 ), [] )
 
184
                wx.EVT_CHOICE( choice, choice.GetId(), self.update_object )
 
185
            self.control = self._choice
 
186
        else:
 
187
            sizer = wx.BoxSizer( orientation )
 
188
            self.create_editor( parent, sizer )
 
189
            parent.SetSizer( sizer )
 
190
 
 
191
        # Synchronize the 'view' to use:
 
192
        # fixme: A normal assignment can cause a crash (for unknown reasons) in
 
193
        # some cases, so we make sure that no notifications are generated:
 
194
        self.trait_setq( view = factory.view )
 
195
        self.sync_value( factory.view_name, 'view', 'from' )
 
196
 
 
197
    #---------------------------------------------------------------------------
 
198
    #  Disposes of the contents of an editor:
 
199
    #---------------------------------------------------------------------------
 
200
 
 
201
    def dispose ( self ):
 
202
        """ Disposes of the contents of an editor.
 
203
        """
 
204
        # Make sure we aren't hanging on to any object refs:
 
205
        self._object_cache = None
 
206
 
 
207
        if self._ui is not None:
 
208
            self._ui.dispose()
 
209
 
 
210
        choice = self._choice
 
211
        if choice is not None:
 
212
            if isinstance( choice, wx.Choice ):
 
213
                wx.EVT_CHOICE( choice, choice.GetId(), None )
 
214
 
 
215
            if self._object is not None:
 
216
                self._object.on_trait_change( self.rebuild_items,
 
217
                                              self._name, remove = True )
 
218
                self._object.on_trait_change( self.rebuild_items,
 
219
                                 self._name + '_items', remove = True )
 
220
 
 
221
            self.factory.on_trait_change( self.rebuild_items, 'values',
 
222
                                          remove = True )
 
223
            self.factory.on_trait_change( self.rebuild_items,
 
224
                                          'values_items', remove = True )
 
225
 
 
226
        super( CustomEditor, self ).dispose()
 
227
 
 
228
    #---------------------------------------------------------------------------
 
229
    #  Creates the editor control:
 
230
    #---------------------------------------------------------------------------
 
231
 
 
232
    def create_editor ( self, parent, sizer ):
 
233
        """ Creates the editor control.
 
234
        """
 
235
        self._panel = TraitsUIPanel( parent, -1 )
 
236
        sizer.Add( self._panel, 1, wx.EXPAND )
 
237
 
 
238
    #---------------------------------------------------------------------------
 
239
    #  Gets the current list of InstanceChoiceItem items:
 
240
    #---------------------------------------------------------------------------
 
241
 
 
242
    def _get_items ( self ):
 
243
        """ Gets the current list of InstanceChoiceItem items.
 
244
        """
 
245
        if self._items is not None:
 
246
            return self._items
 
247
 
 
248
        factory = self.factory
 
249
        if self._value is not None:
 
250
            values = self._value() + factory.values
 
251
        else:
 
252
            values = factory.values
 
253
 
 
254
        items   = []
 
255
        adapter = factory.adapter
 
256
        for value in values:
 
257
            if not isinstance( value, InstanceChoiceItem ):
 
258
                value = adapter( object = value )
 
259
            items.append( value )
 
260
 
 
261
        self._items = items
 
262
 
 
263
        return items
 
264
 
 
265
    #---------------------------------------------------------------------------
 
266
    #  Rebuilds the object selector list:
 
267
    #---------------------------------------------------------------------------
 
268
 
 
269
    def rebuild_items ( self ):
 
270
        """ Rebuilds the object selector list.
 
271
        """
 
272
        # Clear the current cached values:
 
273
        self._items = None
 
274
 
 
275
        # Rebuild the contents of the selector list:
 
276
        name   = None
 
277
        value  = self.value
 
278
        choice = self._choice
 
279
        choice.Clear()
 
280
        for item in self.items:
 
281
            if item.is_selectable():
 
282
                item_name = item.get_name()
 
283
                choice.Append( item_name )
 
284
                if item.is_compatible( value ):
 
285
                    name = item_name
 
286
 
 
287
        # Reselect the current item if possible:
 
288
        if name is not None:
 
289
            choice.SetStringSelection( name )
 
290
        else:
 
291
            # Otherwise, current value is no longer valid, try to discard it:
 
292
            try:
 
293
                self.value = None
 
294
            except:
 
295
                pass
 
296
 
 
297
    #---------------------------------------------------------------------------
 
298
    #  Returns the InstanceChoiceItem for a specified object:
 
299
    #---------------------------------------------------------------------------
 
300
 
 
301
    def item_for ( self, object ):
 
302
        """ Returns the InstanceChoiceItem for a specified object.
 
303
        """
 
304
        for item in self.items:
 
305
            if item.is_compatible( object ):
 
306
                return item
 
307
 
 
308
        return None
 
309
 
 
310
    #---------------------------------------------------------------------------
 
311
    #  Returns the view to use for a specified object:
 
312
    #---------------------------------------------------------------------------
 
313
 
 
314
    def view_for ( self, object, item ):
 
315
        """ Returns the view to use for a specified object.
 
316
        """
 
317
        view = ''
 
318
        if item is not None:
 
319
            view = item.get_view()
 
320
 
 
321
        if view == '':
 
322
            view = self.view
 
323
 
 
324
        return self.ui.handler.trait_view_for( self.ui.info, view, object,
 
325
                                               self.object_name, self.name )
 
326
 
 
327
    #---------------------------------------------------------------------------
 
328
    #  Handles the user selecting a new value from the combo box:
 
329
    #---------------------------------------------------------------------------
 
330
 
 
331
    def update_object ( self, event ):
 
332
        """ Handles the user selecting a new value from the combo box.
 
333
        """
 
334
        name = event.GetString()
 
335
        for item in self.items:
 
336
            if name == item.get_name():
 
337
                id_item = id( item )
 
338
                object  = self._object_cache.get( id_item )
 
339
                if object is None:
 
340
                    object = item.get_object()
 
341
                    if (not self.factory.editable) and item.is_factory:
 
342
                        view = self.view_for( object, self.item_for( object ) )
 
343
                        view.ui( object, self.control, 'modal' )
 
344
 
 
345
                    if self.factory.cachable:
 
346
                        self._object_cache[ id_item ] = object
 
347
 
 
348
                self.value = object
 
349
                self.resynch_editor()
 
350
                break
 
351
 
 
352
    #---------------------------------------------------------------------------
 
353
    #  Updates the editor when the object trait changes external to the editor:
 
354
    #---------------------------------------------------------------------------
 
355
 
 
356
    def update_editor ( self ):
 
357
        """ Updates the editor when the object trait changes externally to the
 
358
            editor.
 
359
        """
 
360
        # Attach the current object value to the control (for use by
 
361
        # DockWindowFeature):
 
362
 
 
363
        # fixme: This code is somewhat fragile since it assumes that if a
 
364
        # DockControl is involved, the parent of this editor will be the
 
365
        # control being managed by the DockControl.
 
366
        parent         = self.control.GetParent()
 
367
        parent._object = self.value
 
368
        dock_control   = getattr( parent, '_dock_control', None )
 
369
        if dock_control is not None:
 
370
            dock_control.reset_tab()
 
371
 
 
372
        # Synchronize the editor contents:
 
373
        self.resynch_editor()
 
374
 
 
375
        # Update the selector (if any):
 
376
        choice = self._choice
 
377
        item   = self.item_for( self.value )
 
378
        if (choice is not None) and (item is not None):
 
379
            name = item.get_name( self.value )
 
380
            if self._object_cache is not None:
 
381
                if choice.FindString( name ) < 0:
 
382
                    choice.Append( name )
 
383
                choice.SetStringSelection( name )
 
384
            else:
 
385
                choice.SetValue( name )
 
386
 
 
387
    #---------------------------------------------------------------------------
 
388
    #  Resynchronizes the contents of the editor when the object trait changes
 
389
    #  external to the editor:
 
390
    #---------------------------------------------------------------------------
 
391
 
 
392
    def resynch_editor ( self ):
 
393
        """ Resynchronizes the contents of the editor when the object trait
 
394
        changes externally to the editor.
 
395
        """
 
396
        panel = self._panel
 
397
        if panel is not None:
 
398
            # Compute/update the maximum size the panel has ever been:
 
399
            dx, dy = panel.GetSizeTuple()
 
400
            mdx    = mdy = 0
 
401
            if self._panel_size is not None:
 
402
                mdx, mdy = self._panel_size
 
403
            self._panel_size = size = wx.Size( max( mdx, dx ), max( mdy, dy ) )
 
404
 
 
405
            # Dispose of the previous contents of the panel:
 
406
            panel.SetSizer( None )
 
407
            if self._ui is not None:
 
408
                self._ui.dispose()
 
409
                self._ui = None
 
410
            else:
 
411
                panel.DestroyChildren()
 
412
 
 
413
            # Create the new content for the panel:
 
414
            sizer   = wx.BoxSizer( wx.VERTICAL )
 
415
            stretch = 0
 
416
            value   = self.value
 
417
            if not isinstance( value, HasTraits ):
 
418
                str_value = ''
 
419
                if value is not None:
 
420
                    str_value = self.str_value
 
421
                control = wx.StaticText( panel, -1, str_value )
 
422
            else:
 
423
                view    = self.view_for( value, self.item_for( value ) )
 
424
                context = value.trait_context()
 
425
                handler = None
 
426
                if isinstance( value, Handler ):
 
427
                    handler = value
 
428
                context.setdefault( 'context', self.object )
 
429
                context.setdefault( 'context_handler', self.ui.handler )
 
430
                self._ui = ui = view.ui( context, panel, 'subpanel',
 
431
                                         value.trait_view_elements(), handler,
 
432
                                         self.factory.id )
 
433
                control         = ui.control
 
434
                self.scrollable = ui._scrollable
 
435
                ui.parent       = self.ui
 
436
 
 
437
                if view.resizable or view.scrollable or ui._scrollable:
 
438
                    stretch = 1
 
439
 
 
440
            # Make sure the panel and its contents are correctly sized (This
 
441
            # code is complicated by the various layout bugs present in wx.
 
442
            # Tamper with it at your own risk!):
 
443
            control.Freeze()
 
444
            if stretch and (size != ( 20, 20 )):
 
445
                control.SetSize( size )
 
446
                panel.SetSize( size )
 
447
            else:
 
448
                panel.SetSize( control.GetSize() )
 
449
            sizer.Add( control, stretch, wx.EXPAND )
 
450
            panel.SetSizer( sizer )
 
451
            control.Thaw()
 
452
            self.control.Layout()
 
453
            parent = self.control.GetParent()
 
454
            parent.Layout()
 
455
 
 
456
            # It is possible that this instance editor is embedded at some level
 
457
            # in a ScrolledWindow. If so, we need to inform the window that the
 
458
            # size of the editor's contents have (potentially) changed:
 
459
            # NB: There is a typo in the wxPython 2.6 code that prevents the
 
460
            # 'SendSizeEvent' from working correctly, so we just skip it.
 
461
            if not is_wx26:
 
462
                while ((parent is not None) and
 
463
                       (not isinstance( parent, wx.ScrolledWindow ))):
 
464
                    parent = parent.GetParent()
 
465
 
 
466
                if parent is not None:
 
467
                    parent.SendSizeEvent()
 
468
 
 
469
    #---------------------------------------------------------------------------
 
470
    #  Handles an error that occurs while setting the object's trait value:
 
471
    #---------------------------------------------------------------------------
 
472
 
 
473
    def error ( self, excp ):
 
474
        """ Handles an error that occurs while setting the object's trait value.
 
475
        """
 
476
        pass
 
477
 
 
478
    #---------------------------------------------------------------------------
 
479
    #  Returns the editor's control for indicating error status:
 
480
    #---------------------------------------------------------------------------
 
481
 
 
482
    def get_error_control ( self ):
 
483
        """ Returns the editor's control for indicating error status.
 
484
        """
 
485
        return (self._choice or self.control)
 
486
 
 
487
    #-- UI preference save/restore interface -----------------------------------
 
488
 
 
489
    #---------------------------------------------------------------------------
 
490
    #  Restores any saved user preference information associated with the
 
491
    #  editor:
 
492
    #---------------------------------------------------------------------------
 
493
 
 
494
    def restore_prefs ( self, prefs ):
 
495
        """ Restores any saved user preference information associated with the
 
496
            editor.
 
497
        """
 
498
        ui = self._ui
 
499
        if (ui is not None) and (prefs.get( 'id' ) == ui.id):
 
500
            ui.set_prefs( prefs.get( 'prefs' ) )
 
501
 
 
502
    #---------------------------------------------------------------------------
 
503
    #  Returns any user preference information associated with the editor:
 
504
    #---------------------------------------------------------------------------
 
505
 
 
506
    def save_prefs ( self ):
 
507
        """ Returns any user preference information associated with the editor.
 
508
        """
 
509
        ui = self._ui
 
510
        if (ui is not None) and (ui.id != ''):
 
511
            return { 'id':    ui.id,
 
512
                     'prefs': ui.get_prefs() }
 
513
 
 
514
        return None
 
515
 
 
516
    #-- Drag and drop event handlers -------------------------------------------
 
517
 
 
518
    #---------------------------------------------------------------------------
 
519
    #  Handles a Python object being dropped on the control:
 
520
    #---------------------------------------------------------------------------
 
521
 
 
522
    def wx_dropped_on ( self, x, y, data, drag_result ):
 
523
        """ Handles a Python object being dropped on the tree.
 
524
        """
 
525
        for item in self.items:
 
526
            if item.is_droppable() and item.is_compatible( data ):
 
527
                if self._object_cache is not None:
 
528
                    self.rebuild_items()
 
529
                self.value = data
 
530
                return drag_result
 
531
 
 
532
        return wx.DragNone
 
533
 
 
534
    #---------------------------------------------------------------------------
 
535
    #  Handles a Python object being dragged over the control:
 
536
    #---------------------------------------------------------------------------
 
537
 
 
538
    def wx_drag_over ( self, x, y, data, drag_result ):
 
539
        """ Handles a Python object being dragged over the tree.
 
540
        """
 
541
        for item in self.items:
 
542
            if item.is_droppable() and item.is_compatible( data ):
 
543
                return drag_result
 
544
 
 
545
        return wx.DragNone
 
546
 
 
547
    #-- Traits event handlers --------------------------------------------------
 
548
 
 
549
    def _view_changed ( self, view ):
 
550
        self.resynch_editor()
 
551
 
 
552
#-------------------------------------------------------------------------------
 
553
#  'SimpleEditor' class:
 
554
#-------------------------------------------------------------------------------
 
555
 
 
556
class SimpleEditor ( CustomEditor ):
 
557
    """ Simple style of editor for instances, which displays a button. Clicking
 
558
    the button displays a dialog box in which the instance can be edited.
 
559
    """
 
560
 
 
561
    # Class constants:
 
562
    orientation = wx.HORIZONTAL
 
563
    extra       = 2
 
564
 
 
565
    #---------------------------------------------------------------------------
 
566
    #  Creates the editor control:
 
567
    #---------------------------------------------------------------------------
 
568
 
 
569
    def create_editor ( self, parent, sizer ):
 
570
        """ Creates the editor control (a button).
 
571
        """
 
572
        self._button = button = wx.Button( parent, -1, '' )
 
573
        sizer.Add( button, 1, wx.EXPAND | wx.LEFT, 5 )
 
574
        wx.EVT_BUTTON( button, button.GetId(), self.edit_instance )
 
575
 
 
576
    #---------------------------------------------------------------------------
 
577
    #  Disposes of the contents of an editor:
 
578
    #---------------------------------------------------------------------------
 
579
 
 
580
    def dispose ( self ):
 
581
        """ Disposes of the contents of an editor.
 
582
        """
 
583
        button = self._button
 
584
        if button is not None:
 
585
            wx.EVT_BUTTON( button, button.GetId(), None )
 
586
 
 
587
        super( SimpleEditor, self ).dispose()
 
588
 
 
589
    #---------------------------------------------------------------------------
 
590
    #  Edit the contents of the object trait when the user clicks the button:
 
591
    #---------------------------------------------------------------------------
 
592
 
 
593
    def edit_instance ( self, event ):
 
594
        """ Edit the contents of the object trait when the user clicks the
 
595
            button.
 
596
        """
 
597
        # Create the user interface:
 
598
        factory = self.factory
 
599
        view    = self.ui.handler.trait_view_for( self.ui.info, factory.view,
 
600
                                                  self.value, self.object_name,
 
601
                                                  self.name )
 
602
        ui = self.value.edit_traits( view, self.control, factory.kind,
 
603
                                     id = factory.id )
 
604
 
 
605
        # Check to see if the view was 'modal', in which case it will already
 
606
        # have been closed (i.e. is None) by the time we get control back:
 
607
        if ui.control is not None:
 
608
            # Position the window on the display:
 
609
            position_window( ui.control )
 
610
 
 
611
            # Chain our undo history to the new user interface if it does not
 
612
            # have its own:
 
613
            if ui.history is None:
 
614
                ui.history = self.ui.history
 
615
 
 
616
    #---------------------------------------------------------------------------
 
617
    #  Resynchronizes the contents of the editor when the object trait changes
 
618
    #  external to the editor:
 
619
    #---------------------------------------------------------------------------
 
620
 
 
621
    def resynch_editor ( self ):
 
622
        """ Resynchronizes the contents of the editor when the object trait
 
623
            changes externally to the editor.
 
624
        """
 
625
        button = self._button
 
626
        if button is not None:
 
627
            label = self.factory.label
 
628
            if label == '':
 
629
                label = user_name_for( self.name )
 
630
            button.SetLabel( label )
 
631
            button.Enable( isinstance( self.value, HasTraits ) )
 
632
 
 
633
### EOF #######################################################################