| 1 | """ |
| 2 | Hello, and welcome to this test of the wxTreeItemData class. |
| 3 | |
| 4 | The wxTreeItemData class can be used to associate a python object with |
| 5 | a wxTreeCtrl item. In this sample, its use is demonstrated via a tree |
| 6 | control that shows the contents of a python namespace according to the |
| 7 | standard dir() command. Every item in the tree has its label taken |
| 8 | from the dir() output, and 'behind it' a reference to the python |
| 9 | object is stored in a wxTreeItemData object. |
| 10 | |
| 11 | As you may have guessed by now, this sample automatically displays |
| 12 | '__doc__' strings if the selected python object happens to have |
| 13 | one. Please expand the pyTree object to learn more about the |
| 14 | implementation. |
| 15 | |
| 16 | Version 1.0, April 4 1999. |
| 17 | Harm van der Heijden (H.v.d.Heijden@phys.tue.nl) |
| 18 | |
| 19 | P.S. Check out the string module. It's imported in this sample not |
| 20 | because it's used, but because it's so beautifully documented... |
| 21 | """ |
| 22 | |
| 23 | from wxPython import wx |
| 24 | import string # Don't use it, but it's fun expanding :-) |
| 25 | |
| 26 | #---------------------------------------------------------------------- |
| 27 | |
| 28 | def _getindent(line): |
| 29 | """Returns the indentation level of the given line.""" |
| 30 | indent = 0 |
| 31 | for c in line: |
| 32 | if c == ' ': indent = indent + 1 |
| 33 | elif c == '\t': indent = indent + 8 |
| 34 | else: break |
| 35 | return indent |
| 36 | |
| 37 | def _sourcefinder(func): |
| 38 | """Given a func_code object, this function tries to find and return |
| 39 | the python source code of the function.""" |
| 40 | try: |
| 41 | f = open(func.co_filename,"r") |
| 42 | except: |
| 43 | return "(could not open file %s)" % (func.co_filename,) |
| 44 | |
| 45 | for i in range(func.co_firstlineno): |
| 46 | line = f.readline() |
| 47 | ind = _getindent(line) |
| 48 | msg = "" |
| 49 | while line: |
| 50 | msg = msg + line |
| 51 | line = f.readline() |
| 52 | # the following should be <= ind, but then we get |
| 53 | # confused by multiline docstrings. Using == works most of |
| 54 | # the time... but not always! |
| 55 | if _getindent(line) == ind: break |
| 56 | return msg |
| 57 | |
| 58 | #---------------------------------------------------------------------- |
| 59 | |
| 60 | class pyTree(wx.wxTreeCtrl): |
| 61 | """ |
| 62 | This wxTreeCtrl derivative displays a tree view of a Python namespace. |
| 63 | Anything from which the dir() command returns a non-empty list is a branch |
| 64 | in this tree. |
| 65 | """ |
| 66 | |
| 67 | def __init__(self, parent, id, root): |
| 68 | """ |
| 69 | Initialize function; because we insert branches into the tree |
| 70 | as needed, we use the ITEM_EXPANDING event handler. The |
| 71 | ITEM_COLLAPSED handler removes the stuff afterwards. The |
| 72 | SEL_CHANGED handler attempts to display interesting |
| 73 | information about the selected object. |
| 74 | """ |
| 75 | wx.wxTreeCtrl.__init__(self, parent, id) |
| 76 | self.root = self.AddRoot(str(root), -1, -1, wx.wxTreeItemData(root)) |
| 77 | if dir(root): |
| 78 | self.SetItemHasChildren(self.root, wx.TRUE) |
| 79 | wx.EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding) |
| 80 | wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed) |
| 81 | wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) |
| 82 | self.output = None |
| 83 | |
| 84 | |
| 85 | def SetOutput(self, output): |
| 86 | """ |
| 87 | Set output function (accepts single string). Used to display string |
| 88 | representation of the selected object by OnSelChanged. |
| 89 | """ |
| 90 | self.output = output |
| 91 | |
| 92 | |
| 93 | def OnItemExpanding(self,event): |
| 94 | """ |
| 95 | The real workhorse of this class. First we retrieve the object |
| 96 | (parent) belonging to the branch that is to be expanded. This |
| 97 | is done by calling GetPyData(parent), which is a short-cut for |
| 98 | GetPyItemData(parent).Get(). |
| 99 | |
| 100 | Then we get the dir() list of that object. For each item in |
| 101 | this list, a tree item is created with associated |
| 102 | wxTreeItemData referencing the child object. We get this |
| 103 | object using child = getattr(parent, item). |
| 104 | |
| 105 | Finally, we check wether the child returns a non-empty dir() |
| 106 | list. If so, it is labeled as 'having children', so that it |
| 107 | may be expanded. When it actually is expanded, this function |
| 108 | will again figure out what the offspring is. |
| 109 | """ |
| 110 | item = event.GetItem() |
| 111 | obj = self.GetPyData( item ) |
| 112 | lst = dir(obj) |
| 113 | for key in lst: |
| 114 | new_obj = getattr(obj,key) |
| 115 | new_item = self.AppendItem( item, key, -1, -1, |
| 116 | wx.wxTreeItemData(new_obj) ) |
| 117 | if dir(new_obj): |
| 118 | self.SetItemHasChildren(new_item, wx.TRUE) |
| 119 | |
| 120 | def OnItemCollapsed(self, event): |
| 121 | """ |
| 122 | We need to remove all children here, otherwise we'll see all |
| 123 | that old rubbish again after the next expansion. |
| 124 | """ |
| 125 | item = event.GetItem() |
| 126 | self.DeleteChildren(item) |
| 127 | |
| 128 | def OnSelChanged(self, event): |
| 129 | """ |
| 130 | If an output function is defined, we try to print some |
| 131 | informative, interesting and thought-provoking stuff to it. |
| 132 | If it has a __doc__ string, we print it. If it's a function or |
| 133 | unbound class method, we attempt to find the python source. |
| 134 | """ |
| 135 | if not self.output: |
| 136 | return |
| 137 | obj = self.GetPyData( event.GetItem() ) |
| 138 | msg = str(obj) |
| 139 | if hasattr(obj, '__doc__'): |
| 140 | msg = msg+"\n\nDocumentation string:\n\n%s" % ( getattr(obj, '__doc__'),) |
| 141 | # Is it a function? |
| 142 | func = None |
| 143 | if hasattr(obj, "func_code"): # normal function |
| 144 | func = getattr(obj, "func_code") |
| 145 | elif hasattr(obj, "im_func"): # unbound class method |
| 146 | func = getattr(getattr(obj, "im_func"), "func_code") |
| 147 | if func: # if we found one, let's try to print the source |
| 148 | msg = msg+"\n\nFunction source:\n\n" + _sourcefinder(func) |
| 149 | |
| 150 | apply(self.output, (msg,)) |
| 151 | |
| 152 | #---------------------------------------------------------------------- |
| 153 | |
| 154 | overview = __doc__ |
| 155 | |
| 156 | def runTest(frame, nb, log): |
| 157 | split = wx.wxSplitterWindow(nb, -1) |
| 158 | tree = pyTree(split, -1, __main__) |
| 159 | text = wx.wxTextCtrl(split, -1, "", wx.wxDefaultPosition, |
| 160 | wx.wxDefaultSize, wx.wxTE_MULTILINE) |
| 161 | split.SplitVertically(tree, text, 200) |
| 162 | tree.SetOutput(text.SetValue) |
| 163 | tree.SelectItem(tree.root) |
| 164 | text.SetBackgroundColour(wxNamedColour("LIGHT BLUE")) |
| 165 | tree.SetBackgroundColour(wxNamedColour("LIGHT BLUE")) |
| 166 | |
| 167 | return split |
| 168 | |
| 169 | |
| 170 | |
| 171 | #---------------------------------------------------------------------- |
| 172 | if __name__ == '__main__': |
| 173 | |
| 174 | class MyFrame(wx.wxFrame): |
| 175 | """Very standard Frame class. Nothing special here!""" |
| 176 | |
| 177 | def __init__(self): |
| 178 | """Make a splitter window; left a tree, right a textctrl. Wow.""" |
| 179 | import __main__ |
| 180 | wx.wxFrame.__init__(self, wx.NULL, -1, "PyTreeItemData Test", |
| 181 | wx.wxDefaultPosition, wx.wxSize(800,500)) |
| 182 | split = wx.wxSplitterWindow(self, -1) |
| 183 | tree = pyTree(split, -1, __main__) |
| 184 | text = wx.wxTextCtrl(split, -1, "", wx.wxDefaultPosition, |
| 185 | wx.wxDefaultSize, wx.wxTE_MULTILINE) |
| 186 | split.SplitVertically(tree, text, 200) |
| 187 | tree.SetOutput(text.SetValue) |
| 188 | tree.SelectItem(tree.root) |
| 189 | |
| 190 | class MyApp(wx.wxApp): |
| 191 | """This class is even less interesting than MyFrame.""" |
| 192 | |
| 193 | def OnInit(self): |
| 194 | """OnInit. Boring, boring, boring!""" |
| 195 | frame = MyFrame() |
| 196 | frame.Show(wx.TRUE) |
| 197 | self.SetTopWindow(frame) |
| 198 | return wx.TRUE |
| 199 | |
| 200 | app = MyApp(0) |
| 201 | app.MainLoop() |
| 202 | |
| 203 | |