user2048643
user2048643

Reputation: 283

customize tkinter menu while menu is open

I want to have less popular menu items fade in gradually from white to black. Is there any way to have the colors update while the menu is still open? I've experimented with postcommand and threads:

def update():
    def color(c):
        animal_menu.config(fg=c)
        root.update()
        print c
    def adapt():
        color('white')
        root.after(100, color('#6E6E6E'))
        root.after(100, color('black'))
##  adapt() ##attempt no.1
##  thread.start_new_thread(adapt, ())  ##attempt no.2

root = Tk()
menubutton = Menubutton(root, text="Animals")
animal_menu = Menu(menubutton, tearoff=0, postcommand=update)
animal_menu.add_command(label="Lion", command=print_time)
animal_menu.add_command(label="Tiger", command=print_time)
animal_menu.add_command(label="Bear", command=print_time)
menubutton.menu = animal_menu
menubutton["menu"]  =  menubutton.menu
menubutton.pack()
root.config()
root.mainloop()

So far, the first attempt runs completely before the menu appears (which makes sense as postcommand is called before posting the menu), and the second attempt runs only when the menu is not open (which I don't understand) as evidenced by the print statements.

Could anybody give me a pointer on how to make the color properly dynamically change to have the items fade in while the menu is open?

Upvotes: 1

Views: 980

Answers (1)

atlasologist
atlasologist

Reputation: 3964

There's a couple of problems with the after method in the callback:

def update():
    def color(c):
        animal_menu.config(fg=c)
        root.update()
        print c
    def adapt():
        color('white')
        root.after(100, color('#6E6E6E'))
        root.after(100, color('black'))
    adapt() ##attempt no.1

First, if you're passing arguments to the function being called in the after, you have to use a lambda expression or just split them by a comma:

root.after(100, color, 'black')

Otherwise, the parenthesis will make that function be evaluated first.

Second, after doesn't work with the typical control flow you're probably used to--it's not evaluated one, then the next--you're setting both after calls to be evaluated after 100ms, so that's what's going to happen.

Here's a working example of a fadein callback:

from Tkinter import *

def fadein(color):
    if color < 111111:
        animal_menu.config(fg='#000000')
    else:
        print color
        animal_menu.config(fg='#' + str(color))
        root.after(100, fadein, color - 111111)

root = Tk()
menubutton = Menubutton(root, text="Animals")
animal_menu = Menu(menubutton, tearoff=0, postcommand=lambda: fadein(999999))
animal_menu.add_command(label="Lion")
animal_menu.add_command(label="Tiger")
animal_menu.add_command(label="Bear")
menubutton.menu = animal_menu
menubutton["menu"]  =  menubutton.menu
menubutton.pack()
root.config()
root.mainloop()

Note the lambda expression, in postcommand, which is needed to pass the argument to fadein().

More info: http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.after-method

Upvotes: 1

Related Questions