1. 树形控件(tree control)

本章内容

  • 创建树形控件并添加项目
  • 使用样式来设计树形控件
  • 在程序中访问树形控件
  • 处理树形控件中的选择
  • 控制项目的可见性

树形控件是用于显示复杂数据的控件。这里,树形控件被设计用来通过分级层来显示数据,你可以看到每 块数据都有父子方面的东西。一个标准的例子就是文件树,其中的目录中有子目录或文件,从而形成了文 件的一个嵌套的层次。另一个例子是HTMLXML文档的文档对象模型(DOM)树。和列表与网格控件一样,树 形控件也提供了在项目显示方面的灵活性,并允许你就地编辑树形控件中的项目。在这一章中,我们将给 你展示如何编辑树形控件中的项目及如何响应用户事件。

1.1. 创建树形控件并添加项目

树形控件是类wx.TreeCtrl的实例。图15.1显示了一个树形控件的样例。

图15.1

例15.1是产生图15.1的代码。这个例子中的机制我们将在后面的部分讨论。注意其中的树形控件中的数据 是来自于一个名为data.py外部文件的。

例15.1 树形控件示例

   1 import wx
   2 import data
   3 class TestFrame(wx.Frame):
   4     def __init__(self):
   5         wx.Frame.__init__(self, None, title="simple tree", size=(400,500))
   6         # Create the tree
   7         self.tree = wx.TreeCtrl(self)
   8         # Add a root node
   9         root = self.tree.AddRoot("wx.Object")
  10         # Add nodes from our data set
  11         self.AddTreeNodes(root, data.tree)
  12         # Bind some interesting events
  13         self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
  14         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
  15         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
  16         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
  17         # Expand the first level
  18         self.tree.Expand(root)
  19     def AddTreeNodes(self, parentItem, items):
  20         """
  21         Recursively traverses the data structure, adding tree nodes to
  22         match it.
  23         """
  24         for item in items:
  25             if type(item) == str:
  26                 self.tree.AppendItem(parentItem, item)
  27             else:
  28                 newItem = self.tree.AppendItem(parentItem, item[0])
  29                 self.AddTreeNodes(newItem, item[1])
  30 
  31     def GetItemText(self, item):
  32         if item:
  33             return self.tree.GetItemText(item)
  34         else:
  35             return ""
  36 
  37     def OnItemExpanded(self, evt):
  38         print "OnItemExpanded: ", self.GetItemText(evt.GetItem())
  39 
  40     def OnItemCollapsed(self, evt):
  41         print "OnItemCollapsed:", self.GetItemText(evt.GetItem())
  42     def OnSelChanged(self, evt):
  43         print "OnSelChanged:   ", self.GetItemText(evt.GetItem())
  44     def OnActivated(self, evt):
  45         print "OnActivated:    ", self.GetItemText(evt.GetItem())
  46 app = wx.PySimpleApp(redirect=True)
  47 frame = TestFrame()
  48 frame.Show()
  49 app.MainLoop()

下面的wx.TreeCtrl的构造函数是一个典型的wxPython窗口部件构造函数:

   1 wx.TreeControl(parent, id=-1, pos=wx.DefaultPosition,
   2         size=wx.DefaultSize, style=wx.TR_HAS_BUTTONS,
   3         validator=wx.DefaultValidator, name="treeCtrl")

其中的参数意义与通常的wx.Window对象相同。该构造函数提供给你了一个没有元素的空的树。

