PageRenderTime 73ms CodeModel.GetById 36ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/idlelib/CallTipWindow.py

http://unladen-swallow.googlecode.com/
Python | 171 lines | 122 code | 25 blank | 24 comment | 30 complexity | 5f672dbf9067bb98b35dd75a367b8498 MD5 | raw file
  1"""A CallTip window class for Tkinter/IDLE.
  2
  3After ToolTip.py, which uses ideas gleaned from PySol
  4Used by the CallTips IDLE extension.
  5
  6"""
  7from Tkinter import *
  8
  9HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
 10HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
 11CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
 12CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")
 13CHECKHIDE_TIME = 100 # miliseconds
 14
 15MARK_RIGHT = "calltipwindowregion_right"
 16
 17class CallTip:
 18
 19    def __init__(self, widget):
 20        self.widget = widget
 21        self.tipwindow = self.label = None
 22        self.parenline = self.parencol = None
 23        self.lastline = None
 24        self.hideid = self.checkhideid = None
 25
 26    def position_window(self):
 27        """Check if needs to reposition the window, and if so - do it."""
 28        curline = int(self.widget.index("insert").split('.')[0])
 29        if curline == self.lastline:
 30            return
 31        self.lastline = curline
 32        self.widget.see("insert")
 33        if curline == self.parenline:
 34            box = self.widget.bbox("%d.%d" % (self.parenline,
 35                                              self.parencol))
 36        else:
 37            box = self.widget.bbox("%d.0" % curline)
 38        if not box:
 39            box = list(self.widget.bbox("insert"))
 40            # align to left of window
 41            box[0] = 0
 42            box[2] = 0
 43        x = box[0] + self.widget.winfo_rootx() + 2
 44        y = box[1] + box[3] + self.widget.winfo_rooty()
 45        self.tipwindow.wm_geometry("+%d+%d" % (x, y))
 46
 47    def showtip(self, text, parenleft, parenright):
 48        """Show the calltip, bind events which will close it and reposition it.
 49        """
 50        # truncate overly long calltip
 51        if len(text) >= 79:
 52            textlines = text.splitlines()
 53            for i, line in enumerate(textlines):
 54                if len(line) > 79:
 55                    textlines[i] = line[:75] + ' ...'
 56            text = '\n'.join(textlines)
 57        self.text = text
 58        if self.tipwindow or not self.text:
 59            return
 60
 61        self.widget.mark_set(MARK_RIGHT, parenright)
 62        self.parenline, self.parencol = map(
 63            int, self.widget.index(parenleft).split("."))
 64
 65        self.tipwindow = tw = Toplevel(self.widget)
 66        self.position_window()
 67        # remove border on calltip window
 68        tw.wm_overrideredirect(1)
 69        try:
 70            # This command is only needed and available on Tk >= 8.4.0 for OSX
 71            # Without it, call tips intrude on the typing process by grabbing
 72            # the focus.
 73            tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
 74                       "help", "noActivates")
 75        except TclError:
 76            pass
 77        self.label = Label(tw, text=self.text, justify=LEFT,
 78                           background="#ffffe0", relief=SOLID, borderwidth=1,
 79                           font = self.widget['font'])
 80        self.label.pack()
 81
 82        self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME,
 83                                            self.checkhide_event)
 84        for seq in CHECKHIDE_SEQUENCES:
 85            self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
 86        self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
 87        self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
 88                                       self.hide_event)
 89        for seq in HIDE_SEQUENCES:
 90            self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
 91
 92    def checkhide_event(self, event=None):
 93        if not self.tipwindow:
 94            # If the event was triggered by the same event that unbinded
 95            # this function, the function will be called nevertheless,
 96            # so do nothing in this case.
 97            return
 98        curline, curcol = map(int, self.widget.index("insert").split('.'))
 99        if curline < self.parenline or \
100           (curline == self.parenline and curcol <= self.parencol) or \
101           self.widget.compare("insert", ">", MARK_RIGHT):
102            self.hidetip()
103        else:
104            self.position_window()
105            self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
106
107    def hide_event(self, event):
108        if not self.tipwindow:
109            # See the explanation in checkhide_event.
110            return
111        self.hidetip()
112
113    def hidetip(self):
114        if not self.tipwindow:
115            return
116
117        for seq in CHECKHIDE_SEQUENCES:
118            self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
119        self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid)
120        self.checkhideid = None
121        for seq in HIDE_SEQUENCES:
122            self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
123        self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
124        self.hideid = None
125
126        self.label.destroy()
127        self.label = None
128        self.tipwindow.destroy()
129        self.tipwindow = None
130
131        self.widget.mark_unset(MARK_RIGHT)
132        self.parenline = self.parencol = self.lastline = None
133
134    def is_active(self):
135        return bool(self.tipwindow)
136
137
138
139###############################
140#
141# Test Code
142#
143class container: # Conceptually an editor_window
144    def __init__(self):
145        root = Tk()
146        text = self.text = Text(root)
147        text.pack(side=LEFT, fill=BOTH, expand=1)
148        text.insert("insert", "string.split")
149        root.update()
150        self.calltip = CallTip(text)
151
152        text.event_add("<<calltip-show>>", "(")
153        text.event_add("<<calltip-hide>>", ")")
154        text.bind("<<calltip-show>>", self.calltip_show)
155        text.bind("<<calltip-hide>>", self.calltip_hide)
156
157        text.focus_set()
158        root.mainloop()
159
160    def calltip_show(self, event):
161        self.calltip.showtip("Hello world")
162
163    def calltip_hide(self, event):
164        self.calltip.hidetip()
165
166def main():
167    # Test code
168    c=container()
169
170if __name__=='__main__':
171    main()