# 第十五章 樹形控件
本章內容
* 創建樹形控件并添加項目
* 使用樣式來設計樹形控件
* 在程序中訪問樹形控件
* 處理樹形控件中的選擇
* 控制項目的可見性
樹形控件是用于顯示復雜數據的控件。這里,樹形控件被設計用來通過分級層來顯示數據,你可以看到每 塊數據都有父子方面的東西。一個標準的例子就是文件樹,其中的目錄中有子目錄或文件,從而形成了文 件的一個嵌套的層次。另一個例子是`HTML`或`XML`文檔的文檔對象模型(`DOM)`樹。和列表與網格控件一樣,樹 形控件也提供了在項目顯示方面的靈活性,并允許你就地編輯樹形控件中的項目。在這一章中,我們將給 你展示如何編輯樹形控件中的項目及如何響應用戶事件。
## 創建樹形控件并添加項目
樹形控件是類`wx.TreeCtrl`的實例。圖15.1顯示了一個樹形控件的樣例。
**圖15.1**

例15.1是產生圖15.1的代碼。這個例子中的機制我們將在后面的部分討論。注意其中的樹形控件中的數據 是來自于一個名為`data.py`外部文件的。
**例15.1** **樹形控件示例**
```
import wx
import data
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="simple tree", size=(400,500))
# Create the tree
self.tree = wx.TreeCtrl(self)
# Add a root node
root = self.tree.AddRoot("wx.Object")
# Add nodes from our data set
self.AddTreeNodes(root, data.tree)
# Bind some interesting events
self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
# Expand the first level
self.tree.Expand(root)
def AddTreeNodes(self, parentItem, items):
"""
Recursively traverses the data structure, adding tree nodes to
match it.
"""
for item in items:
if type(item) == str:
self.tree.AppendItem(parentItem, item)
else:
newItem = self.tree.AppendItem(parentItem, item[0])
self.AddTreeNodes(newItem, item[1])
def GetItemText(self, item):
if item:
return self.tree.GetItemText(item)
else:
return ""
def OnItemExpanded(self, evt):
print "OnItemExpanded: ", self.GetItemText(evt.GetItem())
def OnItemCollapsed(self, evt):
print "OnItemCollapsed:", self.GetItemText(evt.GetItem())
def OnSelChanged(self, evt):
print "OnSelChanged: ", self.GetItemText(evt.GetItem())
def OnActivated(self, evt):
print "OnActivated: ", self.GetItemText(evt.GetItem())
app = wx.PySimpleApp(redirect=True)
frame = TestFrame()
frame.Show()
app.MainLoop()
```
下面的`wx.TreeCtrl`的構造函數是一個典型的`wxPython`窗口部件構造函數:
```
wx.TreeControl(parent, id=-1, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.TR_HAS_BUTTONS,
validator=wx.DefaultValidator, name="treeCtrl")
```
其中的參數意義與通常的`wx.Window`對象相同。該構造函數提供給你了一個沒有元素的空的樹。
**另附`data.py`文件**:
```
# Some sample data for the treectrl samples
tree = [
"wx.AcceleratorTable",
"wx.BrushList",
"wx.BusyInfo",
"wx.Clipboard",
"wx.Colour",
"wx.ColourData",
"wx.ColourDatabase",
"wx.ContextHelp",
["wx.DC", [
"wx.ClientDC",
["wx.MemoryDC", [
"wx.lib.colourchooser.canvas.BitmapBuffer",
["wx.BufferedDC", [
"wx.BufferedPaintDC", ]]]],
"wx.MetaFileDC",
"wx.MirrorDC",
"wx.PaintDC",
"wx.PostScriptDC",
"wx.PrinterDC",
"wx.ScreenDC",
"wx.WindowDC",]],
"wx.DragImage",
"wx.Effects",
"wx.EncodingConverter",
["wx.Event", [
"wx.ActivateEvent",
"wx.CalculateLayoutEvent",
"wx.CloseEvent",
["wx.CommandEvent", [
"wx.calendar.CalendarEvent",
"wx.ChildFocusEvent",
"wx.ContextMenuEvent",
"wx.gizmos.DynamicSashSplitEvent",
"wx.gizmos.DynamicSashUnifyEvent",
"wx.FindDialogEvent",
"wx.grid.GridEditorCreatedEvent",
"wx.HelpEvent",
["wx.NotifyEvent",[
["wx.BookCtrlEvent", [
"wx.ListbookEvent",
"wx.NotebookEvent ",]],
"wx.grid.GridEvent",
"wx.grid.GridRangeSelectEvent",
"wx.grid.GridSizeEvent",
"wx.ListEvent",
"wx.SpinEvent",
"wx.SplitterEvent",
"wx.TreeEvent",
"wx.wizard.WizardEvent ",]],
["wx.PyCommandEvent", [
"wx.lib.colourselect.ColourSelectEvent",
"wx.lib.buttons.GenButtonEvent",
"wx.lib.gridmovers.GridColMoveEvent",
"wx.lib.gridmovers.GridRowMoveEvent",
"wx.lib.intctrl.IntUpdatedEvent",
"wx.lib.masked.combobox.MaskedComboBoxSelectEvent",
"wx.lib.masked.numctrl.NumberUpdatedEvent",
"wx.lib.masked.timectrl.TimeUpdatedEvent ",]],
"wx.SashEvent",
"wx.ScrollEvent",
"wx.stc.StyledTextEvent",
"wx.TextUrlEvent",
"wx.UpdateUIEvent",
"wx.WindowCreateEvent",
"wx.WindowDestroyEvent ",]],
"wx.DisplayChangedEvent",
"wx.DropFilesEvent",
"wx.EraseEvent",
"wx.FocusEvent",
"wx.IconizeEvent",
"wx.IdleEvent",
"wx.InitDialogEvent",
"wx.JoystickEvent",
"wx.KeyEvent",
"wx.MaximizeEvent",
"wx.MenuEvent",
"wx.MouseCaptureChangedEvent",
"wx.MouseEvent",
"wx.MoveEvent",
"wx.NavigationKeyEvent",
"wx.NcPaintEvent",
"wx.PaintEvent",
"wx.PaletteChangedEvent",
"wx.ProcessEvent",
["wx.PyEvent", [
"wx.lib.throbber.UpdateThrobberEvent ",]],
"wx.QueryLayoutInfoEvent",
"wx.QueryNewPaletteEvent",
"wx.ScrollWinEvent",
"wx.SetCursorEvent",
"wx.ShowEvent",
"wx.SizeEvent",
"wx.SysColourChangedEvent",
"wx.TaskBarIconEvent",
"wx.TimerEvent ",]],
["wx.EvtHandler", [
"wx.lib.gridmovers.GridColMover",
"wx.lib.gridmovers.GridRowMover",
"wx.html.HtmlHelpController",
"wx.Menu",
"wx.Process",
["wx.PyApp", [
["wx.App", [
"wx.py.PyAlaCarte.App",
"wx.py.PyAlaMode.App",
"wx.py.PyAlaModeTest.App",
"wx.py.PyCrust.App",
"wx.py.PyShell.App",
["wx.py.filling.App", [
"wx.py.PyFilling.App ",]],
["wx.PySimpleApp", [
"wx.lib.masked.maskededit.test",]],
"wx.PyWidgetTester ",]]]],
"wx.TaskBarIcon",
["wx.Timer", [
"wx.PyTimer ",]],
["wx.Validator", [
["wx.PyValidator",[
"wx.lib.intctrl.IntValidator",]]]],
["wx.Window", [
["wx.lib.colourchooser.canvas.Canvas", [
"wx.lib.colourchooser.pycolourslider.PyColourSlider",
"wx.lib.colourchooser.pypalette.PyPalette",]],
"wx.lib.gridmovers.ColDragWindow",
["wx.Control",[
["wx.BookCtrl", [
"wx.Listbook",
["wx.Notebook",[
"wx.py.editor.EditorNotebook",
"wx.py.editor.EditorShellNotebook",]] ]],
["wx.Button", [
["wx.BitmapButton",[
"wx.lib.colourselect.ColourSelect",
"wx.ContextHelpButton",
"wx.lib.foldmenu.FoldOutMenu ",]] ]],
"wx.calendar.CalendarCtrl",
"wx.CheckBox",
["wx.ComboBox",[
["wx.lib.masked.combobox.BaseMaskedComboBox", [
"wx.lib.masked.combobox.ComboBox",
"wx.lib.masked.combobox.PreMaskedComboBox",]] ]],
["wx.ControlWithItems", [
["wx.Choice",[
"wx.DirFilterListCtrl ",]],
"wx.ListBox",
"wx.CheckListBox ",]],
"wx.Gauge",
"wx.GenericDirCtrl",
"wx.gizmos.LEDNumberCtrl",
["wx.ListCtrl",[
"wx.ListView ",]],
["wx.PyControl",[
"wx.lib.calendar.Calendar",
["wx.lib.buttons.GenButton",[
["wx.lib.buttons.GenBitmapButton",[
["wx.lib.buttons.GenBitmapTextButton",[
"wx.lib.buttons.GenBitmapTextToggleButton",]],
"wx.lib.buttons.GenBitmapToggleButton ",]],
"wx.lib.buttons.GenToggleButton ",]],
"wx.lib.statbmp.GenStaticBitmap",
"wx.lib.stattext.GenStaticText",
"wx.lib.popupctl.PopButton",
"wx.lib.popupctl.PopupControl",
"wx.lib.ticker.Ticker ",]],
"wx.RadioBox",
"wx.RadioButton",
"wx.ScrollBar",
"wx.Slider",
"wx.SpinButton",
"wx.SpinCtrl",
["wx.StaticBitmap",[
"wx.lib.fancytext.StaticFancyText ",]],
"wx.StaticBox",
"wx.StaticLine",
"wx.StaticText",
["wx.stc.StyledTextCtrl",[
["wx.py.editwindow.EditWindow",[
"wx.py.crust.Display",
"wx.py.editor.EditWindow",
"wx.py.filling.FillingText",
"wx.py.shell.Shell",]],
"wx.lib.pyshell.PyShellWindow ",]],
["wx.TextCtrl", [
["wx.lib.masked.textctrl.BaseMaskedTextCtrl",[
"wx.lib.masked.ipaddrctrl.IpAddrCtrl",
"wx.lib.masked.numctrl.NumCtrl",
"wx.lib.masked.textctrl.PreMaskedTextCtrl",
"wx.lib.masked.textctrl.TextCtrl",
"wx.lib.masked.timectrl.TimeCtrl ",]],
"wx.py.crust.Calltip",
"wx.lib.sheet.CTextCellEditor",
"wx.py.crust.DispatcherListing",
"wx.lib.intctrl.IntCtrl",
"wx.lib.rightalign.RightTextCtrl",
"wx.py.crust.SessionListing",]],
"wx.ToggleButton",
"wx.ToolBar",
["wx.TreeCtrl",[
"wx.py.filling.FillingTree",
"wx.gizmos.RemotelyScrolledTreeCtrl ",]],
"wx.gizmos.TreeListCtrl ",]],
"wx.gizmos.DynamicSashWindow",
"wx.lib.multisash.EmptyChild",
"wx.glcanvas.GLCanvas",
"wx.lib.imagebrowser.ImageView",
"wx.MDIClientWindow",
"wx.MenuBar",
"wx.lib.multisash.MultiClient",
"wx.lib.multisash.MultiCloser",
"wx.lib.multisash.MultiCreator",
"wx.lib.multisash.MultiSash",
"wx.lib.multisash.MultiSizer",
"wx.lib.multisash.MultiSplit",
"wx.lib.multisash.MultiViewLeaf",
["wx.Panel",[
"wx.gizmos.EditableListBox",
["wx.lib.filebrowsebutton.FileBrowseButton",[
"wx.lib.filebrowsebutton.DirBrowseButton",
"wx.lib.filebrowsebutton.FileBrowseButtonWithHistory",]],
"wx.lib.floatcanvas.FloatCanvas.FloatCanvas",
"wx.lib.floatcanvas.NavCanvas.NavCanvas",
"wx.NotebookPage",
["wx.PreviewControlBar",[
"wx.PyPreviewControlBar ",]],
"wx.lib.colourchooser.pycolourbox.PyColourBox",
"wx.lib.colourchooser.pycolourchooser.PyColourChooser",
["wx.PyPanel",[
"wx.lib.throbber.Throbber",]],
"wx.lib.shell.PyShell",
"wx.lib.shell.PyShellInput",
"wx.lib.shell.PyShellOutput",
["wx.ScrolledWindow",[
"wx.lib.editor.editor.Editor",
["wx.grid.Grid",[
"wx.lib.sheet.CSheet ",]],
["wx.html.HtmlWindow",[
"wx.lib.ClickableHtmlWindow.PyClickableHtmlWindow",]],
"wx.PreviewCanvas",
"wx.lib.printout.PrintTableDraw",
["wx.PyScrolledWindow",[
"wx.lib.scrolledpanel.ScrolledPanel",]],
"wx.lib.ogl.ShapeCanvas",
"wx.gizmos.SplitterScrolledWindow ",]],
["wx.VScrolledWindow",[
["wx.VListBox", [
"wx.HtmlListBox ",]] ]],
["wx.wizard.WizardPage", [
"wx.wizard.PyWizardPage",
"wx.wizard.WizardPageSimple ",]],
"wx.lib.plot.PlotCanvas",
"wx.lib.wxPlotCanvas.PlotCanvas",
["wx.PopupWindow",[
"wx.lib.foldmenu.FoldOutWindow",
["wx.PopupTransientWindow",[
"wx.TipWindow ",]] ]],
["wx.PyWindow", [
"wx.lib.analogclock.AnalogClockWindow",]],
"wx.lib.gridmovers.RowDragWindow",
["wx.SashWindow",[
"wx.SashLayoutWindow ",]],
"wx.SplashScreenWindow",
["wx.SplitterWindow",[
"wx.py.crust.Crust",
"wx.py.filling.Filling",
"wx.gizmos.ThinSplitterWindow ",]],
"wx.StatusBar",
["wx.TopLevelWindow",[
["wx.Dialog",[
"wx.lib.calendar.CalenDlg",
"wx.ColourDialog",
"wx.DirDialog",
"wx.FileDialog",
"wx.FindReplaceDialog",
"wx.FontDialog",
"wx.lib.imagebrowser.ImageDialog",
"wx.MessageDialog",
"wx.MultiChoiceDialog",
"wx.lib.dialogs.MultipleChoiceDialog",
"wx.PageSetupDialog",
"wx.lib.popupctl.PopupDialog",
"wx.PrintDialog",
"wx.lib.dialogs.ScrolledMessageDialog",
"wx.SingleChoiceDialog",
"wx.TextEntryDialog",
"wx.wizard.Wizard ",]],
["wx.Frame", [
"wx.lib.analogclockopts.ACCustomizationFrame",
"wx.py.filling.FillingFrame",
["wx.py.frame.Frame",[
"wx.py.crust.CrustFrame",
["wx.py.editor.EditorFrame",[
"wx.py.editor.EditorNotebookFrame",]],
"wx.py.shell.ShellFrame",]],
"wx.html.HtmlHelpFrame",
"wx.MDIChildFrame",
"wx.MDIParentFrame",
"wx.MiniFrame",
["wx.PreviewFrame",[
"wx.PyPreviewFrame ",]],
"wx.ProgressDialog",
"wx.SplashScreen",
"wx.lib.splashscreen.SplashScreen",
"wx.lib.masked.maskededit.test2",
"wx.lib.plot.TestFrame ",]] ]],
"wx.gizmos.TreeCompanionWindow ",]] ]] ]],
"wx.FileHistory",
"wx.FileSystem",
"wx.FindReplaceData",
"wx.FontData",
"wx.FontList",
"wx.FSFile",
["wx.GDIObject",[
"wx.Bitmap",
"wx.Brush",
"wx.Cursor",
"wx.Font",
"wx.Icon",
"wx.Palette",
"wx.Pen",
"wx.Region ",]],
"wx.glcanvas.GLContext",
["wx.grid.GridTableBase", [
"wx.grid.GridStringTable",
"wx.grid.PyGridTableBase ",]],
["wx.html.HtmlCell", [
"wx.html.HtmlColourCell",
"wx.html.HtmlContainerCell",
"wx.html.HtmlFontCell",
"wx.html.HtmlWidgetCell",
"wx.html.HtmlWordCell ",]],
"wx.html.HtmlDCRenderer",
"wx.html.HtmlEasyPrinting",
"wx.html.HtmlFilter",
"wx.html.HtmlLinkInfo",
["wx.html.HtmlParser", [
"wx.html.HtmlWinParser ",]],
"wx.html.HtmlTag",
["wx.html.HtmlTagHandler", [
["wx.html.HtmlWinTagHandler", [
"wx.lib.wxpTag.wxpTagHandler ",]] ]],
"wx.Image",
["wx.ImageHandler", [
["wx.BMPHandler", [
["wx.ICOHandler", [
["wx.CURHandler", [
"wx.ANIHandler ",]] ]] ]],
"wx.GIFHandler",
"wx.JPEGHandler",
"wx.PCXHandler",
"wx.PNGHandler",
"wx.PNMHandler",
"wx.TIFFHandler",
"wx.XPMHandler ",]],
"wx.ImageList",
"wx.IndividualLayoutConstraint",
"wx.LayoutAlgorithm",
["wx.LayoutConstraints", [
"wx.lib.anchors.LayoutAnchors",
"wx.lib.layoutf.Layoutf",]],
"wx.ListItem",
"wx.Mask",
"wx.MenuItem",
"wx.MetaFile",
"wx.PageSetupDialogData",
"wx.PenList",
"wx.PrintData",
"wx.PrintDialogData",
"wx.Printer",
["wx.Printout", [
"wx.html.HtmlPrintout",
"wx.lib.plot.PlotPrintout",
"wx.lib.printout.SetPrintout ",]],
["wx.PrintPreview", [
"wx.PyPrintPreview ",]],
"wx.RegionIterator",
["wx.Sizer", [
"wx.BookCtrlSizer",
["wx.BoxSizer", [
"wx.StaticBoxSizer", ]],
["wx.GridSizer", [
["wx.FlexGridSizer", [
"wx.GridBagSizer",]] ]],
"wx.NotebookSizer",
"wx.PySizer",]],
["wx.SizerItem", [
"wx.GBSizerItem",]],
"wx.SystemOptions",
"wx.ToolBarToolBase",
"wx.ToolTip",
"wx.gizmos.TreeListColumnInfo",
"wx.xrc.XmlDocument",
"wx.xrc.XmlResource",
"wx.xrc.XmlResourceHandler ",
]
```
### 如何添加一個root(根)元素?
當你將項目添加到樹時,你首先必須要添加的項目是`root(`根)元素。添加根元素的方法如下所示:
```
AddRoot(text, image=-1, selImage=-1, data=None)
```
你只能添加一個根元素。如果你在已經存在一個根元素后,再添加第二根元素的話,那么`wxPython`將引發一個異常。其中的參數`text`包含用于根元素的顯示字符串。參數`image`是圖像列表中的一個索引,代表要顯示在參數`text`旁邊的圖像。這個圖像列表將在15.5節中作更詳細的討論,但是現在只要知道它的行為類似于用于列表控件的圖像列表。參數`data`是一個與項目相關的數據對象,主要的目的是分類。
`AddRoot()`方法返回一個關于根項目的`ID`。樹形控件使用它自己的類`wx.TreeItemId`來管理項目。在大多數時候,你不需要關心`ID`的具體值,你只需要知道每個項目都有一個唯一的`wx.TreeItemId`就夠了,并且這個值可以使用等號測試。`wx.TreeItemId`不映射到任何簡單的類型——它的值沒有任何的關聯性,因為你只是把它用于相等測試。
### 如何將更多的項目添加到樹中?
一旦你有了根元素,你就可以開始向樹中添加元素了。用的最多的方法是`AppendItem(parent,?text,?image=`-1, `selImage=`-1, `data=None)`。參數`parent`是已有的樹項目的`wx.TreeItemId`,它作為新項目的父親。參數`text`是顯示新項目的文本字符串。參數`image`和`selImage`的意義與方法`AddRoot()`中的相同。該方法將新的項目放置到其父項目的孩子列表的末尾。這個方法返回新創建的項目的`wx.TreeItemId`。如果你想給新的項目添加子項目的話,你需要擁有這個`ID`。一個示例如下:
```
rootId = tree.AddRoot("The Root")
childId = tree.AppendItem(rootId, "A Child")
grandChildId = tree.AppendItem(childId, "A Grandchild")
```
上面的這個代碼片斷增加了一個`root(`根)項目,然后給根項目添加了一個子項目,然后給子項目添加了它的子項目。
如果要將子項目添加到孩子列表的開頭的話,使用方法`PrependItem(parent,?text,?image=`-1, `selImage=`-1, `data=None)`。
如果你想將一個項目插入樹的任意點上,你可以使用后面的兩種方法之一。第一個是`InsertItem(parent,?previous,?text,image=`-1, `selImage=`-1, `data=None)`。其中參數`previous`其父項目中的子列表中的項目的`wx.TreeItemId`。插入的項目將放置在該項目的后面。第二個方法是`InsertItemBefore(parent,?before,?text,?image=`-1, `selImage=`-1, `data=None)`。該方法將新的項目放置在`before`所代表的項目之前。然而參數`before`不是一個項目的`ID`。它是項目在孩子列表中的整數索引。第二個方法返回新項目的一個`wx.TreeItemId`。
### 如何管理項目?
要去掉樹中的一個項目,可以使用`Delete(item)`方法,其中參數`item`是該項目的`wx.TreeItemId`。調用這個方法將導致一個`EVT_TREE_Delete_ITEM`類型的樹的事件被觸發。后面的章節我們將討論樹的事件類型。要刪除一個項目的所有子項目,而留下該項目自身,可以使用方法`DeleteChildren(item)`,其中參數`item`也是一個`wx.TreeItemId`。該方法不產生一個刪除事件。要清除整個樹,使用方法`DeleteAllItems()`。該方法為每個項目生成一個刪除事件,但是,這在某些老版的`Windows`系統上不工作。
一旦你將一個項目添加到樹中,你就可以使用方法`GetItemText(item)`來得到該項目在顯示在樹中的文本,其中參數`item`是一個`wx.TreeItemId`。如果你想改變一個項目的顯示文本,可以使用方法`SetItemText(item,?text)`,其中參數`item`是一個`wx.TreeItemId`,參數`text`是一個新的顯示文本。
最后,你可以使用方法`GetCount()`來得到樹中項目的總數。如果你想得到特定項目下的子項目的數量,可以使用方法`GetChildrenCount(item,?recursively=True)`。其中參數`item`是一個`wx.TreeItemId`,參數`recursively`如果為`False`,那么該方法只返回直接的子項目的數量,如果為`True`,則返回所有的子項目而不關嵌套有多深。
## 樹控件的顯示樣式
樹控件的顯示樣式分為四類。第一類定義了樹中顯示在父項目的文本旁的按鈕(用以展開或折疊子項目)。它們顯示在表15.1中。
**表15.1** **樹控件中的按鈕**
| | |
| --- | --- |
| `wx.TR_HAS_BUTTONS` | 按鈕。在`Windows`上,+用于標明項目可以被展開,-表示可以折疊。 |
| `wx.TR_NO_BUTTONS` | 沒有按鈕。 |
接下來的一類顯示在表15.2中,它們決定樹控件將連接線繪制在何處。
**表15.2** **樹控件中的連接線**
| | |
| --- | --- |
| `wx.TR_LINES_AT_ROOT` | 如果設置了這個樣式,那么樹控件將在多個`root`項目之間繪制連線。注意,如果`wx.TR_HIDE_ROOT`被設置了,那么你就有多個`root`項目。 |
| `wx.TR_NO_LINES` | 如果設置了這個樣式,那么樹控件將不在兄弟項目間繪制連接線。這個樣式將代替`wx.TR_LINES_AT_ROOT`。 |
| `wx.TR_ROW_LINES?` | 樹控件在行之間將繪制邊距。 |
第三類樣式顯示在表15.3中,用于控制樹控件的選擇模式。
**表15.3** **樹控件的選擇模式**
| | |
| --- | --- |
| `wx.TR_EXTENDED` | 可以選擇多個不連續的項。不是對所有的系統有效。 |
| `wx.TR_MULTIPLE` | 可以選擇一塊且僅一塊連續的項。 |
| `wx.TR_SINGLE` | 一次只能選擇一個結點。這是默認模式。 |
表15.4顯示了其它的一樣可作用于樹的顯示的樣式。
**表15.4** **樹的其它顯示樣式**
| | |
| --- | --- |
| `wx.TR_FULL_ROW_HIGHLIGHT` | 如果設置了這個樣式,那么當被選擇時,被選項的整個行將高亮顯示。默認情況下,只是文本區高亮。在`Windows`上,該樣式只在也設置`wx.NO_LINES`時有效。 |
| `wx.TR_HAS_VARIABLE_ROW_HEIGHT` | 如果設置了這個樣式,則行的高度將根據其中的圖像和文本而不同。否則,所有的行將是同樣的高度(取決于最高的行)。 |
| `wx.TR_HIDE_ROOT` | 如果設置了這個樣式,則通過`AddRoot()`確定的`root`元素將不被顯示。此時該結節的所有子項就如同它們是`root`一樣顯示。這個樣式用于讓一個樹具有多個`root`元素的外觀。 |
最后,`wx.TR_DEFAULT_STYLE`讓你的樹顯示出最接近當前操作系統本地控件的樣式。在程序中,你可以使用`SetWindowStyle(styles)`來改變樣式,其中參數`styles`是你想要的新樣式。
樹形控件有幾個方法可以用以改變它的顯示特性。在這些方法中,參數`item`是你想要改變的項的`wx.TreeItemId`。你可以使用方法`SetItemBackgroundColor(item,?col)`來設置項目的背景色,其中參數`col`是一個`wx.Colour`或其它能夠轉換為顏色的東西。你可以使用`SetItemTextColour(item,col)`來改變文本的顏色。你可以使用`SetItemFont(item,?font)`來設置項目的顯示字體,其中參數`font`是一個`wx.Font`實例。如果你只想顯示文本為粗體,你可以使用方法`SetItemBold(item,?bold=True)`,其中參數`bold`是一個布爾值,它決定是否顯示為粗體。上面的四個`set`*方法都有對應的`get`*方法,如下所示: `GetItemBackgroundColor(item),?GetItemTextColour(item),?GetItemFont(item),?`和`IsBold(item)`。其中的`item`參數是一個`wx.TreeItemId`。
## 對樹形控件的元素排序
對樹形控件的元素排序的基本機制是方法`SortChildren(item)`。其中參數`item`是`wx.TreeItemId`的一個實例。該方法對此項目的子項目按顯示字符串的字母的順序進行排序。
對于樹的排序,每個樹項目都需要有已分派的數據,不管你是否使用默認排序。在默認情況中,所指派的數據是`None`,但是對于排序任務,在樹控件中你還是需要顯式地設置。
在15.1節,我們提及到了讓你能夠創建一個樹項目及將該項目與一個任意數據對象相關聯的方法。我們也告訴你不要使用這個機制。數據項目是一個`wx.TreeItemData`。在`wxPython`中在一預定義的快捷的方法,使你能夠用以將一個`Python`對象與一個樹項目關聯起來。
快捷的`set`*方法是`SetItemPyData(item,?obj)`。其中參數`item`是一個`wx.TreeItemId`,參數`obj`是一個任意的`Python`對象,`wxPython`在后臺管理這個關聯。當你想得到這個數據項時,你可以調用`GetItemPyData(item)`,它返回相關的`Python`對象。
注意:對于`wx.TreeItemData`有一個特別的構造函數:`wx.TreeItemData(obj)`,其中參數`obj`是一個`Python`對象。因此你可以使用`GetItemData(item)`和`SetItemData(item,?obj)`方法來處理這個`Python`數據。這是 `SetItemPyData()`方法的后臺機制。這一信息在某些時候可能對你是有用的,但是大部分時候,你還是應該使用`SetItemPyData(item,?obj)`和`GetItemPyData(item)`方法。
要使用關聯的數據來排序你的樹,你的樹必須是一個`wx.TreeCtrl`的自定義的子類,并且你必須覆蓋`OnCompareItems(item1,?item2)`方法。其中參數`item1,?item2`是要比較的兩個項的`wx.TreeItemId`實例。如果`item1`應該排在`item2`的前面,則方法返回-1,如果`item1`應該排在`item2`的后面,則返回1,相等則返回0。該方法是在當樹控件為了排序而比較計算每個項時自動被調用的。你可以在`OnCompareItems()`方法中做你想做的事情。尤其是,你可以如下調用`GetItemPyData()`方法:
```
def OnCompareItems(self, item1, item2);
data1 = self.GetItemPyData(item1)
data2 = self.GetItemPyData(item2)
return cmp(data1, data2)
```
## 控制與每項相關的圖像
用于樹形控件的圖像是由一個圖像列表來維護的,這非常類似于列表控件中的圖像維護。有關創建圖像列表的細節,參見13章。一旦你創建了圖像列表,你就可以使用`SetImageList(imageList)`或`AssignImageList(imageList)`方法把它分配給樹控件。前者使得圖像列表可以被其它控件共享,后者的圖像列表所有權屬于樹控件。之后,你可能使用方法`GetImageList()`來得到該圖像列表。圖15.2顯示了一個帶有一些圖像的樹。
**圖15.2**

