3
3
# Inspired By And Heavily Based On wxGenericTreeCtrl.
5
5
# Andrea Gavana, @ 17 May 2006
6
# Latest Revision: 14 Apr 2010, 12.00 GMT
6
# Latest Revision: 21 Jun 2011, 22.00 GMT
12
12
# No Limit In What Could Be Added To This Class. The First Things That Comes
15
# 1. Implement The Style TR_EXTENDED (I Have Never Used It, But It May Be Useful).
17
# 2. Add Support For 3-State CheckBoxes (Is That Really Useful?).
19
# 3. Try To Implement A More Flicker-Free Background Image In Cases Like
15
# 1. Try To Implement A More Flicker-Free Background Image In Cases Like
20
16
# Centered Or Stretched Image (Now CustomTreeCtrl Supports Only Tiled
21
17
# Background Images).
23
# 4. Try To Mimic Windows wx.TreeCtrl Expanding/Collapsing behaviour: CustomTreeCtrl
19
# 2. Try To Mimic Windows wx.TreeCtrl Expanding/Collapsing behaviour: CustomTreeCtrl
24
20
# Suddenly Expands/Collapses The Nodes On Mouse Click While The Native Control
25
21
# Has Some Kind Of "Smooth" Expanding/Collapsing, Like A Wave. I Don't Even
26
22
# Know Where To Start To Do That.
28
# 5. Speed Up General OnPaint Things? I Have No Idea, Here CustomTreeCtrl Is Quite
24
# 3. Speed Up General OnPaint Things? I Have No Idea, Here CustomTreeCtrl Is Quite
29
25
# Fast, But We Should See On Slower Machines.
59
55
* CheckBox-type items: checkboxes are easy to handle, just selected or unselected
60
56
state with no particular issues in handling the item's children;
57
* Added support for 3-state value checkbox items;
61
58
* RadioButton-type items: since I elected to put radiobuttons in CustomTreeCtrl, I
62
59
needed some way to handle them, that made sense. So, I used the following approach:
77
74
* Whatever non-toplevel widget can be attached next to an item;
78
75
* Possibility to horizontally align the widgets attached to tree items on the
77
* Possibility to align the widgets attached to tree items to the rightmost edge of CustomTreeCtrl;
80
78
* Default selection style, gradient (horizontal/vertical) selection style and Windows
81
79
Vista selection style;
82
80
* Customized drag and drop images built on the fly;
84
82
* Setting the CustomTreeCtrl check/radio item icons to a personalized imagelist;
85
83
* Changing the style of the lines that connect the items (in terms of `wx.Pen` styles);
86
84
* Using an image as a CustomTreeCtrl background (currently only in "tile" mode);
85
* Adding images to any item in the leftmost area of the CustomTreeCtrl client window.
88
87
And a lot more. Check the demo for an almost complete review of the functionalities.
101
100
- ``TR_AUTO_CHECK_PARENT``: automatically checks/unchecks the item parent;
102
101
- ``TR_AUTO_TOGGLE_CHILD``: automatically toggles the item children.
104
And a style you can use to force the horizontal alignment of all the widgets
103
And two styles you can use to force the horizontal alignment of all the widgets
105
104
attached to the tree items:
107
- ``TR_ALIGN_WINDOWS``: aligns horizontally the windows belongiing to the item on the
106
- ``TR_ALIGN_WINDOWS``: aligns horizontally the windows belonging to the item on the
108
- ``TR_ALIGN_WINDOWS_RIGHT``: aligns to the rightmost position the windows belonging
109
to the item on the same tree level.
111
111
All the methods available in `wx.TreeCtrl` are also available in CustomTreeCtrl.
168
168
``TR_AUTO_TOGGLE_CHILD`` 0x8000 Only meaningful foe checkbox-type items: when a parent item is checked/unchecked its children are toggled accordingly.
169
169
``TR_AUTO_CHECK_PARENT`` 0x10000 Only meaningful foe checkbox-type items: when a child item is checked/unchecked its parent item is checked/unchecked as well.
170
170
``TR_ALIGN_WINDOWS`` 0x20000 Flag used to align windows (in items with windows) at the same horizontal position.
171
``TR_ALIGN_WINDOWS_RIGHT`` 0x40000 Flag used to align windows (in items with windows) to the rightmost edge of `CustomTreeCtrl`.
171
172
============================== =========== ==================================================
212
213
CustomTreeCtrl is distributed under the wxPython license.
214
Latest Revision: Andrea Gavana @ 14 Apr 2010, 12.00 GMT
215
Latest Revision: Andrea Gavana @ 21 Jun 2011, 22.00 GMT
224
225
from wx.lib.expando import ExpandoTextCtrl
247
251
TreeItemIcon_Checked = 0 # check button, checked
248
252
TreeItemIcon_NotChecked = 1 # check button, not checked
249
TreeItemIcon_Flagged = 2 # radio button, selected
250
TreeItemIcon_NotFlagged = 3 # radio button, not selected
253
TreeItemIcon_Undetermined = 2 # check button, undetermined
254
TreeItemIcon_Flagged = 3 # radio button, selected
255
TreeItemIcon_NotFlagged = 4 # radio button, not selected
252
257
# ----------------------------------------------------------------------------
253
258
# CustomTreeCtrl flags
298
303
""" its parent item is checked/unchecked as well. """
299
304
TR_ALIGN_WINDOWS = 0x20000 # to align windows horizontally for items at the same level
300
305
""" Flag used to align windows (in items with windows) at the same horizontal position. """
306
TR_ALIGN_WINDOWS_RIGHT = 0x40000 # to align windows to the rightmost edge of CustomTreeCtrl
307
""" Flag used to align windows (in items with windows) to the rightmost edge of `CustomTreeCtrl`."""
302
309
TR_DEFAULT_STYLE = wx.TR_DEFAULT_STYLE # default style for the tree control
303
310
""" The set of flags that are closest to the defaults for the native control for a""" \
493
500
are dealing with.
495
502
:param `style`: the main L{CustomTreeCtrl} window style flag;
496
:param `shiftDown`: ``True`` if the ``Shift`` has is pressed, ``False`` otherwise;
497
:param `ctrlDown`: ``True`` if the ``Ctrl`` has is pressed, ``False`` otherwise;
503
:param `shiftDown`: ``True`` if the ``Shift`` key is pressed, ``False`` otherwise;
504
:param `ctrlDown`: ``True`` if the ``Ctrl`` key is pressed, ``False`` otherwise;
500
507
is_multiple = (style & TR_MULTIPLE) != 0
861
868
return self._evtKey.GetKeyCode()
864
def SetKeyEvent(self, evt):
871
def SetKeyEvent(self, event):
866
873
Sets the keyboard data (for ``EVT_TREE_KEY_DOWN`` event only).
868
875
:param `event`: a L{TreeEvent} event to be processed.
874
881
def GetLabel(self):
999
1006
# -----------------------------------------------------------------------------
1000
# Auxiliary Classes: TreeRenameTimer
1007
# Auxiliary Classes: TreeEditTimer
1001
1008
# -----------------------------------------------------------------------------
1003
class TreeRenameTimer(wx.Timer):
1010
class TreeEditTimer(wx.Timer):
1004
1011
""" Timer used for enabling in-place edit."""
1006
1013
def __init__(self, owner):
1128
1135
# needs to be notified that the user decided
1129
1136
# not to change the tree item label, and that
1130
1137
# the edit has been cancelled
1131
self._owner.OnRenameCancelled(self._itemEdited)
1138
self._owner.OnCancelEdit(self._itemEdited)
1134
if not self._owner.OnRenameAccept(self._itemEdited, value):
1141
if not self._owner.OnAcceptEdit(self._itemEdited, value):
1135
1142
# vetoed by the user
1219
1226
# focus problems:
1221
1228
if not self.AcceptChanges():
1222
self._owner.OnRenameCancelled(self._itemEdited)
1229
self._owner.OnCancelEdit(self._itemEdited)
1224
1231
# We must let the native text control handle focus, too, otherwise
1225
1232
# it could have problems with the cursor (e.g., in wxGTK).
1338
1345
self._images[TreeItemIcon_Expanded] = _NO_IMAGE
1339
1346
self._images[TreeItemIcon_SelectedExpanded] = _NO_IMAGE
1341
self._checkedimages = [None, None, None, None]
1348
self._checkedimages = [None, None, None, None, None]
1349
self._leftimage = _NO_IMAGE
1343
1351
self._x = 0 # (virtual) offset from top
1344
1352
self._y = 0 # (virtual) offset from left
1353
1361
self._isItalic = False # render the label in italic font
1354
1362
self._ownsAttr = False # delete attribute when done
1355
1363
self._type = ct_type # item type: 0=normal, 1=check, 2=radio
1356
self._checked = False # only meaningful for check and radio
1364
self._is3State = False # true for 3-state checkbox items
1365
self._checked = 0 # only meaningful for check and radio items
1357
1366
self._enabled = True # flag to enable/disable an item
1358
1367
self._hypertext = False # indicates if the item is hypertext
1359
1368
self._visited = False # visited state for an hypertext item
1362
1371
# do not construct the array for normal items
1363
1372
self._checkedimages[TreeItemIcon_Checked] = 0
1364
1373
self._checkedimages[TreeItemIcon_NotChecked] = 1
1365
self._checkedimages[TreeItemIcon_Flagged] = 2
1366
self._checkedimages[TreeItemIcon_NotFlagged] = 3
1374
self._checkedimages[TreeItemIcon_Undetermined] = 2
1375
self._checkedimages[TreeItemIcon_Flagged] = 3
1376
self._checkedimages[TreeItemIcon_NotFlagged] = 4
1369
1379
if parent.GetType() == 2 and not parent.IsChecked():
1381
1391
Returns whether the item is ok or not.
1383
:note: This method simply returns ``True``, it has been added for
1393
:note: This method always returns ``True``, it has been added for
1384
1394
backward compatibility with the wxWidgets C++ implementation.
1430
1440
================================= ========================
1431
1441
``TreeItemIcon_Checked`` To get the checkbox checked item image
1432
1442
``TreeItemIcon_NotChecked`` To get the checkbox unchecked item image
1443
``TreeItemIcon_Undetermined`` To get the checkbox undetermined state item image
1433
1444
``TreeItemIcon_Flagged`` To get the radiobutton checked image
1434
1445
``TreeItemIcon_NotFlagged`` To get the radiobutton unchecked image
1435
1446
================================= ========================
1440
1451
return self._checkedimages[which]
1454
def GetLeftImage(self):
1456
Returns the leftmost image associated to this item, i.e. the image on the
1457
leftmost part of the client area of L{CustomTreeCtrl}.
1460
return self._leftimage
1443
1463
def GetData(self):
1444
1464
"""Returns the data associated to this item."""
1459
1479
self._images[which] = image
1482
def SetLeftImage(self, image):
1484
Sets the item leftmost image, i.e. the image associated to the item on the leftmost
1485
part of the L{CustomTreeCtrl} client area.
1487
:param `image`: an index within the left image list specifying the image to
1488
use for the item in the leftmost part of the client area.
1491
self._leftimage = image
1462
1494
def SetData(self, data):
1502
1534
def GetX(self):
1503
"""Returns the x position on an item. """
1535
"""Returns the `x` position on an item, in logical coordinates. """
1508
1540
def GetY(self):
1509
"""Returns the y position on an item. """
1541
"""Returns the `y` position on an item, in logical coordinates. """
1514
1546
def SetX(self, x):
1516
Sets the x position on an item.
1548
Sets the `x` position on an item, in logical coordinates.
1518
1550
:param `x`: an integer specifying the x position of the item.
1789
1821
return not self._isCollapsed
1826
Returns whether the item is checked or not.
1828
:note: This is meaningful only for checkbox-like and radiobutton-like items.
1832
return self.Get3StateValue()
1834
return self._checked
1837
def Get3StateValue(self):
1839
Gets the state of a 3-state checkbox item.
1841
:return: ``wx.CHK_UNCHECKED`` when the checkbox is unchecked, ``wx.CHK_CHECKED``
1842
when it is checked and ``wx.CHK_UNDETERMINED`` when it's in the undetermined
1845
:note: This method raises an exception when the function is used with a 2-state
1848
:note: This method is meaningful only for checkbox-like items.
1851
if not self.Is3State():
1852
raise Exception("Get3StateValue can only be used with 3-state checkbox items.")
1854
return self._checked
1859
Returns whether or not the checkbox item is a 3-state checkbox.
1861
:return: ``True`` if this checkbox is a 3-state checkbox, ``False`` if it's a
1862
2-state checkbox item.
1864
:note: This method is meaningful only for checkbox-like items.
1867
return self._is3State
1870
def Set3StateValue(self, state):
1872
Sets the checkbox item to the given `state`.
1874
:param `state`: can be one of: ``wx.CHK_UNCHECKED`` (check is off), ``wx.CHK_CHECKED``
1875
(check is on) or ``wx.CHK_UNDETERMINED`` (check is mixed).
1877
:note: This method raises an exception when the checkbox item is a 2-state checkbox
1878
and setting the state to ``wx.CHK_UNDETERMINED``.
1880
:note: This method is meaningful only for checkbox-like items.
1883
if not self._is3State and state == wx.CHK_UNDETERMINED:
1884
raise Exception("Set3StateValue can only be used with 3-state checkbox items.")
1886
self._checked = state
1889
def Set3State(self, allow):
1891
Sets whether the item has a 3-state value checkbox assigned to it or not.
1893
:param `allow`: ``True`` to set an item as a 3-state checkbox, ``False`` to set it
1894
to a 2-state checkbox.
1896
:return: ``True`` if the change was successful, ``False`` otherwise.
1898
:note: This method is meaningful only for checkbox-like items.
1904
self._is3State = allow
1792
1908
def IsChecked(self):
1910
This is just a maybe more readable synonym for L{GetValue}.
1794
1911
Returns whether the item is checked or not.
1796
1913
:note: This is meaningful only for checkbox-like and radiobutton-like items.
1799
return self._checked
1916
return self.GetValue()
1802
1919
def Check(self, checked=True):
2104
2221
if self._type == 0:
2107
if self.IsChecked():
2224
checked = self.IsChecked()
2108
2227
if self._type == 1: # Checkbox
2109
return self._checkedimages[TreeItemIcon_Checked]
2228
if checked == wx.CHK_CHECKED:
2229
return self._checkedimages[TreeItemIcon_Checked]
2231
return self._checkedimages[TreeItemIcon_Undetermined]
2110
2232
else: # Radiobutton
2111
2233
return self._checkedimages[TreeItemIcon_Flagged]
2165
2287
``TR_AUTO_TOGGLE_CHILD`` 0x8000 Only meaningful foe checkbox-type items: when a parent item is checked/unchecked its children are toggled accordingly.
2166
2288
``TR_AUTO_CHECK_PARENT`` 0x10000 Only meaningful foe checkbox-type items: when a child item is checked/unchecked its parent item is checked/unchecked as well.
2167
2289
``TR_ALIGN_WINDOWS`` 0x20000 Flag used to align windows (in items with windows) at the same horizontal position.
2290
``TR_ALIGN_WINDOWS_RIGHT`` 0x40000 Flag used to align windows (in items with windows) to the rightmost edge of `CustomTreeCtrl`.
2168
2291
============================== =========== ==================================================
2170
2293
:param `validator`: window validator;
2194
2317
self._hilightUnfocusedBrush2 = wx.Brush(backcolour)
2196
2319
# image list for icons
2197
self._imageListNormal = self._imageListButtons = self._imageListState = self._imageListCheck = None
2198
self._ownsImageListNormal = self._ownsImageListButtons = self._ownsImageListState = False
2320
self._imageListNormal = self._imageListButtons = self._imageListState = self._imageListCheck = self._imageListLeft = None
2321
self._ownsImageListNormal = self._ownsImageListButtons = self._ownsImageListState = self._ownsImageListLeft = False
2200
2323
# Drag and drop initial settings
2201
2324
self._dragCount = 0
2289
2412
# A constant to use my translation of RendererNative.DrawTreeItemButton
2290
2413
# if the wxPython version is less than 2.6.2.1.
2291
if wx.VERSION_STRING < "2.6.2.1":
2414
if _VERSION_STRING < "2.6.2.1":
2292
2415
self._drawingfunction = DrawTreeItemButton
2294
2417
self._drawingfunction = wx.RendererNative.Get().DrawTreeItemButton
2393
2516
render = wx.RendererNative.Get()
2518
if checked == wx.CHK_CHECKED:
2396
2519
flag = wx.CONTROL_CHECKED
2520
elif checked == wx.CHK_UNDETERMINED:
2521
flag = wx.CONTROL_UNDETERMINED
2404
2529
render.DrawCheckBox(self, mdc, (0, 0, x, y), flag)
2406
render.DrawRadioButton(self, mdc, (0, 0, x, y), flag)
2531
if _VERSION_STRING < "2.9":
2532
render.DrawRadioButton(self, mdc, (0, 0, x, y), flag)
2534
render.DrawRadioBitmap(self, mdc, (0, 0, x, y), flag)
2408
2536
mdc.SelectObject(wx.NullBitmap)
2409
2537
bmp.SetMaskColour(mask)
2562
2690
return item.IsChecked()
2693
def GetItem3StateValue(self, item):
2695
Gets the state of a 3-state checkbox item.
2697
:param `item`: an instance of L{GenericTreeItem}.
2699
:return: ``wx.CHK_UNCHECKED`` when the checkbox is unchecked, ``wx.CHK_CHECKED``
2700
when it is checked and ``wx.CHK_UNDETERMINED`` when it's in the undetermined
2703
:note: This method raises an exception when the function is used with a 2-state
2706
:note: This method is meaningful only for checkbox-like items.
2709
return item.Get3StateValue()
2712
def IsItem3State(self, item):
2714
Returns whether or not the checkbox item is a 3-state checkbox.
2716
:param `item`: an instance of L{GenericTreeItem}.
2718
:return: ``True`` if this checkbox is a 3-state checkbox, ``False`` if it's a
2719
2-state checkbox item.
2721
:note: This method is meaningful only for checkbox-like items.
2724
return item.Is3State()
2727
def SetItem3StateValue(self, item, state):
2729
Sets the checkbox item to the given `state`.
2731
:param `item`: an instance of L{GenericTreeItem};
2732
:param `state`: can be one of: ``wx.CHK_UNCHECKED`` (check is off), ``wx.CHK_CHECKED``
2733
(check is on) or ``wx.CHK_UNDETERMINED`` (check is mixed).
2735
:note: This method raises an exception when the checkbox item is a 2-state checkbox
2736
and setting the state to ``wx.CHK_UNDETERMINED``.
2738
:note: This method is meaningful only for checkbox-like items.
2741
item.Set3StateValue(state)
2744
def SetItem3State(self, item, allow):
2746
Sets whether the item has a 3-state value checkbox assigned to it or not.
2748
:param `item`: an instance of L{GenericTreeItem};
2749
:param `allow`: ``True`` to set an item as a 3-state checkbox, ``False`` to set it
2750
to a 2-state checkbox.
2752
:return: ``True`` if the change was successful, ``False`` otherwise.
2754
:note: This method is meaningful only for checkbox-like items.
2757
return item.Set3State(allow)
2565
2760
def CheckItem2(self, item, checked=True, torefresh=False):
2567
2762
Used internally to avoid ``EVT_TREE_ITEM_CHECKED`` events.
2614
2809
events ``EVT_TREE_ITEM_CHECKING`` and ``EVT_TREE_ITEM_CHECKED``.
2616
2811
:param `item`: an instance of L{GenericTreeItem};
2617
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
2812
:param `checked`: for a radiobutton-type item, ``True`` to check it, ``False``
2813
to uncheck it. For a checkbox-type item, it can be one of ``wx.CHK_UNCHECKED``
2814
when the checkbox is unchecked, ``wx.CHK_CHECKED`` when it is checked and
2815
``wx.CHK_UNDETERMINED`` when it's in the undetermined state.
2620
2818
# Should we raise an error here?!?
2935
3141
return item.GetImage(which)
3144
def GetItemLeftImage(self, item):
3146
Returns the item leftmost image, i.e. the image associated to the item on the leftmost
3147
part of the L{CustomTreeCtrl} client area.
3149
:param `item`: an instance of L{GenericTreeItem}.
3152
return item.GetLeftImage()
2938
3155
def GetPyData(self, item):
2940
3157
Returns the data associated to an item.
3024
3241
self.RefreshLine(item)
3244
def SetItemLeftImage(self, item, image):
3246
Sets the item leftmost image, i.e. the image associated to the item on the leftmost
3247
part of the L{CustomTreeCtrl} client area.
3249
:param `item`: an instance of L{GenericTreeItem};
3250
:param `image`: an index within the left image list specifying the image to
3251
use for the item in the leftmost part of the client area.
3254
item.SetLeftImage(image)
3256
dc = wx.ClientDC(self)
3257
self.CalculateSize(item, dc)
3258
self.RefreshLine(item)
3027
3261
def SetPyData(self, item, data):
3029
3263
Sets the data associated to an item.
3882
4116
return lastGoodItem
3885
def ResetTextControl(self):
3886
""" Called by L{TreeTextCtrl} when it marks itself for deletion. """
4119
def ResetEditControl(self):
4120
""" Called by L{EditCtrl} when it marks itself for deletion. """
3888
self._textCtrl.Destroy()
3889
self._textCtrl = None
4122
if self._editCtrl is not None:
4123
self._editCtrl.Destroy()
4124
self._editCtrl = None
3891
4126
self.CalculatePositions()
4102
4337
return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data)
4105
def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
4340
def InsertItemByIndex(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
4107
4342
Inserts an item after the given previous.
4109
4344
:param `parentId`: an instance of L{GenericTreeItem} representing the
4111
:param `before`: the index at which we should insert the new item;
4346
:param `idPrevious`: the index at which we should insert the new item;
4112
4347
:param `text`: the item text label;
4113
4348
:param `ct_type`: the item type (see L{SetItemType} for a list of valid
4127
4362
# should we give a warning here?
4128
4363
return self.AddRoot(text, ct_type, wnd, image, selImage, data)
4130
return self.DoInsertItem(parentId, before, text, ct_type, wnd, image, selImage, data)
4365
return self.DoInsertItem(parentId, idPrevious, text, ct_type, wnd, image, selImage, data)
4133
4368
def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
4213
4448
:param `item`: an instance of L{GenericTreeItem}.
4216
if self._textCtrl != None and item != self._textCtrl.item() and self.IsDescendantOf(item, self._textCtrl.item()):
4217
self._textCtrl.StopEditing()
4451
if self._editCtrl != None and item != self._editCtrl.item() and self.IsDescendantOf(item, self._editCtrl.item()):
4452
self._editCtrl.StopEditing()
4219
4454
if item != self._key_current and self.IsDescendantOf(item, self._key_current):
4220
4455
self._key_current = None
4251
4486
self._dirty = True # do this first so stuff below doesn't cause flicker
4253
if self._textCtrl != None and self.IsDescendantOf(item, self._textCtrl.item()):
4488
if self._editCtrl != None and self.IsDescendantOf(item, self._editCtrl.item()):
4254
4489
# can't delete the item being edited, cancel editing it first
4255
self._textCtrl.StopEditing()
4490
self._editCtrl.StopEditing()
4257
4492
parent = item.GetParent()
4531
4769
def SelectAll(self):
4532
""" Selects all the item in the tree. """
4771
Selects all the item in the tree.
4773
:note: This method can be used only if L{CustomTreeCtrl} has the ``TR_MULTIPLE`` or ``TR_EXTENDED``
4534
4777
if not self.HasAGWFlag(TR_MULTIPLE) and not self.HasAGWFlag(TR_EXTENDED):
4535
4778
raise Exception("SelectAll can be used only with multiple selection enabled.")
4592
4835
:param `item1`: an instance of L{GenericTreeItem}, representing the first
4593
4836
item in the range to select;
4594
4837
:param `item2`: an instance of L{GenericTreeItem}, representing the last
4595
item in the range to select.
4838
item in the range to select.
4840
:note: This method can be used only if L{CustomTreeCtrl} has the ``TR_MULTIPLE`` or ``TR_EXTENDED``
4844
if not self.HasAGWFlag(TR_MULTIPLE) and not self.HasAGWFlag(TR_EXTENDED):
4845
raise Exception("SelectItemRange can be used only with multiple selection enabled.")
4598
4847
self._select_me = None
4903
5152
return self._imageListCheck
5155
def GetLeftImageList(self):
5157
Returns the image list for L{CustomTreeCtrl} filled with images to be used on
5158
the leftmost part of the client area. Any item can have a leftmost image associated
5162
return self._imageListLeft
4906
5165
def CalculateLineHeight(self):
4907
5166
""" Calculates the height of a line. """
4951
5210
if height > self._lineHeight:
4952
5211
self._lineHeight = height
5213
if self._imageListLeft:
5215
# Calculate a self._lineHeight value from the leftmost image sizes.
5216
# May be toggle off. Then CustomTreeCtrl will spread when
5217
# necessary (which might look ugly).
5218
n = self._imageListLeft.GetImageCount()
5222
width, height = self._imageListLeft.GetSize(i)
5224
if height > self._lineHeight:
5225
self._lineHeight = height
4954
5227
if self._lineHeight < 30:
4955
5228
self._lineHeight += 2 # at least 2 pixels
4984
5257
bmp = imageList.GetBitmap(ii)
4985
5258
newbmp = MakeDisabledBitmap(bmp)
4986
5259
self._grayedImageList.Add(newbmp)
5262
def SetLeftImageList(self, imageList):
5264
Sets the image list for L{CustomTreeCtrl} filled with images to be used on
5265
the leftmost part of the client area. Any item can have a leftmost image associated
5268
:param `imageList`: an instance of `wx.ImageList`.
5271
self._imageListLeft = imageList
5272
self._ownsImageListLeft = False
5275
# Don't do any drawing if we're setting the list to NULL,
5276
# since we may be in the process of deleting the tree control.
5278
self.CalculateLineHeight()
5280
# We gray out the image list to use the grayed icons with disabled items
5281
sz = imageList.GetSize(0)
5282
self._grayedImageListLeft = wx.ImageList(sz[0], sz[1], True, 0)
5284
for ii in xrange(imageList.GetImageCount()):
5285
bmp = imageList.GetBitmap(ii)
5286
newbmp = MakeDisabledBitmap(bmp)
5287
self._grayedImageListLeft.Add(newbmp)
4989
5290
def SetStateImageList(self, imageList):
5054
5355
x=sizex, y=sizey))
5357
self._imageListCheck.Add(self.GetControlBmp(checkbox=True,
5361
self._grayedCheckList.Add(self.GetControlBmp(checkbox=True,
5057
5366
# Get the Radio Buttons
5058
5367
self._imageListCheck.Add(self.GetControlBmp(checkbox=False,
5123
5432
self._ownsImageListButtons = True
5435
def AssignLeftImageList(self, imageList):
5437
Assigns the image list for L{CustomTreeCtrl} filled with images to be used on
5438
the leftmost part of the client area. Any item can have a leftmost image associated
5441
:param `imageList`: an instance of `wx.ImageList`.
5444
self.SetLeftImageList(imageList)
5445
self._ownsImageListLeft = True
5126
5448
# -----------------------------------------------------------------------------
5128
5450
# -----------------------------------------------------------------------------
5501
5831
item.GetY() + ((total_h > hcheck) and [(total_h-hcheck)/2] or [0])[0],
5502
5832
wx.IMAGELIST_DRAW_TRANSPARENT)
5834
if leftimage != _NO_IMAGE:
5835
if item.IsEnabled():
5836
imglist = self._imageListLeft
5838
imglist = self._grayedImageListLeft
5840
imglist.Draw(leftimage, dc,
5842
item.GetY() + ((total_h > l_image_h) and [(total_h-l_image_h)/2] or [0])[0],
5843
wx.IMAGELIST_DRAW_TRANSPARENT)
5504
5845
dc.SetBackgroundMode(wx.TRANSPARENT)
5505
5846
extraH = ((total_h > text_h) and [(total_h - text_h)/2] or [0])[0]
5524
5865
if item.GetHeight() > item.GetWindowSize()[1]:
5525
5866
ya += (item.GetHeight() - item.GetWindowSize()[1])/2
5527
if align and level in self.absoluteWindows:
5528
wndx = self.absoluteWindows[level] + item.GetX() + 2
5869
# Horizontal alignment of windows
5870
if level in self.absoluteWindows:
5871
wndx = self.absoluteWindows[level] + item.GetX() + 2
5874
# Rightmost alignment of windows
5875
wndx = self.GetClientSize().x - item.GetWindowSize().x - 2
5530
5877
if not wnd.IsShown():
5545
5892
:param `dc`: an instance of `wx.DC`;
5546
5893
:param `level`: the item level in the tree hierarchy;
5547
5894
:param `y`: the current vertical position in the `wx.PyScrolledWindow`;
5548
:param `align`: ``True`` if we want to align windows (in items with windows)
5549
at the same horizontal position.
5895
:param `align`: an integer specifying the alignment type:
5897
=============== =========================================
5898
`align` Value Description
5899
=============== =========================================
5900
0 No horizontal alignment of windows (in items with windows).
5901
1 Windows (in items with windows) are aligned at the same horizontal position.
5902
2 Windows (in items with windows) are aligned at the rightmost edge of L{CustomTreeCtrl}.
5903
=============== =========================================
5552
5907
x = level*self._indent
5910
if self._imageListLeft:
5911
left_image_list += self._imageListLeft.GetBitmap(0).GetWidth()
5913
x += left_image_list
5554
5915
if not self.HasAGWFlag(TR_HIDE_ROOT):
5772
6133
dc.SetFont(self._normalFont)
5773
6134
dc.SetPen(self._dottedPen)
5775
align = self.HasAGWFlag(TR_ALIGN_WINDOWS)
6138
if self.HasAGWFlag(TR_ALIGN_WINDOWS):
6140
elif self.HasAGWFlag(TR_ALIGN_WINDOWS_RIGHT):
5777
6144
self.PaintLevel(self._anchor, dc, 0, y, align)
5882
6253
if self._current is None or self._key_current is None:
6255
self._current = self._key_current = self.GetFirstVisibleItem()
5887
6257
# how should the selection work for this event?
5888
6258
is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetAGWWindowStyleFlag(),
5925
6295
event.SetEventObject(self)
5926
6296
self.GetEventHandler().ProcessEvent(event)
5928
elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE]:
6298
elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE, wx.WXK_NUMPAD_ENTER]:
5930
6300
if not self.IsItemEnabled(self._current):
5938
6308
self.GetEventHandler().ProcessEvent(event)
5940
6310
if keyCode == wx.WXK_SPACE and self.GetItemType(self._current) > 0:
5941
checked = not self.IsItemChecked(self._current)
6311
if self.IsItem3State(self._current):
6312
checked = self.GetItem3StateValue(self._current)
6313
checked = (checked+1)%3
6315
checked = not self.IsItemChecked(self._current)
5942
6317
self.CheckItem(self._current, checked)
5944
6319
# in any case, also generate the normal key event for this key,
6264
6639
wx.YieldIfNeeded()
6266
if self._textCtrl != None and item != self._textCtrl.item():
6267
self._textCtrl.StopEditing()
6641
if self._editCtrl != None and item != self._editCtrl.item():
6642
self._editCtrl.StopEditing()
6269
self._textCtrl = TreeTextCtrl(self, item=item)
6270
self._textCtrl.SetFocus()
6644
self._editCtrl = TreeTextCtrl(self, item=item)
6645
self._editCtrl.SetFocus()
6273
6648
def GetEditControl(self):
6277
6652
simultaneously).
6280
return self._textCtrl
6283
def OnRenameAccept(self, item, value):
6655
return self._editCtrl
6658
def OnAcceptEdit(self, item, value):
6285
Called by L{TreeTextCtrl}, to accept the changes and to send the
6660
Called by L{EditCtrl}, to accept the changes and to send the
6286
6661
``EVT_TREE_END_LABEL_EDIT`` event.
6288
6663
:param `item`: an instance of L{GenericTreeItem};
6298
6673
return not self.GetEventHandler().ProcessEvent(le) or le.IsAllowed()
6301
def OnRenameCancelled(self, item):
6676
def OnCancelEdit(self, item):
6303
Called by L{TreeTextCtrl}, to cancel the changes and to send the
6678
Called by L{EditCtrl}, to cancel the changes and to send the
6304
6679
``EVT_TREE_END_LABEL_EDIT`` event.
6306
6681
:param `item`: an instance of L{GenericTreeItem}.
6341
6716
underMouseChanged = underMouse != self._underMouse
6343
6718
if underMouse and (flags & TREE_HITTEST_ONITEM) and not event.LeftIsDown() and \
6344
not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
6719
not self._isDragging and (not self._editTimer or not self._editTimer.IsRunning()):
6345
6720
underMouse = underMouse
6347
6722
underMouse = None
6356
6731
# Determines what item we are hovering over and need a tooltip for
6357
6732
hoverItem = thisItem
6359
# We do not want a tooltip if we are dragging, or if the rename timer is running
6360
if underMouseChanged and not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
6734
# We do not want a tooltip if we are dragging, or if the edit timer is running
6735
if underMouseChanged and not self._isDragging and (not self._editTimer or not self._editTimer.IsRunning()):
6362
6737
if hoverItem is not None:
6363
6738
# Ask the tree control what tooltip (if any) should be shown
6522
6897
self._dragCount = 0
6524
6899
if item == None:
6525
if self._textCtrl != None and item != self._textCtrl.item():
6526
self._textCtrl.StopEditing()
6900
if self._editCtrl != None and item != self._editCtrl.item():
6901
self._editCtrl.StopEditing()
6527
6902
return # we hit the blank area
6529
6904
if event.RightDown():
6531
if self._textCtrl != None and item != self._textCtrl.item():
6532
self._textCtrl.StopEditing()
6906
if self._editCtrl != None and item != self._editCtrl.item():
6907
self._editCtrl.StopEditing()
6534
6909
self._hasFocus = True
6535
6910
self.SetFocusIgnoringChildren()
6571
6946
if item == self._current and (flags & TREE_HITTEST_ONITEMLABEL) and self.HasAGWFlag(TR_EDIT_LABELS):
6573
if self._renameTimer:
6575
if self._renameTimer.IsRunning():
6950
if self._editTimer.IsRunning():
6577
self._renameTimer.Stop()
6952
self._editTimer.Stop()
6581
self._renameTimer = TreeRenameTimer(self)
6956
self._editTimer = TreeEditTimer(self)
6583
self._renameTimer.Start(_DELAY, True)
6958
self._editTimer.Start(_DELAY, True)
6585
6960
self._lastOnSame = False
6588
6963
else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
6590
6965
if not item or not item.IsEnabled():
6591
if self._textCtrl != None and item != self._textCtrl.item():
6592
self._textCtrl.StopEditing()
6966
if self._editCtrl != None and item != self._editCtrl.item():
6967
self._editCtrl.StopEditing()
6595
if self._textCtrl != None and item != self._textCtrl.item():
6596
self._textCtrl.StopEditing()
6970
if self._editCtrl != None and item != self._editCtrl.item():
6971
self._editCtrl.StopEditing()
6598
6973
self._hasFocus = True
6599
6974
self.SetFocusIgnoringChildren()
6618
6993
if event.LeftDown():
6619
6994
if flags & TREE_HITTEST_ONITEM and self.HasAGWFlag(TR_FULL_ROW_HIGHLIGHT):
6620
6995
self.DoSelectItem(item, not self.HasAGWFlag(TR_MULTIPLE))
6621
self.CheckItem(item, not self.IsItemChecked(item))
6997
if self.IsItem3State(item):
6998
checked = self.GetItem3StateValue(item)
6999
checked = (checked+1)%3
7001
checked = not self.IsItemChecked(item)
7003
self.CheckItem(item, checked)
6710
def CalculateSize(self, item, dc, level=-1, align=False):
7092
def CalculateSize(self, item, dc, level=-1, align=0):
6712
7094
Calculates overall position and size of an item.
6714
7096
:param `item`: an instance of L{GenericTreeItem};
6715
7097
:param `dc`: an instance of `wx.DC`;
6716
7098
:param `level`: the item level in the tree hierarchy;
6717
:param `align`: ``True`` if we want to align windows (in items with windows)
6718
at the same horizontal position.
7099
:param `align`: an integer specifying the alignment type:
7101
=============== =========================================
7102
`align` Value Description
7103
=============== =========================================
7104
0 No horizontal alignment of windows (in items with windows).
7105
1 Windows (in items with windows) are aligned at the same horizontal position.
7106
2 Windows (in items with windows) are aligned at the rightmost edge of L{CustomTreeCtrl}.
7107
=============== =========================================
6721
7111
attr = item.GetAttributes()
6772
7162
totalHeight = max(total_h, item.GetWindowSize()[1])
6774
7164
if level >= 0 and wnd:
6776
7166
if level in self.absoluteWindows:
6777
7167
self.absoluteWindows[level] = max(self.absoluteWindows[level], image_w+text_w+wcheck+2)
6779
7169
self.absoluteWindows[level] = image_w+text_w+wcheck+2
6781
7171
self.absoluteWindows[level] = max(self.absoluteWindows[level], image_w+text_w+wcheck+2)
6783
7173
item.SetWidth(totalWidth)
6784
7174
item.SetHeight(totalHeight)
6787
def CalculateLevel(self, item, dc, level, y, align=False):
7177
def CalculateLevel(self, item, dc, level, y, align=0):
6789
7179
Calculates the level of an item inside the tree hierarchy.
6792
7182
:param `dc`: an instance of `wx.DC`;
6793
7183
:param `level`: the item level in the tree hierarchy;
6794
7184
:param `y`: the current vertical position inside the `wx.PyScrolledWindow`;
6795
:param `align`: ``True`` if we want to align windows (in items with windows)
6796
at the same horizontal position.
7185
:param `align`: an integer specifying the alignment type:
7187
=============== =========================================
7188
`align` Value Description
7189
=============== =========================================
7190
0 No horizontal alignment of windows (in items with windows).
7191
1 Windows (in items with windows) are aligned at the same horizontal position.
7192
2 Windows (in items with windows) are aligned at the rightmost edge of L{CustomTreeCtrl}.
7193
=============== =========================================
6799
7197
x = level*self._indent
6851
7249
y = self.CalculateLevel(self._anchor, dc, 0, y) # start recursion
6853
if self.HasAGWFlag(TR_ALIGN_WINDOWS):
7251
if self.HasAGWFlag(TR_ALIGN_WINDOWS) or self.HasAGWFlag(TR_ALIGN_WINDOWS_RIGHT):
7252
align = (self.HasAGWFlag(TR_ALIGN_WINDOWS) and [1] or [2])[0]
6855
y = self.CalculateLevel(self._anchor, dc, 0, y, align=True) # start recursion
7254
y = self.CalculateLevel(self._anchor, dc, 0, y, align) # start recursion
6858
7257
def RefreshSubtree(self, item):
6928
7327
self.RefreshSelectedUnder(child)
7330
def RefreshItemWithWindows(self, item=None):
7332
Refreshes the items with which a window is associated.
7334
:param `item`: an instance of L{GenericTreeItem}. If `item` is ``None``, then the
7335
recursive refresh starts from the root node.
7337
:note: This method is called only if the style ``TR_ALIGN_WINDOWS_RIGHT`` is used.
7340
if self._freezeCount:
7345
self.RefreshItemWithWindows(self._anchor)
7348
wnd = item.GetWindow()
7349
if wnd and wnd.IsShown():
7350
self.RefreshLine(item)
7352
children = item.GetChildren()
7353
for child in children:
7354
self.RefreshItemWithWindows(child)
6931
7357
def Freeze(self):
6933
7359
Freeze L{CustomTreeCtrl}.