home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Python 1.4 / twit / mactwit_browser.py < prev    next >
Encoding:
Python Source  |  1996-09-24  |  13.4 KB  |  430 lines  |  [TEXT/Pyth]

  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.
  3.  
  4. # june 1996
  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 ;-)
  9.  
  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...
  13.  
  14. # XXX to do:
  15. # Arrow key support
  16. # Copy & Paste? 
  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. 
  20.  
  21. version = "1.0"
  22.  
  23. import FrameWork
  24. import EasyDialogs
  25. import Dlg
  26. import Res
  27. import Qd
  28. import List
  29. import sys
  30. from Types import *
  31. from QuickDraw import *
  32. import string
  33. import time
  34. import os
  35.  
  36. # The initial object to start browsing with. Can be anything, but 'sys' makes kindof sense.
  37. start_object = sys
  38.  
  39. # Resource definitions
  40. ID_MAIN = 516
  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
  44. MAIN_TEXT = 11
  45. MAIN_LEFT = 1
  46. MAIN_RIGHT = 2
  47. MAIN_RESET = 12
  48. MAIN_CLOSE = 13
  49. MAIN_LINE = 14
  50.  
  51. def Initialize():
  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(?!;-)
  55.     try:
  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)
  59.     except Res.Error:
  60.         savewd = os.getcwd()
  61.         ourparentdir = os.path.split(openresfile.func_code.co_filename)[0]
  62.         os.chdir(ourparentdir)        
  63.         try:
  64.             Res.OpenResFile("mactwit_browse.rsrc")
  65.         except Res.Error, arg:
  66.             EasyDialogs.Message("Cannot open mactwit_browse.rsrc: "+arg[1])
  67.             sys.exit(1)
  68.         os.chdir(savewd)
  69.  
  70. def main():
  71.     Initialize()
  72.     PythonBrowse()
  73.  
  74. # this is all there is to it to make an application. 
  75. class PythonBrowse(FrameWork.Application):
  76.     def __init__(self):
  77.         FrameWork.Application.__init__(self)
  78.         VarBrowser(self).open(start_object)
  79.         self.mainloop()
  80.     
  81.     def do_about(self, id, item, window, event):
  82.         EasyDialogs.Message(self.__class__.__name__ + " version " + version + "\rby Just van Rossum")
  83.     
  84.     def quit(self, *args):
  85.         raise self
  86.  
  87. class MyList:
  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
  92.         self.rect = rect
  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,
  95.                     0, 1, 0, 1)
  96.         self.wid = wid
  97.         self.active = 0
  98.         self.itemnum = itemnum
  99.     
  100.     def setcontent(self, content, title = ""):
  101.         # first, gather some stuff
  102.         keylist = []
  103.         valuelist = []
  104.         thetype = type(content)
  105.         if thetype == DictType:
  106.             keylist = content.keys()
  107.             keylist.sort()
  108.             for key in keylist:
  109.                 valuelist.append(content[key])
  110.         elif thetype == ListType:
  111.             keylist = valuelist = content
  112.         elif thetype == TupleType:
  113.             
  114.             keylist = valuelist = []
  115.             for i in content:
  116.                 keylist.append(i)
  117.         else:
  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__")
  135.             keylist.sort()
  136.             for key in keylist:
  137.                 valuelist.append(getattr(content, key))
  138.         if content <> None:
  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
  153.         self.title = title
  154.     
  155.     # draw a frame around the list, List Manager doesn't do that
  156.     def drawframe(self):
  157.         Qd.SetPort(self.wid)
  158.         Qd.FrameRect(self.rect)
  159.         rect2 = Qd.InsetRect(self.rect, -3, -3)
  160.         save = Qd.GetPenState()
  161.         Qd.PenSize(2, 2)
  162.         if self.active:
  163.             Qd.PenPat(Qd.qd.black)
  164.         else:
  165.             Qd.PenPat(Qd.qd.white)
  166.         # draw (or erase) an extra frame to indicate this is the acive list (or not)
  167.         Qd.FrameRect(rect2)
  168.         Qd.SetPenState(save)
  169.         
  170.         
  171.  
  172. class VarBrowser(FrameWork.DialogWindow):
  173.     def open(self, start_object, title = ""):
  174.         FrameWork.DialogWindow.open(self, ID_MAIN)
  175.         if title <> "":
  176.             windowtitle = self.wid.GetWTitle()
  177.             self.wid.SetWTitle(windowtitle + " >> " + title)
  178.         else:
  179.             if hasattr(start_object, "__name__"):
  180.                 windowtitle = self.wid.GetWTitle()
  181.                 self.wid.SetWTitle(windowtitle + " >> " + str(getattr(start_object, "__name__")) )
  182.                 
  183.         self.SetPort()
  184.         Qd.TextFont(3)
  185.         Qd.TextSize(9)
  186.         self.lists = []
  187.         self.listitems = []
  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)
  194.         self.leftover = []
  195.         self.rightover = []
  196.         self.setup(start_object, title)
  197.         
  198.     def close(self):
  199.         self.lists = []
  200.         self.listitems = []
  201.         self.do_postclose()
  202.     
  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)
  209.         
  210.     def do_listhit(self, event, item):
  211.         (what, message, when, where, modifiers) = event
  212.         Qd.SetPort(self.wid)
  213.         where = Qd.GlobalToLocal(where)
  214.         for list in self.lists:
  215.             list.active = 0
  216.         list = self.lists[self.listitems.index(item)]
  217.         list.active = 1
  218.         for l in self.lists:
  219.             l.drawframe()
  220.         
  221.         point = (0,0)
  222.         ok, point = list.list.LGetSelect(1, point)
  223.         if ok:
  224.             oldsel = point[1]
  225.         else:
  226.             oldsel = -1
  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)
  231.         
  232.         index = self.listitems.index(item) + 1
  233.         point = (0,0)
  234.         ok, point = list.list.LGetSelect(1, point)
  235.         if oldsel == point[1]:
  236.             return    # selection didn't change, do nothing.
  237.         if not ok:
  238.             for i in range(index, len(self.listitems)):
  239.                 self.lists[i].setcontent(None)
  240.             self.rightover = []
  241.             return
  242.             
  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]]
  247.         
  248.         self.settextitem("")
  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)
  260.                 index = index - 1
  261.             newlist = self.lists[index]
  262.             newlist.setcontent(value, key)
  263.         else:
  264.             index = index - 1
  265.             self.settextitem( str(value) + "\r" + cleantype(value))
  266.         for i in range(index + 1, len(self.listitems)):
  267.             self.lists[i].setcontent(None)
  268.         self.rightover = []
  269.     
  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])
  274.     
  275.     def do_rawupdate(self, window, event):
  276.         Qd.SetPort(self.wid)
  277.         iType, iHandle, iRect = window.GetDialogItem(MAIN_LINE)
  278.         Qd.FrameRect(iRect)
  279.         for list in self.lists:
  280.             Qd.FrameRect(list.rect)
  281.             if list.active:
  282.                 # see MyList.drawframe
  283.                 rect2 = Qd.InsetRect(list.rect, -3, -3)
  284.                 save = Qd.GetPenState()
  285.                 Qd.PenSize(2, 2)
  286.                 Qd.FrameRect(rect2)
  287.                 Qd.SetPenState(save)
  288.         for list in self.lists:
  289.             list.list.LUpdate(self.wid.GetWindowPort().visRgn)
  290.         
  291.     def do_activate(self, activate, event):
  292.         for list in self.lists:
  293.             list.list.LActivate(activate)
  294.         
  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 == []:
  299.             return
  300.         self.scroll(-1)
  301.         point = (0, 0)
  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):
  305.             point = (0, 0)
  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]
  320.         else:
  321.             self.lists[-1].setcontent(None)
  322.             self.lists[-1].active = 0
  323.         for list in self.lists:
  324.             list.drawframe()
  325.     
  326.     # scroll everything one 'unit' to the right
  327.     def do_leftbutton(self):
  328.         if self.leftover == []:
  329.             return
  330.         self.scroll(1)
  331.         if self.lists[-1].content <> None:
  332.             point = (0, 0)
  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):
  336.             point = (0, 0)
  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]
  351.         else:
  352.             self.lists[0].setcontent(None)
  353.             self.lists[0].active = 0
  354.     
  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
  364.         self.SetPort()
  365.         save = Qd.GetPenState()
  366.         Qd.PenPat(Qd.qd.gray)
  367.         Qd.PenMode(srcXor)
  368.         # how far do we have to scroll?
  369.         distance = self.lists[1].rect[0] - self.lists[0].rect[0]
  370.         step = 30
  371.         lasttime = time.clock()    # for delay
  372.         # do it
  373.         for i in range(0, distance, step):
  374.             if i <> 0:
  375.                 Qd.FrameRgn(mylastregion)    # erase last region
  376.                 Qd.OffsetRgn(mylastregion, step * leftright, 0)
  377.             # draw gray region
  378.             Qd.FrameRgn(myregion)
  379.             Qd.OffsetRgn(myregion, step * leftright, 0)
  380.             while time.clock() - lasttime < 0.05:
  381.                 pass    # delay
  382.             lasttime = time.clock()
  383.         # clean up after your dog
  384.         Qd.FrameRgn(mylastregion)
  385.         Qd.SetPenState(save)
  386.     
  387.     def reset(self):
  388.         for list in self.lists:
  389.             point = (0,0)
  390.             ok, point = list.list.LGetSelect(1, point)
  391.             if ok:
  392.                 sel = list.keylist[point[1]]
  393.             list.setcontent(list.content, list.title)
  394.             if ok:
  395.                 list.list.LSetSelect(1, (0, list.keylist.index(sel)))
  396.                 list.list.LAutoScroll()
  397.     
  398.     def do_itemhit(self, item, event):
  399.         if item in self.listitems:
  400.             self.do_listhit(event, item)
  401.         elif item == MAIN_LEFT:
  402.             self.do_leftbutton()
  403.         elif item == MAIN_RIGHT:
  404.             self.do_rightbutton()
  405.         elif item == MAIN_CLOSE:
  406.             self.close()
  407.         elif item == MAIN_RESET:
  408.             self.reset()
  409.  
  410. # helper function that returns a short string containing the type of an arbitrary object
  411. # eg: cleantype("wat is dit nu weer?") -> '(string)'
  412. def cleantype(obj):
  413.     # type() typically returns something like: <type 'string'>
  414.     items = string.split(str(type(obj)), "'")
  415.     if len(items) == 3:
  416.         return '(' + items[1] + ')'
  417.     else:
  418.         # just in case, I don't know.
  419.         return str(type(obj))
  420.     
  421. # helper for VarBrowser.scroll
  422. def AddRect2Rgn(theRect, theRgn):
  423.     rRgn = Qd.NewRgn()
  424.     Qd.RectRgn(rRgn, theRect)
  425.     Qd.UnionRgn(rRgn, theRgn, theRgn)
  426.  
  427.  
  428. if __name__ == "__main__":
  429.     main()
  430.