4
Traits 3.0 introduces a new editor, called the **TabularEditor**, located in the
5
*traitsui.wx.extras.tabular_editor* module that can be used for many
6
of the same purposes as the existing **TableEditor**. However, while similar in
7
function, each editor has its own particular strengths and weaknesses.
9
Some of the strengths of the new **TabularEditor** are:
11
- **Very fast**. The editor uses a *virtual* model which only accesses data
12
from the underlying data model as needed. For example, if you have a million
13
element array, but can only view 50 rows at a time, the editor will only
14
request 50 rows at a time.
16
- **Very flexible data model**. The editor uses a new adapter model for
17
interfacing with your underlying data that allows it to easily deal with many
18
types of data representation: from lists of objects, to arrays of numbers, to
19
tuples of tuples, and many other formats as well.
21
- **Supports a useful set of data operations**. The editor includes built-in
22
support for a number of useful data operations, including:
24
- Moving the selection up and down using the keyboard arrow keys.
25
- Moving rows up and down using the keyboard arrow keys.
26
- Inserting and deleting rows using the keyboard.
27
- Begin editing table rows using the keyboard.
28
- Drag and drop of table items to and from the editor, including support for
29
both *copy* and *move* operations for single or multiple table items.
31
- **Visually appealing**. The editor, in general, uses the underlying OS's
32
native table or grid control, and as a result often looks better than the
33
control used by the **TableEditor**.
35
- **Supports displaying text and images in any cell**. Note however that
36
the images displayed must all be the same size for optimal results.
38
Some of the weaknesses of the **TabularEditor** compared to the **TableEditor**
41
- **Not as full-featured**. The **TableEditor** includes support for arbitrary
42
data filters and searches and different types of data sorting. The differences
43
here may narrow over time as new features get added to the **TabularEditor**.
45
- **Limited data editing capabilities**: The **TabularEditor** only supports
46
editing textual values, unlike the **TableEditor**, which supports a wide
47
variety of column editors and can be extended with more as needed. This is
48
due to limitations of the underlying native OS control used by the editor.
50
Setting up a **TabularEditor** for use in a Traits UI is divided into two main
53
- Configuring the **TabularEditor** object.
54
- Creating a suitable adapter (or adapters) for use with the editor.
56
We'll start off first with a description of the **TabularEditor** class, and
57
describe the adapter interface in a later section.
59
The TabularEditor Class
60
-----------------------
62
The **TabularEditor** class defines a large number of traits which are used to
63
configure the editor. We'll divide the set of traits into several categories,
64
and describe each in turn.
70
A boolean value which specifies whether or not column headers should be
71
displayed at the top of the table. It defaults to **True**.
74
A boolean value which specifies whether or not horizontal lines should be
75
drawn between rows in the table. It defaults to **True**.
78
A boolean value which specifies whether or not vertical lines should be
79
drawn between columns in the table. It defaults to **True**.
85
A boolean value that specifies whether or not the user is allowed to edit
86
data in the table. It defaults to **True**.
89
A boolean value that specifies whether or not the user is allowed to select
90
multiple rows in the table at once. It defaults to **False**.
93
A list of strings that specify what operations the user is allowed to
94
perform on items in the table. The possible values are:
96
- **delete**: The user can delete table rows.
97
- **insert**: The user can insert new table rows at any position in the
99
- **append**: The user can append new table rows to the end of the table.
100
- **edit**: The user can edit the contents of table rows.
101
- **move**: The user can move table rows within the table.
103
You should include in the list all operations that you want to allow the
104
user to perform (e.g. *['delete','insert','append']*).
107
A boolean value that specifies whether *drag move* operations are allowed
108
(**True**), or should all drag operations be treated as *drag copy*
109
operations (**False**). The default is **False**.
112
An instance of **TabularAdapter** that the editor uses to interface to the
113
underlying **Item** data supplied to the editor. This is normally an
114
instance of a subclass of **TabularAdapter** specially written for the type
115
of data being edited. This will be described more fully in the
116
**TabularAdapter** section.
119
An optional string that specifies the extended trait name of a trait that
120
contains the **TabularAdapter** the editor should used. If the value of the
121
trait changes while the editor is active, the editor will automatically
122
start using the new adapter. Normally you will use either the *adapter*
123
trait *or* the *adapter_name* trait, but not both. However, it is possible
124
to use both together if needed.
127
An optional list of **ImageResource** objects describing a set of images
128
that can be displayed in the table's cells. Specifying a set here allows you
129
to refer to the images by name within the adapter. However, it is also
130
possible for the adapter to supply the **ImageResource** object directly
131
for any image it wants to display.
133
Event Handling Traits
134
---------------------
137
An optional string that specifies the extended trait name of a trait that
138
contains the currently selected table items. For a single-select mode
139
editor, this should be a scalar trait, and for a multi-select mode editor,
140
it should be a list trait. The type of the trait should be the same as the
141
type of data being displayed in the table. This trait can be used to both
142
set and get the current table selection.
145
An optional string that specifies the extended trait name of a trait that
146
contains the currently selected table item indices. For a single-select mode
147
editor this should be an **Int** value, and for a multi-select mode editor
148
it should be a **List(Int)** value. This trait can be used to both set and
149
get the current table selection.
152
An optional string that specifies the extended trait name of a trait that
153
contains the currently activated table item. It should be an instance of the
154
table item data type. The trait can only be used to get the value of
155
the most recently activated table item. An item is activated either by the
156
user double-clicking on it or by pressing the **Enter** key when the item
160
An optional string that specifies the extended trait name of a trait that
161
contains the currently activated table item index. It should be an **Int**
162
value. The trait can only be used to get the index of the most recently
163
activated table item. An item is activated either by the user
164
double-clicking on it or by pressing the **Enter** key when the item is
168
An optional string that specifies the extended trait name of a trait that
169
contains a **TabularEditorEvent** object containing the information
170
associated with the most recent left mouse button click within the editor.
171
The trait can only be used to get the **TabularEditorEvent** object. The
172
**TabularEditorEvent** object is described in the next section.
175
An optional string that specifies the extended trait name of a trait that
176
contains a **TabularEditorEvent** object containing the information
177
associated with the most recent left mouse button double-click within the
178
editor. The trait can only be used to get the **TabularEditorEvent**
179
object. The **TabularEditorEvent** object is described in the next section.
182
An optional string that specifies the extended trait name of a trait that
183
contains a **TabularEditorEvent** object containing the information
184
associated with the most recent right mouse button click within the editor.
185
The trait can only be used to get the **TabularEditorEvent** object. The
186
**TabularEditorEvent** object is described in the next section.
189
An optional string that specifies the extended trait name of a trait that
190
contains a **TabularEditorEvent** object containing the information
191
associated with the most recent right mouse button double-click within the
192
editor. The trait can only be used to get the **TabularEditorEvent**
193
object. The **TabularEditorEvent** object is described in the next section.
195
The TabularEditorEvent Class
196
----------------------------
198
Objects of the **TabularEditorEvent** class contain information related to a
199
mouse button click that occurs within the editor. The class has no methods,
200
but does define the following traits:
203
An integer specifying the index of the table item that was clicked on.
206
A value specifying the column id of the table cell that was clicked on.
207
This value will correspond to the second element of the tuple used to
208
define the column in the **TabularEditor** adapter supplied to the editor.
209
This will be described in a later section.
212
The data item corresponding to the table row that was clicked on. The type
213
of this data will depend upon the type of data contained in the underlying
216
The TabularEditor User Interface
217
--------------------------------
219
Depending upon how you have configured the **TabularEditor** and its associated
220
adapter, the following user interface features may be available:
222
- **Up arrow**: Move the selection up one line.
223
- **Down arrow**: Move the selection down one line.
224
- **Page down**: Append a new item to the end of the list (*'append'*).
225
- **Left arrow**: Move the current selection up one line (*'move'*).
226
- **Right arrow**: Move the current selection down one line (*'move'*).
227
- **Backspace, Delete**: Delete all items in the current selection from the
229
- **Enter, Escape**: Edit the current selection (*'edit'*).
230
- **Insert**: Insert a new item before the current selection (*'insert'*).
232
In the preceding list, the values in parentheses refer to the operation that
233
must be included in the **TabularEditor** *operations* trait in order for the
234
specified key to have any effect.
236
The *append*, *move*, *edit* and *insert* operations are only available if a
237
single item is selected. The *delete* operation works when the selection has
240
Depending upon how the **TabularEditor** and adapter are specified, drag and
241
drop operations may also be available. If multiple items are selected and the
242
user drags one of the selected items, all selected items will be included in
243
the drag operation. If the user drags a non-selected item, only that item will
246
The editor also supports both *drag-move* and *drag-copy* semantics. A
247
*drag-move* operation means that the dragged items will be sent to the target
248
and removed from the list data. A *drag-copy* operation means that the
249
dragged items will be sent to the target, but will *not* be deleted from the
250
**Item** data. Note that in a *drag-copy* operation, if you do not want the
251
target to receive the same data contained in the list, then you must return a
252
copy or clone of the data when the editor requests the drag data from the
255
You can prevent *drag-move* operations by making sure that the
256
**TabularEditor** *drag_move* trait is set to **False** (the default).
258
Note that the default operation when a user begins a drag operation is
259
*drag_move*. A *drag-copy* operation occurs when the user also holds the
260
*Ctrl* key down during the drag operation (the mouse pointer changes to indicate
261
the change in drag semantics). If *drag_move* operations are disabled by
262
setting the **TabularEditor** *drag_move* trait to **False**, any *drag-move*
263
operation is automatically treated as a *drag_copy*.
265
The tabular editor only allows the user to edit the first column of data in the
266
table (a restriction imposed by the underlying OS widget). If the *'edit'*
267
operation is enabled, the user can begin editing the first column either by
268
clicking on the row twice, or by selecting the row and pressing the **Enter** or
271
Finally, the user can resize columns in the table by dragging the column title
272
dividers left or right with the mouse. Once resized in this manner, the column
273
remains that size until the user resizes the column again. This is true
274
even if you assigned a dynamic width to the column (see the **TabularAdapter**
275
section for more information about what this means). If the user wants to allow
276
a previously user-sized column to be restored to its original developer
277
specified size again, they must right-click on the column title to *release*
278
its user specified size and restore its original size.
280
If you enable *persistence* for the editor by specifying a non-empty *id* trait
281
for the editor's **Item** and **View** objects, any user specified column
282
widths will be saved across application sessions.
284
The TabularAdapter Class
285
------------------------
287
The power and flexibility of the tabular editor is mostly a result of the
288
**TabularAdapter** class, which is the base class from which all tabular editor
289
adapters must be derived.
291
The **TabularEditor** object interfaces between the underlying toolkit widget
292
and your program, while the **TabularAdapter** object associated with the
293
editor interfaces between the editor and your data.
295
The design of the **TabularAdapter** base class is such that it tries to make
296
simple cases simple and complex cases possible. How it accomplishes this is what
297
we'll be discussing in the following sections.
299
The TabularAdapter *columns* Trait
300
----------------------------------
302
First up is the **TabularAdapter** *columns* trait, which is a list of values
303
which define, in presentation order, the set of columns to be displayed by the
304
associated **TabularEditor**.
306
Each entry in the *columns* list can have one of two forms:
311
where *string* is the user interface name of the column (which will appear in
312
the table column header) and *any* is any value that you want to use to
313
identify that column to your adapter. Normally this value is either a trait name
314
or an integer index value, but it can be any value you want. If only *string*
315
is specified, then *any* is the index of the *string* within *columns*.
317
For example, say you want to display a table containing a list of tuples, each
318
of which has three values: a name, an age, and a weight. You could then use
319
the following value for the *columns* trait::
321
columns = [ 'Name', 'Age', 'Weight' ]
323
By default, the *any* values (also referred to in later sections as the
324
*column ids*) for the columns will be the corresponding tuple index values.
326
Say instead that you have a list of **Person** objects, with *name*, *age* and
327
*weight* traits that you want to display in the table. Then you could use the
328
following *columns* value instead::
330
columns = [ ( 'Name', 'name' ),
332
( 'Weight', 'weight' ) ]
334
In this case, the *column ids* are the names of the traits you want to display
337
Note that it is possible to dynamically modify the contents of the *columns*
338
trait while the **TabularEditor** is active. The **TabularEditor** will
339
automatically modify the table to show the new set of defined columns.
341
The Core TabularAdapter Interface
342
---------------------------------
344
In this section, we'll describe the core interface to the **TabularAdapter**
345
class. This is the actual interface used by the **TabularEditor** to access your
346
data and display attributes. In the most complex data representation cases,
347
these are the methods that you must override in order to have the greatest
348
control over what the editor sees and does.
350
However, the base **TabularAdapter** class provides default implementations for
351
all of these methods. In subsequent sections, we'll look at how these default
352
implementations provide simple means of customizing the adapter to your needs.
353
But for now, let's start by covering the details of the core interface itself.
355
To reduce the amount of repetition, we'll use the following definitions in all
356
of the method argument lists that follow in this section:
359
The object whose trait is being edited by the **TabularEditor**.
362
The name of the trait the **TabularEditor** is editing.
365
The row index (starting with 0) of a table item.
368
The column index (starting with 0) of a table column.
370
The adapter interface consists of a number of methods which can be divided into
371
two main categories: those which are sensitive to the type of a particular table
372
item, and those which are not. We'll begin with the methods that are
373
sensitive to an item's type:
375
get_alignment ( object, trait, column )
376
Returns the alignment style to use for a specified column.
378
The possible values that can be returned are: *'left'*, *'center'* or
379
*'right'*. All table items share the same alignment for a specified column.
381
get_width ( object, trait, column )
382
Returns the width to use for a specified column. The result can either be a
383
float or integer value.
385
If the value is <= 0, the column will have a *default* width, which is the
386
same as specifying a width of *0.1*.
388
If the value is > 1.0, it is converted to an integer and the result is
389
the width of the column in pixels. This is referred to as a *fixed width*
392
If the value is a float such that 0.0 < value <= 1.0, it is treated as the
393
*unnormalized fraction of the available space* that is to be assigned to the
394
column. What this means requires a little explanation.
396
To arrive at the size in pixels of the column at any given time, the editor
397
adds together all of the *unnormalized fraction* values returned for all
398
columns in the table to arrive at a total value. Each
399
*unnormalized fraction* is then divided by the total to create a
400
*normalized fraction*. Each column is then assigned an amount of space in
401
pixels equal to the maximum of 30 or its *normalized fraction* multiplied
402
by the *available space*. The *available space* is defined as the actual
403
width of the table minus the width of all *fixed width* columns. Note that
404
this calculation is performed each time the table is resized in the user
405
interface, thus allowing columns of this type to increase or decrease their
406
width dynamically, while leaving *fixed width* columns unchanged.
408
get_can_edit ( object, trait, row )
409
Returns a boolean value indicating whether the user can edit a specified
410
*object.trait[row]* item.
412
A **True** result indicates that the value can be edited, while a **False**
413
result indicates that it cannot.
415
get_drag ( object, trait, row )
416
Returns the value to be *dragged* for a specified *object.trait[row]* item.
417
A result of **None** means that the item cannot be dragged. Note that the
418
value returned does not have to be the actual row item. It can be any
419
value that you want to drag in its place. In particular, if you want the
420
drag target to receive a copy of the row item, you should return a copy or
421
clone of the item in its place.
423
Also note that if multiple items are being dragged, and this method returns
424
**None** for any item in the set, no drag operation is performed.
426
get_can_drop ( object, trait, row, value )
427
Returns whether the specified *value* can be dropped on the specified
428
*object.trait[row]* item. A value of **True** means the *value* can be
429
dropped; and a value of **False** indicates that it cannot be dropped.
431
The result is used to provide the user positive or negative drag feedback
432
while dragging items over the table. *Value* will always be a single value,
433
even if multiple items are being dragged. The editor handles multiple drag
434
items by making a separate call to *get_can_drop* for each item being
437
get_dropped ( object, trait, row, value )
438
Returns how to handle a specified *value* being dropped on a specified
439
*object.trait[row]* item. The possible return values are:
441
- **'before'**: Insert the specified *value* before the dropped on item.
442
- **'after'**: Insert the specified *value* after the dropped on item.
444
Note there is no result indicating *do not drop* since you will have already
445
indicated that the *object* can be dropped by the result returned from a
446
previous call to *get_can_drop*.
448
get_font ( object, trait, row )
449
Returns the font to use for displaying a specified *object.trait[row]* item.
451
A result of **None** means use the default font; otherwise a **wx.Font**
452
object should be returned. Note that all columns for the specified table row
453
will use the font value returned.
455
get_text_color ( object, trait, row )
456
Returns the text color to use for a specified *object.trait[row]* item.
458
A result of **None** means use the default text color; otherwise a
459
**wx.Colour** object should be returned. Note that all columns for the
460
specified table row will use the text color value returned.
462
get_bg_color ( object, trait, row )
463
Returns the background color to use for a specified *object.trait[row]*
466
A result of **None** means use the default background color; otherwise a
467
**wx.Colour** object should be returned. Note that all columns for the
468
specified table row will use the background color value returned.
470
get_image ( object, trait, row, column )
471
Returns the image to display for a specified *object.trait[row].column*
474
A result of **None** means no image will be displayed in the specified table
475
cell. Otherwise the result should either be the name of the image, or an
476
**ImageResource** object specifying the image to display.
478
A name is allowed in the case where the image is specified in the
479
**TabularEditor** *images* trait. In that case, the name should be the same
480
as the string specified in the **ImageResource** constructor.
482
get_format ( object, trait, row, column )
483
Returns the Python formatting string to apply to the specified
484
*object.trait[row].column* item in order to display it in the table.
486
The result can be any Python string containing exactly one Python formatting
487
sequence, such as *'%.4f'* or *'(%5.2f)'*.
489
get_text ( object, trait, row, column )
490
Returns a string containing the text to display for a specified
491
*object.trait[row].column* item.
493
If the underlying data representation for a specified item is not a string,
494
then it is your responsibility to convert it to one before returning it as
497
set_text ( object, trait, row, text ):
498
Sets the value for the specified *object.trait[row].column* item to the
499
string specified by *text*.
501
If the underlying data does not store the value as text, it is your
502
responsibility to convert *text* to the correct representation used. This
503
method is called when the user completes an editing operation on a table
506
get_tooltip ( object, trait, row, column )
507
Returns a string containing the tooltip to display for a specified
508
*object.trait[row].column* item.
510
You should return the empty string if you do not wish to display a tooltip.
512
The following are the remaining adapter methods, which are not sensitive to the
513
type of item or column data:
515
get_item ( object, trait, row )
516
Returns the specified *object.trait[row]* item.
518
The value returned should be the value that exists (or *logically* exists)
519
at the specified *row* in your data. If your data is not really a list or
520
array, then you can just use *row* as an integer *key* or *token* that
521
can be used to retrieve a corresponding item. The value of *row* will
522
always be in the range: 0 <= row < *len( object, trait )* (i.e. the result
523
returned by the adapter *len* method).
525
len ( object, trait )
526
Returns the number of row items in the specified *object.trait* list.
528
The result should be an integer greater than or equal to 0.
530
delete ( object, trait, row )
531
Deletes the specified *object.trait[row]* item.
533
This method is only called if the *delete* operation is specified in the
534
**TabularEditor** *operation* trait, and the user requests that the item be
535
deleted from the table. The adapter can still choose not to delete the
536
specified item if desired, although that may prove confusing to the user.
538
insert ( object, trait, row, value )
539
Inserts *value* at the specified *object.trait[row]* index. The specified
542
- An item being moved from one location in the data to another.
543
- A new item created by a previous call to *get_default_value*.
544
- An item the adapter previously approved via a call to *get_can_drop*.
546
The adapter can still choose not to insert the item into the data, although
547
that may prove confusing to the user.
549
get_default_value ( object, trait )
550
Returns a new default value for the specified *object.trait* list.
552
This method is called when *insert* or *append* operations are allowed and
553
the user requests that a new item be added to the table. The result should
554
be a new instance of whatever underlying representation is being used for
557
Creating a Custom TabularAdapter
558
--------------------------------
560
Having just taken a look at the core **TabularAdapter** interface, you might now
561
be thinking that there are an awful lot of methods that need to be specified to
562
get an adapter up and running. But as we mentioned earlier, **TabularAdapter**
563
is not an abstract base class. It is a concrete base class with implementations
564
for each of the methods in its interface. And the implementations are written
565
in such a way that you will hopefully hardly ever need to override them.
567
In this section, we'll explain the general implementation style used by these
568
methods, and how you can take advantage of them in creating your own adapters.
570
One of the things you probably noticed as you read through the core adapter
571
interface section is that most of the methods have names of the form:
572
*get_xxx* or *set_xxx*, which is similar to the familiar *getter/setter* pattern
573
used when defining trait properties. The adapter interface is purposely defined
574
this way so that it can expose and leverage a simple set of design rules.
576
The design rules are followed consistently in the implementations of all of the
577
adapter methods described in the first section of the core adapter interface, so
578
that once you understand how they work, you can easily apply the design pattern
579
to all items in that section. Then, only in the case where the design rules will
580
not work for your application will you ever have to override any of those
581
**TabularAdapter** base class method implementations.
583
So the first thing to understand is that if an adapter method name has the form:
584
*get_xxx* or *set_xxx* it really is dealing with some kind of trait called
585
*xxx*, or which contains *xxx* in its name. For example, the *get_alignment*
586
method retrieves the value of some *alignment* trait defined on the adapter.
587
In the following discussion we'll simply refer to an attribute name generically
588
as *attribute*, but you will need to replace it by an actual attribute name
589
(e.g. *alignment*) in your adapter.
591
The next thing to keep in mind is that the adapter interface is designed to
592
easily deal with items that are not all of the same type. As we just said, the
593
design rules apply to all adapter methods in the first group, which were
594
defined as methods which are sensitive to an item's type. Item type sensitivity
595
plays an important part in the design rules, as we will see shortly.
597
With this in mind, we now describe the simple design rules used by the first
598
group of methods in the **TabularAdapter** class:
600
- When getting or setting an adapter attribute, the method first retrieves the
601
underlying item for the specified data row. The item, and type (i.e. class) of
602
the item, are then used in the next rule.
604
- The method gets or sets the first trait it finds on the adapter that matches
605
one of the following names:
607
- *classname_columnid_attribute*
608
- *classsname_attribute*
609
- *columnid_attribute*
614
- *classname* is the name of the class of the item found in the first step, or
615
one of its base class names, searched in the order defined by the *mro*
616
(**method resolution order**) for the item's class.
617
- *columnid* is the column id specified by the developer in the adapter's
618
*column* trait for the specified table column.
619
- *attribute* is the attribute name as described previously (e.g.
622
Note that this last rule always finds a matching trait, since the
623
**TabularAdapter** base class provides traits that match the simple *attribute*
624
form for all attributes these rules apply to. Some of these are simple traits,
625
while others are properties. We'll describe the behavior of all these *default*
628
The basic idea is that rather than override the first group of core adapter
629
methods, you simply define one or more simple traits or trait properties on your
630
**TabularAdapter** subclass that provide or accept the specified information.
632
All of the adapter methods in the first group provide a number of arguments,
633
such as *object*, *trait*, *row* and *column*. In order to define a trait
634
property, which cannot be passed this information directly, the adapter always
635
stores the arguments and values it computes in the following adapter traits,
636
where they can be easily accessed by a trait getter or setter method:
638
- *row*: The table row being accessed.
639
- *column*: The column id of the table column being accessed (not its index).
640
- *item*: The data item for the specified table row (i.e. the item determined
641
in the first step described above).
642
- *value*: In the case of a *set_xxx* method, the value to be set; otherwise it
645
As mentioned previously, the **TabularAdapter** class provides trait definitions
646
for all of the attributes these rules apply to. You can either use the
647
default values as they are, override the default, set a new value, or completely
648
replace the trait definition in a subclass. A description of the default trait
649
implementation for each attribute is as follows:
651
default_value = Any( '' )
652
The default value for a new row.
654
The default value is the empty string, but you will normally need to assign
655
a different (default) value.
658
The default Python formatting string for a column item.
660
The default value is *'%s'* which will simply convert the column item to a
661
displayable string value.
664
The text to display for the column item.
666
The implementation of the property checks the type of the column's
669
- If it is an integer, it returns *format%item[column_id]*.
670
- Otherwise, it returns *format%item.column_id*.
672
Note that *format* refers to the value returned by a call to *get_format*
673
for the current column item.
675
text_color = Property
676
The text color for a row item.
678
The property implementation checks to see if the current table row is even
679
or odd, and based on the result returns the value of the *even_text_color*
680
or *odd_text_color* trait if the value is not **None**, and the value of the
681
*default_text_color* trait if it is. The definition of these additional
682
traits are as follows:
684
- odd_text_color = Color( None )
685
- even_text_color = Color( None )
686
- default_text_color = Color( None )
688
Remember that a **None** value means use the default text color.
691
The background color for a row item.
693
The property implementation checks to see if the current table row is even
694
or odd, and based on the result returns the value of the *even_bg_color* or
695
*odd_bg_color* trait if the value is not **None**, and the value of the
696
*default_bg_color* trait if it is. The definition of these additional
697
traits are as follows:
699
- odd_bg_color = Color( None )
700
- even_bg_color = Color( None )
701
- default_bg_color = Color( None )
703
Remember that a **None** value means use the default background color.
705
alignment = Enum( 'left', 'center', 'right' )
706
The alignment to use for a specified column.
708
The default value is *'left'*.
711
The width of a specified column.
713
The default value is *-1*, which means a dynamically sized column with an
714
*unnormalized fractional* value of *0.1*.
716
can_edit = Bool( True )
717
Specifies whether the text value of the current item can be edited.
719
The default value is **True**, which means that the user can edit the value.
722
A property which returns the value to be dragged for a specified row item.
724
The property implementation simply returns the current row item.
726
can_drop = Bool( False )
727
Specifies whether the specified value be dropped on the current item.
729
The default value is **False**, meaning that the value cannot be dropped.
731
dropped = Enum( 'after', 'before' )
732
Specifies where a dropped item should be placed in the table relative to
733
the item it is dropped on.
735
The default value is *'after'*.
738
The font to use for the current item.
740
The default value is the standard default Traits font value.
743
The name of the default image to use for a column.
745
The default value is **None**, which means that no image will be displayed
748
# The text of a row/column item:
752
The tooltip information for a column item.
754
The default value is the empty string, which means no tooltip information
755
will be displayed for the column.
757
The preceding discussion applies to all of the methods defined in the first
758
group of **TabularAdapter** interface methods. However, the design rules do not
759
apply to the remaining five adapter methods, although they all provide a useful
760
default implementation:
762
get_item ( object, trait, row )
763
Returns the value of the *object.trait[row]* item.
765
The default implementation assumes the trait defined by *object.trait* is a
766
*sequence* and attempts to return the value at index *row*. If an error
767
occurs, it returns **None** instead. This definition should work correctly
768
for lists, tuples and arrays, or any other object that is indexable, but
769
will have to be overridden for all other cases.
771
Note that this method is the one called in the first design rule described
772
previously to retrieve the item at the current table row.
774
len ( object, trait )
775
Returns the number of items in the specified *object.trait* list.
777
Again, the default implementation assumes the trait defined by
778
*object.trait* is a *sequence* and attempts to return the result of calling
779
*len( object.trait )*. It will need to be overridden for any type of data
780
which for which *len* will not work.
782
delete ( object, trait, row )
783
Deletes the specified *object.trait[row]* item.
785
The default implementation assumes the trait defined by *object.trait* is a
786
mutable sequence and attempts to perform a *del object.trait[row]*
789
insert ( object, trait, row, value )
790
Inserts a new value at the specified *object.trait[row]* index.
792
The default implementation assumes the trait defined by *object.trait* is a
793
mutable sequence and attempts to perform an *object.trait[row:row]=[value]*
796
get_default_value ( object, trait )
797
Returns a new default value for the specified *object.trait* list.
799
The default implementation simply returns the value of the adapter's
800
*default_value* trait.
802
A TabularAdapter Example
803
------------------------
805
Having now learned about the core adapter interface as well as the design rules
806
supported by the default method implementations, you're probably wondering how
807
you can use a **TabularAdapter** for creating a real user interface.
809
So in this section we'll cover a simple example of creating a **TabularAdapter**
810
subclass to try and show how all of the pieces fit together.
812
In subsequent tutorials, we'll provide complete examples of creating
813
user interfaces using both the **TabularEditor** and **TabularAdapter** in
816
For this example, let's assume we have the following two classes::
818
class Person ( HasTraits ):
824
class MarriedPerson ( Person ):
826
partner = Instance( Person )
828
where **Person** represents a single person, and **MarriedPerson** represents
829
a married person and is derived from **Person** but adds the *partner* trait to
830
reference the person they are married to.
832
Now, assume we also have the following additional class::
834
class Report ( HasTraits ):
836
people = List( Person )
838
which has a *people* trait which contains a list of both **Person** and
839
**MarriedPerson** objects, and we want to create a tabular display showing the
840
following information:
844
- The person's address
845
- The name of the person's spouse (if any)
849
- We want to use a Courier 10 point font for each line in the table.
850
- We want the age column to be right, instead of left, justified
851
- If the person is a minor (age < 18) and married, we want to show a red flag
852
image in the age column.
853
- If the person is married, we want to make the background color for that row
856
Given this set of requirements, we can now define the following
857
**TabularAdapter** subclass::
859
class ReportAdapter ( TabularAdapter ):
861
columns = [ ( 'Name', 'name' ),
863
( 'Address', 'address' )
864
( 'Spouse', 'spouse' ) ]
867
age_alignment = Constant( 'right' )
868
MarriedPerson_age_image = Property
869
MarriedPerson_bg_color = Color( 0xE0E0FF )
870
MarriedPerson_spouse_text = Property
871
Person_spouse_text = Constant( '' )
873
def _get_MarriedPerson_age_image ( self ):
874
if self.item.age < 18:
878
def _get_MarriedPerson_spouse_text ( self ):
879
return self.item.partner.name
881
Hopefully, this simple example conveys some of the power and flexibility that
882
the **TabularAdapter** class provides you. But, just in case it doesn't, let's
883
go over some of the more interesting details:
885
- Note the values in the *columns* trait. The first three values define
886
*column ids* which map directly to traits defined on our data objects, while
887
the last one defines an arbitrary string which we define so that we can
888
reference it in the *MarriedPerson_spouse_text* and *Person_spouse_text* trait
891
- Since the font we want to use applies to all table rows, we just specify a
892
new default value for the existing **TabularAdapter** *font* trait.
894
- Since we only want to override the default left alignment for the age column,
895
we simply define an *age_alignment* trait as a constant *'right'* value. We
896
could have also used *age_alignment = Str('right')*, but *Constant* never
897
requires storage to be used in an object.
899
- We define the *MarriedPerson_age_image* property to handle putting the
900
*red flag* image in the age column. By including the class name of the items
901
it applies to, we only need to check the *age* value in determining what
904
- Similary, we use the *MarriedPerson_bg_color* trait to ensure that each
905
**MarriedPerson** object has the correct background color in the table.
907
- Finally, we use the *MarriedPerson_spouse_text* and *Person_spouse_text*
908
traits, one a property and the other a simple constant value, to determine
909
what text to display in the *Spouse* column for the different object types.
910
Note that even though a **MarriedPerson** is both a **Person** and a
911
**MarriedPerson**, it will correctly use the *MarriedPerson_spouse_text* trait
912
since the search for a matching trait is always made in *mro* order.
914
Although this is completely subjective, some of the things that the author
915
feels stand out about this approach are:
917
- The class definition is short and sweet. Less code is good.
918
- The bulk of the code is declarative. Less room for logic errors.
919
- There is only one bit of logic in the class (the *if* statement in the
920
*MarriedPerson_age_image* property implementation). Again, less logic usually
921
translates into more reliable code).
922
- The defined traits and even the property implementation method names read
923
very descriptively. *_get_MarriedPerson_age_image* pretty much says what you
924
would write in a comment or doc string. The implementation almost is the
927
Look for a complete traits UI example based on this sample problem definition in
928
the *Single and Married Person Example* tutorial in this section.
930
Now, as the complexity of a tabular view increases, the definition of the
931
**TabularAdapter** class could possibly start to get large and unwieldy. At
932
this point we could begin refactoring our design to use the **ITabularAdapter**
933
interface and **AnITabularAdapter** implementation class to create
934
*sub-adapters* that can be added to our **TabularAdapter** subclass to extend
935
its functionality even further. Creating sub-adapters and adding them via the
936
**TabularAdapter** *adapters* trait is a topic covered in a follow-on tutorial.