1
#------------------------------------------------------------------------------
3
# Copyright (c) 2006, Enthought, Inc.
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
11
# Thanks for using Enthought open source!
13
# Author: David C. Morrill
16
#------------------------------------------------------------------------------
18
""" Defines tree node classes and editors for various types of values.
21
#-------------------------------------------------------------------------------
23
#-------------------------------------------------------------------------------
25
from __future__ import absolute_import
29
from types import FunctionType, MethodType
31
from traits.api import Any, Bool, HasPrivateTraits, HasTraits, Instance, List, Str
33
from .tree_node import ObjectTreeNode, TreeNode, TreeNodeObject
35
from .editors.tree_editor import TreeEditor
37
#-------------------------------------------------------------------------------
38
# 'SingleValueTreeNodeObject' class:
39
#-------------------------------------------------------------------------------
41
class SingleValueTreeNodeObject ( TreeNodeObject ):
42
""" A tree node for objects of types that have a single value.
45
#---------------------------------------------------------------------------
47
#---------------------------------------------------------------------------
49
# The parent of this node
50
parent = Instance( TreeNodeObject )
55
# User-specified override of the default label
61
# Is the value readonly?
62
readonly = Bool( False )
64
#---------------------------------------------------------------------------
65
# Returns whether chidren of this object are allowed or not:
66
#---------------------------------------------------------------------------
68
def tno_allows_children ( self, node ):
69
""" Returns whether this object can have children (False for this
74
#---------------------------------------------------------------------------
75
# Returns whether or not the object has children:
76
#---------------------------------------------------------------------------
78
def tno_has_children ( self, node ):
79
""" Returns whether the object has children (False for this class).
83
#---------------------------------------------------------------------------
84
# Returns whether or not the object's children can be renamed:
85
#---------------------------------------------------------------------------
87
def tno_can_rename ( self, node ):
88
""" Returns whether the object's children can be renamed (False for
93
#---------------------------------------------------------------------------
94
# Returns whether or not the object's children can be copied:
95
#---------------------------------------------------------------------------
97
def tno_can_copy ( self, node ):
98
""" Returns whether the object's children can be copied (True for this
103
#---------------------------------------------------------------------------
104
# Returns whether or not the object's children can be deleted:
105
#---------------------------------------------------------------------------
107
def tno_can_delete ( self, node ):
108
""" Returns whether the object's children can be deleted (False for
113
#---------------------------------------------------------------------------
114
# Returns whether or not the object's children can be inserted (or just
116
#---------------------------------------------------------------------------
118
def tno_can_insert ( self, node ):
119
""" Returns whether the object's children can be inserted (False,
120
meaning children are appended, for this class).
124
#---------------------------------------------------------------------------
125
# Returns the icon for a specified object:
126
#---------------------------------------------------------------------------
128
def tno_get_icon ( self, node, is_expanded ):
129
""" Returns the icon for a specified object.
131
return ('@icons:%s_node' % self.__class__.__name__[ : -4 ].lower())
133
#---------------------------------------------------------------------------
134
# Sets the label for a specified node:
135
#---------------------------------------------------------------------------
137
def tno_set_label ( self, node, label ):
138
""" Sets the label for a specified object.
144
#---------------------------------------------------------------------------
145
# Gets the label to display for a specified object:
146
#---------------------------------------------------------------------------
148
def tno_get_label ( self, node ):
149
""" Gets the label to display for a specified object.
155
return self.format_value( self.value )
157
return '%s: %s' % ( self.name, self.format_value( self.value ) )
159
#---------------------------------------------------------------------------
160
# Returns the formatted version of the value:
161
#---------------------------------------------------------------------------
163
def format_value ( self, value ):
164
""" Returns the formatted version of the value.
168
#---------------------------------------------------------------------------
169
# Returns the correct node type for a specified value:
170
#---------------------------------------------------------------------------
172
def node_for ( self, name, value ):
173
""" Returns the correct node type for a specified value.
175
for type, node in basic_types():
176
if isinstance( value, type ):
180
if inspect.isclass( value ):
183
elif hasattr( value, '__class__' ):
186
return node( parent = self,
189
readonly = self.readonly )
191
#-------------------------------------------------------------------------------
192
# 'MultiValueTreeNodeObject' class:
193
#-------------------------------------------------------------------------------
195
class MultiValueTreeNodeObject ( SingleValueTreeNodeObject ):
196
""" A tree node for objects of types that have multiple values.
199
#---------------------------------------------------------------------------
200
# Returns whether chidren of this object are allowed or not:
201
#---------------------------------------------------------------------------
203
def tno_allows_children ( self, node ):
204
""" Returns whether this object can have children (True for this class).
208
#---------------------------------------------------------------------------
209
# Returns whether or not the object has children:
210
#---------------------------------------------------------------------------
212
def tno_has_children ( self, node ):
213
""" Returns whether the object has children (True for this class).
217
#-------------------------------------------------------------------------------
218
# 'StringNode' class:
219
#-------------------------------------------------------------------------------
221
class StringNode ( SingleValueTreeNodeObject ):
222
""" A tree node for strings.
225
#---------------------------------------------------------------------------
226
# Returns the formatted version of the value:
227
#---------------------------------------------------------------------------
229
def format_value ( self, value ):
230
""" Returns the formatted version of the value.
233
if len( value ) > 80:
234
value = '%s...%s' % ( value[ :42 ], value[ -35: ] )
236
return '%s [%d]' % ( repr( value ), n )
238
#-------------------------------------------------------------------------------
240
#-------------------------------------------------------------------------------
242
class NoneNode ( SingleValueTreeNodeObject ):
243
""" A tree node for None values.
247
#-------------------------------------------------------------------------------
249
#-------------------------------------------------------------------------------
251
class BoolNode ( SingleValueTreeNodeObject ):
252
""" A tree node for Boolean values.
256
#-------------------------------------------------------------------------------
258
#-------------------------------------------------------------------------------
260
class IntNode ( SingleValueTreeNodeObject ):
261
""" A tree node for integer values.
265
#-------------------------------------------------------------------------------
267
#-------------------------------------------------------------------------------
269
class FloatNode ( SingleValueTreeNodeObject ):
270
""" A tree node for floating point values.
274
#-------------------------------------------------------------------------------
275
# 'ComplexNode' class:
276
#-------------------------------------------------------------------------------
278
class ComplexNode ( SingleValueTreeNodeObject ):
279
""" A tree node for complex number values.
283
#-------------------------------------------------------------------------------
285
#-------------------------------------------------------------------------------
287
class OtherNode ( SingleValueTreeNodeObject ):
288
""" A tree node for single-value types for which there is not another
293
#-------------------------------------------------------------------------------
295
#-------------------------------------------------------------------------------
297
class TupleNode ( MultiValueTreeNodeObject ):
298
""" A tree node for tuples.
300
#---------------------------------------------------------------------------
301
# Returns the formatted version of the value:
302
#---------------------------------------------------------------------------
304
def format_value ( self, value ):
305
""" Returns the formatted version of the value.
307
return 'Tuple(%d)' % len( value )
309
#---------------------------------------------------------------------------
310
# Returns whether or not the object has children:
311
#---------------------------------------------------------------------------
313
def tno_has_children ( self, node ):
314
""" Returns whether the object has children, based on the length of
317
return (len( self.value ) > 0)
319
#---------------------------------------------------------------------------
320
# Gets the object's children:
321
#---------------------------------------------------------------------------
323
def tno_get_children ( self, node ):
324
""" Gets the object's children.
326
node_for = self.node_for
328
if len( value ) > 500:
329
return ([ node_for( '[%d]' % i, x )
330
for i, x in enumerate( value[ : 250 ] ) ] +
331
[ StringNode( value = '...', readonly = True ) ] +
332
[ node_for( '[%d]' % i, x )
333
for i, x in enumerate( value[ -250: ] ) ])
335
return [ node_for( '[%d]' % i, x ) for i, x in enumerate( value ) ]
337
#-------------------------------------------------------------------------------
339
#-------------------------------------------------------------------------------
341
class ListNode ( TupleNode ):
342
""" A tree node for lists.
345
#---------------------------------------------------------------------------
346
# Returns the formatted version of the value:
347
#---------------------------------------------------------------------------
349
def format_value ( self, value ):
350
""" Returns the formatted version of the value.
352
return 'List(%d)' % len( value )
354
#---------------------------------------------------------------------------
355
# Returns whether or not the object's children can be deleted:
356
#---------------------------------------------------------------------------
358
def tno_can_delete ( self, node ):
359
""" Returns whether the object's children can be deleted.
361
return (not self.readonly)
363
#---------------------------------------------------------------------------
364
# Returns whether or not the object's children can be inserted (or just
366
#---------------------------------------------------------------------------
368
def tno_can_insert ( self, node ):
369
""" Returns whether the object's children can be inserted (vs.
372
return (not self.readonly)
374
#-------------------------------------------------------------------------------
376
#-------------------------------------------------------------------------------
378
class SetNode ( ListNode ):
379
""" A tree node for sets.
382
#---------------------------------------------------------------------------
383
# Returns the formatted version of the value:
384
#---------------------------------------------------------------------------
386
def format_value ( self, value ):
387
""" Returns the formatted version of the value.
389
return 'Set(%d)' % len( value )
391
#-------------------------------------------------------------------------------
393
#-------------------------------------------------------------------------------
395
class ArrayNode ( TupleNode ):
396
""" A tree node for arrays.
399
#---------------------------------------------------------------------------
400
# Returns the formatted version of the value:
401
#---------------------------------------------------------------------------
403
def format_value ( self, value ):
404
""" Returns the formatted version of the value.
406
return 'Array(%s)' % ','.join( [ str( n ) for n in value.shape ] )
408
#-------------------------------------------------------------------------------
410
#-------------------------------------------------------------------------------
412
class DictNode ( TupleNode ):
413
""" A tree node for dictionaries.
416
#---------------------------------------------------------------------------
417
# Returns the formatted version of the value:
418
#---------------------------------------------------------------------------
420
def format_value ( self, value ):
421
""" Returns the formatted version of the value.
423
return 'Dict(%d)' % len( value )
425
#---------------------------------------------------------------------------
426
# Gets the object's children:
427
#---------------------------------------------------------------------------
429
def tno_get_children ( self, node ):
430
""" Gets the object's children.
432
node_for = self.node_for
433
items = [ ( repr( k ), v ) for k, v in self.value.items() ]
434
items.sort( lambda l, r: cmp( l[0], r[0] ) )
435
if len( items ) > 500:
436
return ([ node_for( '[%s]' % k, v ) for k, v in items[: 250 ] ] +
437
[ StringNode( value = '...', readonly = True ) ] +
438
[ node_for( '[%s]' % k, v ) for k, v in items[ -250: ] ])
440
return [ node_for( '[%s]' % k, v ) for k, v in items ]
442
#---------------------------------------------------------------------------
443
# Returns whether or not the object's children can be deleted:
444
#---------------------------------------------------------------------------
446
def tno_can_delete ( self, node ):
447
""" Returns whether the object's children can be deleted.
449
return (not self.readonly)
451
#-------------------------------------------------------------------------------
452
# 'FunctionNode' class:
453
#-------------------------------------------------------------------------------
455
class FunctionNode ( SingleValueTreeNodeObject ):
456
""" A tree node for functions
459
#---------------------------------------------------------------------------
460
# Returns the formatted version of the value:
461
#---------------------------------------------------------------------------
463
def format_value ( self, value ):
464
""" Returns the formatted version of the value.
466
return 'Function %s()' % ( value.func_code.co_name )
468
#---------------------------------------------------------------------------
469
# 'MethodNode' class:
470
#---------------------------------------------------------------------------
472
class MethodNode ( MultiValueTreeNodeObject ):
474
#---------------------------------------------------------------------------
475
# Returns the formatted version of the value:
476
#---------------------------------------------------------------------------
478
def format_value ( self, value ):
479
""" Returns the formatted version of the value.
482
if value.im_self is None:
485
return '%sound method %s.%s()' % (
487
value.im_class.__name__,
488
value.im_func.func_code.co_name )
490
#---------------------------------------------------------------------------
491
# Returns whether or not the object has children:
492
#---------------------------------------------------------------------------
494
def tno_has_children ( self, node ):
495
""" Returns whether the object has children.
497
return (self.value.im_func != None)
499
#---------------------------------------------------------------------------
500
# Gets the object's children:
501
#---------------------------------------------------------------------------
503
def tno_get_children ( self, node ):
504
""" Gets the object's children.
506
return [ self.node_for( 'Object', self.value.im_self ) ]
508
#-------------------------------------------------------------------------------
509
# 'ObjectNode' class:
510
#-------------------------------------------------------------------------------
512
class ObjectNode ( MultiValueTreeNodeObject ):
513
""" A tree node for objects.
516
#---------------------------------------------------------------------------
517
# Returns the formatted version of the value:
518
#---------------------------------------------------------------------------
520
def format_value ( self, value ):
521
""" Returns the formatted version of the value.
524
klass = value.__class__.__name__
527
return '%s(0x%08X)' % ( klass, id( value ) )
529
#---------------------------------------------------------------------------
530
# Returns whether or not the object has children:
531
#---------------------------------------------------------------------------
533
def tno_has_children ( self, node ):
534
""" Returns whether the object has children.
537
return (len( self.value.__dict__ ) > 0)
541
#---------------------------------------------------------------------------
542
# Gets the object's children:
543
#---------------------------------------------------------------------------
545
def tno_get_children ( self, node ):
546
""" Gets the object's children.
548
items = [ ( k, v ) for k, v in self.value.__dict__.items() ]
549
items.sort( lambda l, r: cmp( l[0], r[0] ) )
550
return [ self.node_for( '.' + k, v ) for k, v in items ]
552
#-------------------------------------------------------------------------------
554
#-------------------------------------------------------------------------------
556
class ClassNode ( ObjectNode ):
557
""" A tree node for classes.
560
#---------------------------------------------------------------------------
561
# Returns the formatted version of the value:
562
#---------------------------------------------------------------------------
564
def format_value ( self, value ):
565
""" Returns the formatted version of the value.
567
return value.__name__
569
#-------------------------------------------------------------------------------
570
# 'TraitsNode' class:
571
#-------------------------------------------------------------------------------
573
class TraitsNode ( ObjectNode ):
574
""" A tree node for traits.
577
#---------------------------------------------------------------------------
578
# Returns whether or not the object has children:
579
#---------------------------------------------------------------------------
581
def tno_has_children ( self, node ):
582
""" Returns whether the object has children.
584
return (len( self._get_names() ) > 0)
586
#---------------------------------------------------------------------------
587
# Gets the object's children:
588
#---------------------------------------------------------------------------
590
def tno_get_children ( self, node ):
591
""" Gets the object's children.
593
names = self._get_names()
596
node_for = self.node_for
600
item_value = getattr( value, name, '<unknown>' )
601
except Exception, excp:
602
item_value = '<%s>' % excp
603
nodes.append( node_for( '.' + name, item_value ) )
607
#---------------------------------------------------------------------------
608
# Gets the names of all defined traits/attributes:
609
#---------------------------------------------------------------------------
611
def _get_names ( self ):
612
""" Gets the names of all defined traits or attributes.
616
for name in value.trait_names( type = lambda x: x != 'event' ):
618
for name in value.__dict__.keys():
622
#---------------------------------------------------------------------------
623
# Sets up/Tears down a listener for 'children replaced' on a specified
625
#---------------------------------------------------------------------------
627
def tno_when_children_replaced ( self, node, listener, remove ):
628
""" Sets up or removes a listener for children being replaced on a
631
self._listener = listener
632
self.value.on_trait_change( self._children_replaced, remove = remove,
635
def _children_replaced ( self ):
636
self._listener( self )
638
#---------------------------------------------------------------------------
639
# Sets up/Tears down a listener for 'children changed' on a specified
641
#---------------------------------------------------------------------------
643
def tno_when_children_changed ( self, node, listener, remove ):
644
""" Sets up or removes a listener for children being changed on a
649
#-------------------------------------------------------------------------------
651
#-------------------------------------------------------------------------------
653
class RootNode ( MultiValueTreeNodeObject ):
657
#---------------------------------------------------------------------------
658
# Returns the formatted version of the value:
659
#---------------------------------------------------------------------------
661
def format_value ( self, value ):
662
""" Returns the formatted version of the value.
666
#---------------------------------------------------------------------------
667
# Gets the object's children:
668
#---------------------------------------------------------------------------
670
def tno_get_children ( self, node ):
671
""" Gets the object's children.
673
return [ self.node_for( '', self.value ) ]
675
#-------------------------------------------------------------------------------
676
# Define the mapping of object types to nodes:
677
#-------------------------------------------------------------------------------
684
if _basic_types is None:
685
# Create the mapping of object types to nodes:
687
( type( None ), NoneNode ),
689
( unicode, StringNode ),
692
( float, FloatNode ),
693
( complex, ComplexNode ),
694
( tuple, TupleNode ),
698
( FunctionType, FunctionNode ),
699
( MethodType, MethodNode ),
700
( HasTraits, TraitsNode )
704
from numpy import array
706
_basic_types.append( ( type( array( [ 1 ] ) ), ArrayNode ) )
712
#-------------------------------------------------------------------------------
713
# '_ValueTree' class:
714
#-------------------------------------------------------------------------------
716
class _ValueTree ( HasPrivateTraits ):
718
#---------------------------------------------------------------------------
720
#---------------------------------------------------------------------------
722
# List of arbitrary Python values contained in the tree:
723
values = List( SingleValueTreeNodeObject )
725
#-------------------------------------------------------------------------------
726
# Defines the value tree editor(s):
727
#-------------------------------------------------------------------------------
729
# Nodes in a value tree:
732
node_for = [ NoneNode, StringNode, BoolNode, IntNode, FloatNode,
733
ComplexNode, OtherNode, TupleNode, ListNode, ArrayNode,
734
DictNode, SetNode, FunctionNode, MethodNode, ObjectNode,
735
TraitsNode, RootNode, ClassNode ] )
738
# Editor for a value tree:
739
value_tree_editor = TreeEditor(
743
nodes = value_tree_nodes
746
# Editor for a value tree with a root:
747
value_tree_editor_with_root = TreeEditor(
752
node_for = [ NoneNode, StringNode, BoolNode, IntNode, FloatNode,
753
ComplexNode, OtherNode, TupleNode, ListNode, ArrayNode,
754
DictNode, SetNode, FunctionNode, MethodNode,
755
ObjectNode, TraitsNode, RootNode, ClassNode ]
757
TreeNode( node_for = [ _ValueTree ],
760
move = [ SingleValueTreeNodeObject ],
763
icon_group = 'traits_node',
764
icon_open = 'traits_node' )
768
#-------------------------------------------------------------------------------
769
# Defines a 'ValueTree' trait:
770
#-------------------------------------------------------------------------------
772
# Trait for a value tree:
773
ValueTree = Instance( _ValueTree, (), editor = value_tree_editor_with_root )