I am building an app in tkinter and i am using custom built buttons that i have created in illustrator. I want my button to behave as follows: 1. Button in 'off' position, 2. mouse enters button screen space and is clicked. 3. button turns on for 0.1 seconds, then turns off again. In order to achieve this, i use an 'off' image of button. When the left mouse button is clicked over the button image, the program changes the image of the button to 'on' image. then after some time say 0.1 seconds, the image switches back to 'off' position. to give a bit of context the button in question is like a selector with up and down arrows, pressing an arrow will show the relevant arrow light up, and some current selection will be changed to the next available selection.
I am having trouble doing this, no matter how i organise my code, putting 'off image', 'on image', 'off image' just shows the off image all of the time.
the code below is a succinct version of my code. I would like to point out that i am aware that how the code is written at the moment, will cause the off image to show, because im re assigning it to the 'off' image instantly :) this is just to show the example.
from tkinter import *
from PIL import ImageTk, Image
# TKINTER WINDOW
root = Tk()
root.title("example")
w=1200
h=800
ws = root.winfo_screenwidth()
hs = root.winfo_screenheight()
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
off_image = ImageTk.PhotoImage(Image.open("example_img1.png"))
on_image = ImageTk.PhotoImage(Image.open("example_img2.png"))
on_off_image_list = [off_image, on_image]
on_off_button = Label(image=on_off_image_list[0])
def on_off_func(event):
on_off_button = Label(image=on_off_image_list[1])
on_off_button.bind("<Button-1>", on_off_func)
on_off_button.place(x=100, y=100)
# NEED SOME CODE HERE THAT ENABLES THE ABOVE (ON IMAGE ) TO BE SHOWN
# THEN SHOW THE ORIGINAL (OFF IMAGE) AGAIN
on_off_button = Label(image=on_off_image_list[0])
on_off_button.bind("<Button-1>", on_off_func)
on_off_button.place(x=100, y=100)
on_off_button.bind("<Button-1>", on_off_func)
on_off_button.place(x=100, y=100)
root.mainloop()
First: you need to configure the existing Label with the config
method, NOT make a new Label to cover up the old one. Second: You need to use the after
method to schedule the reset in 0.1 seconds (100 milliseconds). Like this:
from tkinter import *
from PIL import ImageTk, Image
# TKINTER WINDOW
root = Tk()
root.title("example")
root.geometry('200x200')
off_image = ImageTk.PhotoImage(Image.open("example_img1.png"))
on_image = ImageTk.PhotoImage(Image.open("example_img2.png"))
def reset(event=None):
on_off_button.config(image=off_image)
def on_click(event=None):
on_off_button.config(image=on_image)
on_off_button.after(100, reset) # run the reset in 100 milliseconds
on_off_button = Label(image=off_image)
on_off_button.bind("<Button-1>", on_click)
on_off_button.pack()
root.mainloop()
However this is really an ideal place to make a small custom widget. Your own type of button. If you want to jump in the deep end, here's how you would do that:
import tkinter as tk # PEP8 compliant import style
from PIL import ImageTk, Image
from functools import lru_cache
@lru_cache(None)
def load_img(filename):
"""fast loading of repeated images"""
return ImageTk.PhotoImage(Image.open(filename))
class CustomButton(tk.Label):
def __init__(self, master=None, command=None, **kwargs):
self.command = command
self.off_image = load_img("example_img1.png")
self.on_image = load_img("example_img2.png")
super().__init__(master, image=self.off_image, **kwargs)
self.bind("<Button-1>", self.on_click)
def reset(self, event=None):
self.config(image=self.off_image)
def on_click(self, event=None):
self.config(image=self.on_image)
if self.command: self.command()
self.after(100, self.reset) # run the reset in 100 milliseconds
# EXAMPLE USE TKINTER WINDOW
root = tk.Tk()
root.title("example")
root.geometry('200x200')
button1 = CustomButton(root)
button1.pack()
button2 = CustomButton(root)
button2.pack()
button3 = CustomButton(root)
button3.pack()
root.mainloop()