2 ComboTreeBox provides a ComboBox that pops up a tree instead of a list.
4 ComboTreeBox tries to provide the same interface as ComboBox as much as
5 possible. However, whereas the ComboBox widget uses indices to access
6 items in the list of choices, ComboTreeBox uses TreeItemId's instead. If
7 you add an item to the ComboTreeBox (using Append or Insert), the
8 TreeItemId associated with the added item is returned. You can then use
9 that TreeItemId to add items as children of that first item. For
12 >>> from wx.lib.combotreebox import ComboTreeBox
13 >>> combo = ComboTreeBox(parent)
14 >>> item1 = combo.Append('Item 1') # Add a root item
15 >>> item1a = combo.Append('Item 1a', parent=item1) # Add a child to item1
17 You can also add client data to each of the items like this:
18 >>> item1 = combo.Append('Item 1', clientData=somePythonObject)
19 >>> item1a = combo.Append('Item 1a', parent=item1,
20 ... clientData=someOtherPythonObject)
22 And later fetch the client data like this:
23 >>> somePythonObject = combo.GetClientData(item1)
25 To get the client data of the currently selected item (if any):
26 >>> currentItem = combo.GetSelection()
28 >>> somePythonObject = combo.GetClientData(currentItem)
30 Supported styles are the same as for ComboBox, i.e. wx.CB_READONLY and
31 wx.CB_SORT. Provide them as usual:
32 >>> combo = ComboTreeBox(parent, style=wx.CB_READONLY|wx.CB_SORT)
34 Supported platforms: wxMSW and wxMAC natively, wxGTK by means of a
37 Author: Frank Niessink <frank@niessink.com>
38 Copyright 2006, Frank Niessink
39 License: wxWidgets license
46 __all__
= ['ComboTreeBox'] # Export only the ComboTreeBox widget
49 # ---------------------------------------------------------------------------
52 class IterableTreeCtrl(wx
.TreeCtrl
):
54 TreeCtrl is the same as wx.TreeCtrl, with a few convenience methods
55 added for easier navigation of items. """
57 def GetPreviousItem(self
, item
):
59 GetPreviousItem(self, TreeItemId item) -> TreeItemId
61 Returns the item that is on the line immediately above item
62 (as is displayed when the tree is fully expanded). The returned
63 item is invalid if item is the first item in the tree.
65 previousSibling
= self
.GetPrevSibling(item
)
67 return self
.GetLastChildRecursively(previousSibling
)
69 parent
= self
.GetItemParent(item
)
70 if parent
== self
.GetRootItem() and \
71 (self
.GetWindowStyle() & wx
.TR_HIDE_ROOT
):
72 # Return an invalid item, because the root item is hidden
73 return previousSibling
77 def GetNextItem(self
, item
):
79 GetNextItem(self, TreeItemId item) -> TreeItemId
81 Returns the item that is on the line immediately below item
82 (as is displayed when the tree is fully expanded). The returned
83 item is invalid if item is the last item in the tree.
85 if self
.ItemHasChildren(item
):
86 firstChild
, cookie
= self
.GetFirstChild(item
)
89 return self
.GetNextSiblingRecursively(item
)
91 def GetFirstItem(self
):
93 GetFirstItem(self) -> TreeItemId
95 Returns the very first item in the tree. This is the root item
96 unless the root item is hidden. In that case the first child of
97 the root item is returned, if any. If the tree is empty, an
98 invalid tree item is returned.
100 rootItem
= self
.GetRootItem()
101 if rootItem
and (self
.GetWindowStyle() & wx
.TR_HIDE_ROOT
):
102 firstChild
, cookie
= self
.GetFirstChild(rootItem
)
107 def GetLastChildRecursively(self
, item
):
109 GetLastChildRecursively(self, TreeItemId item) -> TreeItemId
111 Returns the last child of the last child ... of item. If item
112 has no children, item itself is returned. So the returned item
113 is always valid, assuming a valid item has been passed.
116 while self
.ItemHasChildren(lastChild
):
117 lastChild
= self
.GetLastChild(lastChild
)
120 def GetNextSiblingRecursively(self
, item
):
122 GetNextSiblingRecursively(self, TreeItemId item) -> TreeItemId
124 Returns the next sibling of item if it has one. If item has no
125 next sibling the next sibling of the parent of item is returned.
126 If the parent has no next sibling the next sibling of the parent
127 of the parent is returned, etc. If none of the ancestors of item
128 has a next sibling, an invalid item is returned.
130 if item
== self
.GetRootItem():
131 return wx
.TreeItemId() # Return an invalid TreeItemId
132 nextSibling
= self
.GetNextSibling(item
)
136 parent
= self
.GetItemParent(item
)
137 return self
.GetNextSiblingRecursively(parent
)
139 def GetSelection(self
):
140 """ Extend GetSelection to never return the root item if the
141 root item is hidden. """
142 selection
= super(IterableTreeCtrl
, self
).GetSelection()
143 if selection
== self
.GetRootItem() and \
144 (self
.GetWindowStyle() & wx
.TR_HIDE_ROOT
):
145 return wx
.TreeItemId() # Return an invalid TreeItemId
150 # ---------------------------------------------------------------------------
153 class BasePopupFrame(wx
.MiniFrame
):
155 BasePopupFrame is the base class for platform specific
156 versions of the PopupFrame. The PopupFrame is the frame that
157 is popped up by ComboTreeBox. It contains the tree of items
158 that the user can select one item from. Upon selection, or
159 when focus is lost, the frame is hidden. """
161 def __init__(self
, parent
):
162 super(BasePopupFrame
, self
).__init
__(parent
,
163 style
=wx
.DEFAULT_FRAME_STYLE
& wx
.FRAME_FLOAT_ON_PARENT
&
164 ~
(wx
.RESIZE_BORDER | wx
.CAPTION
))
165 self
._createInterior
()
166 self
._layoutInterior
()
167 self
._bindEventHandlers
()
169 def _createInterior(self
):
170 self
._tree
= IterableTreeCtrl(self
,
171 style
=wx
.TR_HIDE_ROOT|wx
.TR_LINES_AT_ROOT|wx
.TR_HAS_BUTTONS
)
172 self
._tree
.AddRoot('Hidden root node')
174 def _layoutInterior(self
):
175 frameSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
176 frameSizer
.Add(self
._tree
, flag
=wx
.EXPAND
, proportion
=1)
177 self
.SetSizerAndFit(frameSizer
)
179 def _bindEventHandlers(self
):
180 self
._tree
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
181 self
._tree
.Bind(wx
.EVT_TREE_ITEM_ACTIVATED
, self
.OnItemActivated
)
182 self
._tree
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseClick
)
184 def _bindKillFocus(self
):
185 self
._tree
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
187 def _unbindKillFocus(self
):
188 self
._tree
.Unbind(wx
.EVT_KILL_FOCUS
)
190 def OnKillFocus(self
, event
):
191 # We hide the frame rather than destroy it, so it can be
192 # popped up again later:
194 self
.GetParent().NotifyNoItemSelected()
197 def OnChar(self
, keyEvent
):
198 if self
._keyShouldHidePopup
(keyEvent
):
200 self
.GetParent().NotifyNoItemSelected()
203 def _keyShouldHidePopup(self
, keyEvent
):
204 return keyEvent
.GetKeyCode() == wx
.WXK_ESCAPE
206 def OnMouseClick(self
, event
):
207 item
, flags
= self
._tree
.HitTest(event
.GetPosition())
208 if item
and (flags
& wx
.TREE_HITTEST_ONITEMLABEL
):
209 self
._tree
.SelectItem(item
)
211 self
.GetParent().NotifyItemSelected(self
._tree
.GetItemText(item
))
215 def OnItemActivated(self
, event
):
216 item
= event
.GetItem()
218 self
.GetParent().NotifyItemSelected(self
._tree
.GetItemText(item
))
221 self
._bindKillFocus
()
222 wx
.CallAfter(self
._tree
.SetFocus
)
223 super(BasePopupFrame
, self
).Show()
226 self
._unbindKillFocus
()
227 super(BasePopupFrame
, self
).Hide()
233 class MSWPopupFrame(BasePopupFrame
):
235 # Comply with the MS Windows Combobox behaviour: if the text in
236 # the text field is not in the tree, the first item in the tree
238 if not self
._tree
.GetSelection():
239 self
._tree
.SelectItem(self
._tree
.GetFirstItem())
240 super(MSWPopupFrame
, self
).Show()
243 class MACPopupFrame(BasePopupFrame
):
244 def _bindKillFocus(self
):
245 # On wxMac, the kill focus event doesn't work, but the
246 # deactivate event does:
247 self
.Bind(wx
.EVT_ACTIVATE
, self
.OnKillFocus
)
249 def _unbindKillFocus(self
):
250 self
.Unbind(wx
.EVT_ACTIVATE
)
252 def OnKillFocus(self
, event
):
253 if not event
.GetActive(): # We received a deactivate event
255 wx
.CallAfter(self
.GetParent().NotifyNoItemSelected
)
259 class GTKPopupFrame(BasePopupFrame
):
260 def _keyShouldHidePopup(self
, keyEvent
):
261 # On wxGTK, Alt-Up also closes the popup:
262 return super(GTKPopupFrame
, self
)._keyShouldHidePopup
(keyEvent
) or \
263 (keyEvent
.AltDown() and keyEvent
.GetKeyCode() == wx
.WXK_UP
)
266 # ---------------------------------------------------------------------------
269 class BaseComboTreeBox(object):
270 """ BaseComboTreeBox is the base class for platform specific
271 versions of the ComboTreeBox. """
273 def __init__(self
, *args
, **kwargs
):
274 style
= kwargs
.pop('style', 0)
275 if style
& wx
.CB_READONLY
:
276 style
&= ~wx
.CB_READONLY
# We manage readonlyness ourselves
277 self
._readOnly
= True
279 self
._readOnly
= False
280 if style
& wx
.CB_SORT
:
281 style
&= ~wx
.CB_SORT
# We manage sorting ourselves
285 super(BaseComboTreeBox
, self
).__init
__(style
=style
, *args
, **kwargs
)
286 self
._createInterior
()
287 self
._layoutInterior
()
288 self
._bindEventHandlers
()
290 # Methods to construct the widget.
292 def _createInterior(self
):
293 self
._popupFrame
= self
._createPopupFrame
()
294 self
._text
= self
._createTextCtrl
()
295 self
._button
= self
._createButton
()
296 self
._tree
= self
._popupFrame
.GetTree()
298 def _createTextCtrl(self
):
299 return self
# By default, the text control is the control itself.
301 def _createButton(self
):
302 return self
# By default, the dropdown button is the control itself.
304 def _createPopupFrame(self
):
305 # It is a subclass responsibility to provide the right PopupFrame,
306 # depending on platform:
307 raise NotImplementedError
309 def _layoutInterior(self
):
310 pass # By default, there is no layout to be done.
312 def _bindEventHandlers(self
):
313 for eventSource
, eventType
, eventHandler
in self
._eventsToBind
():
314 eventSource
.Bind(eventType
, eventHandler
)
316 def _eventsToBind(self
):
318 _eventsToBind(self) -> [(eventSource, eventType, eventHandler), ...]
320 _eventsToBind returns a list of eventSource, eventType,
321 eventHandlers tuples that will be bound. This method can be
322 extended to bind additional events. In that case, don't
323 forget to call _eventsToBind on the super class. """
324 return [(self
._text
, wx
.EVT_KEY_DOWN
, self
.OnKeyDown
),
325 (self
._text
, wx
.EVT_TEXT
, self
.OnText
),
326 (self
._button
, wx
.EVT_BUTTON
, self
.OnMouseClick
)]
330 def OnMouseClick(self
, event
):
332 # Note that we don't call event.Skip() to prevent popping up the
333 # ComboBox's own box.
335 def OnKeyDown(self
, keyEvent
):
336 if self
._keyShouldNavigate
(keyEvent
):
337 self
._navigateUpOrDown
(keyEvent
)
338 elif self
._keyShouldPopUpTree
(keyEvent
):
343 def _keyShouldPopUpTree(self
, keyEvent
):
344 return (keyEvent
.AltDown() or keyEvent
.MetaDown()) and \
345 keyEvent
.GetKeyCode() == wx
.WXK_DOWN
347 def _keyShouldNavigate(self
, keyEvent
):
348 return keyEvent
.GetKeyCode() in (wx
.WXK_DOWN
, wx
.WXK_UP
) and not \
349 self
._keyShouldPopUpTree
(keyEvent
)
351 def _navigateUpOrDown(self
, keyEvent
):
352 item
= self
.GetSelection()
354 navigationMethods
= {wx
.WXK_DOWN
: self
._tree
.GetNextItem
,
355 wx
.WXK_UP
: self
._tree
.GetPreviousItem
}
356 getNextItem
= navigationMethods
[keyEvent
.GetKeyCode()]
357 nextItem
= getNextItem(item
)
359 nextItem
= self
._tree
.GetFirstItem()
361 self
.SetSelection(nextItem
)
363 def OnText(self
, event
):
365 item
= self
.FindString(self
._text
.GetValue())
367 if self
._tree
.GetSelection() != item
:
368 self
._tree
.SelectItem(item
)
370 self
._tree
.Unselect()
372 # Methods called by the PopupFrame, to let the ComboTreeBox know
373 # about what the user did.
375 def NotifyItemSelected(self
, text
):
376 """ Simulate selection of an item by the user. This is meant to
377 be called by the PopupFrame when the user selects an item. """
378 self
._text
.SetValue(text
)
379 self
._postComboBoxSelectedEvent
(text
)
382 def _postComboBoxSelectedEvent(self
, text
):
383 """ Simulate a selection event. """
384 event
= wx
.CommandEvent(wx
.wxEVT_COMMAND_COMBOBOX_SELECTED
,
386 event
.SetString(text
)
387 self
.GetEventHandler().ProcessEvent(event
)
389 def NotifyNoItemSelected(self
):
390 """ This is called by the PopupFrame when the user closes the
391 PopupFrame, without selecting an item. """
394 # Misc methods, not part of the ComboBox API.
400 Pops up the frame with the tree.
402 comboBoxSize
= self
.GetSize()
403 x
, y
= self
.GetParent().ClientToScreen(self
.GetPosition())
405 width
= comboBoxSize
[0]
407 self
._popupFrame
.SetDimensions(x
, y
, width
, height
)
408 # On wxGTK, when the Combobox width has been increased a call
409 # to SetMinSize is needed to force a resize of the popupFrame:
410 self
._popupFrame
.SetMinSize((width
, height
))
411 self
._popupFrame
.Show()
415 GetTree(self) -> wx.TreeCtrl
417 Returns the tree control that is popped up.
419 return self
._popupFrame
.GetTree()
421 def FindClientData(self
, clientData
, parent
=None):
423 FindClientData(self, PyObject clientData, TreeItemId parent=None)
426 Finds the *first* item in the tree with client data equal to the
427 given clientData. If no such item exists, an invalid item is
430 parent
= parent
or self
._tree
.GetRootItem()
431 child
, cookie
= self
._tree
.GetFirstChild(parent
)
433 if self
.GetClientData(child
) == clientData
:
436 result
= self
.FindClientData(clientData
, child
)
439 child
, cookie
= self
._tree
.GetNextChild(parent
, cookie
)
442 def SetClientDataSelection(self
, clientData
):
444 SetClientDataSelection(self, PyObject clientData) -> bool
446 Selects the item with the provided clientData in the control.
447 Returns True if the item belonging to the clientData has been
448 selected, False if it wasn't found in the control.
450 item
= self
.FindClientData(clientData
)
452 self
._tree
.SelectItem(item
)
457 # The following methods are all part of the ComboBox API (actually
458 # the ControlWithItems API) and have been adapted to take TreeItemIds
459 # as parameter and return TreeItemIds, rather than indices.
461 def Append(self
, itemText
, parent
=None, clientData
=None):
463 Append(self, String itemText, TreeItemId parent=None, PyObject
464 clientData=None) -> TreeItemId
466 Adds the itemText to the control, associating the given clientData
467 with the item if not None. If parent is None, itemText is added
468 as a root item, else itemText is added as a child item of
469 parent. The return value is the TreeItemId of the newly added
472 parent
= self
._tree
.GetRootItem()
473 item
= self
._tree
.AppendItem(parent
, itemText
,
474 data
=wx
.TreeItemData(clientData
))
476 self
._tree
.SortChildren(parent
)
483 Removes all items from the control.
485 return self
._tree
.DeleteAllItems()
487 def Delete(self
, item
):
489 Delete(self, TreeItemId item)
491 Deletes the item from the control.
493 return self
._tree
.Delete(item
)
495 def FindString(self
, string
, parent
=None):
497 FindString(self, String string, TreeItemId parent=None) -> TreeItemId
499 Finds the *first* item in the tree with a label equal to the
500 given string. If no such item exists, an invalid item is
503 parent
= parent
or self
._tree
.GetRootItem()
504 child
, cookie
= self
._tree
.GetFirstChild(parent
)
506 if self
._tree
.GetItemText(child
) == string
:
509 result
= self
.FindString(string
, child
)
512 child
, cookie
= self
._tree
.GetNextChild(parent
, cookie
)
515 def GetSelection(self
):
517 GetSelection(self) -> TreeItemId
519 Returns the TreeItemId of the selected item or an invalid item
520 if no item is selected.
522 selectedItem
= self
._tree
.GetSelection()
523 if selectedItem
and selectedItem
!= self
._tree
.GetRootItem():
526 return self
.FindString(self
.GetValue())
528 def GetString(self
, item
):
530 GetString(self, TreeItemId item) -> String
532 Returns the label of the given item.
535 return self
._tree
.GetItemText(item
)
539 def GetStringSelection(self
):
541 GetStringSelection(self) -> String
543 Returns the label of the selected item or an empty string if no item
546 return self
.GetValue()
548 def Insert(self
, itemText
, previous
=None, parent
=None, clientData
=None):
550 Insert(self, String itemText, TreeItemId previous=None, TreeItemId
551 parent=None, PyObject clientData=None) -> TreeItemId
553 Insert an item into the control before the ``previous`` item
554 and/or as child of the ``parent`` item. The itemText is associated
555 with clientData when not None.
557 data
= wx
.TreeItemData(clientData
)
559 parent
= self
._tree
.GetRootItem()
561 item
= self
._tree
.InsertItemBefore(parent
, 0, itemText
, data
=data
)
563 item
= self
._tree
.InsertItem(parent
, previous
, itemText
, data
=data
)
565 self
._tree
.SortChildren(parent
)
570 IsEmpty(self) -> bool
572 Returns True if the control is empty or False if it has some items.
574 return self
.GetCount() == 0
578 GetCount(self) -> int
580 Returns the number of items in the control.
582 # Note: We don't need to substract 1 for the hidden root item,
583 # because the TreeCtrl does that for us
584 return self
._tree
.GetCount()
586 def SetSelection(self
, item
):
588 SetSelection(self, TreeItemId item)
590 Sets the provided item to be the selected item.
592 self
._tree
.SelectItem(item
)
593 self
._text
.SetValue(self
._tree
.GetItemText(item
))
595 Select
= SetSelection
597 def SetString(self
, item
, string
):
599 SetString(self, TreeItemId item, String string)
601 Sets the label for the provided item.
603 self
._tree
.SetItemText(item
, string
)
605 self
._tree
.SortChildren(self
._tree
.GetItemParent(item
))
607 def SetStringSelection(self
, string
):
609 SetStringSelection(self, String string) -> bool
611 Selects the item with the provided string in the control.
612 Returns True if the provided string has been selected, False if
613 it wasn't found in the control.
615 item
= self
.FindString(string
)
617 if self
._text
.GetValue() != string
:
618 self
._text
.SetValue(string
)
619 self
._tree
.SelectItem(item
)
624 def GetClientData(self
, item
):
626 GetClientData(self, TreeItemId item) -> PyObject
628 Returns the client data associated with the given item, if any.
630 return self
._tree
.GetItemPyData(item
)
632 def SetClientData(self
, item
, clientData
):
634 SetClientData(self, TreeItemId item, PyObject clientData)
636 Associate the given client data with the provided item.
638 self
._tree
.SetItemPyData(item
, clientData
)
642 GetValue(self) -> String
644 Returns the current value in the combobox text field.
646 if self
._text
== self
:
647 return super(BaseComboTreeBox
, self
).GetValue()
649 return self
._text
.GetValue()
651 def SetValue(self
, value
):
653 SetValue(self, String value)
655 Sets the text for the combobox text field.
657 NB: For a combobox with wxCB_READONLY style the string must be
658 in the combobox choices list, otherwise the call to SetValue()
661 item
= self
._tree
.GetSelection()
662 if not item
or self
._tree
.GetItemText(item
) != value
:
663 item
= self
.FindString(value
)
664 if self
._readOnly
and not item
:
666 if self
._text
== self
:
667 super(BaseComboTreeBox
, self
).SetValue(value
)
669 self
._text
.SetValue(value
)
671 if self
._tree
.GetSelection() != item
:
672 self
._tree
.SelectItem(item
)
674 self
._tree
.Unselect()
677 class NativeComboTreeBox(BaseComboTreeBox
, wx
.ComboBox
):
678 """ NativeComboTreeBox, and any subclass, uses the native ComboBox as
679 basis, but prevent it from popping up its drop down list and
680 instead pops up a PopupFrame containing a tree of items. """
682 def _eventsToBind(self
):
683 events
= super(NativeComboTreeBox
, self
)._eventsToBind
()
684 # Bind all mouse click events to self.OnMouseClick so we can
685 # intercept those events and prevent the native Combobox from
686 # popping up its list of choices.
687 for eventType
in (wx
.EVT_LEFT_DOWN
, wx
.EVT_LEFT_DCLICK
,
688 wx
.EVT_MIDDLE_DOWN
, wx
.EVT_MIDDLE_DCLICK
,
689 wx
.EVT_RIGHT_DOWN
, wx
.EVT_RIGHT_DCLICK
):
690 events
.append((self
._button
, eventType
, self
.OnMouseClick
))
692 events
.append((self
, wx
.EVT_CHAR
, self
.OnChar
))
695 def OnChar(self
, event
):
696 # OnChar is only called when in read only mode. We don't call
697 # event.Skip() on purpose, to prevent the characters from being
698 # displayed in the text field.
702 class MSWComboTreeBox(NativeComboTreeBox
):
703 """ MSWComboTreeBox adds one piece of functionality as compared to
704 NativeComboTreeBox: when the user browses through the tree, the
705 ComboTreeBox's text field is continuously updated to show the
706 currently selected item in the tree. If the user cancels
707 selecting a new item from the tree, e.g. by hitting escape, the
708 previous value (the one that was selected before the PopupFrame
709 was popped up) is restored. """
711 def _createPopupFrame(self
):
712 return MSWPopupFrame(self
)
714 def _eventsToBind(self
):
715 events
= super(MSWComboTreeBox
, self
)._eventsToBind
()
716 events
.append((self
._tree
, wx
.EVT_TREE_SEL_CHANGED
,
717 self
.OnSelectionChangedInTree
))
720 def OnSelectionChangedInTree(self
, event
):
721 item
= event
.GetItem()
723 selectedValue
= self
._tree
.GetItemText(item
)
724 if self
.GetValue() != selectedValue
:
725 self
.SetValue(selectedValue
)
728 def _keyShouldPopUpTree(self
, keyEvent
):
729 return super(MSWComboTreeBox
, self
)._keyShouldPopUpTree
(keyEvent
) or \
730 (keyEvent
.GetKeyCode() == wx
.WXK_F4
) or \
731 ((keyEvent
.AltDown() or keyEvent
.MetaDown()) and \
732 keyEvent
.GetKeyCode() == wx
.WXK_UP
)
734 def SetValue(self
, value
):
735 """ Extend SetValue to also select the text in the
736 ComboTreeBox's text field. """
737 super(MSWComboTreeBox
, self
).SetValue(value
)
738 # We select the text in the ComboTreeBox's text field.
739 # There is a slight complication, however. When the control is
740 # deleted, SetValue is called. But if we call SetMark at that
741 # time, wxPython will crash. We can prevent this by comparing the
742 # result of GetLastPosition and the length of the value. If they
743 # match, all is fine. If they don't match, we don't call SetMark.
744 if self
._text
.GetLastPosition() == len(value
):
745 self
._text
.SetMark(0, self
._text
.GetLastPosition())
747 def Popup(self
, *args
, **kwargs
):
748 """ Extend Popup to store a copy of the current value, so we can
749 restore it later (in NotifyNoItemSelected). This is necessary
750 because MSWComboTreeBox will change the value as the user
751 browses through the items in the popped up tree. """
752 self
._previousValue
= self
.GetValue()
753 super(MSWComboTreeBox
, self
).Popup(*args
, **kwargs
)
755 def NotifyNoItemSelected(self
, *args
, **kwargs
):
756 """ Restore the value copied previously, because the user has
757 not selected a new value. """
758 self
.SetValue(self
._previousValue
)
759 super(MSWComboTreeBox
, self
).NotifyNoItemSelected(*args
, **kwargs
)
762 class MACComboTreeBox(NativeComboTreeBox
):
763 def _createPopupFrame(self
):
764 return MACPopupFrame(self
)
766 def _createButton(self
):
767 return self
.GetChildren()[0] # The choice button
769 def _keyShouldNavigate(self
, keyEvent
):
770 return False # No navigation with up and down on wxMac
772 def _keyShouldPopUpTree(self
, keyEvent
):
773 return super(MACComboTreeBox
, self
)._keyShouldPopUpTree
(keyEvent
) or \
774 keyEvent
.GetKeyCode() == wx
.WXK_DOWN
777 class GTKComboTreeBox(BaseComboTreeBox
, wx
.Panel
):
778 """ The ComboTreeBox widget for wxGTK. This is actually a work
779 around because on wxGTK, there doesn't seem to be a way to intercept
780 mouse events sent to the Combobox. Intercepting those events is
781 necessary to prevent the Combobox from popping up the list and pop up
782 the tree instead. So, until wxPython makes intercepting those events
783 possible we build a poor man's Combobox ourselves using a TextCtrl and
786 def _createPopupFrame(self
):
787 return GTKPopupFrame(self
)
789 def _createTextCtrl(self
):
791 style
= wx
.TE_READONLY
794 return wx
.TextCtrl(self
, style
=style
)
796 def _createButton(self
):
797 bitmap
= wx
.ArtProvider
.GetBitmap(wx
.ART_GO_DOWN
, client
=wx
.ART_BUTTON
)
798 return wx
.BitmapButton(self
, bitmap
=bitmap
)
800 def _layoutInterior(self
):
801 panelSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
802 panelSizer
.Add(self
._text
, flag
=wx
.EXPAND
, proportion
=1)
803 panelSizer
.Add(self
._button
)
804 self
.SetSizerAndFit(panelSizer
)
807 # ---------------------------------------------------------------------------
810 def ComboTreeBox(*args
, **kwargs
):
811 """ Factory function to create the right ComboTreeBox depending on
812 platform. You may force a specific class, e.g. for testing
813 purposes, by setting the keyword argument 'platform', e.g.
814 'platform=GTK' or 'platform=MSW' or platform='MAC'. """
816 platform
= kwargs
.pop('platform', None) or wx
.PlatformInfo
[0][4:7]
817 ComboTreeBoxClassName
= '%sComboTreeBox' % platform
818 ComboTreeBoxClass
= globals()[ComboTreeBoxClassName
]
819 return ComboTreeBoxClass(*args
, **kwargs
)