例15.2是產生圖15.2的代碼。它使用了`ArtProvider`對象來提供圖像。
**例15.2** **一個帶有圖標的樹控件**
```
#-*- encoding:UTF-8 -*-
import wx
import data
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None,
title="simple tree with icons", size=(400,500))
# 創建一個圖像列表
il = wx.ImageList(16,16)
# 添加圖像到列表
self.fldridx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FOLDER,
wx.ART_OTHER, (16,16)))
self.fldropenidx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,
wx.ART_OTHER, (16,16)))
self.fileidx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE,
wx.ART_OTHER, (16,16)))
# 創建樹
self.tree = wx.TreeCtrl(self)
# 給樹分配圖像列表
self.tree.AssignImageList(il)
root = self.tree.AddRoot("wx.Object")
self.tree.SetItemImage(root, self.fldridx,
wx.TreeItemIcon_Normal)# 設置根的圖像
self.tree.SetItemImage(root, self.fldropenidx,
wx.TreeItemIcon_Expanded)
self.AddTreeNodes(root, data.tree)
self.tree.Expand(root)
def AddTreeNodes(self, parentItem, items):
for item in items:
if type(item) == str:
newItem = self.tree.AppendItem(parentItem, item)
self.tree.SetItemImage(newItem, self.fileidx,
wx.TreeItemIcon_Normal)# 設置數據圖像
else:
newItem = self.tree.AppendItem(parentItem, item[0])
self.tree.SetItemImage(newItem, self.fldridx,
wx.TreeItemIcon_Normal)# 設置結點的圖像
self.tree.SetItemImage(newItem, self.fldropenidx,
wx.TreeItemIcon_Expanded)
self.AddTreeNodes(newItem, item[1])
def GetItemText(self, item):
if item:
return self.tree.GetItemText(item)
else:
return ""
app = wx.PySimpleApp(redirect=True)
frame = TestFrame()
frame.Show()
app.MainLoop()
```
如你所見,當你添加項目到列表中時,對于未選和選中狀態,有兩個不同的圖像可用于分配給項目。和列表控件一樣,你可以指定圖像在圖像列表中的索引。如果你想在項目被創建后得到所分配的圖像,你可以使用方法`GetItemImage(item,?which=wx.TreeItemIcon_Normal)`。其中參數`item`是項目的`wx.TreeItemId`。參數`which`控制你將得到了是哪個圖像,默認值`wx.TreeItemIcon_Normal`,將得到該項目的未選狀態的圖像的索引。`which`的另一個值`wx.TreeItemIcon_Selected`的使用,將返回選中狀態的圖像,`wx.TreeItemIcon_Expanded`和`wxTreeItemIcon_SelectedExpanded`返回當該樹項目被展開時所使用的圖像。注意,后者的兩個圖像不能使用添加方法來被設置——如果你想設置的話,你必須使用方法`SetItemImage(item,??image,??which=wx.TreeItemIcon_Normal)`來實現。其中參數`item`是`wx.TreeItemId`的實例,參數`image`是新圖像的整數索引,參數`which`同`get`*方法。
## 使用編程的方式訪問樹。
在15.1節中,我們談到沒有直接得到一個給定項目的子項目的`Python`列表的方法,至于一個特定子項目的索引就更不用說了。要實現這個,我們需要使用本節所說的遍歷方法來訪問樹的結點。
要開始遍歷樹,我們首先要使用`GetRootItem()`來得到根元素。該方法返回樹的根元素的`wx.TreeItemId`。之后,你就可以使用諸如`GetItemText()`或`GetItemPyData()`之類的方法來獲取關于根元素的更多的信息。
一旦你得到了一個項目,你就可以通過迭代器來遍歷子項目的列表。你可以使用方法`GetFirstChild(item)`來得到子樹中的第一個孩子,該方法返回一個二元元組(`child,?cookie)`。參數`item`是第一個孩子的`wx.TreeItemId`。除了告訴你每一個孩子是什么外,該方法還初始化一個迭代對象,該對象使你能夠遍歷該樹。`cookie`值只是一個標志,它使得樹控件能夠同時保持對多個迭代器的跟蹤,而它們之間不會彼此干擾。
一旦你由`GetFirstChild()`得到了`cookie`,你就可以通過反復調用`GetNextChild(item,?cookie)`來得到其余的孩子。這里的`item`父項的`ID`,`cookie`是由`GetFirstChild()`或前一個`GetNextChild()`調用返回的。`GetNextChild()`方法也返回一個二元元組(`child,?cookie)`。如果此時沒有下一個項目了,那么你就到達了孩子列表的末尾,系統將返回一個無效的孩子`ID`。你可以通過使用`wx.TreeItemId.IsOk()`或`__nonzero__`方法測試這種情況。下面的函數返回給定項目的所有孩子的文本的一個列表。
```
def getChildren(tree, parent):
result = []
item, cookie = tree.GetFirstChild(parent)
while item:
result.append(tree.GetItemText(item))
item, cookie = tree.getNextChild(parent, cookie)
return result
```
這個函數得到給定父項目的第一個孩子,然后將第一個孩子的文本添加到列表中,然后通過循環來得到每個子項目的文本并添加到列表中,直到得到一個無效的項,這時就返回`result`。
要得到父項目的最后一個孩子,你可以使用方法`GetLastChild(item)`,它返回列表中的最后的項目的`wx.TreeItemId`。由于這個方法不用于驅動迭代器來遍歷整個孩子列表,所以它不需要`cookie`機制。如果你有這個最后的子項且你想得到它的父項,可以使用方法`GetItemParent(item)`來返回給定項的父項的`ID`。
你可以使用方法`GetNextSibling(item)`和`GetPrevSibling(item)`來在同級的項目間前后訪問。這些方法均返回相關項的`wx.TreeItemId`。由這些方法同樣不用于驅動迭代器,所以它們都不需要一個`cookie`。當你已經到達列表的兩頭時,將沒有下一項或前一項,那么這些方法將返回一個無效的項(例如:`item.IsOk()?==?False`)。
要確定一個項是否有孩子,使用方法`ItemHasChildren(item)`,該方法返回布爾值`True`或`False`。你可以使用方法`SetItemHasChildren(item,?hasChildren=True)`來將一個項設置為有子項,如果這樣,即使該項沒有實際的子項,它也將顯示得與有子項的一樣。也就是說該項旁邊會有一個擴展或折疊的按鈕,以便展開或折疊。這通常被用于實現一個虛的樹控件。這個技術將在15.7節中演示。
## 管理樹中的選擇
樹形控件允許你通過程序的方式管理樹中的被選項。基本的方法是`SelectItem(item,select=True)?`。在單選樹控件中,該方法將選擇指定項`item`(`wx.TreeItemId`),同時自動撤消對先前選項的選擇。如果參數`select`的取值是`False`,那么該方法將取消對參數`item`代表的項的選擇。在一個可多選的樹控件中,`SelectItem()`方法只改變`item`所代表的項的狀態,而不改變樹中其它項的選擇狀態。在可多選的樹中,你也可以使用方法`ToggleItemSelection(item)`,它只切換參數`item`所代表項的選擇狀態。
對于取消選擇還有三個快捷的方法。方法`Unselect()`取消單選模式樹中的當前被選項的選擇。在多選模式樹中,使用`UnselectAll()`來取消所有的選擇。如果你只想取消在一個多選樹中的一個項的被選狀態,可以使用方法`UnselectItem(item)`。
你也可以使用方法`IsSelected(item)`來查詢一個項目的選擇狀態,該方法返回布爾值`True`或`False`。對于單選樹,你可能使用方法`GetSelection()`來得到當前被選項目的`wx.TreeItemId`。對于多選樹,可以使用方法`GetSelections()`來得到所有被選項的`wx.TreeItemId`的一個`Python`列表。
當樹控件中發生選擇變化的時候,有兩個事件將被觸發并可被捕獲。第一個事件是`wx.EVT_TREE_SEL_CHANGING,`它在被選項實際改變之前發生。如果你要處理這個事件,你可以使用事件的`Veto()`方法來阻止選擇的改變。在選擇已經改變之后,事件`wx.EVT_TREE_SEL_CHANGED`被觸發。這兩個事件的類是`wx.TreeEvent`,這將在15.8節中作更完整的討論。
## 控制項目的可見性
在樹控件中有兩種機制可以讓你用編程的方式控制某項目的可見性。你可以使用方法`Collapse(item)`和`Expand(item)`指定給定的樹項目是展開的或折疊的。這些方法改變樹控件的顯示,并且如果對一個沒有子項的項目調用該方法將不起作用。這兒還有一個方便的函數:`CollapseAndReset(item)`,該方法折疊指定的項,并刪除指定項的所有孩子。另外,方法`Toggle(item)`用于切換項目的展開和折疊狀態。你可以使用方法`IsExpanded(item)`來查詢項目的當前展開狀態。
展開或折疊一個樹項目觸發兩事件。在展開或折疊前,事件`wx.EVT_TREE_ITEM_COLLAPSING?`或`wx.EVT_TREE_ITEM_EXPANDING`被觸發。在你的處理方法中,你可以使用事件的`Veto()`方法來阻止展開或折疊。在展開或折疊發生后,事件`EVT_TREE_ITEM_COLLAPSED`或`wx.EVT_TREE_ITEM_EXPANDED`被觸發。這四個事件都是類`wx.TreeEvent`的事件類型。
**虛樹**
展開和折疊項目的一個令人感興趣的的用法是創建一個虛樹,新項目僅當父項展開時添加。例15.3顯示這樣一個樣列。
**例15.3** **展開時動態添加新的項目**
```
#-*- encoding:UTF-8 -*-
import wx
import data
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="virtual tree with icons", size=(400,500))
il = wx.ImageList(16,16)
self.fldridx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16,16)))
self.fldropenidx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, (16,16)))
self.fileidx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16,16)))
self.tree = wx.TreeCtrl(self)
self.tree.AssignImageList(il)
root = self.tree.AddRoot("wx.Object")
self.tree.SetItemImage(root, self.fldridx,
wx.TreeItemIcon_Normal)
self.tree.SetItemImage(root, self.fldropenidx,
wx.TreeItemIcon_Expanded)
# Instead of adding nodes for the whole tree, just attach some
# data to the root node so that it can find and add its child
# nodes when it is expanded, and mark it as having children so
# it will be expandable.
self.tree.SetItemPyData(root, data.tree)#創建一個根
self.tree.SetItemHasChildren(root, True)
# Bind some interesting events
# 綁定事件
self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding, self.tree)
self.tree.Expand(root)
def AddTreeNodes(self, parentItem):#給父項目添加結節
"""
Add nodes for just the children of the parentItem
"""
items = self.tree.GetItemPyData(parentItem)
for item in items:
if type(item) == str:
# a leaf node
newItem = self.tree.AppendItem(parentItem, item)
self.tree.SetItemImage(newItem, self.fileidx,
wx.TreeItemIcon_Normal)
else:
# this item has children
newItem = self.tree.AppendItem(parentItem, item[0])
self.tree.SetItemImage(newItem, self.fldridx,
wx.TreeItemIcon_Normal)
self.tree.SetItemImage(newItem, self.fldropenidx,
wx.TreeItemIcon_Expanded)
self.tree.SetItemPyData(newItem, item[1])
self.tree.SetItemHasChildren(newItem, True)
def GetItemText(self, item):
if item:
return self.tree.GetItemText(item)
else:
return ""
def OnItemExpanded(self, evt):
print "OnItemExpanded: ", self.GetItemText(evt.GetItem())
def OnItemExpanding(self, evt):#當展開時創建結點
# When the item is about to be expanded add the first level of child nodes
print "OnItemExpanding:", self.GetItemText(evt.GetItem())
self.AddTreeNodes(evt.GetItem())
def OnItemCollapsed(self, evt):
print "OnItemCollapsed:", self.GetItemText(evt.GetItem())
# And remove them when collapsed as we don't need them any longer
self.tree.DeleteChildren(evt.GetItem())#折疊時刪除結點
def OnSelChanged(self, evt):
print "OnSelChanged: ", self.GetItemText(evt.GetItem())
def OnActivated(self, evt):
print "OnActivated: ", self.GetItemText(evt.GetItem())
app = wx.PySimpleApp(redirect=True)
frame = TestFrame()
frame.Show()
app.MainLoop()
```
這個機制可以被擴展來從外部源讀取數據以查看。這個機制除了可以被用來建造一個文件樹外,我們會提及數據庫中的數據的可能性,以便對文件的結構不感趣的你不用全面研究文件結構。
**控制可見性**
有大量的方法使你能夠管理那些項目是可見的。一個對象的不可見,可能是因為它沒處在含有滾動條的框中的可見區域或是因為它處在折疊項中。你可以使用`IsVisible(item)`方法來確定項目是否可見,該方法在項目是可見時返回`True`,不可見返回`False`。你可以通過使用方法`EnsureVisible(item)`迫使指定的項變成可見的。如果需要的話,該方法將通過展開該項的父項(以及父項的父項,以此類推)來迫使指定項變成可見的,然后滾動該樹,以使指定項處于控件的可見部分。如果你只需要滾動,可以使用`ScrollTo(item)`方法來完成。
遍歷樹中的可見部分,首先要使用的方法是`GetFirstVisibleItem()`。該方法返回顯示的可見部分中的最頂端的項目的`wx.TreeItemId`。然后通過使用`GetNextVisible(item)`方法來遍歷,該方法的`item`參數來自`GetFirstVisibleItem()`和`GetNextVisible()`的返回值。如果移動的方向向上的話,使用方法`GetPreviousVisible(item)`。如果參數`item`不可見的話,返回值是一個無效的項。
還有幾個別的方法可用于項目的顯示。樹控件有一個屬性,該屬性用于設置縮進。可以通過方法`GetIndent()`和方法`SetIndent(indent)`來得到和設置該屬性的當前值。其中`indent`參數是縮進的像素值(整數)。
要得到關于指定點的樹項目的信息,使用方法`HitTest(point)`,其中`point`是樹控件中的相關位置的一個`wx.Point`。方法的返回值是一個(`item,?flags)`元組,其中的`item`是相關位置的項的`wx.TreeItemId`或`None`值。如果指定位置沒有項目,那么一個無效的項目將返回。`flags`部分是一個位掩碼,它給出了相關的信息。表15.5包含了`flags`的一個完整列表。
還有兩個方法,它們讓你可以處理屏幕上項目的實際的邊界。方法`GetBoundingRect(item,?textOnly=False)`返回一個`wx.Rect`實例,該實例對應于屏幕上文本項的矩形邊界區域。其中參數`item`是項目的`wx.TreeItemId`。如果參數`textOnly`為`True`,那么該矩形僅包括項目的顯示文本所覆蓋的區域。如果為`False`,那么該矩形也包括圖像區域。在這兩種情況中,矩形都包括從樹控件的邊緣到內嵌的顯示項間的空白區域。如果`item`代表的項目當前是不可見的,那么兩種方法都返回`None`。
**表15.5**
| | |
| --- | --- |
| `wx.TREE_HITTEST_ABOVE` | 該位置在樹的客戶區的上面,不是任何項目的一部分。 |
| `wx.TREE_HITTEST_BELOW` | 該位置在樹的客戶區的下面,不是任何項目的一部分。 |
| `wx.TREE_HITTEST_NOWhere` | 該位置處在樹的客戶區中,不是任何項目的一部分。 |
| `wx.TREE_HITTEST_ONITEMBUTTON` | 該位置位于展開/折疊圖標按鈕上,是項目的一部分。 |
| `wx.TREE_HITTEST_ONITEMICON` | 該位置位于項目的圖像部分上。 |
| `wx.TREE_HITTEST_ONITEMINDENT` | 該位置位于項目的顯示文本的左邊縮進區域中。 |
| `wx.TREE_HITTEST_ONITEMLABEL` | 該位置位于項目的顯示文本中。 |
| `wx.TREE_HITTEST_ONITEMRIGHT` | 該位置是項目的顯示文本的右邊。 |
| `wx.TREE_HITTEST_ONITEMSTATEICON` | 該位置是在項目的狀態圖標中。 |
| `wx.TREE_HITTEST_TOLEFT` | 該位置在樹的客戶區的左面,不是任何項目的一部分。 |
| `wx.TREE_HITTEST_TORIGHT` | 該位置在樹的客戶區的右面,不是任何項目的一部分。 |
## 使樹控件可編輯
樹控件可以被設置為允許用戶編輯樹項目的顯示文本。這通過在創建樹控件時使用樣式標記`wx.TR_EDIT_LABELS`來實現。使用了該樣式標記后,樹控件的行為就類似于可編輯的列表控件了。編輯一個樹項目會給出一個文本控件來讓用戶編輯文本。按下`esc`則取消編輯。按下回車鍵或在文本控件外敲擊將確認編輯。
你可以在程序中使用`EditLabel(item)`方法來啟動對特定項的編輯。參數`item`是你想要編輯的項的`wx.TreeItemId`。要終止編輯,可以使用方法`EndEditLabel(cancelEdit)`。由于一次只能有一個活動編輯項,所以這里不需要指定項目的`ID`。參數`cancelEdit`是一個布爾值。如果為`True`,取消編輯,如果為`False`,則不取消。如果因為某種原因,你需要訪問當前所用的文本編輯控件,你可以調用方法`GetEditControl()`,該方法返回用于當前編輯的`wx.TextCtrl`實例,如果當前沒有編輯,則返回`None`。當前該方法只工作于`Windows`系統下。
當一個編輯會話開始時(通過用戶選擇或調用`EditLabel()`方法),`wx.EVT_TREE_BEGIN_LABEL_EDIT`類型的`wx.TreeEvent`事件被觸發。如果使用`Veto()`方法否決了該事件的話,那么編輯將不會開始。當會話結束時(通過用戶的敲擊或調用`EndEditLabel()`方法),一個`wx.EVT_TREE_END_LABEL_EDIT`類型的事件被觸發。這個事件也可以被否決,這樣的話,編輯就被取消了,項目不會被改變。
## 響應樹控件的其它的用戶事件
在這一節,我們將討論`wx.TreeEvent`類的屬性。表15.6列出了這些屬性。
**表15.6** **`wx.TreeEvent`的屬性**
| | |
| --- | --- |
| `GetKeyCode()` | 返回所按鍵的整數按鍵碼。只對`wx.EVT_TREE_KEY_DOWN`事件類型有效。如果任一修飾鍵(`CTRL,SHIFT,and?ALT`之類的)也被按下,該屬性不會告知你。 |
| `GetItem()` | 返回與事件相關的項的`wx.TreeItemId`。 |
| `GetKeyEvent()` | 只對`wx.EVT_TREE_KEY_DOWN`事件有效。返回`wx.KeyEvent`。該事件可以被用來告知你在該事件期間,是否有修飾鍵被按下。 |
| `GetLabel()` | 返回項目的當前文本標簽。只對`wx.EVT_TREE_BEGIN_LABEL_EDIT`和`wx.EVT_TREE_END_LABEL_EDIT`有效。 |
| `GetPoint()` | 返回與該事件相關的鼠標位置的一個`wx.Point`。只對拖動事件有效。 |
| `IsEditCancelled()` | 只對`wx.EVT_TREE_END_LABEL_EDIT`有效。如果用戶通過取消來結束當前的編輯則返回`True`,否則返回`False`。 |
| `SetToolTip(tooltip)` | 只對`wx.EVT_TREE_ITEM_GETTOOLTIP`事件有效。這使你能夠得到關于項目的提示。該屬性只作在`Windows`系統上。 |
表15.7列出了幾個不適合在表15.6中列出的`wx.TreeEvent`的事件類型,它們有時也是用的。
**表15.7** **樹控件的另外的幾個事件**
| | |
| --- | --- |
| `wx.EVT_TREE_BEGIN_DRAG` | 當用戶通過按下鼠標左鍵來拖動樹中的一個項目時,觸發該事件。要讓拖動時能夠做些事情,該事件的處理函數必須顯式地調用事件的`Allow()`方法。 |
| `wx.EVT_TREE_BEGIN_RDRAG` | 當用戶通過按下鼠標右鍵來拖動樹中的一個項目時,觸發該事件。要讓拖動時能夠做些事情,該事件的處理函數必須顯式地調用事件的`Allow()`方法。 |
| `wx.EVT_TREE_ITEM_ACTIVATED` | 當一個項目通過雙擊被激活時,觸發該事件。 |
| `wx.EVT_TREE_ITEM_GETTOOLTIP` | 當鼠標停留在樹中的一個項目上時,觸發該事件。該事件可用來為項目設置特定的提示。只需要在事件對像中簡單地設置標簽參數,其它的將由系統來完成。 |
| `wx.EVT_TREE_KEY_DOWN` | 在樹控件獲得焦點的情況下,當一個鍵被按下時觸發該事件。 |
上面這些就你需要了解的有關樹控件的屬性。下面,我們將通過一個有用的另一種形式的樹控件來結束本章。
## 使用樹列表控件
除了`wx.TreeCtrl`,`wxPython`也提供了`wx.gizmos.TreeListCtrl`,它是樹控件和報告模式的列表控件的組合。除了本章中所討論的`wx.TreeCtrl`的特性外,`TreeListCtrl`能夠顯示與每行相關的數據的附加列。圖15.3顯示了樹列表控件的樣子。
**圖15.3**

例15.4是產生上圖的代碼
**例15.4** **使用樹列表控件**
```
import wx
import wx.gizmos
import data
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="TreeListCtrl", size=(400,500))
# Create an image list
il = wx.ImageList(16,16)
# Get some standard images from the art provider and add them
# to the image list
self.fldridx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16,16)))
self.fldropenidx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, (16,16)))
self.fileidx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16,16)))
# Create the tree
# 創建樹列表控件
self.tree = wx.gizmos.TreeListCtrl(self, style =
wx.TR_DEFAULT_STYLE
| wx.TR_FULL_ROW_HIGHLIGHT)
# Give it the image list
self.tree.AssignImageList(il)
# create some columns
#創建一些列
self.tree.AddColumn("Class Name")
self.tree.AddColumn("Description")
self.tree.SetMainColumn(0) # the one with the tree in it...
self.tree.SetColumnWidth(0, 200)
self.tree.SetColumnWidth(1, 200)
# Add a root node and assign it some images
root = self.tree.AddRoot("wx.Object")
self.tree.SetItemText(root, "A description of wx.Object", 1)#給列添加文本
self.tree.SetItemImage(root, self.fldridx,
wx.TreeItemIcon_Normal)
self.tree.SetItemImage(root, self.fldropenidx,
wx.TreeItemIcon_Expanded)
# Add nodes from our data set
self.AddTreeNodes(root, data.tree)
# Bind some interesting events
self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
# Expand the first level
self.tree.Expand(root)
def AddTreeNodes(self, parentItem, items):
"""
Recursively traverses the data structure, adding tree nodes to
match it.
"""
for item in items:
if type(item) == str:
newItem = self.tree.AppendItem(parentItem, item)
self.tree.SetItemText(newItem, "A description of %s" % item, 1)#給列添加文本
self.tree.SetItemImage(newItem, self.fileidx,
wx.TreeItemIcon_Normal)
else:
newItem = self.tree.AppendItem(parentItem, item[0])
self.tree.SetItemText(newItem, "A description of %s" % item[0], 1)
self.tree.SetItemImage(newItem, self.fldridx,
wx.TreeItemIcon_Normal)
self.tree.SetItemImage(newItem, self.fldropenidx,
wx.TreeItemIcon_Expanded)
self.AddTreeNodes(newItem, item[1])
def GetItemText(self, item):
if item:
return self.tree.GetItemText(item)
else:
return ""
def OnItemExpanded(self, evt):
print "OnItemExpanded: ", self.GetItemText(evt.GetItem())
def OnItemCollapsed(self, evt):
print "OnItemCollapsed:", self.GetItemText(evt.GetItem())
def OnSelChanged(self, evt):
print "OnSelChanged: ", self.GetItemText(evt.GetItem())
def OnActivated(self, evt):
print "OnActivated: ", self.GetItemText(evt.GetItem())
app = wx.PySimpleApp(redirect=True)
frame = TestFrame()
frame.Show()
app.MainLoop()
```
樹列表控件的很多方法和列表控件的相似,所以我們就再也沒有列出并說明了。
## 本章小結
1、樹控件提供給你了一個如文件樹或`XML`文檔樣的嵌套緊湊的外觀。樹控件是類`wx.TreeCtrl`的實例。有時,人你會想子類化`wx.TreeCtrl`,尤其是如果你需要實現自定義的排序的時候。
2、要向樹中添加項目,首先要用方法`AddRoot(text,?image=`-1, `selImage=`-1, `data=None)`。該函數的返回值是一個代表樹的`root`項目的`wx.TreeItemId`。樹控件使用`wx.TreeItemId`作為它自己的標識符類型,而非和其它控件一樣使用整數的`ID`。一旦你得到了`root`項,你就可以使用方法`AppendItem(parent,?text,?image=`-1, `selImage=`-1, `data=None)`來添加子項目,參數`parent`是父項目的`ID`。該方法返回新項目的`wx.TreeItemId`。另外還有一些用來將新的項目添加在不同位置的方法。方法`Delete(item)`從樹中移除一個項目,方法`DeleteChildren(item)`移除指定項的所有子項目。
3、樹控件有一些用來改變樹的顯示外觀的樣式。一套是用來控制展開或折疊項目的按鈕類型的。另一套是用來控制項目間的連接線的。第三套是用于控制樹是單選還是多選的。你也可以使用樣式通過隱藏樹的實際的`root`,來模擬一個有著多個`root`項的樹。
4、默認情況下,樹的項目通常是按照顯示文本的字母順序排序的。但是要使這能夠實現,你必須給每項分配數據。實現這個的最容易的方法是使用`SetItemPyData(item,?obj)`,它給項目分配一個任意的`Python`對象。如果你想使用該數據去寫一個自定義的排序函數,你必須繼承`wx.TreeCtrl`類并覆蓋方法`OnCompareItems(item1,?item2)`,其中的參數`item1`和`item2`是要比較的項的`ID`。
5、樹控件使用一個圖像列表來管理圖像,類似于列表控件管理圖像的方法。你可以使用`SetImageList(imageList)`或`AssignImageList(imageList)`來分配一個圖像列表給樹控件。然后,當新的項目被添加到列表時,你可以將它們與圖像列表中的特定的索引聯系起來。
6、沒有特定的函數可以讓你得到一個父項的子項目列表。替而代之的是,你需要去遍歷子項目列表,這通過使用方法`GetFirstChild(item)`作為開始。
7、你可以使用方法`SelectItem(item,?select=True)`來管理樹的項目的選擇。在一個多選樹中,你可以使用`ToggleItemSelection(item)`來改變給定項的狀態。你可以使用`IsSelected(item)`來查詢一個項目的狀態。你也可以使用`Expand(item)`或`Collapse(item)`展開或折疊一個項,或使用`Toggle(item)`來切換它的狀態。
8、樣式`wx.TR_EDIT_LABELS`使得樹控件可為用戶所編輯。一個可編輯的列表中,用戶可以選擇一個項,并鍵入一個新的標簽。按下`esc`來取消編輯而不對項目作任何改變。你也可以通過 `wx.EVT_TREE_END_LABEL_EDIT?`事件來否決編輯。類`wx.TreeEvent`提供了允許訪問當前被處理的項目的顯示文本的屬性。
- 活學活用wxPython
- 前言
- 致謝
- 關于本書
- 第一部分
- 第一章 歡迎使用wxPython
- 第二章 給wxPython程序一個堅實的基礎
- 第三章 在事件驅動環境中開發
- 第四章 用PyCrust使得wxPython更易處理
- 第五章 繪制藍圖
- 第六章 使用wxPython基本構件
- 第二部分 基礎wxPython
- 第七章 使用基礎控件
- 第八章 將構件放入窗體中
- 第九章 通過對話框讓用戶選擇
- 第十章 創建和使用wxPython菜單
- 第十一章 使用sizer放置構件
- 第十二章 操作基本圖像
- 第三部分 高級wxPython
- 第十三章 建造列表控件并管理列表項
- 第十四章 網格控件
- 第十五章 樹形控件
- 第十六章 在應用程序中加入HTML
- 第十七章 wxPython的打印構架
- 第十八章 使用wxPython的其他功能