Commit | Line | Data |
---|---|---|
8bf5d46e RD |
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 |