| """Primitive class browser. |
| |
| XXX TO DO: |
| |
| - reparse when source changed |
| - add popup menu with more options (e.g. doc strings, base classes, imports) |
| - show function argument list (have to do pattern matching on source) |
| - should the classes and methods lists also be in the module's menu bar? |
| |
| """ |
| |
| import os |
| import string |
| import pyclbr |
| from Tkinter import * |
| import tkMessageBox |
| from WindowList import ListedToplevel |
| from Separator import HSeparator |
| |
| from ScrolledList import ScrolledList |
| |
| |
| class ClassBrowser: |
| |
| def __init__(self, flist, name, path=[]): |
| root = flist.root |
| try: |
| dict = pyclbr.readmodule(name, path) |
| except ImportError, msg: |
| tkMessageBox.showerror("Import error", str(msg), parent=root) |
| return |
| if not dict: |
| tkMessageBox.showerror("Nothing to browse", |
| "Module %s defines no classes" % name, parent=root) |
| return |
| self.flist = flist |
| self.dict = dict |
| self.root = root |
| self.top = top = ListedToplevel(root) |
| self.top.protocol("WM_DELETE_WINDOW", self.close) |
| self.top.bind("<Escape>", self.close) |
| top.wm_title("Class Browser - " + name) |
| top.wm_iconname("ClBrowser") |
| self.sepa = HSeparator(top) |
| leftframe, rightframe = self.sepa.parts() |
| self.leftframe = leftframe |
| self.rightframe = rightframe |
| leftframe.pack(side="left", fill="both", expand=1) |
| # Create help label |
| self.helplabel = Label(leftframe, text="Module %s" % name, |
| relief="groove", borderwidth=2) |
| self.helplabel.pack(fill="x") |
| # Create top frame, with scrollbar and listbox |
| self.classviewer = ClassViewer( |
| self.leftframe, self.flist, self) |
| # Load the classes |
| self.load_classes(dict, name) |
| |
| def close(self, event=None): |
| self.classviewer = None |
| self.methodviewer = None |
| self.top.destroy() |
| |
| def load_classes(self, dict, module): |
| self.classviewer.load_classes(dict, module) |
| if self.methodframe: |
| self.methodframe.destroy() |
| self.methodframe = None |
| self.methodviewer = None |
| |
| methodframe = None |
| methodhelplabel = None |
| methodviewer = None |
| |
| def show_methods(self, cl): |
| if not self.methodframe: |
| self.methodframe = Frame(self.rightframe) |
| self.methodframe.pack(side="right", expand=1, fill="both") |
| self.methodhelplabel = Label(self.methodframe, |
| relief="groove", borderwidth=2) |
| self.methodhelplabel.pack(fill="x") |
| self.methodviewer = MethodViewer(self.methodframe, self.flist) |
| self.methodhelplabel.config(text="Class %s" % cl.name) |
| self.methodviewer.load_methods(cl) |
| |
| |
| class ClassViewer(ScrolledList): |
| |
| def __init__(self, master, flist, browser): |
| ScrolledList.__init__(self, master, width=40) |
| self.flist = flist |
| self.browser = browser |
| |
| def load_classes(self, dict, module): |
| self.clear() |
| self.dict = dict |
| items = [] |
| for key, value in dict.items(): |
| if value.module == module: |
| items.append((value.lineno, key, value)) |
| items.sort() |
| for lineno, key, value in items: |
| s = key |
| if value.super: |
| super = [] |
| for sup in value.super: |
| name = sup.name |
| if sup.module != value.module: |
| name = "%s.%s" % (sup.module, name) |
| super.append(name) |
| s = s + "(%s)" % string.join(super, ", ") |
| self.append(s) |
| |
| def getname(self, index): |
| name = self.listbox.get(index) |
| i = string.find(name, '(') |
| if i >= 0: |
| name = name[:i] |
| return name |
| |
| def getclass(self, index): |
| return self.dict[self.getname(index)] |
| |
| def on_select(self, index): |
| self.show_methods(index) |
| |
| def on_double(self, index): |
| self.show_source(index) |
| |
| def show_methods(self, index): |
| cl = self.getclass(index) |
| self.browser.show_methods(cl) |
| |
| def show_source(self, index): |
| cl = self.getclass(index) |
| if os.path.isfile(cl.file): |
| edit = self.flist.open(cl.file) |
| edit.gotoline(cl.lineno) |
| |
| |
| class MethodViewer(ScrolledList): |
| |
| def __init__(self, master, flist): |
| ScrolledList.__init__(self, master) |
| self.flist = flist |
| |
| classinfo = None |
| |
| def load_methods(self, cl): |
| self.classinfo = cl |
| self.clear() |
| items = [] |
| for name, lineno in cl.methods.items(): |
| items.append((lineno, name)) |
| items.sort() |
| for item, name in items: |
| self.append(name) |
| |
| def click_event(self, event): |
| pass |
| |
| def on_double(self, index): |
| self.show_source(self.get(index)) |
| |
| def show_source(self, name): |
| if os.path.isfile(self.classinfo.file): |
| edit = self.flist.open(self.classinfo.file) |
| edit.gotoline(self.classinfo.methods[name]) |