0

I'm currently trying to make buttons using labels in tkinter. These buttons have an 'Enter', 'Leave' and 'Button-1' binding. When the user hovers over the label, the image changes and get back to normal when he leaves the label. When he clicks, the event label changes its image and set the other labels' image to "normal", but somehow it doesn't work. When a label has been clicked on, if I click on another label and hover over the first one, the image changes once as expected with the 'Enter' binding, but won't change on 'Leave'. I'm using Python 3.6 along with tkinter and PIL for the images.

Here's the concerned function :

def changeBtnPic(buttons, pics, pics_hover, width, height, event) :
    for i in range (0, len(buttons)) : 
        clicked_frame = event.widget
        if clicked_frame == buttons[i] : 
            changeImage(pics_hover[i], buttons[i], width, height)
            buttons[i].unbind('<Leave>')
        elif clicked_frame != buttons[i] :
            changeImage(pics[i], buttons[i], width, height)
            buttons[i].bind('<Leave>', changeImage(pics[i], buttons[i], width, height))

This is the changeImage function it calls :

def changeImage(img_loc, container, width, height) :
    img = PIL.Image.open(img_loc)
    img = img.resize((width, height), PIL.Image.ANTIALIAS)
    img = PIL.ImageTk.PhotoImage(img)

    container.config(image = img)
    container.image = img

Here are the labels creation and binding :

lbtn_npcs    = generateImage(dir + '/resources/images/buttons/npcs.png', 204, 100, fr_top,  bg = '#252A29', x = '1054', y = '0', cur = 'hand2')
lbtn_enemies = generateImage(dir + '/resources/images/buttons/enemies.png', 204, 100, fr_top,  bg = '#252A29', x = '830', y = '0', cur = 'hand2')
lbtn_players = generateImage(dir + '/resources/images/buttons/players.png', 204, 100, fr_top,  bg = '#252A29', x = '606', y = '0', cur = 'hand2')

buttons_top = [lbtn_npcs, lbtn_enemies, lbtn_players]
pics        = [dir + "/resources/images/buttons/npcs.png", dir + "/resources/images/buttons/enemies.png", dir + "/resources/images/buttons/players.png"]
pics_hover  = [dir + "/resources/images/buttons/npcs_hover.png", dir + "/resources/images/buttons/enemies_hover.png", dir + "/resources/images/buttons/players_hover.png"].

lbtn_npcs.bind("<Enter>", lambda event : changeImage(dir + "/resources/images/buttons/npcs_hover.png", lbtn_npcs, 204, 100))
lbtn_npcs.bind("<Leave>", lambda event : changeImage(dir + "/resources/images/buttons/npcs.png", lbtn_npcs, 204, 100))
lbtn_npcs.bind("<Button-1>", lambda event : changeBtnPic(buttons_top, pics, pics_hover, 204, 100, event))

lbtn_enemies.bind("<Enter>", lambda event : changeImage(dir + "/resources/images/buttons/enemies_hover.png", lbtn_enemies, 204, 100))
lbtn_enemies.bind("<Leave>", lambda event : changeImage(dir + "/resources/images/buttons/enemies.png", lbtn_enemies, 204, 100))
lbtn_enemies.bind("<Button-1>", lambda event : changeBtnPic(buttons_top, pics, pics_hover, 204, 100, event))

lbtn_players.bind("<Enter>", lambda event : changeImage(dir + "/resources/images/buttons/players_hover.png", lbtn_players, 204, 100))
lbtn_players.bind("<Leave>", lambda event : changeImage(dir + "/resources/images/buttons/players.png", lbtn_players, 204, 100))
lbtn_players.bind("<Button-1>", lambda event : changeBtnPic(buttons_top, pics, pics_hover, 204, 100, event))

def generateImage(img_loc, width, height, container, **kwargs) :

    bg = kwargs.get('bg')
    x  = kwargs.get('x')
    y  = kwargs.get('y')

    img = PIL.Image.open(img_loc)
    img = img.resize((width, height), PIL.Image.ANTIALIAS)
    img = PIL.ImageTk.PhotoImage(img)

    lb_img = tk.Label(container, image = img, bg = "#252A29")
    if bg :
        lb_img.config(bg = bg)
    lb_img.image = img
    lb_img.pack()

    if x and y :
        lb_img.place(x = int(x), y = int(y))
    return(lb_img)

I suppose the rebind is not working but I don't know if this really is the cause nor how to fix it. Do you have any ideas or leads?

Thank you !

KanarchiK
  • 33
  • 3
  • You can't post pictures of your code, we need you to copy and paste the actual code :) – Daniel Oct 26 '17 at 09:34
  • @Coal_ Done. Sorry, I didn't see this option ^^ – KanarchiK Oct 26 '17 at 09:40
  • There's a little issue with your indentation as well, please fix the first code block. Please provide at least a full example. Where's your code for `lbtn_npcs`, `lbtn_enemies` and `lbtn_players`? Please paste everything here if you can. – Daniel Oct 26 '17 at 09:46
  • @Coal_ Done again, sorry for the mistakes ! – KanarchiK Oct 26 '17 at 10:05

1 Answers1

0

Instead of using lambda, try using partial. Include from functools import partial at the top of your code, and replace all calls like

lambda event : changeImage(dir + "/resources/images/buttons/players_hover.png", lbtn_players, 204, 100)

with

partial(change_image, dir + "/resources/images/buttons/players_hover.png", lbtn_players, 204, 100)

The link below should explain to you why this works (Assuming I have identified the issue correctly). How does the functools partial work in Python?

Benjamin James Drury
  • 2,353
  • 1
  • 14
  • 27
  • It didn't work. It raises a TypeError, telling me the function takes 4 arguments but 5 were given, which is normal if I understood the partial function correctly. Yet I don't understand what the 5th argument could be. Still thanks for this function, I had never heard of it, I might dig into it a little more ! – KanarchiK Oct 26 '17 at 19:50
  • I think bind might be trying to pass an event object when calling change_image through partial, which is why it's saying 5 arguments. Try adding an additional parameter. – Benjamin James Drury Oct 27 '17 at 08:08