How to make a Button using the tkinter Canvas widget?

11,930

Tkinter doesn't allow you to directly draw on widgets other than the canvas, and canvas drawings will always be below embedded widgets.

The simple solution is to create the effect of a button using just the canvas. There's really nothing special about doing this: just create a canvas, then add bindings for ButtonPress and ButtonRelease to simulate a button being pressed.

Here's a rough idea:

class CustomButton(tk.Canvas):
    def __init__(self, parent, width, height, color, command=None):
        tk.Canvas.__init__(self, parent, borderwidth=1, 
            relief="raised", highlightthickness=0)
        self.command = command

        padding = 4
        id = self.create_oval((padding,padding,
            width+padding, height+padding), outline=color, fill=color)
        (x0,y0,x1,y1)  = self.bbox("all")
        width = (x1-x0) + padding
        height = (y1-y0) + padding
        self.configure(width=width, height=height)
        self.bind("<ButtonPress-1>", self._on_press)
        self.bind("<ButtonRelease-1>", self._on_release)

    def _on_press(self, event):
        self.configure(relief="sunken")

    def _on_release(self, event):
        self.configure(relief="raised")
        if self.command is not None:
            self.command()

To complete the illusion you'll want to set a binding on <Enter> and <Leave> (to simulate the active state), and also make sure that the cursor is over the button on a button release -- notice how real buttons don't do anything if you move the mouse away before releasing.

Share:
11,930
Zagorax
Author by

Zagorax

Updated on June 21, 2022

Comments

  • Zagorax
    Zagorax almost 2 years

    I want to obtain a button out of a Canvas. I've tried to pack the canvas in the button widget, but that didn't work. Googling a bit, I've found (here: How do you create a Button on a tkinter Canvas?) that the Canvas method create_window might help. But there should be something wrong in the way I'm using it.

    import Tkinter
    
    DIM = 100
    
    root = Tkinter.Tk()
    frame = Tkinter.Frame(root)
    
    button = Tkinter.Button(None, width=DIM, height=DIM, command=root.quit)
    
    circle = Tkinter.Canvas(frame, width=DIM, height=DIM)
    circle.create_oval(5, 5, DIM-5, DIM-5, fill="red")
    circle.create_window(0, 0, window=button)
    
    frame.grid()
    circle.grid(row=1, column=1)
    
    root.mainloop()
    

    If I erase the create_window line, I can se my painting but I can't (obviously) click on it. But in this way, the button widget cover my circle and shows a sad empty button.

    Basically, I want to create a button with a red circle painted inside.

  • WinEunuuchs2Unix
    WinEunuuchs2Unix almost 3 years
    I found your answer reposted as a question in: Rounded button tkinter python and suggested an edit to link that question here.