# Author: JerryKing # Ver: 1.0 from string import ascii_letters, digits import re, sys, os, sets, inspect, Tkinter class AutoComplete: def __init__(self, win): self.win = win self.WordChars = ascii_letters + digits + '_.' self.NameSpace = {} self.filename = "" self.active = False self.record = "" self.count = 0 self.win.bind("", self.OnChar) def GetWordChars(self): WordChars = ascii_letters + digits + '_' if True: return WordChars + '.' else: return WordChars def WordStartPosition(self, pos): row, column = pos.split('.') i = int(column) - 1 while i >= 0: if self.win.get(row+'.'+`i`, row+'.'+`i+1`) in self.WordChars: i -= 1 else: break result = row + '.' + `i+1` return result def WordEndPosition(self, pos): row, column = pos.split('.') end = self.win.index("insert lineend").split('.')[1] i = int(column) + 1 while i <= int(end): if self.win.get(row+'.'+`i-1`, row+'.'+`i`) in self.WordChars: i += 1 else: break result = row + '.' + `i-1` return result def GetWord(self, whole=None): pos = self.win.index("insert") start = self.WordStartPosition(pos) if whole: end = self.WordEndPosition(pos) return self.win.get(start, end) else: return self.win.get(start, "insert") def ImportDocument(self): dir = os.path.dirname(self.filename) if dir not in sys.path: sys.path.insert(0, dir) r = re.compile(r"^\s*from\s+.*$|^\s*import\s+.*$", re.M) result = r.findall(self.win.get("0.0", "end")) result = [s.strip() for s in result] for line in result: if line.startswith("from"): try: exec(line) in self.NameSpace except: pass elif line.startswith('import'): try: exec(line) in self.NameSpace except: pass def Evaluate(self, word): try: obj = eval(word, self.NameSpace) return obj except: self.ImportDocument() try: obj = eval(word, self.NameSpace) return obj except: return None def GetWordObject(self, word=None, whole=None): if not word: word = self.GetWord(whole) try: return self.Evaluate(word) except: return None def OnChar(self, event): def Deactive(): self.listbox.master.master.destroy() self.record = "" self.count = 0 self.active = False key = event.char if key == '.': obj = self.GetWordObject(whole=True) if obj: self.MakeGUI(obj) self.active = True return elif re.match("[_a-zA-Z0-9]", key): if self.active == True: self.record += key.upper() self.count = len(self.record) self.win.insert("insert", key) else: return elif event.keysym == "BackSpace": if self.active == True and self.count > -1: row, column = self.win.index("insert").split('.') column = int(column) - 1 self.win.delete(row + '.' + `column`, "insert") self.record = self.record[:-1] self.count -= 1 if self.count == -1: Deactive() return else: if self.active == True: row, column = self.win.index("insert").split('.') column = int(column) - 1 self.win.delete(row + '.' + `column`, "insert") Deactive() return "break" else: return elif event.keysym == "Return": if self.active == True: length = len(self.record) row, column = self.win.index("insert").split('.') column = int(column) - length self.win.delete(row + '.' + `column`, "insert") self.win.insert("insert", self.listbox.get("active")) Deactive() return elif event.keysym in ("Up", "Down", "Shift_R", "Shift_L", "Control_L", "Control_R", "Alt_L", "Alt_R", "parenleft", "parenright"): return else: if self.active == True: Deactive() return FilteredAttr = [] if self.active == True: for attr in self.attrs: if re.match('^' + self.record + "[A-Z0-9]*", attr.upper()): FilteredAttr.append(attr) if FilteredAttr: self.listbox.delete('0', "end") for attr in FilteredAttr: self.listbox.insert("end", attr) self.listbox.select_set('0') else: Deactive() def OnDClick(self, event): length = len(self.record) row, column = self.win.index("insert").split('.') column = int(column) - length self.win.delete(row + '.' + `column`, "insert") self.win.insert("insert", self.listbox.get("active")) self.listbox.master.master.destroy() self.record = "" self.active = False def MakeGUI(self, obj): x, y, cx, cy = self.win.bbox("insert") x = x + self.win.winfo_rootx() + 2 y = y + cy + self.win.winfo_rooty() tl = Tkinter.Toplevel(self.win.master) tl.wm_overrideredirect(1) tl.wm_geometry("+%d+%d" % (x, y)) form = Tkinter.Frame(tl, highlightthickness=1, highlightcolor="darkgreen") form.pack(fill="both", expand="yes") sb = Tkinter.Scrollbar(form, highlightthickness=0) sb.pack(side="right", fill="y") self.listbox = Tkinter.Listbox(form, highlightthickness=0, relief="flat", yscrollcommand=sb.set, height=6) self.attrs = dir(obj) for attr in self.attrs: self.listbox.insert("end", attr) self.listbox.pack(side="left", fill="both") self.listbox.select_set('0') sb.config(command=self.listbox.yview) self.listbox.focus() self.listbox.bind("", self.OnChar) self.listbox.bind("", self.OnDClick) if __name__ == "__main__": t = Tkinter.Text(font=("courier", 10)) t.pack() t.insert("end", "import Tkinter\nt = Tkinter") a = AutoComplete(t) t.mainloop()