另附data.py文件

   1 # Some sample data for the treectrl samples
   2 tree = [
   3     "wx.AcceleratorTable",
   4     "wx.BrushList",
   5     "wx.BusyInfo",
   6     "wx.Clipboard",
   7     "wx.Colour",
   8     "wx.ColourData",
   9     "wx.ColourDatabase",
  10     "wx.ContextHelp",
  11     ["wx.DC", [
  12           "wx.ClientDC",
  13           ["wx.MemoryDC", [
  14                 "wx.lib.colourchooser.canvas.BitmapBuffer",
  15                 ["wx.BufferedDC", [
  16                       "wx.BufferedPaintDC", ]]]],
  17           "wx.MetaFileDC",
  18           "wx.MirrorDC",
  19           "wx.PaintDC",
  20           "wx.PostScriptDC",
  21           "wx.PrinterDC",
  22           "wx.ScreenDC",
  23           "wx.WindowDC",]],
  24     "wx.DragImage",
  25     "wx.Effects",
  26     "wx.EncodingConverter",
  27     ["wx.Event", [
  28           "wx.ActivateEvent",
  29           "wx.CalculateLayoutEvent",
  30           "wx.CloseEvent",
  31           ["wx.CommandEvent", [
  32                 "wx.calendar.CalendarEvent",
  33                 "wx.ChildFocusEvent",
  34                 "wx.ContextMenuEvent",
  35                 "wx.gizmos.DynamicSashSplitEvent",
  36                 "wx.gizmos.DynamicSashUnifyEvent",
  37                 "wx.FindDialogEvent",
  38                 "wx.grid.GridEditorCreatedEvent",
  39                 "wx.HelpEvent",
  40                 ["wx.NotifyEvent",[
  41                       ["wx.BookCtrlEvent", [
  42                             "wx.ListbookEvent",
  43                             "wx.NotebookEvent ",]],
  44                       "wx.grid.GridEvent",
  45                       "wx.grid.GridRangeSelectEvent",
  46                       "wx.grid.GridSizeEvent",
  47                       "wx.ListEvent",
  48                       "wx.SpinEvent",
  49                       "wx.SplitterEvent",
  50                       "wx.TreeEvent",
  51                       "wx.wizard.WizardEvent ",]],
  52                 ["wx.PyCommandEvent", [
  53                       "wx.lib.colourselect.ColourSelectEvent",
  54                       "wx.lib.buttons.GenButtonEvent",
  55                       "wx.lib.gridmovers.GridColMoveEvent",
  56                       "wx.lib.gridmovers.GridRowMoveEvent",
  57                       "wx.lib.intctrl.IntUpdatedEvent",
  58                       "wx.lib.masked.combobox.MaskedComboBoxSelectEvent",
  59                       "wx.lib.masked.numctrl.NumberUpdatedEvent",
  60                       "wx.lib.masked.timectrl.TimeUpdatedEvent ",]],
  61                 "wx.SashEvent",
  62                 "wx.ScrollEvent",
  63                 "wx.stc.StyledTextEvent",
  64                 "wx.TextUrlEvent",
  65                 "wx.UpdateUIEvent",
  66                 "wx.WindowCreateEvent",
  67                 "wx.WindowDestroyEvent ",]],
  68           "wx.DisplayChangedEvent",
  69           "wx.DropFilesEvent",
  70           "wx.EraseEvent",
  71           "wx.FocusEvent",
  72           "wx.IconizeEvent",
  73           "wx.IdleEvent",
  74           "wx.InitDialogEvent",
  75           "wx.JoystickEvent",
  76           "wx.KeyEvent",
  77           "wx.MaximizeEvent",
  78           "wx.MenuEvent",
  79           "wx.MouseCaptureChangedEvent",
  80           "wx.MouseEvent",
  81           "wx.MoveEvent",
  82           "wx.NavigationKeyEvent",
  83           "wx.NcPaintEvent",
  84           "wx.PaintEvent",
  85           "wx.PaletteChangedEvent",
  86           "wx.ProcessEvent",
  87           ["wx.PyEvent", [
  88                 "wx.lib.throbber.UpdateThrobberEvent ",]],
  89           "wx.QueryLayoutInfoEvent",
  90           "wx.QueryNewPaletteEvent",
  91           "wx.ScrollWinEvent",
  92           "wx.SetCursorEvent",
  93           "wx.ShowEvent",
  94           "wx.SizeEvent",
  95           "wx.SysColourChangedEvent",
  96           "wx.TaskBarIconEvent",
  97           "wx.TimerEvent ",]],
  98     ["wx.EvtHandler", [
  99           "wx.lib.gridmovers.GridColMover",
 100           "wx.lib.gridmovers.GridRowMover",
 101           "wx.html.HtmlHelpController",
 102           "wx.Menu",
 103           "wx.Process",
 104           ["wx.PyApp", [
 105                 ["wx.App", [
 106                       "wx.py.PyAlaCarte.App",
 107                       "wx.py.PyAlaMode.App",
 108                       "wx.py.PyAlaModeTest.App",
 109                       "wx.py.PyCrust.App",
 110                       "wx.py.PyShell.App",
 111                       ["wx.py.filling.App", [
 112                             "wx.py.PyFilling.App ",]],
 113                       ["wx.PySimpleApp", [
 114                             "wx.lib.masked.maskededit.test",]],
 115                       "wx.PyWidgetTester ",]]]],
 116 
 117           "wx.TaskBarIcon",
 118           ["wx.Timer", [
 119                 "wx.PyTimer ",]],
 120           ["wx.Validator", [
 121                 ["wx.PyValidator",[
 122                       "wx.lib.intctrl.IntValidator",]]]],
 123           ["wx.Window", [
 124                 ["wx.lib.colourchooser.canvas.Canvas", [
 125                       "wx.lib.colourchooser.pycolourslider.PyColourSlider",
 126                       "wx.lib.colourchooser.pypalette.PyPalette",]],
 127                 "wx.lib.gridmovers.ColDragWindow",
 128                 ["wx.Control",[
 129                       ["wx.BookCtrl", [
 130                             "wx.Listbook",
 131                             ["wx.Notebook",[
 132                                   "wx.py.editor.EditorNotebook",
 133                                   "wx.py.editor.EditorShellNotebook",]] ]],
 134                       ["wx.Button", [
 135                             ["wx.BitmapButton",[
 136                                   "wx.lib.colourselect.ColourSelect",
 137                                   "wx.ContextHelpButton",
 138                                   "wx.lib.foldmenu.FoldOutMenu ",]] ]],
 139                       "wx.calendar.CalendarCtrl",
 140                       "wx.CheckBox",
 141                       ["wx.ComboBox",[
 142                             ["wx.lib.masked.combobox.BaseMaskedComboBox", [
 143                                   "wx.lib.masked.combobox.ComboBox",
 144                                   "wx.lib.masked.combobox.PreMaskedComboBox",]] ]],
 145                       ["wx.ControlWithItems", [
 146                             ["wx.Choice",[
 147                                   "wx.DirFilterListCtrl ",]],
 148                             "wx.ListBox",
 149                                   "wx.CheckListBox ",]],
 150                       "wx.Gauge",
 151                       "wx.GenericDirCtrl",
 152                       "wx.gizmos.LEDNumberCtrl",
 153                       ["wx.ListCtrl",[
 154                             "wx.ListView ",]],
 155                       ["wx.PyControl",[
 156                             "wx.lib.calendar.Calendar",
 157                             ["wx.lib.buttons.GenButton",[
 158                                   ["wx.lib.buttons.GenBitmapButton",[
 159                                         ["wx.lib.buttons.GenBitmapTextButton",[
 160                                               "wx.lib.buttons.GenBitmapTextToggleButton",]],
 161                                         "wx.lib.buttons.GenBitmapToggleButton ",]],
 162                                   "wx.lib.buttons.GenToggleButton ",]],
 163                             "wx.lib.statbmp.GenStaticBitmap",
 164                             "wx.lib.stattext.GenStaticText",
 165                             "wx.lib.popupctl.PopButton",
 166                             "wx.lib.popupctl.PopupControl",
 167                             "wx.lib.ticker.Ticker ",]],
 168                       "wx.RadioBox",
 169                       "wx.RadioButton",
 170                       "wx.ScrollBar",
 171                       "wx.Slider",
 172                       "wx.SpinButton",
 173                       "wx.SpinCtrl",
 174                       ["wx.StaticBitmap",[
 175                             "wx.lib.fancytext.StaticFancyText ",]],
 176                       "wx.StaticBox",
 177                       "wx.StaticLine",
 178                       "wx.StaticText",
 179                       ["wx.stc.StyledTextCtrl",[
 180                             ["wx.py.editwindow.EditWindow",[
 181                                   "wx.py.crust.Display",
 182                                   "wx.py.editor.EditWindow",
 183                                   "wx.py.filling.FillingText",
 184                                   "wx.py.shell.Shell",]],
 185                             "wx.lib.pyshell.PyShellWindow ",]],
 186                       ["wx.TextCtrl", [
 187                             ["wx.lib.masked.textctrl.BaseMaskedTextCtrl",[
 188                                   "wx.lib.masked.ipaddrctrl.IpAddrCtrl",
 189                                   "wx.lib.masked.numctrl.NumCtrl",
 190                                   "wx.lib.masked.textctrl.PreMaskedTextCtrl",
 191                                   "wx.lib.masked.textctrl.TextCtrl",
 192                                   "wx.lib.masked.timectrl.TimeCtrl ",]],
 193                             "wx.py.crust.Calltip",
 194                             "wx.lib.sheet.CTextCellEditor",
 195                             "wx.py.crust.DispatcherListing",
 196                             "wx.lib.intctrl.IntCtrl",
 197                             "wx.lib.rightalign.RightTextCtrl",
 198                             "wx.py.crust.SessionListing",]],
 199                       "wx.ToggleButton",
 200                       "wx.ToolBar",
 201                       ["wx.TreeCtrl",[
 202                             "wx.py.filling.FillingTree",
 203                             "wx.gizmos.RemotelyScrolledTreeCtrl ",]],
 204                       "wx.gizmos.TreeListCtrl ",]],
 205                 "wx.gizmos.DynamicSashWindow",
 206                 "wx.lib.multisash.EmptyChild",
 207                 "wx.glcanvas.GLCanvas",
 208                 "wx.lib.imagebrowser.ImageView",
 209                 "wx.MDIClientWindow",
 210                 "wx.MenuBar",
 211                 "wx.lib.multisash.MultiClient",
 212                 "wx.lib.multisash.MultiCloser",
 213                 "wx.lib.multisash.MultiCreator",
 214                 "wx.lib.multisash.MultiSash",
 215                 "wx.lib.multisash.MultiSizer",
 216                 "wx.lib.multisash.MultiSplit",
 217                 "wx.lib.multisash.MultiViewLeaf",
 218                 ["wx.Panel",[
 219                       "wx.gizmos.EditableListBox",
 220                       ["wx.lib.filebrowsebutton.FileBrowseButton",[
 221                             "wx.lib.filebrowsebutton.DirBrowseButton",
 222                             "wx.lib.filebrowsebutton.FileBrowseButtonWithHistory",]],
 223                       "wx.lib.floatcanvas.FloatCanvas.FloatCanvas",
 224                       "wx.lib.floatcanvas.NavCanvas.NavCanvas",
 225                       "wx.NotebookPage",
 226                       ["wx.PreviewControlBar",[
 227                             "wx.PyPreviewControlBar ",]],
 228                       "wx.lib.colourchooser.pycolourbox.PyColourBox",
 229                       "wx.lib.colourchooser.pycolourchooser.PyColourChooser",
 230                       ["wx.PyPanel",[
 231                             "wx.lib.throbber.Throbber",]],
 232                       "wx.lib.shell.PyShell",
 233                       "wx.lib.shell.PyShellInput",
 234                       "wx.lib.shell.PyShellOutput",
 235                       ["wx.ScrolledWindow",[
 236                             "wx.lib.editor.editor.Editor",
 237                             ["wx.grid.Grid",[
 238                                   "wx.lib.sheet.CSheet ",]],
 239                             ["wx.html.HtmlWindow",[
 240                                   "wx.lib.ClickableHtmlWindow.PyClickableHtmlWindow",]],
 241                             "wx.PreviewCanvas",
 242                             "wx.lib.printout.PrintTableDraw",
 243                             ["wx.PyScrolledWindow",[
 244                                   "wx.lib.scrolledpanel.ScrolledPanel",]],
 245                             "wx.lib.ogl.ShapeCanvas",
 246                             "wx.gizmos.SplitterScrolledWindow ",]],
 247                       ["wx.VScrolledWindow",[
 248                             ["wx.VListBox", [
 249                                   "wx.HtmlListBox ",]] ]],
 250                       ["wx.wizard.WizardPage", [
 251                             "wx.wizard.PyWizardPage",
 252                             "wx.wizard.WizardPageSimple ",]],
 253                 "wx.lib.plot.PlotCanvas",
 254                 "wx.lib.wxPlotCanvas.PlotCanvas",
 255                 ["wx.PopupWindow",[
 256                       "wx.lib.foldmenu.FoldOutWindow",
 257                       ["wx.PopupTransientWindow",[
 258                             "wx.TipWindow ",]] ]],
 259                 ["wx.PyWindow", [
 260                       "wx.lib.analogclock.AnalogClockWindow",]],
 261                 "wx.lib.gridmovers.RowDragWindow",
 262                 ["wx.SashWindow",[
 263                       "wx.SashLayoutWindow ",]],
 264                 "wx.SplashScreenWindow",
 265                 ["wx.SplitterWindow",[
 266                       "wx.py.crust.Crust",
 267                       "wx.py.filling.Filling",
 268                       "wx.gizmos.ThinSplitterWindow ",]],
 269                 "wx.StatusBar",
 270                 ["wx.TopLevelWindow",[
 271                       ["wx.Dialog",[
 272                             "wx.lib.calendar.CalenDlg",
 273                             "wx.ColourDialog",
 274                             "wx.DirDialog",
 275                             "wx.FileDialog",
 276                             "wx.FindReplaceDialog",
 277                             "wx.FontDialog",
 278                             "wx.lib.imagebrowser.ImageDialog",
 279                             "wx.MessageDialog",
 280                             "wx.MultiChoiceDialog",
 281                             "wx.lib.dialogs.MultipleChoiceDialog",
 282                             "wx.PageSetupDialog",
 283                             "wx.lib.popupctl.PopupDialog",
 284                             "wx.PrintDialog",
 285                             "wx.lib.dialogs.ScrolledMessageDialog",
 286                             "wx.SingleChoiceDialog",
 287                             "wx.TextEntryDialog",
 288                             "wx.wizard.Wizard ",]],
 289                       ["wx.Frame", [
 290                             "wx.lib.analogclockopts.ACCustomizationFrame",
 291                             "wx.py.filling.FillingFrame",
 292                             ["wx.py.frame.Frame",[
 293                                   "wx.py.crust.CrustFrame",
 294                                   ["wx.py.editor.EditorFrame",[
 295                                         "wx.py.editor.EditorNotebookFrame",]],
 296                                   "wx.py.shell.ShellFrame",]],
 297                             "wx.html.HtmlHelpFrame",
 298                             "wx.MDIChildFrame",
 299                             "wx.MDIParentFrame",
 300                             "wx.MiniFrame",
 301                             ["wx.PreviewFrame",[
 302                                   "wx.PyPreviewFrame ",]],
 303                             "wx.ProgressDialog",
 304                             "wx.SplashScreen",
 305                             "wx.lib.splashscreen.SplashScreen",
 306                             "wx.lib.masked.maskededit.test2",
 307                             "wx.lib.plot.TestFrame ",]] ]],
 308                 "wx.gizmos.TreeCompanionWindow ",]] ]] ]],
 309     "wx.FileHistory",
 310     "wx.FileSystem",
 311     "wx.FindReplaceData",
 312     "wx.FontData",
 313     "wx.FontList",
 314     "wx.FSFile",
 315     ["wx.GDIObject",[
 316           "wx.Bitmap",
 317           "wx.Brush",
 318           "wx.Cursor",
 319           "wx.Font",
 320           "wx.Icon",
 321           "wx.Palette",
 322           "wx.Pen",
 323           "wx.Region ",]],
 324     "wx.glcanvas.GLContext",
 325     ["wx.grid.GridTableBase", [
 326           "wx.grid.GridStringTable",
 327           "wx.grid.PyGridTableBase ",]],
 328     ["wx.html.HtmlCell", [
 329           "wx.html.HtmlColourCell",
 330           "wx.html.HtmlContainerCell",
 331           "wx.html.HtmlFontCell",
 332           "wx.html.HtmlWidgetCell",
 333           "wx.html.HtmlWordCell ",]],
 334     "wx.html.HtmlDCRenderer",
 335     "wx.html.HtmlEasyPrinting",
 336     "wx.html.HtmlFilter",
 337     "wx.html.HtmlLinkInfo",
 338     ["wx.html.HtmlParser", [
 339           "wx.html.HtmlWinParser ",]],
 340     "wx.html.HtmlTag",
 341     ["wx.html.HtmlTagHandler", [
 342           ["wx.html.HtmlWinTagHandler", [
 343                 "wx.lib.wxpTag.wxpTagHandler ",]] ]],
 344     "wx.Image",
 345     ["wx.ImageHandler", [
 346           ["wx.BMPHandler", [
 347                 ["wx.ICOHandler", [
 348                       ["wx.CURHandler", [
 349                             "wx.ANIHandler ",]] ]] ]],
 350           "wx.GIFHandler",
 351           "wx.JPEGHandler",
 352           "wx.PCXHandler",
 353           "wx.PNGHandler",
 354           "wx.PNMHandler",
 355           "wx.TIFFHandler",
 356           "wx.XPMHandler ",]],
 357     "wx.ImageList",
 358     "wx.IndividualLayoutConstraint",
 359     "wx.LayoutAlgorithm",
 360     ["wx.LayoutConstraints", [
 361           "wx.lib.anchors.LayoutAnchors",
 362           "wx.lib.layoutf.Layoutf",]],
 363     "wx.ListItem",
 364     "wx.Mask",
 365     "wx.MenuItem",
 366     "wx.MetaFile",
 367     "wx.PageSetupDialogData",
 368     "wx.PenList",
 369     "wx.PrintData",
 370     "wx.PrintDialogData",
 371     "wx.Printer",
 372     ["wx.Printout", [
 373           "wx.html.HtmlPrintout",
 374           "wx.lib.plot.PlotPrintout",
 375           "wx.lib.printout.SetPrintout ",]],
 376     ["wx.PrintPreview", [
 377           "wx.PyPrintPreview ",]],
 378     "wx.RegionIterator",
 379     ["wx.Sizer", [
 380           "wx.BookCtrlSizer",
 381           ["wx.BoxSizer", [
 382                 "wx.StaticBoxSizer", ]],
 383           ["wx.GridSizer", [
 384                 ["wx.FlexGridSizer", [
 385                       "wx.GridBagSizer",]] ]],
 386           "wx.NotebookSizer",
 387           "wx.PySizer",]],
 388     ["wx.SizerItem", [
 389           "wx.GBSizerItem",]],
 390     "wx.SystemOptions",
 391     "wx.ToolBarToolBase",
 392     "wx.ToolTip",
 393     "wx.gizmos.TreeListColumnInfo",
 394     "wx.xrc.XmlDocument",
 395     "wx.xrc.XmlResource",
 396     "wx.xrc.XmlResourceHandler ",
 397 ]

