1 """A simple Mac-only browse utility to peek at the inner data structures of Python."""
2 # Minor modifications by Jack to facilitate incorporation in twit.
5 # Written by Just van Rossum <just@knoware.nl>, please send comments/improvements.
6 # Loosely based on Jack Jansens's PICTbrowse.py, but depends on his fabulous FrameWork.py
7 # XXX Some parts are *very* poorly solved. Will fix. Guido has to check if all the
8 # XXX "python-peeking" is done correctly. I kindof reverse-engineered it ;-)
10 # disclaimer: although I happen to be the brother of Python's father, programming is
11 # not what I've been trained to do. So don't be surprised if you find anything that's not
12 # as nice as it could be...
17 # MAIN_TEXT item should not contain (type); should be below or something.
18 # MAIN_TEXT item should check if a string is binary or not: convert to '/000' style
19 # or convert newlines.
31 from QuickDraw
import *
36 # The initial object to start browsing with. Can be anything, but 'sys' makes kindof sense.
39 # Resource definitions
41 NUM_LISTS
= 4 # the number of lists used. could be changed, but the dlg item numbers should be consistent
42 MAIN_TITLE
= 3 # this is only the first text item, the other three ID's should be 5, 7 and 9
43 MAIN_LIST
= 4 # this is only the first list, the other three ID's should be 6, 8 and 10
52 # this bit ensures that this module will also work as an applet if the resources are
53 # in the resource fork of the applet
54 # stolen from Jack, so it should work(?!;-)
56 # if this doesn't raise an error, we are an applet containing the necessary resources
57 # so we don't have to bother opening the resource file
58 dummy
= Res
.GetResource('DLOG', ID_MAIN
)
61 ourparentdir
= os
.path
.split(openresfile
.func_code
.co_filename
)[0]
62 os
.chdir(ourparentdir
)
64 Res
.OpenResFile("mactwit_browse.rsrc")
65 except Res
.Error
, arg
:
66 EasyDialogs
.Message("Cannot open mactwit_browse.rsrc: "+arg
[1])
74 # this is all there is to it to make an application.
75 class PythonBrowse(FrameWork
.Application
):
77 FrameWork
.Application
.__init
__(self
)
78 VarBrowser(self
).open(start_object
)
81 def do_about(self
, id, item
, window
, event
):
82 EasyDialogs
.Message(self
.__class
__.__name
__ + " version " + version
+ "\rby Just van Rossum")
84 def quit(self
, *args
):
88 def __init__(self
, wid
, rect
, itemnum
):
89 # wid is the window (dialog) where our list is going to be in
90 # rect is it's item rectangle (as in dialog item)
91 # itemnum is the itemnumber in the dialog
93 rect2
= rect
[0]+1, rect
[1]+1, rect
[2]-16, rect
[3]-1 # Scroll bar space, that's 15 + 1, Jack!
94 self
.list = List
.LNew(rect2
, (0, 0, 1, 0), (0,0), 0, wid
,
98 self
.itemnum
= itemnum
100 def setcontent(self
, content
, title
= ""):
101 # first, gather some stuff
104 thetype
= type(content
)
105 if thetype
== DictType
:
106 keylist
= content
.keys()
109 valuelist
.append(content
[key
])
110 elif thetype
== ListType
:
111 keylist
= valuelist
= content
112 elif thetype
== TupleType
:
114 keylist
= valuelist
= []
118 # XXX help me! is all this correct? is there more I should consider???
119 # XXX is this a sensible way to do it in the first place????
120 # XXX I'm not familiar enough with Python's guts to be sure. GUIDOOOOO!!!
121 if hasattr(content
, "__dict__"):
122 keylist
= keylist
+ content
.__dict
__.keys()
123 if hasattr(content
, "__methods__"):
124 keylist
= keylist
+ content
.__methods
__
125 if hasattr(content
, "__members__"):
126 keylist
= keylist
+ content
.__members
__
127 if hasattr(content
, "__class__"):
128 keylist
.append("__class__")
129 if hasattr(content
, "__bases__"):
130 keylist
.append("__bases__")
131 if hasattr(content
, "__name__"):
132 title
= content
.__name
__
133 if "__name__" not in keylist
:
134 keylist
.append("__name__")
137 valuelist
.append(getattr(content
, key
))
139 title
= title
+ "\r" + cleantype(content
)
140 # now make that list!
141 tp
, h
, rect
= self
.wid
.GetDialogItem(self
.itemnum
- 1)
142 Dlg
.SetDialogItemText(h
, title
[:255])
143 self
.list.LDelRow(0, 1)
144 self
.list.LSetDrawingMode(0)
145 self
.list.LAddRow(len(keylist
), 0)
146 for i
in range(len(keylist
)):
147 self
.list.LSetCell(str(keylist
[i
]), (0, i
))
148 self
.list.LSetDrawingMode(1)
149 self
.list.LUpdate(self
.wid
.GetWindowPort().visRgn
)
150 self
.content
= content
151 self
.keylist
= keylist
152 self
.valuelist
= valuelist
155 # draw a frame around the list, List Manager doesn't do that
158 Qd
.FrameRect(self
.rect
)
159 rect2
= Qd
.InsetRect(self
.rect
, -3, -3)
160 save
= Qd
.GetPenState()
163 Qd
.PenPat(Qd
.qd
.black
)
165 Qd
.PenPat(Qd
.qd
.white
)
166 # draw (or erase) an extra frame to indicate this is the acive list (or not)
172 class VarBrowser(FrameWork
.DialogWindow
):
173 def open(self
, start_object
, title
= ""):
174 FrameWork
.DialogWindow
.open(self
, ID_MAIN
)
176 windowtitle
= self
.wid
.GetWTitle()
177 self
.wid
.SetWTitle(windowtitle
+ " >> " + title
)
179 if hasattr(start_object
, "__name__"):
180 windowtitle
= self
.wid
.GetWTitle()
181 self
.wid
.SetWTitle(windowtitle
+ " >> " + str(getattr(start_object
, "__name__")) )
188 for i
in range(NUM_LISTS
):
189 self
.listitems
.append(MAIN_LIST
+ 2 * i
) # dlg item numbers... have to be consistent
190 for i
in self
.listitems
:
191 tp
, h
, rect
= self
.wid
.GetDialogItem(i
)
192 list = MyList(self
.wid
, rect
, i
)
193 self
.lists
.append(list)
196 self
.setup(start_object
, title
)
203 def setup(self
, start_object
, title
= ""):
204 # here we set the starting point for our expedition
205 self
.start
= start_object
206 self
.lists
[0].setcontent(start_object
, title
)
207 for list in self
.lists
[1:]:
208 list.setcontent(None)
210 def do_listhit(self
, event
, item
):
211 (what
, message
, when
, where
, modifiers
) = event
213 where
= Qd
.GlobalToLocal(where
)
214 for list in self
.lists
:
216 list = self
.lists
[self
.listitems
.index(item
)]
222 ok
, point
= list.list.LGetSelect(1, point
)
227 # This should be: list.list.LClick(where, modifiers)
228 # Since the selFlags field of the list is not accessible from Python I have to do it like this.
229 # The effect is that you can't select more items by using shift or command.
230 list.list.LClick(where
, 0)
232 index
= self
.listitems
.index(item
) + 1
234 ok
, point
= list.list.LGetSelect(1, point
)
235 if oldsel
== point
[1]:
236 return # selection didn't change, do nothing.
238 for i
in range(index
, len(self
.listitems
)):
239 self
.lists
[i
].setcontent(None)
243 if point
[1] >= len(list.keylist
):
244 return # XXX is this still necessary? is ok really true?
245 key
= str(list.keylist
[point
[1]])
246 value
= list.valuelist
[point
[1]]
249 thetype
= type(value
)
250 if thetype
== ListType
or \
251 thetype
== TupleType
or \
252 thetype
== DictType
or \
253 hasattr(value
, "__dict__") or \
254 hasattr(value
, "__methods__") or \
255 hasattr(value
, "__members__"): # XXX or, or... again: did I miss something?
256 if index
>= len(self
.listitems
):
257 # we've reached the right side of our dialog. move everything to the left
258 # (by pushing the rightbutton...)
259 self
.do_rightbutton(1)
261 newlist
= self
.lists
[index
]
262 newlist
.setcontent(value
, key
)
265 self
.settextitem( str(value
) + "\r" + cleantype(value
))
266 for i
in range(index
+ 1, len(self
.listitems
)):
267 self
.lists
[i
].setcontent(None)
270 # helper to set the big text item at the bottom of the dialog.
271 def settextitem(self
, text
):
272 tp
, h
, rect
= self
.wid
.GetDialogItem(MAIN_TEXT
)
273 Dlg
.SetDialogItemText(h
, text
[:255])
275 def do_rawupdate(self
, window
, event
):
277 iType
, iHandle
, iRect
= window
.GetDialogItem(MAIN_LINE
)
279 for list in self
.lists
:
280 Qd
.FrameRect(list.rect
)
282 # see MyList.drawframe
283 rect2
= Qd
.InsetRect(list.rect
, -3, -3)
284 save
= Qd
.GetPenState()
288 for list in self
.lists
:
289 list.list.LUpdate(self
.wid
.GetWindowPort().visRgn
)
291 def do_activate(self
, activate
, event
):
292 for list in self
.lists
:
293 list.list.LActivate(activate
)
295 # scroll everything one 'unit' to the left
296 # XXX I don't like the way this works. Too many 'manual' assignments
297 def do_rightbutton(self
, force
= 0):
298 if not force
and self
.rightover
== []:
302 ok
, point
= self
.lists
[0].list.LGetSelect(1, point
)
303 self
.leftover
.append(point
, self
.lists
[0].content
, self
.lists
[0].title
, self
.lists
[0].active
)
304 for i
in range(len(self
.lists
)-1):
306 ok
, point
= self
.lists
[i
+1].list.LGetSelect(1, point
)
307 self
.lists
[i
].setcontent(self
.lists
[i
+1].content
, self
.lists
[i
+1].title
)
308 self
.lists
[i
].list.LSetSelect(ok
, point
)
309 self
.lists
[i
].list.LAutoScroll()
310 self
.lists
[i
].active
= self
.lists
[i
+1].active
311 self
.lists
[i
].drawframe()
312 if len(self
.rightover
) > 0:
313 point
, content
, title
, active
= self
.rightover
[-1]
314 self
.lists
[-1].setcontent(content
, title
)
315 self
.lists
[-1].list.LSetSelect(1, point
)
316 self
.lists
[-1].list.LAutoScroll()
317 self
.lists
[-1].active
= active
318 self
.lists
[-1].drawframe()
319 del self
.rightover
[-1]
321 self
.lists
[-1].setcontent(None)
322 self
.lists
[-1].active
= 0
323 for list in self
.lists
:
326 # scroll everything one 'unit' to the right
327 def do_leftbutton(self
):
328 if self
.leftover
== []:
331 if self
.lists
[-1].content
<> None:
333 ok
, point
= self
.lists
[-1].list.LGetSelect(1, point
)
334 self
.rightover
.append(point
, self
.lists
[-1].content
, self
.lists
[-1].title
, self
.lists
[-1].active
)
335 for i
in range(len(self
.lists
)-1, 0, -1):
337 ok
, point
= self
.lists
[i
-1].list.LGetSelect(1, point
)
338 self
.lists
[i
].setcontent(self
.lists
[i
-1].content
, self
.lists
[i
-1].title
)
339 self
.lists
[i
].list.LSetSelect(ok
, point
)
340 self
.lists
[i
].list.LAutoScroll()
341 self
.lists
[i
].active
= self
.lists
[i
-1].active
342 self
.lists
[i
].drawframe()
343 if len(self
.leftover
) > 0:
344 point
, content
, title
, active
= self
.leftover
[-1]
345 self
.lists
[0].setcontent(content
, title
)
346 self
.lists
[0].list.LSetSelect(1, point
)
347 self
.lists
[0].list.LAutoScroll()
348 self
.lists
[0].active
= active
349 self
.lists
[0].drawframe()
350 del self
.leftover
[-1]
352 self
.lists
[0].setcontent(None)
353 self
.lists
[0].active
= 0
355 # create some visual feedback when 'scrolling' the lists to the left or to the right
356 def scroll(self
, leftright
): # leftright should be 1 or -1
357 # first, build a region containing all list rectangles
358 myregion
= Qd
.NewRgn()
359 mylastregion
= Qd
.NewRgn()
360 for list in self
.lists
:
361 AddRect2Rgn(list.rect
, myregion
)
362 AddRect2Rgn(list.rect
, mylastregion
)
363 # set the pen, but save it's state first
365 save
= Qd
.GetPenState()
366 Qd
.PenPat(Qd
.qd
.gray
)
368 # how far do we have to scroll?
369 distance
= self
.lists
[1].rect
[0] - self
.lists
[0].rect
[0]
371 lasttime
= time
.clock() # for delay
373 for i
in range(0, distance
, step
):
375 Qd
.FrameRgn(mylastregion
) # erase last region
376 Qd
.OffsetRgn(mylastregion
, step
* leftright
, 0)
378 Qd
.FrameRgn(myregion
)
379 Qd
.OffsetRgn(myregion
, step
* leftright
, 0)
380 while time
.clock() - lasttime
< 0.05:
382 lasttime
= time
.clock()
383 # clean up after your dog
384 Qd
.FrameRgn(mylastregion
)
388 for list in self
.lists
:
390 ok
, point
= list.list.LGetSelect(1, point
)
392 sel
= list.keylist
[point
[1]]
393 list.setcontent(list.content
, list.title
)
395 list.list.LSetSelect(1, (0, list.keylist
.index(sel
)))
396 list.list.LAutoScroll()
398 def do_itemhit(self
, item
, event
):
399 if item
in self
.listitems
:
400 self
.do_listhit(event
, item
)
401 elif item
== MAIN_LEFT
:
403 elif item
== MAIN_RIGHT
:
404 self
.do_rightbutton()
405 elif item
== MAIN_CLOSE
:
407 elif item
== MAIN_RESET
:
410 # helper function that returns a short string containing the type of an arbitrary object
411 # eg: cleantype("wat is dit nu weer?") -> '(string)'
413 # type() typically returns something like: <type 'string'>
414 items
= string
.split(str(type(obj
)), "'")
416 return '(' + items
[1] + ')'
418 # just in case, I don't know.
419 return str(type(obj
))
421 # helper for VarBrowser.scroll
422 def AddRect2Rgn(theRect
, theRgn
):
424 Qd
.RectRgn(rRgn
, theRect
)
425 Qd
.UnionRgn(rRgn
, theRgn
, theRgn
)
428 if __name__
== "__main__":