/Lib/idlelib/ClassBrowser.py

http://unladen-swallow.googlecode.com/ · Python · 221 lines · 167 code · 28 blank · 26 comment · 38 complexity · 5fb8da23ad9a843cf649c8ebc2e8215e MD5 · raw file

  1. """Class browser.
  2. XXX TO DO:
  3. - reparse when source changed (maybe just a button would be OK?)
  4. (or recheck on window popup)
  5. - add popup menu with more options (e.g. doc strings, base classes, imports)
  6. - show function argument list? (have to do pattern matching on source)
  7. - should the classes and methods lists also be in the module's menu bar?
  8. - add base classes to class browser tree
  9. """
  10. import os
  11. import sys
  12. import pyclbr
  13. import PyShell
  14. from WindowList import ListedToplevel
  15. from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
  16. from configHandler import idleConf
  17. class ClassBrowser:
  18. def __init__(self, flist, name, path):
  19. # XXX This API should change, if the file doesn't end in ".py"
  20. # XXX the code here is bogus!
  21. self.name = name
  22. self.file = os.path.join(path[0], self.name + ".py")
  23. self.init(flist)
  24. def close(self, event=None):
  25. self.top.destroy()
  26. self.node.destroy()
  27. def init(self, flist):
  28. self.flist = flist
  29. # reset pyclbr
  30. pyclbr._modules.clear()
  31. # create top
  32. self.top = top = ListedToplevel(flist.root)
  33. top.protocol("WM_DELETE_WINDOW", self.close)
  34. top.bind("<Escape>", self.close)
  35. self.settitle()
  36. top.focus_set()
  37. # create scrolled canvas
  38. theme = idleConf.GetOption('main','Theme','name')
  39. background = idleConf.GetHighlight(theme, 'normal')['background']
  40. sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
  41. sc.frame.pack(expand=1, fill="both")
  42. item = self.rootnode()
  43. self.node = node = TreeNode(sc.canvas, None, item)
  44. node.update()
  45. node.expand()
  46. def settitle(self):
  47. self.top.wm_title("Class Browser - " + self.name)
  48. self.top.wm_iconname("Class Browser")
  49. def rootnode(self):
  50. return ModuleBrowserTreeItem(self.file)
  51. class ModuleBrowserTreeItem(TreeItem):
  52. def __init__(self, file):
  53. self.file = file
  54. def GetText(self):
  55. return os.path.basename(self.file)
  56. def GetIconName(self):
  57. return "python"
  58. def GetSubList(self):
  59. sublist = []
  60. for name in self.listclasses():
  61. item = ClassBrowserTreeItem(name, self.classes, self.file)
  62. sublist.append(item)
  63. return sublist
  64. def OnDoubleClick(self):
  65. if os.path.normcase(self.file[-3:]) != ".py":
  66. return
  67. if not os.path.exists(self.file):
  68. return
  69. PyShell.flist.open(self.file)
  70. def IsExpandable(self):
  71. return os.path.normcase(self.file[-3:]) == ".py"
  72. def listclasses(self):
  73. dir, file = os.path.split(self.file)
  74. name, ext = os.path.splitext(file)
  75. if os.path.normcase(ext) != ".py":
  76. return []
  77. try:
  78. dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
  79. except ImportError, msg:
  80. return []
  81. items = []
  82. self.classes = {}
  83. for key, cl in dict.items():
  84. if cl.module == name:
  85. s = key
  86. if hasattr(cl, 'super') and cl.super:
  87. supers = []
  88. for sup in cl.super:
  89. if type(sup) is type(''):
  90. sname = sup
  91. else:
  92. sname = sup.name
  93. if sup.module != cl.module:
  94. sname = "%s.%s" % (sup.module, sname)
  95. supers.append(sname)
  96. s = s + "(%s)" % ", ".join(supers)
  97. items.append((cl.lineno, s))
  98. self.classes[s] = cl
  99. items.sort()
  100. list = []
  101. for item, s in items:
  102. list.append(s)
  103. return list
  104. class ClassBrowserTreeItem(TreeItem):
  105. def __init__(self, name, classes, file):
  106. self.name = name
  107. self.classes = classes
  108. self.file = file
  109. try:
  110. self.cl = self.classes[self.name]
  111. except (IndexError, KeyError):
  112. self.cl = None
  113. self.isfunction = isinstance(self.cl, pyclbr.Function)
  114. def GetText(self):
  115. if self.isfunction:
  116. return "def " + self.name + "(...)"
  117. else:
  118. return "class " + self.name
  119. def GetIconName(self):
  120. if self.isfunction:
  121. return "python"
  122. else:
  123. return "folder"
  124. def IsExpandable(self):
  125. if self.cl:
  126. try:
  127. return not not self.cl.methods
  128. except AttributeError:
  129. return False
  130. def GetSubList(self):
  131. if not self.cl:
  132. return []
  133. sublist = []
  134. for name in self.listmethods():
  135. item = MethodBrowserTreeItem(name, self.cl, self.file)
  136. sublist.append(item)
  137. return sublist
  138. def OnDoubleClick(self):
  139. if not os.path.exists(self.file):
  140. return
  141. edit = PyShell.flist.open(self.file)
  142. if hasattr(self.cl, 'lineno'):
  143. lineno = self.cl.lineno
  144. edit.gotoline(lineno)
  145. def listmethods(self):
  146. if not self.cl:
  147. return []
  148. items = []
  149. for name, lineno in self.cl.methods.items():
  150. items.append((lineno, name))
  151. items.sort()
  152. list = []
  153. for item, name in items:
  154. list.append(name)
  155. return list
  156. class MethodBrowserTreeItem(TreeItem):
  157. def __init__(self, name, cl, file):
  158. self.name = name
  159. self.cl = cl
  160. self.file = file
  161. def GetText(self):
  162. return "def " + self.name + "(...)"
  163. def GetIconName(self):
  164. return "python" # XXX
  165. def IsExpandable(self):
  166. return 0
  167. def OnDoubleClick(self):
  168. if not os.path.exists(self.file):
  169. return
  170. edit = PyShell.flist.open(self.file)
  171. edit.gotoline(self.cl.methods[self.name])
  172. def main():
  173. try:
  174. file = __file__
  175. except NameError:
  176. file = sys.argv[0]
  177. if sys.argv[1:]:
  178. file = sys.argv[1]
  179. else:
  180. file = sys.argv[0]
  181. dir, file = os.path.split(file)
  182. name = os.path.splitext(file)[0]
  183. ClassBrowser(PyShell.flist, name, [dir])
  184. if sys.stdin is sys.__stdin__:
  185. mainloop()
  186. if __name__ == "__main__":
  187. main()