1.1.1. 如何添加一个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不映射到任何简单的类型——它的值没有任何的关联性,因为你只是把它用于相等测试。

1.1.2. 如何将更多的项目添加到树中?

一旦你有了根元素,你就可以开始向树中添加元素了。用的最多的方法是AppendItem(parent, text, image=-1, selImage=-1, data=None)。参数parent是已有的树项目的wx.TreeItemId,它作为新项目的父亲。参数text是显示新项目的文本字符串。参数imageselImage的意义与方法AddRoot()中的相同。该方法将新的项目放置到其父项目的孩子列表的末尾。这个方法返回新创建的项目的wx.TreeItemId。如果你想给新的项目添加子项目的话,你需要拥有这个ID。一个示例如下:

   1 rootId = tree.AddRoot("The Root")
   2 childId = tree.AppendItem(rootId, "A Child")
   3 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

1.1.3. 如何管理项目?

要去掉树中的一个项目,可以使用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,则返回所有的子项目而不关嵌套有多深。

1.2. 树控件的显示样式

树控件的显示样式分为四类。第一类定义了树中显示在父项目的文本旁的按钮(用以展开或折叠子项目)。它们显示在表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

1.3. 对树形控件的元素排序

对树形控件的元素排序的基本机制是方法SortChildren(item)。其中参数itemwx.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()方法:

   1 def OnCompareItems(self, item1, item2);
   2     data1 = self.GetItemPyData(item1)
   3     data2 = self.GetItemPyData(item2)
   4     return cmp(data1, data2)

