pythonuser-interfacetkinterfigma

TKinter button over transparent background


I am trying to understand how to apply a button to a transparent background while keeping its shape. When I generate the code below, there is a gray background around the border that appears, and it also looks like it loses its shape.

Colors Used

Sidebar: #2E3A4B at 53%

Button: #2C2F33 at 100%

from tkinter import *


def btn_clicked():
    """ Prints to console a message every time the button is clicked """
    print("Button Clicked")


root = Tk()

# Configures the frame, and sets up the canvas
root.geometry("1440x1024")
root.configure(bg="#ffffff")
canvas = Canvas(root, bg="#ffffff", height=1024, width=1440, bd=0, highlightthickness=0, relief="ridge")
canvas.place(x=0, y=0)

background_img = PhotoImage(file=f"background.png")
background = canvas.create_image(719.5, 512.5, image=background_img)

img0 = PhotoImage(file=f"img0.png")
alarm_button = Button(image=img0, borderwidth=0, highlightthickness=0, command=btn_clicked, relief="flat")

alarm_button.place(x=9, y=119, width=90, height=90)

root.resizable(False, False)
root.mainloop()

Required Images

enter image description here enter image description here

How it looks:

enter image description here

How it should look:

enter image description here


Solution

  • Good news! I was able to get that answer to a related question you found to work. To make it easier to reuse I've converted it into a formal class and added a couple of methods. In addition I made it flash the image off and back on when it's clicked to give the user some visual feedback like "real" tkinter Buttons do.

    Note that it responds to mouse button <ButtonRelease-1> events. That's a better choice in most cases than the <Button-1> event because if the user accidentally presses the button, they can move the mouse off the widget image to avoid setting off the event.

    Turns out that using the PIL module was unnecessary. Here's the code:

    import tkinter as tk  # PEP 8 recommends avoiding `import *`.
    
    
    class CanvasButton:
        """ Create leftmost mouse button clickable canvas image object.
    
        The x, y coordinates are relative to the top-left corner of the canvas.
        """
        flash_delay = 100  # Milliseconds.
    
        def __init__(self, canvas, x, y, image_path, command, state=tk.NORMAL):
            self.canvas = canvas
            self.btn_image = tk.PhotoImage(file=image_path)
            self.canvas_btn_img_obj = canvas.create_image(x, y, anchor='nw', state=state,
                                                          image=self.btn_image)
            canvas.tag_bind(self.canvas_btn_img_obj, "<ButtonRelease-1>",
                            lambda event: (self.flash(), command()))
        def flash(self):
            self.set_state(tk.HIDDEN)
            self.canvas.after(self.flash_delay, self.set_state, tk.NORMAL)
    
        def set_state(self, state):
            """ Change canvas button image's state.
    
            Normally, image objects are created in state tk.NORMAL. Use value
            tk.DISABLED to make it unresponsive to the mouse, or use tk.HIDDEN to
            make it invisible.
            """
            self.canvas.itemconfigure(self.canvas_btn_img_obj, state=state)
    
    
    BGR_IMG_PATH = "sunset_background.png"
    BUTTON_IMG_PATH = "alarm_button.png"
    
    def btn_clicked():
        """ Prints to console a message every time the button is clicked """
        print("Button Clicked")
    
    root = tk.Tk()
    
    background_img = tk.PhotoImage(file=BGR_IMG_PATH)
    bgr_width, bgr_height = background_img.width(), background_img.height()
    
    root.geometry(f'{bgr_width}x{bgr_height}')
    root.title("TKinter button over transparent background")
    root.configure(bg="white")
    
    canvas = tk.Canvas(root, bg="white", height=bgr_height, width=bgr_width, bd=0,
                       highlightthickness=0, relief="ridge")
    canvas.place(x=0, y=0)
    
    background = canvas.create_image(0, 0, anchor='nw', image=background_img)
    
    canvas_btn1 = CanvasButton(canvas, 0, 128, BUTTON_IMG_PATH, btn_clicked)
    canvas_btn2 = CanvasButton(canvas, 0, 256, BUTTON_IMG_PATH, btn_clicked)
    
    root.resizable(False, False)
    root.mainloop()
    

    Screenshot of the result:

    Screenshot of the result

    Close up:

    screenshot of close up