'How does one get tooltips working for notebook tabs in python tkinter?
I'm working with python 3.8 and tkinter to create a GUI that uses a ttk notebook (tabbed window). I want to have tooltips displayed for the individual tabs but I can't see how to add the tooltip for the tab. I have a class defined that takes an object and text and adds the tooltip and it works for buttons and other objects.
I have example code here:
'''
At no time were Katz, Dawgs, Mice or Squirrels harmed in any way during the development of this application. But...
Well just lets say that skunk next door had it coming!
'''
import tkinter as tk
from tkinter import ttk
class CreateToolTip(object):
"""
create a tooltip for a given widget
"""
def __init__(self, widget, text='widget info'):
self.waittime = 250 #miliseconds
self.wraplength = 180 #pixels
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.leave)
self.widget.bind("<ButtonPress>", self.leave)
self.id = None
self.tw = None
def enter(self, event=None):
self.schedule()
def leave(self, event=None):
self.unschedule()
self.hidetip()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.showtip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.widget.after_cancel(id)
def showtip(self, event=None):
x = y = 0
x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 20
# creates a toplevel window
self.tw = tk.Toplevel(self.widget)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(self.tw, text=self.text, justify='left',
background="cornsilk", relief='solid', borderwidth=1,
wraplength = self.wraplength)
label.pack(ipadx=1)
def hidetip(self):
tw = self.tw
self.tw= None
if tw:
tw.destroy()
class MyCanvas(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tabControl = ttk.Notebook(self)
self.geometry('600x300')
self.title('An Indepth study of the Differences Between Katz and Dawgs!')
self.tab = {}
self.tab['dawg tab'] = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab['dawg tab'], text='dawgs', state='normal')
self.tab['katz tab'] = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab['katz tab'], text='katz', state='normal')
self.tabControl.enable_traversal()
btn1 = tk.Button(self.tab['dawg tab'], text='Squirrel')
btn1.grid(row=1, column=0)
CreateToolTip(btn1, 'Despised by dawgs!!')
btn2 = tk.Button(self.tab['katz tab'], text='Mice')
btn2.grid(row=1, sticky='EW')
CreateToolTip(btn2, 'Katz find them a tasty treat.')
self.tabControl.grid(row=0, column=0, sticky="W")
# testing ...
if __name__ == '__main__':
root = MyCanvas()
root.mainloop()
Any advice or constructive comments would be greatly appreciated. And yes, the tooltip class was totally plagiarized from someone else here on stack overflow.
Cheers!!
So after a few days and lots of help from a friend here is what I have that actually works:
'''
At no time were Katz, Dawgs, Mice or Squirrels harmed in any way during the development of this application. But...
Well lets just say that skunk next door had it coming!
'''
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.scrolledtext as tkst
class MyCanvas(ttk.Notebook):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tooltips = dict()
self.tab_list = dict()
self.current_tab = None
self.waittime = 250 #miliseconds
self.wraplength = 180 #pixels
self.id = None
self.tw = None
self.bind('<Motion>', self.motion)
self.bind('<ButtonPress>', self.leave)
self.line_number = 0
def leave(self, event=None):
self.unschedule()
self.hide_tip()
def schedule(self):
self.unschedule()
self.id = self.after(self.waittime, self.show_tip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.after_cancel(id)
def show_tip(self):
if self.tw:
return
if self.current_tab in self.tooltips.keys():
x = y = 0
x, y, cx, cy = self.bbox('insert')
x += self.winfo_rootx() + 25
y += self.winfo_rooty() - 20
# creates a toplevel window
self.tw = tk.Toplevel(self)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry(f'+{x}+{y}')
self.tw.wm_attributes('-topmost', True)
label = ttk.Label(self.tw, text=self.tooltips[self.current_tab], background='cornsilk')
label.pack()
def add(self, *args, **kwargs):
if 'tooltip' in kwargs.keys():
tooltip = kwargs['tooltip']
del kwargs['tooltip']
else:
tooltip = None
self.tab_list[kwargs.get('text')] = args[0]
super().add(*args, **kwargs)
if tooltip and ('text' in kwargs.keys()):
self.set_tooltip(kwargs['text'], tooltip)
def set_tooltip(self, tab_text, tooltip):
for idx in range(self.index('end')):
if self.tab(idx, 'text') == tab_text:
self.tooltips[tab_text] = tooltip
return True
return False
def motion(self, event):
if self.identify(event.x, event.y) == 'label':
index = self.index(f'@{event.x},{event.y}')
if self.current_tab != self.tab(index, 'text'):
self.current_tab = self.tab(index, 'text')
self.show_tip()
else:
self.hide_tip()
self.current_tab = None
def hide_tip(self):
tw = self.tw
self.tw = None
if tw:
tw.destroy()
def insert_text_to_tab(self, **kwargs):
string_var = tk.StringVar()
if 'tab' in kwargs.keys():
the_tab = kwargs.get('tab')
string_var.set(kwargs.get('data'))
the_label = tk.Label(self.tab_list[the_tab], textvariable=string_var, background='lightblue')
the_label.pack()
def get_scrolled_text_widget(parent):
st_widget = tkst.ScrolledText(parent)
return st_widget
if __name__ == '__main__':
root = tk.Tk()
root.title('An Indepth study of the Differences Between Katz and Dawgs!')
canvas = MyCanvas(root)
canvas.pack(fill='both', expand=1)
canvas.add(get_scrolled_text_widget(canvas), text='Dawgz', tooltip='A quadraped that doesn\'t look like a kat')
canvas.add(get_scrolled_text_widget(canvas), text='Katz', tooltip='A quadraped that doesn\'t look like a dawg')
canvas.insert_text_to_tab(data='Dawgz hate squirrels!!', tab='Dawgz')
canvas.insert_text_to_tab(data='Katz find mice a tasty treat.', tab='Katz')
root.mainloop()
So it would appear that I was thinking about it a bit wrong and I had only to think of each tabbed entry as an object instead of an attribute (I'm sure the python peanut gallery are going to be all over me for saying that) the solution is quit straight forward.
Cheers!!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