1.4. 控制与每项相关的图像

用于树形控件的图像是由一个图像列表来维护的,这非常类似于列表控件中的图像维护。有关创建图像列表的细节,参见13章。一旦你创建了图像列表,你就可以使用SetImageList(imageList)AssignImageList(imageList)方法把它分配给树控件。前者使得图像列表可以被其它控件共享,后者的图像列表所有权属于树控件。之后,你可能使用方法GetImageList()来得到该图像列表。图15.2显示了一个带有一些图像的树。

图15.2

例15.2是产生图15.2的代码。它使用了ArtProvider对象来提供图像。

例15.2 一个带有图标的树控件

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 import data
   4 class TestFrame(wx.Frame):
   5     def __init__(self):
   6         wx.Frame.__init__(self, None,
   7                 title="simple tree with icons", size=(400,500))
   8         # 创建一个图像列表
   9         il = wx.ImageList(16,16)
  10         # 添加图像到列表
  11         self.fldridx = il.Add(
  12             wx.ArtProvider.GetBitmap(wx.ART_FOLDER,
  13                     wx.ART_OTHER, (16,16)))
  14         self.fldropenidx = il.Add(
  15             wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,
  16                     wx.ART_OTHER, (16,16)))
  17         self.fileidx = il.Add(
  18             wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE,
  19                     wx.ART_OTHER, (16,16)))
  20 
  21         # 创建树
  22         self.tree = wx.TreeCtrl(self)
  23         # 给树分配图像列表
  24         self.tree.AssignImageList(il)
  25         root = self.tree.AddRoot("wx.Object")
  26         self.tree.SetItemImage(root, self.fldridx,
  27                                wx.TreeItemIcon_Normal)# 设置根的图像
  28         self.tree.SetItemImage(root, self.fldropenidx,
  29                                wx.TreeItemIcon_Expanded)
  30 
  31         self.AddTreeNodes(root, data.tree)
  32         self.tree.Expand(root)
  33 
  34     def AddTreeNodes(self, parentItem, items):
  35         for item in items:
  36             if type(item) == str:
  37                 newItem = self.tree.AppendItem(parentItem, item)
  38                 self.tree.SetItemImage(newItem, self.fileidx,
  39                                        wx.TreeItemIcon_Normal)# 设置数据图像
  40             else:
  41                 newItem = self.tree.AppendItem(parentItem, item[0])
  42                 self.tree.SetItemImage(newItem, self.fldridx,
  43                                        wx.TreeItemIcon_Normal)# 设置结点的图像
  44                 self.tree.SetItemImage(newItem, self.fldropenidx,
  45                                        wx.TreeItemIcon_Expanded)
  46 
  47                 self.AddTreeNodes(newItem, item[1])
  48 
  49     def GetItemText(self, item):
  50         if item:
  51             return self.tree.GetItemText(item)
  52         else:
  53             return ""
  54 
  55 app = wx.PySimpleApp(redirect=True)
  56 frame = TestFrame()
  57 frame.Show()
  58 app.MainLoop()

