| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | """A call-tip window class for Tkinter/IDLE.
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | After tooltip.py, which uses ideas gleaned from PySol. | 
					
						
							|  |  |  | Used by calltip.py. | 
					
						
							| 
									
										
										
										
											2002-12-12 19:15:39 +00:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | from tkinter import Label, LEFT, SOLID, TclError | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | from idlelib.tooltip import TooltipBase | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HIDE_EVENT = "<<calltipwindow-hide>>" | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>") | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | CHECKHIDE_EVENT = "<<calltipwindow-checkhide>>" | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>") | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | CHECKHIDE_TIME = 100  # milliseconds | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | MARK_RIGHT = "calltipwindowregion_right" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | class CalltipWindow(TooltipBase): | 
					
						
							|  |  |  |     """A call-tip widget for tkinter text widgets.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, text_widget): | 
					
						
							|  |  |  |         """Create a call-tip; shown by showtip().
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text_widget: a Text widget with code for which call-tips are desired | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # Note: The Text widget will be accessible as self.anchor_widget | 
					
						
							|  |  |  |         super(CalltipWindow, self).__init__(text_widget) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.label = self.text = None | 
					
						
							|  |  |  |         self.parenline = self.parencol = self.lastline = None | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         self.hideid = self.checkhideid = None | 
					
						
							| 
									
										
										
										
											2012-03-13 14:18:36 -07:00
										 |  |  |         self.checkhide_after_id = None | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |     def get_position(self): | 
					
						
							|  |  |  |         """Choose the position of the call-tip.""" | 
					
						
							|  |  |  |         curline = int(self.anchor_widget.index("insert").split('.')[0]) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         if curline == self.parenline: | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |             anchor_index = (self.parenline, self.parencol) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |             anchor_index = (curline, 0) | 
					
						
							|  |  |  |         box = self.anchor_widget.bbox("%d.%d" % anchor_index) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         if not box: | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |             box = list(self.anchor_widget.bbox("insert")) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |             # align to left of window | 
					
						
							|  |  |  |             box[0] = 0 | 
					
						
							|  |  |  |             box[2] = 0 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         return box[0] + 2, box[1] + box[3] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def position_window(self): | 
					
						
							|  |  |  |         "Reposition the window if needed." | 
					
						
							|  |  |  |         curline = int(self.anchor_widget.index("insert").split('.')[0]) | 
					
						
							|  |  |  |         if curline == self.lastline: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.lastline = curline | 
					
						
							|  |  |  |         self.anchor_widget.see("insert") | 
					
						
							|  |  |  |         super(CalltipWindow, self).position_window() | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |     def showtip(self, text, parenleft, parenright): | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         """Show the call-tip, bind events which will close it and reposition it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text: the text to display in the call-tip | 
					
						
							|  |  |  |         parenleft: index of the opening parenthesis in the text widget | 
					
						
							|  |  |  |         parenright: index of the closing parenthesis in the text widget, | 
					
						
							|  |  |  |                     or the end of the line if there is no closing parenthesis | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2018-06-20 02:18:49 -04:00
										 |  |  |         # Only called in calltip.Calltip, where lines are truncated | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         self.text = text | 
					
						
							|  |  |  |         if self.tipwindow or not self.text: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         self.anchor_widget.mark_set(MARK_RIGHT, parenright) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         self.parenline, self.parencol = map( | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |             int, self.anchor_widget.index(parenleft).split(".")) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         super(CalltipWindow, self).showtip() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._bind_events() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def showcontents(self): | 
					
						
							|  |  |  |         """Create the call-tip widget.""" | 
					
						
							|  |  |  |         self.label = Label(self.tipwindow, text=self.text, justify=LEFT, | 
					
						
							| 
									
										
										
										
											2019-03-10 20:18:40 -04:00
										 |  |  |                            background="#ffffd0", foreground="black", | 
					
						
							|  |  |  |                            relief=SOLID, borderwidth=1, | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |                            font=self.anchor_widget['font']) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         self.label.pack() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def checkhide_event(self, event=None): | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         """Handle CHECK_HIDE_EVENT: call hidetip or reschedule.""" | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         if not self.tipwindow: | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |             # If the event was triggered by the same event that unbound | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |             # this function, the function will be called nevertheless, | 
					
						
							|  |  |  |             # so do nothing in this case. | 
					
						
							| 
									
										
										
										
											2017-06-27 07:02:32 +03:00
										 |  |  |             return None | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Hide the call-tip if the insertion cursor moves outside of the | 
					
						
							|  |  |  |         # parenthesis. | 
					
						
							|  |  |  |         curline, curcol = map(int, self.anchor_widget.index("insert").split('.')) | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         if curline < self.parenline or \ | 
					
						
							|  |  |  |            (curline == self.parenline and curcol <= self.parencol) or \ | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |            self.anchor_widget.compare("insert", ">", MARK_RIGHT): | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |             self.hidetip() | 
					
						
							| 
									
										
										
										
											2017-06-27 07:02:32 +03:00
										 |  |  |             return "break" | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Not hiding the call-tip. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.position_window() | 
					
						
							|  |  |  |         # Re-schedule this function to be called again in a short while. | 
					
						
							|  |  |  |         if self.checkhide_after_id is not None: | 
					
						
							|  |  |  |             self.anchor_widget.after_cancel(self.checkhide_after_id) | 
					
						
							|  |  |  |         self.checkhide_after_id = \ | 
					
						
							|  |  |  |             self.anchor_widget.after(CHECKHIDE_TIME, self.checkhide_event) | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def hide_event(self, event): | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         """Handle HIDE_EVENT by calling hidetip.""" | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         if not self.tipwindow: | 
					
						
							|  |  |  |             # See the explanation in checkhide_event. | 
					
						
							| 
									
										
										
										
											2017-06-27 07:02:32 +03:00
										 |  |  |             return None | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         self.hidetip() | 
					
						
							| 
									
										
										
										
											2017-06-27 07:02:32 +03:00
										 |  |  |         return "break" | 
					
						
							| 
									
										
										
										
											2001-07-12 22:26:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |     def hidetip(self): | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         """Hide the call-tip.""" | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         if not self.tipwindow: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self.label.destroy() | 
					
						
							|  |  |  |         except TclError: | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  |         self.label = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.parenline = self.parencol = self.lastline = None | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self.anchor_widget.mark_unset(MARK_RIGHT) | 
					
						
							|  |  |  |         except TclError: | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self._unbind_events() | 
					
						
							|  |  |  |         except (TclError, ValueError): | 
					
						
							|  |  |  |             # ValueError may be raised by MultiCall | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super(CalltipWindow, self).hidetip() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _bind_events(self): | 
					
						
							|  |  |  |         """Bind event handlers.""" | 
					
						
							|  |  |  |         self.checkhideid = self.anchor_widget.bind(CHECKHIDE_EVENT, | 
					
						
							|  |  |  |                                                    self.checkhide_event) | 
					
						
							|  |  |  |         for seq in CHECKHIDE_SEQUENCES: | 
					
						
							|  |  |  |             self.anchor_widget.event_add(CHECKHIDE_EVENT, seq) | 
					
						
							|  |  |  |         self.anchor_widget.after(CHECKHIDE_TIME, self.checkhide_event) | 
					
						
							|  |  |  |         self.hideid = self.anchor_widget.bind(HIDE_EVENT, | 
					
						
							|  |  |  |                                               self.hide_event) | 
					
						
							|  |  |  |         for seq in HIDE_SEQUENCES: | 
					
						
							|  |  |  |             self.anchor_widget.event_add(HIDE_EVENT, seq) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _unbind_events(self): | 
					
						
							|  |  |  |         """Unbind event handlers.""" | 
					
						
							|  |  |  |         for seq in CHECKHIDE_SEQUENCES: | 
					
						
							|  |  |  |             self.anchor_widget.event_delete(CHECKHIDE_EVENT, seq) | 
					
						
							|  |  |  |         self.anchor_widget.unbind(CHECKHIDE_EVENT, self.checkhideid) | 
					
						
							|  |  |  |         self.checkhideid = None | 
					
						
							|  |  |  |         for seq in HIDE_SEQUENCES: | 
					
						
							|  |  |  |             self.anchor_widget.event_delete(HIDE_EVENT, seq) | 
					
						
							|  |  |  |         self.anchor_widget.unbind(HIDE_EVENT, self.hideid) | 
					
						
							|  |  |  |         self.hideid = None | 
					
						
							| 
									
										
										
										
											2005-11-18 22:05:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-01 18:52:37 -04:00
										 |  |  | def _calltip_window(parent):  # htest # | 
					
						
							| 
									
										
										
										
											2014-10-17 01:31:35 -04:00
										 |  |  |     from tkinter import Toplevel, Text, LEFT, BOTH | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     top = Toplevel(parent) | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |     top.title("Test call-tips") | 
					
						
							| 
									
										
										
										
											2016-07-10 17:28:10 -04:00
										 |  |  |     x, y = map(int, parent.geometry().split('+')[1:]) | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |     top.geometry("250x100+%d+%d" % (x + 175, y + 150)) | 
					
						
							| 
									
										
										
										
											2014-10-17 01:31:35 -04:00
										 |  |  |     text = Text(top) | 
					
						
							|  |  |  |     text.pack(side=LEFT, fill=BOTH, expand=1) | 
					
						
							|  |  |  |     text.insert("insert", "string.split") | 
					
						
							|  |  |  |     top.update() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |     calltip = CalltipWindow(text) | 
					
						
							| 
									
										
										
										
											2014-10-17 01:31:35 -04:00
										 |  |  |     def calltip_show(event): | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  |         calltip.showtip("(s='Hello world')", "insert", "end") | 
					
						
							| 
									
										
										
										
											2014-10-17 01:31:35 -04:00
										 |  |  |     def calltip_hide(event): | 
					
						
							|  |  |  |         calltip.hidetip() | 
					
						
							|  |  |  |     text.event_add("<<calltip-show>>", "(") | 
					
						
							|  |  |  |     text.event_add("<<calltip-hide>>", ")") | 
					
						
							|  |  |  |     text.bind("<<calltip-show>>", calltip_show) | 
					
						
							|  |  |  |     text.bind("<<calltip-hide>>", calltip_hide) | 
					
						
							| 
									
										
										
										
											2018-08-05 09:21:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-17 01:31:35 -04:00
										 |  |  |     text.focus_set() | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-15 18:20:55 -04:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     from unittest import main | 
					
						
							| 
									
										
										
										
											2018-06-19 23:00:35 -04:00
										 |  |  |     main('idlelib.idle_test.test_calltip_w', verbosity=2, exit=False) | 
					
						
							| 
									
										
										
										
											2018-06-15 18:20:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-24 18:48:18 -04:00
										 |  |  |     from idlelib.idle_test.htest import run | 
					
						
							|  |  |  |     run(_calltip_window) |