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