如你所见,当你添加项目到列表中时,对于未选和选中状态,有两个不同的图像可用于分配给项目。和列表控件一样,你可以指定图像在图像列表中的索引。如果你想在项目被创建后得到所分配的图像,你可以使用方法GetItemImage(item, which=wx.TreeItemIcon_Normal)。其中参数item是项目的wx.TreeItemId。参数which控制你将得到了是哪个图像,默认值wx.TreeItemIcon_Normal,将得到该项目的未选状态的图像的索引。which的另一个值wx.TreeItemIcon_Selected的使用,将返回选中状态的图像,wx.TreeItemIcon_ExpandedwxTreeItemIcon_SelectedExpanded返回当该树项目被展开时所使用的图像。注意,后者的两个图像不能使用添加方法来被设置——如果你想设置的话,你必须使用方法SetItemImage(item,  image,  which=wx.TreeItemIcon_Normal)来实现。其中参数itemwx.TreeItemId的实例,参数image是新图像的整数索引,参数whichget*方法。

1.5. 使用编程的方式访问树。

在15.1节中,我们谈到没有直接得到一个给定项目的子项目的Python列表的方法,至于一个特定子项目的索引就更不用说了。要实现这个,我们需要使用本节所说的遍历方法来访问树的结点。

要开始遍历树,我们首先要使用GetRootItem()来得到根元素。该方法返回树的根元素的wx.TreeItemId。之后,你就可以使用诸如GetItemText()GetItemPyData()之类的方法来获取关于根元素的更多的信息。

