171 lines · 5.8 KB · Python
RawDownload
"""A CallTip window class for Tkinter/IDLE. After ToolTip.py, which uses ideas gleaned from PySolUsed by the CallTips IDLE extension. """from Tkinter import * HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")CHECKHIDE_TIME = 100 # miliseconds MARK_RIGHT = "calltipwindowregion_right" class CallTip:     def __init__(self, widget):        self.widget = widget        self.tipwindow = self.label = None        self.parenline = self.parencol = None        self.lastline = None        self.hideid = self.checkhideid = None     def position_window(self):        """Check if needs to reposition the window, and if so - do it."""        curline = int(self.widget.index("insert").split('.')[0])        if curline == self.lastline:            return        self.lastline = curline        self.widget.see("insert")        if curline == self.parenline:            box = self.widget.bbox("%d.%d" % (self.parenline,                                              self.parencol))        else:            box = self.widget.bbox("%d.0" % curline)        if not box:            box = list(self.widget.bbox("insert"))            # align to left of window            box[0] = 0            box[2] = 0        x = box[0] + self.widget.winfo_rootx() + 2        y = box[1] + box[3] + self.widget.winfo_rooty()        self.tipwindow.wm_geometry("+%d+%d" % (x, y))     def showtip(self, text, parenleft, parenright):        """Show the calltip, bind events which will close it and reposition it.        """        # truncate overly long calltip        if len(text) >= 79:            textlines = text.splitlines()            for i, line in enumerate(textlines):                if len(line) > 79:                    textlines[i] = line[:75] + ' ...'            text = '\n'.join(textlines)        self.text = text        if self.tipwindow or not self.text:            return         self.widget.mark_set(MARK_RIGHT, parenright)        self.parenline, self.parencol = map(            int, self.widget.index(parenleft).split("."))         self.tipwindow = tw = Toplevel(self.widget)        self.position_window()        # remove border on calltip window        tw.wm_overrideredirect(1)        try:            # This command is only needed and available on Tk >= 8.4.0 for OSX            # Without it, call tips intrude on the typing process by grabbing            # the focus.            tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,                       "help", "noActivates")        except TclError:            pass        self.label = Label(tw, text=self.text, justify=LEFT,                           background="#ffffe0", relief=SOLID, borderwidth=1,                           font = self.widget['font'])        self.label.pack()         self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME,                                            self.checkhide_event)        for seq in CHECKHIDE_SEQUENCES:            self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)        self.widget.after(CHECKHIDE_TIME, self.checkhide_event)        self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,                                       self.hide_event)        for seq in HIDE_SEQUENCES:            self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)     def checkhide_event(self, event=None):        if not self.tipwindow:            # If the event was triggered by the same event that unbinded            # this function, the function will be called nevertheless,            # so do nothing in this case.            return        curline, curcol = map(int, self.widget.index("insert").split('.'))        if curline < self.parenline or \           (curline == self.parenline and curcol <= self.parencol) or \           self.widget.compare("insert", ">", MARK_RIGHT):            self.hidetip()        else:            self.position_window()            self.widget.after(CHECKHIDE_TIME, self.checkhide_event)     def hide_event(self, event):        if not self.tipwindow:            # See the explanation in checkhide_event.            return        self.hidetip()     def hidetip(self):        if not self.tipwindow:            return         for seq in CHECKHIDE_SEQUENCES:            self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)        self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid)        self.checkhideid = None        for seq in HIDE_SEQUENCES:            self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)        self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)        self.hideid = None         self.label.destroy()        self.label = None        self.tipwindow.destroy()        self.tipwindow = None         self.widget.mark_unset(MARK_RIGHT)        self.parenline = self.parencol = self.lastline = None     def is_active(self):        return bool(self.tipwindow)   ################################# Test Code#class container: # Conceptually an editor_window    def __init__(self):        root = Tk()        text = self.text = Text(root)        text.pack(side=LEFT, fill=BOTH, expand=1)        text.insert("insert", "string.split")        root.update()        self.calltip = CallTip(text)         text.event_add("<<calltip-show>>", "(")        text.event_add("<<calltip-hide>>", ")")        text.bind("<<calltip-show>>", self.calltip_show)        text.bind("<<calltip-hide>>", self.calltip_hide)         text.focus_set()        root.mainloop()     def calltip_show(self, event):        self.calltip.showtip("Hello world")     def calltip_hide(self, event):        self.calltip.hidetip() def main():    # Test code    c=container() if __name__=='__main__':    main()