Delrius Euphoria
Delrius Euphoria

Reputation: 15098

Smooth fading of window before closing tkinter python

I made a simple tooltip with tkinter and added a fade effect but then there is problem with code:

import tkinter as tk
import time

class ToolTips():
    def __init__(self,widget,text,triggerkey='<Enter>',releasekey='<Leave>'):
        self.widget = widget
        self.text = text
        self.bg = '#ffffe0'
        self.widget.bind(triggerkey,self.add)
        self.widget.bind(releasekey,self.remove)
        self.widget.bind('<ButtonPress>',self.remove)
        
    def add(self,event):
        self.master = tk.Toplevel(bg=self.bg)
        self.master.geometry(f'+{event.x_root}+{event.y_root}')
        self.master.overrideredirect(1)
        self.master.attributes('-topmost',True)
        self.frame = tk.Frame(self.master,bg=self.bg,highlightbackground="black", highlightcolor="black", highlightthickness=1)
        self.frame.pack()
        self.label = tk.Label(self.frame,text=self.text,bg=self.bg,justify=tk.LEFT)
        self.label.pack(padx=1,pady=3)
    
    def remove(self,*args):
        alpha = self.master.attributes('-alpha')
        if alpha > 0:
            alpha -= 0.5
            self.master.attributes('-alpha',alpha)
            self.master.after(100,self.remove)
        else:
            self.master.destroy()

#USAGE OF CLASS
root = tk.Tk()

l = tk.Label(root,text='Hover',font=('hevletica',21))
l.pack()
l.focus_force()

obj = ToolTips(l,text='There is alot more to this? Are you curious?\nBla Bla...')

root.mainloop()

Here remove() was supposed to give a fade effect, but then its not clean and sometimes if you leave the label and enter the label faster than 100 ms, then the tooltip does not destroy and will stay there for ever till the application is completely exited. So is there any easy way,if not, any way, to achieve a smooth fade for this tooltip. Also feel free to point out other mistakes in the code too :)

The answer here has a fade effect, but I dont seem to be able to implement it with my code.

Thanks in advance :D

Upvotes: 1

Views: 284

Answers (2)

jizhihaoSAMA
jizhihaoSAMA

Reputation: 12672

Try to use after_cancel. For efficiency, There is no need to create the Toplevel each time when mouse hover this text.Just set the -alpha to 0.

import tkinter as tk
import time


class ToolTips():
    def __init__(self, widget, text, triggerkey='<Enter>', releasekey='<Leave>'):
        self.widget = widget
        self.text = text
        self.bg = '#ffffe0'
        self.widget.bind(triggerkey, self.add)
        self.widget.bind(releasekey, self.remove)
        self.widget.bind('<ButtonPress>', self.remove)
        self.hide_status = None

        self.master = tk.Toplevel(bg=self.bg)
        self.master.overrideredirect(1)
        self.master.attributes('-topmost', True)
        self.frame = tk.Frame(self.master, bg=self.bg, highlightbackground="black", highlightcolor="black",
                              highlightthickness=1)
        self.frame.pack()
        self.label = tk.Label(self.frame, text=self.text, bg=self.bg, justify=tk.LEFT)
        self.label.pack(padx=1, pady=3)
        self.master.attributes('-alpha', 0)

    def add(self, event):
        self.master.attributes('-alpha', 1)
        self.master.geometry(f'+{event.x_root}+{event.y_root}')

    def remove(self, event, alpha=1):
        if alpha > 0:
            alpha -= 0.01
            self.master.attributes('-alpha', alpha)
            if self.hide_status:
                self.master.after_cancel(self.hide_status)
            self.hide_status = self.master.after(10, lambda : self.remove(event=None, alpha=alpha))
        else:
            self.master.attributes('-alpha', 0)


# USAGE OF CLASS
root = tk.Tk()

l = tk.Label(root, text='Hover', font=('hevletica', 21))
l.pack()
l.focus_force()

obj = ToolTips(l, text='There is alot more to this? Are you curious?\nBla Bla...')

root.mainloop()

Upvotes: 1

Thingamabobs
Thingamabobs

Reputation: 8057

The problem in which you running into is that you creating a new tooltip before you sure that there is the last one destroyed. So your reference might be loosed sometimes. To avoid that you need to keep track of the recent and destroy it before crafting a new.

import tkinter as tk
import time

class ToolTips():
    def __init__(self,widget,text,triggerkey='<Enter>',releasekey='<Leave>'):
        self.widget = widget
        self.text = text
        self.bg = '#ffffe0'
        self.widget.bind(triggerkey,self.add)
        self.widget.bind(releasekey,self.remove)
        self.widget.bind('<ButtonPress>',self.remove)
        self.recent = None
        
    def add(self,event):
        if self.recent != None:
            self.recent.destroy()
        self.master = tk.Toplevel(bg=self.bg)
        self.master.geometry(f'+{event.x_root}+{event.y_root}')
        self.master.overrideredirect(1)
        self.master.attributes('-topmost',True)
        self.frame = tk.Frame(self.master,bg=self.bg,highlightbackground="black", highlightcolor="black", highlightthickness=1)
        self.frame.pack()
        self.label = tk.Label(self.frame,text=self.text,bg=self.bg,justify=tk.LEFT)
        self.label.pack(padx=1,pady=3)
        self.recent=self.master
    
    def remove(self,*args):
        alpha = self.master.attributes('-alpha')
        if alpha > 0:
            alpha -= 0.5
            self.master.attributes('-alpha',alpha)
            self.master.after(100,self.remove)
        else:
            self.master.destroy()

#USAGE OF CLASS
root = tk.Tk()

l = tk.Label(root,text='Hover',font=('hevletica',21))
l.pack()
l.focus_force()

obj = ToolTips(l,text='There is alot more to this? Are you curious?\nBla Bla...')

root.mainloop()

Upvotes: 1

Related Questions