一旦你得到了一个项目,你就可以通过迭代器来遍历子项目的列表。你可以使用方法GetFirstChild(item)来得到子树中的第一个孩子,该方法返回一个二元元组(child, cookie)。参数item是第一个孩子的wx.TreeItemId。除了告诉你每一个孩子是什么外,该方法还初始化一个迭代对象,该对象使你能够遍历该树。cookie值只是一个标志,它使得树控件能够同时保持对多个迭代器的跟踪,而它们之间不会彼此干扰。

一旦你由GetFirstChild()得到了cookie,你就可以通过反复调用GetNextChild(item, cookie)来得到其余的孩子。这里的item父项的IDcookie是由GetFirstChild()或前一个GetNextChild()调用返回的。GetNextChild()方法也返回一个二元元组(child, cookie)。如果此时没有下一个项目了,那么你就到达了孩子列表的末尾,系统将返回一个无效的孩子ID。你可以通过使用wx.TreeItemId.IsOk()__nonzero__方法测试这种情况。下面的函数返回给定项目的所有孩子的文本的一个列表。

   1 def getChildren(tree, parent):
   2     result = []
   3     item, cookie = tree.GetFirstChild(parent)
   4     while item:
   5         result.append(tree.GetItemText(item))
   6     item, cookie = tree.getNextChild(parent, cookie)
   7     return result

这个函数得到给定父项目的第一个孩子,然后将第一个孩子的文本添加到列表中,然后通过循环来得到每个子项目的文本并添加到列表中,直到得到一个无效的项,这时就返回result

要得到父项目的最后一个孩子,你可以使用方法GetLastChild(item),它返回列表中的最后的项目的wx.TreeItemId。由于这个方法不用于驱动迭代器来遍历整个孩子列表,所以它不需要cookie机制。如果你有这个最后的子项且你想得到它的父项,可以使用方法GetItemParent(item)来返回给定项的父项的ID

你可以使用方法GetNextSibling(item)GetPrevSibling(item)来在同级的项目间前后访问。这些方法均返回相关项的wx.TreeItemId。由这些方法同样不用于驱动迭代器,所以它们都不需要一个cookie。当你已经到达列表的两头时,将没有下一项或前一项,那么这些方法将返回一个无效的项(例如:item.IsOk() == False)。

要确定一个项是否有孩子,使用方法ItemHasChildren(item),该方法返回布尔值TrueFalse。你可以使用方法SetItemHasChildren(item, hasChildren=True)来将一个项设置为有子项,如果这样,即使该项没有实际的子项,它也将显示得与有子项的一样。也就是说该项旁边会有一个扩展或折叠的按钮,以便展开或折叠。这通常被用于实现一个虚的树控件。这个技术将在15.7节中演示。

1.6. 管理树中的选择

树形控件允许你通过程序的方式管理树中的被选项。基本的方法是SelectItem(item,select=True) 。在单选树控件中,该方法将选择指定项itemwx.TreeItemId),同时自动撤消对先前选项的选择。如果参数select的取值是False,那么该方法将取消对参数item代表的项的选择。在一个可多选的树控件中,SelectItem()方法只改变item所代表的项的状态,而不改变树中其它项的选择状态。在可多选的树中,你也可以使用方法ToggleItemSelection(item),它只切换参数item所代表项的选择状态。

对于取消选择还有三个快捷的方法。方法Unselect()取消单选模式树中的当前被选项的选择。在多选模式树中,使用UnselectAll()来取消所有的选择。如果你只想取消在一个多选树中的一个项的被选状态,可以使用方法UnselectItem(item)

你也可以使用方法IsSelected(item)来查询一个项目的选择状态,该方法返回布尔值TrueFalse。对于单选树,你可能使用方法GetSelection()来得到当前被选项目的wx.TreeItemId。对于多选树,可以使用方法GetSelections()来得到所有被选项的wx.TreeItemId的一个Python列表。

