]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/PyCrust/filling.py
9a846b4def2776ed72f9242e81fb24c0d694a46c
[wxWidgets.git] / wxPython / wxPython / lib / PyCrust / filling.py
1 """PyCrust Filling is the gui tree control through which a user can
2 navigate the local namespace or any object."""
3
4 __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
5 __cvsid__ = "$Id$"
6 __revision__ = "$Revision$"[11:-2]
7
8 from wxPython import wx
9 from wxPython import stc
10 from version import VERSION
11 import dispatcher
12 import inspect
13 import introspect
14 import keyword
15 import sys
16 import types
17
18 try:
19 True
20 except NameError:
21 True = 1==1
22 False = 1==0
23
24 COMMONTYPES = [getattr(types, t) for t in dir(types) \
25 if not t.startswith('_') \
26 and t not in ('ClassType', 'InstanceType', 'ModuleType')]
27
28 DOCTYPES = ('BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
29 'FunctionType', 'GeneratorType', 'InstanceType',
30 'LambdaType', 'MethodType', 'ModuleType',
31 'UnboundMethodType', 'method-wrapper')
32
33 SIMPLETYPES = [getattr(types, t) for t in dir(types) \
34 if not t.startswith('_') and t not in DOCTYPES]
35
36 try:
37 COMMONTYPES.append(type(''.__repr__)) # Method-wrapper in version 2.2.x.
38 except AttributeError:
39 pass
40
41
42 class FillingTree(wx.wxTreeCtrl):
43 """PyCrust FillingTree based on wxTreeCtrl."""
44
45 name = 'PyCrust Filling Tree'
46 revision = __revision__
47
48 def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
49 size=wx.wxDefaultSize, style=wx.wxTR_DEFAULT_STYLE,
50 rootObject=None, rootLabel=None, rootIsNamespace=0,
51 static=False):
52 """Create a PyCrust FillingTree instance."""
53 wx.wxTreeCtrl.__init__(self, parent, id, pos, size, style)
54 self.rootIsNamespace = rootIsNamespace
55 import __main__
56 if rootObject is None:
57 rootObject = __main__.__dict__
58 self.rootIsNamespace = 1
59 if rootObject is __main__.__dict__ and rootLabel is None:
60 rootLabel = 'locals()'
61 if not rootLabel:
62 rootLabel = 'Ingredients'
63 rootData = wx.wxTreeItemData(rootObject)
64 self.item = self.root = self.AddRoot(rootLabel, -1, -1, rootData)
65 self.SetItemHasChildren(self.root, self.hasChildren(rootObject))
66 wx.EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding)
67 wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed)
68 wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
69 wx.EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
70 if not static:
71 dispatcher.connect(receiver=self.push, signal='Interpreter.push')
72
73 def push(self, command, more):
74 """Receiver for Interpreter.push signal."""
75 self.display()
76
77 def OnItemExpanding(self, event):
78 """Add children to the item."""
79 busy = wx.wxBusyCursor()
80 item = event.GetItem()
81 if self.IsExpanded(item):
82 return
83 self.addChildren(item)
84 # self.SelectItem(item)
85
86 def OnItemCollapsed(self, event):
87 """Remove all children from the item."""
88 busy = wx.wxBusyCursor()
89 item = event.GetItem()
90 # self.CollapseAndReset(item)
91 # self.DeleteChildren(item)
92 # self.SelectItem(item)
93
94 def OnSelChanged(self, event):
95 """Display information about the item."""
96 busy = wx.wxBusyCursor()
97 self.item = event.GetItem()
98 self.display()
99
100 def OnItemActivated(self, event):
101 """Launch a DirFrame."""
102 item = event.GetItem()
103 text = self.getFullName(item)
104 obj = self.GetPyData(item)
105 frame = FillingFrame(parent=self, size=(600, 100), rootObject=obj,
106 rootLabel=text, rootIsNamespace=False)
107 frame.Show()
108
109 def hasChildren(self, obj):
110 """Return true if object has children."""
111 if self.getChildren(obj):
112 return True
113 else:
114 return False
115
116 def getChildren(self, obj):
117 """Return dictionary with attributes or contents of object."""
118 busy = wx.wxBusyCursor()
119 otype = type(obj)
120 if otype is types.DictType \
121 or str(otype)[17:23] == 'BTrees' and hasattr(obj, 'keys'):
122 return obj
123 d = {}
124 if otype is types.ListType or otype is types.TupleType:
125 for n in range(len(obj)):
126 key = '[' + str(n) + ']'
127 d[key] = obj[n]
128 if otype not in COMMONTYPES:
129 for key in introspect.getAttributeNames(obj):
130 # Believe it or not, some attributes can disappear,
131 # such as the exc_traceback attribute of the sys
132 # module. So this is nested in a try block.
133 try:
134 d[key] = getattr(obj, key)
135 except:
136 pass
137 return d
138
139 def addChildren(self, item):
140 self.DeleteChildren(item)
141 obj = self.GetPyData(item)
142 children = self.getChildren(obj)
143 if not children:
144 return
145 keys = children.keys()
146 keys.sort(lambda x, y: cmp(x.lower(), y.lower()))
147 for key in keys:
148 itemtext = str(key)
149 # Show string dictionary items with single quotes, except
150 # for the first level of items, if they represent a
151 # namespace.
152 if type(obj) is types.DictType \
153 and type(key) is types.StringType \
154 and (item != self.root \
155 or (item == self.root and not self.rootIsNamespace)):
156 itemtext = repr(key)
157 child = children[key]
158 data = wx.wxTreeItemData(child)
159 branch = self.AppendItem(parent=item, text=itemtext, data=data)
160 self.SetItemHasChildren(branch, self.hasChildren(child))
161
162 def display(self):
163 item = self.item
164 if self.IsExpanded(item):
165 self.addChildren(item)
166 self.setText('')
167 obj = self.GetPyData(item)
168 if obj is None: # Windows bug fix.
169 return
170 self.SetItemHasChildren(item, self.hasChildren(obj))
171 otype = type(obj)
172 text = ''
173 text += self.getFullName(item)
174 text += '\n\nType: ' + str(otype)
175 try:
176 value = str(obj)
177 except:
178 value = ''
179 if otype is types.StringType or otype is types.UnicodeType:
180 value = repr(obj)
181 text += '\n\nValue: ' + value
182 if otype not in SIMPLETYPES:
183 try:
184 text += '\n\nDocstring:\n\n"""' + \
185 inspect.getdoc(obj).strip() + '"""'
186 except:
187 pass
188 if otype is types.InstanceType:
189 try:
190 text += '\n\nClass Definition:\n\n' + \
191 inspect.getsource(obj.__class__)
192 except:
193 pass
194 else:
195 try:
196 text += '\n\nSource Code:\n\n' + \
197 inspect.getsource(obj)
198 except:
199 pass
200 self.setText(text)
201
202 def getFullName(self, item, partial=''):
203 """Return a syntactically proper name for item."""
204 name = self.GetItemText(item)
205 parent = None
206 obj = None
207 if item != self.root:
208 parent = self.GetItemParent(item)
209 obj = self.GetPyData(parent)
210 # Apply dictionary syntax to dictionary items, except the root
211 # and first level children of a namepace.
212 if (type(obj) is types.DictType \
213 or str(type(obj))[17:23] == 'BTrees' \
214 and hasattr(obj, 'keys')) \
215 and ((item != self.root and parent != self.root) \
216 or (parent == self.root and not self.rootIsNamespace)):
217 name = '[' + name + ']'
218 # Apply dot syntax to multipart names.
219 if partial:
220 if partial[0] == '[':
221 name += partial
222 else:
223 name += '.' + partial
224 # Repeat for everything but the root item
225 # and first level children of a namespace.
226 if (item != self.root and parent != self.root) \
227 or (parent == self.root and not self.rootIsNamespace):
228 name = self.getFullName(parent, partial=name)
229 return name
230
231 def setText(self, text):
232 """Display information about the current selection."""
233
234 # This method will likely be replaced by the enclosing app to
235 # do something more interesting, like write to a text control.
236 print text
237
238 def setStatusText(self, text):
239 """Display status information."""
240
241 # This method will likely be replaced by the enclosing app to
242 # do something more interesting, like write to a status bar.
243 print text
244
245
246 if wx.wxPlatform == '__WXMSW__':
247 faces = { 'times' : 'Times New Roman',
248 'mono' : 'Courier New',
249 'helv' : 'Lucida Console',
250 'lucida' : 'Lucida Console',
251 'other' : 'Comic Sans MS',
252 'size' : 10,
253 'lnsize' : 9,
254 'backcol': '#FFFFFF',
255 }
256 else: # GTK
257 faces = { 'times' : 'Times',
258 'mono' : 'Courier',
259 'helv' : 'Helvetica',
260 'other' : 'new century schoolbook',
261 'size' : 12,
262 'lnsize' : 10,
263 'backcol': '#FFFFFF',
264 }
265
266
267 class FillingText(stc.wxStyledTextCtrl):
268 """PyCrust FillingText based on wxStyledTextCtrl."""
269
270 name = 'PyCrust Filling Text'
271 revision = __revision__
272
273 def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
274 size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN,
275 static=False):
276 """Create a PyCrust FillingText instance."""
277 stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
278 # Configure various defaults and user preferences.
279 self.config()
280 dispatcher.connect(receiver=self.fontsizer, signal='FontIncrease')
281 dispatcher.connect(receiver=self.fontsizer, signal='FontDecrease')
282 dispatcher.connect(receiver=self.fontsizer, signal='FontDefault')
283 if not static:
284 dispatcher.connect(receiver=self.push, signal='Interpreter.push')
285
286 def push(self, command, more):
287 """Receiver for Interpreter.push signal."""
288 self.Refresh()
289
290 def fontsizer(self, signal):
291 """Receiver for Font* signals."""
292 size = self.GetZoom()
293 if signal == 'FontIncrease':
294 size += 1
295 elif signal == 'FontDecrease':
296 size -= 1
297 elif signal == 'FontDefault':
298 size = 0
299 self.SetZoom(size)
300
301 def config(self):
302 """Configure shell based on user preferences."""
303 self.SetMarginWidth(1, 0)
304
305 self.SetLexer(stc.wxSTC_LEX_PYTHON)
306 self.SetKeyWords(0, ' '.join(keyword.kwlist))
307
308 self.setStyles(faces)
309 self.SetViewWhiteSpace(0)
310 self.SetTabWidth(4)
311 self.SetUseTabs(0)
312 self.SetReadOnly(1)
313 try:
314 self.SetWrapMode(1)
315 except AttributeError:
316 pass
317
318 def setStyles(self, faces):
319 """Configure font size, typeface and color for lexer."""
320
321 # Default style
322 self.StyleSetSpec(stc.wxSTC_STYLE_DEFAULT,
323 "face:%(mono)s,size:%(size)d" % faces)
324
325 self.StyleClearAll()
326
327 # Built in styles
328 self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER,
329 "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces)
330 self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR,
331 "face:%(mono)s" % faces)
332 self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT,
333 "fore:#0000FF,back:#FFFF88")
334 self.StyleSetSpec(stc.wxSTC_STYLE_BRACEBAD,
335 "fore:#FF0000,back:#FFFF88")
336
337 # Python styles
338 self.StyleSetSpec(stc.wxSTC_P_DEFAULT,
339 "face:%(mono)s" % faces)
340 self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE,
341 "fore:#007F00,face:%(mono)s" % faces)
342 self.StyleSetSpec(stc.wxSTC_P_NUMBER,
343 "")
344 self.StyleSetSpec(stc.wxSTC_P_STRING,
345 "fore:#7F007F,face:%(mono)s" % faces)
346 self.StyleSetSpec(stc.wxSTC_P_CHARACTER,
347 "fore:#7F007F,face:%(mono)s" % faces)
348 self.StyleSetSpec(stc.wxSTC_P_WORD,
349 "fore:#00007F,bold")
350 self.StyleSetSpec(stc.wxSTC_P_TRIPLE,
351 "fore:#7F0000")
352 self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE,
353 "fore:#000033,back:#FFFFE8")
354 self.StyleSetSpec(stc.wxSTC_P_CLASSNAME,
355 "fore:#0000FF,bold")
356 self.StyleSetSpec(stc.wxSTC_P_DEFNAME,
357 "fore:#007F7F,bold")
358 self.StyleSetSpec(stc.wxSTC_P_OPERATOR,
359 "")
360 self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER,
361 "")
362 self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK,
363 "fore:#7F7F7F")
364 self.StyleSetSpec(stc.wxSTC_P_STRINGEOL,
365 "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
366
367 def SetText(self, *args, **kwds):
368 self.SetReadOnly(0)
369 stc.wxStyledTextCtrl.SetText(self, *args, **kwds)
370 self.SetReadOnly(1)
371
372
373 class Filling(wx.wxSplitterWindow):
374 """PyCrust Filling based on wxSplitterWindow."""
375
376 name = 'PyCrust Filling'
377 revision = __revision__
378
379 def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
380 size=wx.wxDefaultSize, style=wx.wxSP_3D,
381 name='Filling Window', rootObject=None,
382 rootLabel=None, rootIsNamespace=0, static=False):
383 """Create a PyCrust Filling instance."""
384 wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name)
385 self.tree = FillingTree(parent=self, rootObject=rootObject,
386 rootLabel=rootLabel,
387 rootIsNamespace=rootIsNamespace,
388 static=static)
389 self.text = FillingText(parent=self, static=static)
390 self.SplitVertically(self.tree, self.text, 200)
391 self.SetMinimumPaneSize(1)
392 # Override the filling so that descriptions go to FillingText.
393 self.tree.setText = self.text.SetText
394 # Display the root item.
395 ## self.tree.SelectItem(self.tree.root)
396 self.tree.display()
397
398
399 class FillingFrame(wx.wxFrame):
400 """Frame containing the PyCrust filling, or namespace tree component."""
401
402 name = 'PyCrust Filling Frame'
403 revision = __revision__
404
405 def __init__(self, parent=None, id=-1, title='PyFilling',
406 pos=wx.wxDefaultPosition, size=wx.wxDefaultSize,
407 style=wx.wxDEFAULT_FRAME_STYLE, rootObject=None,
408 rootLabel=None, rootIsNamespace=0, static=False):
409 """Create a PyCrust FillingFrame instance."""
410 wx.wxFrame.__init__(self, parent, id, title, pos, size, style)
411 intro = 'PyFilling - The Tastiest Namespace Inspector'
412 self.CreateStatusBar()
413 self.SetStatusText(intro)
414 import images
415 self.SetIcon(images.getPyCrustIcon())
416 self.filling = Filling(parent=self, rootObject=rootObject,
417 rootLabel=rootLabel,
418 rootIsNamespace=rootIsNamespace,
419 static=static)
420 # Override so that status messages go to the status bar.
421 self.filling.tree.setStatusText = self.SetStatusText
422
423
424 class App(wx.wxApp):
425 """PyFilling standalone application."""
426
427 def OnInit(self):
428 wx.wxInitAllImageHandlers()
429 self.fillingFrame = FillingFrame()
430 self.fillingFrame.Show(True)
431 self.SetTopWindow(self.fillingFrame)
432 return True
433
434
435
436