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