当树控件中发生选择变化的时候,有两个事件将被触发并可被捕获。第一个事件是wx.EVT_TREE_SEL_CHANGING,它在被选项实际改变之前发生。如果你要处理这个事件,你可以使用事件的Veto()方法来阻止选择的改变。在选择已经改变之后,事件wx.EVT_TREE_SEL_CHANGED被触发。这两个事件的类是wx.TreeEvent,这将在15.8节中作更完整的讨论。

1.7. 控制项目的可见性

在树控件中有两种机制可以让你用编程的方式控制某项目的可见性。你可以使用方法Collapse(item)Expand(item)指定给定的树项目是展开的或折叠的。这些方法改变树控件的显示,并且如果对一个没有子项的项目调用该方法将不起作用。这儿还有一个方便的函数:CollapseAndReset(item),该方法折叠指定的项,并删除指定项的所有孩子。另外,方法Toggle(item)用于切换项目的展开和折叠状态。你可以使用方法IsExpanded(item)来查询项目的当前展开状态。

展开或折叠一个树项目触发两事件。在展开或折叠前,事件wx.EVT_TREE_ITEM_COLLAPSING wx.EVT_TREE_ITEM_EXPANDING被触发。在你的处理方法中,你可以使用事件的Veto()方法来阻止展开或折叠。在展开或折叠发生后,事件EVT_TREE_ITEM_COLLAPSEDwx.EVT_TREE_ITEM_EXPANDED被触发。这四个事件都是类wx.TreeEvent的事件类型。

虚树

展开和折叠项目的一个令人感兴趣的的用法是创建一个虚树,新项目仅当父项展开时添加。例15.3显示这样一个样列。

例15.3 展开时动态添加新的项目

   1 #-*- encoding:UTF-8 -*-
   2 import wx
   3 import data
   4 class TestFrame(wx.Frame):
   5     def __init__(self):
   6         wx.Frame.__init__(self, None, title="virtual tree with icons", size=(400,500))
   7         il = wx.ImageList(16,16)
   8         self.fldridx = il.Add(
   9             wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16,16)))
  10         self.fldropenidx = il.Add(
  11             wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,   wx.ART_OTHER, (16,16)))
  12         self.fileidx = il.Add(
  13             wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16,16)))
  14         self.tree = wx.TreeCtrl(self)
  15         self.tree.AssignImageList(il)
  16         root = self.tree.AddRoot("wx.Object")
  17         self.tree.SetItemImage(root, self.fldridx,
  18                                wx.TreeItemIcon_Normal)
  19         self.tree.SetItemImage(root, self.fldropenidx,
  20                                wx.TreeItemIcon_Expanded)
  21 
  22         # Instead of adding nodes for the whole tree, just attach some
  23         # data to the root node so that it can find and add its child
  24         # nodes when it is expanded, and mark it as having children so
  25         # it will be expandable.
  26         self.tree.SetItemPyData(root, data.tree)#创建一个根
  27         self.tree.SetItemHasChildren(root, True)
  28 
  29         # Bind some interesting events
  30 # 绑定事件
  31         self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
  32         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
  33         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
  34         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
  35         self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding, self.tree)
  36         self.tree.Expand(root)
  37 
  38     def AddTreeNodes(self, parentItem):#给父项目添加结节
  39         """
  40         Add nodes for just the children of the parentItem
  41         """
  42         items = self.tree.GetItemPyData(parentItem)
  43         for item in items:
  44             if type(item) == str:
  45                 # a leaf node
  46                 newItem = self.tree.AppendItem(parentItem, item)
  47                 self.tree.SetItemImage(newItem, self.fileidx,
  48                                        wx.TreeItemIcon_Normal)
  49             else:
  50                 # this item has children
  51                 newItem = self.tree.AppendItem(parentItem, item[0])
  52                 self.tree.SetItemImage(newItem, self.fldridx,
  53                                        wx.TreeItemIcon_Normal)
  54                 self.tree.SetItemImage(newItem, self.fldropenidx,
  55                                        wx.TreeItemIcon_Expanded)
  56                 self.tree.SetItemPyData(newItem, item[1])
  57                 self.tree.SetItemHasChildren(newItem, True)
  58 
  59 
  60     def GetItemText(self, item):
  61         if item:
  62             return self.tree.GetItemText(item)
  63         else:
  64             return ""
  65 
  66     def OnItemExpanded(self, evt):
  67         print "OnItemExpanded: ", self.GetItemText(evt.GetItem())
  68     def OnItemExpanding(self, evt):#当展开时创建结点
  69         # When the item is about to be expanded add the first level of child nodes
  70         print "OnItemExpanding:", self.GetItemText(evt.GetItem())
  71         self.AddTreeNodes(evt.GetItem())
  72 
  73     def OnItemCollapsed(self, evt):
  74         print "OnItemCollapsed:", self.GetItemText(evt.GetItem())
  75         # And remove them when collapsed as we don't need them any longer
  76         self.tree.DeleteChildren(evt.GetItem())#折叠时删除结点
  77     def OnSelChanged(self, evt):
  78         print "OnSelChanged:   ", self.GetItemText(evt.GetItem())
  79     def OnActivated(self, evt):
  80         print "OnActivated:    ", self.GetItemText(evt.GetItem())
  81 app = wx.PySimpleApp(redirect=True)
  82 frame = TestFrame()
  83 frame.Show()
  84 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.TreeItemIdNone值。如果指定位置没有项目,那么一个无效的项目将返回。flags部分是一个位掩码,它给出了相关的信息。表15.5包含了flags的一个完整列表。

还有两个方法,它们让你可以处理屏幕上项目的实际的边界。方法GetBoundingRect(item, textOnly=False)返回一个wx.Rect实例,该实例对应于屏幕上文本项的矩形边界区域。其中参数item是项目的wx.TreeItemId。如果参数textOnlyTrue,那么该矩形仅包括项目的显示文本所覆盖的区域。如果为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

该位置在树的客户区的右面,不是任何项目的一部分。

1.8. 使树控件可编辑

树控件可以被设置为允许用户编辑树项目的显示文本。这通过在创建树控件时使用样式标记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类型的事件被触发。这个事件也可以被否决,这样的话,编辑就被取消了,项目不会被改变。

1.9. 响应树控件的其它的用户事件

在这一节,我们将讨论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_EDITwx.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

在树控件获得焦点的情况下,当一个键被按下时触发该事件。

上面这些就你需要了解的有关树控件的属性。下面,我们将通过一个有用的另一种形式的树控件来结束本章。

1.10. 使用树列表控件

除了wx.TreeCtrlwxPython也提供了wx.gizmos.TreeListCtrl,它是树控件和报告模式的列表控件的组合。除了本章中所讨论的wx.TreeCtrl的特性外,TreeListCtrl能够显示与每行相关的数据的附加列。图15.3显示了树列表控件的样子。

图15.3

例15.4是产生上图的代码

例15.4 使用树列表控件

   1 import wx
   2 import wx.gizmos
   3 import data
   4 class TestFrame(wx.Frame):
   5     def __init__(self):
   6         wx.Frame.__init__(self, None, title="TreeListCtrl", size=(400,500))
   7         # Create an image list
   8         il = wx.ImageList(16,16)
   9         # Get some standard images from the art provider and add them
  10         # to the image list
  11         self.fldridx = il.Add(
  12             wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16,16)))
  13         self.fldropenidx = il.Add(
  14             wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,   wx.ART_OTHER, (16,16)))
  15         self.fileidx = il.Add(
  16             wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16,16)))
  17 
  18         # Create the tree
  19 # 创建树列表控件
  20         self.tree = wx.gizmos.TreeListCtrl(self, style =
  21                                            wx.TR_DEFAULT_STYLE
  22                                            | wx.TR_FULL_ROW_HIGHLIGHT)
  23         # Give it the image list
  24         self.tree.AssignImageList(il)
  25         # create some columns
  26 #创建一些列
  27         self.tree.AddColumn("Class Name")
  28         self.tree.AddColumn("Description")
  29         self.tree.SetMainColumn(0) # the one with the tree in it...
  30         self.tree.SetColumnWidth(0, 200)
  31         self.tree.SetColumnWidth(1, 200)
  32         # Add a root node and assign it some images
  33         root = self.tree.AddRoot("wx.Object")
  34         self.tree.SetItemText(root, "A description of wx.Object", 1)#给列添加文本
  35         self.tree.SetItemImage(root, self.fldridx,
  36                                wx.TreeItemIcon_Normal)
  37         self.tree.SetItemImage(root, self.fldropenidx,
  38                                wx.TreeItemIcon_Expanded)
  39 
  40         # Add nodes from our data set
  41         self.AddTreeNodes(root, data.tree)
  42         # Bind some interesting events
  43         self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
  44         self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
  45         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
  46         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
  47         # Expand the first level
  48         self.tree.Expand(root)
  49 
  50     def AddTreeNodes(self, parentItem, items):
  51         """
  52         Recursively traverses the data structure, adding tree nodes to
  53         match it.
  54         """
  55         for item in items:
  56             if type(item) == str:
  57                 newItem = self.tree.AppendItem(parentItem, item)
  58                 self.tree.SetItemText(newItem, "A description of %s" % item, 1)#给列添加文本
  59                 self.tree.SetItemImage(newItem, self.fileidx,
  60                                        wx.TreeItemIcon_Normal)
  61             else:
  62                 newItem = self.tree.AppendItem(parentItem, item[0])
  63                 self.tree.SetItemText(newItem, "A description of %s" % item[0], 1)
  64                 self.tree.SetItemImage(newItem, self.fldridx,
  65                                        wx.TreeItemIcon_Normal)
  66                 self.tree.SetItemImage(newItem, self.fldropenidx,
  67                                        wx.TreeItemIcon_Expanded)
  68 
  69                 self.AddTreeNodes(newItem, item[1])
  70 
  71     def GetItemText(self, item):
  72         if item:
  73             return self.tree.GetItemText(item)
  74         else:
  75             return ""
  76 
  77     def OnItemExpanded(self, evt):
  78         print "OnItemExpanded: ", self.GetItemText(evt.GetItem())
  79 
  80     def OnItemCollapsed(self, evt):
  81         print "OnItemCollapsed:", self.GetItemText(evt.GetItem())
  82     def OnSelChanged(self, evt):
  83         print "OnSelChanged:   ", self.GetItemText(evt.GetItem())
  84     def OnActivated(self, evt):
  85         print "OnActivated:    ", self.GetItemText(evt.GetItem())
  86 app = wx.PySimpleApp(redirect=True)
  87 frame = TestFrame()
  88 frame.Show()
  89 app.MainLoop()

树列表控件的很多方法和列表控件的相似,所以我们就再也没有列出并说明了。

1.11. 本章小结

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),其中的参数item1item2是要比较的项的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提供了允许访问当前被处理的项目的显示文本的属性。