thestarsatnight
thestarsatnight

Reputation: 757

Is it possible to put a canvas as the icon of a button?

I have nice polygon I drew in a Tkinter canvas. I would like put that as the icon of a Tkinter button.

I see in this how to put a button on a canvas, but that covers up the polygon. And of course, if I put the polygon over the button, the button is not accessible.

I am trying to avoid using an external image. I'd like to keep the code self-contained and not be dependent on the path to an image. Things will be break if we ever move the code.

from Tkinter import *

class Example(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.widget()

    def Xshape (self,canvas,x,y,p,t,outline='black',fill='green',width=1):

        points =  []
        s = t - p
        for i in (1,-1):
            points.extend(( x,         y + (i*p) ))
            points.extend(( x + (i*s), y + (i*t) ))
            points.extend(( x + (i*t), y + (i*s) ))
            points.extend(( x + (i*p), y))
            points.extend(( x + (i*t), y - (i*s)  ))
            points.extend(( x + (i*s), y - (i*t)  ))

        canvas.create_polygon(points,outline=outline,
                              fill=fill,width=width)

    def widget(self):

        cWidth=64.
        cHeight=64.
        self.canv = Canvas(frame,width=cWidth,height=cHeight)
        self.Xshape(self.canv, (cWidth+2)/2,(cHeight+2)/2, cHeight/5,cHeight/2)
        self.toggle = Button(frame,relief=FLAT,text='test')
        self.win = self.canv.create_window(cWidth/2,cHeight/2,anchor=CENTER,window=self.toggle)  
        self.canv.grid(row=0,column=2)


root = Tk()
root.geometry('100x100+10+50')
root.wm_title("Telescope Health Status")
root.grid_rowconfigure( 1, weight=1)
root.grid_columnconfigure( 0, weight=1)

frame = Frame(root,bg='light blue',padx=30)
frame.grid(row=0, column=0, columnspan=20, sticky='ew')

app = Example(root)
app.mainloop()

This only puts a button over the polygon. I want to put the polygon inside the button.

Upvotes: 1

Views: 254

Answers (1)

j_4321
j_4321

Reputation: 16169

Instead of putting your canvas in a button, you can make the canvas itself emulates a button's behavior by using bindings on Enter, Leave and mouse events:

  • The Canvas relief is set to "raised" with a borderwidth of 1 to make it look like a button

  • The background color is changed into a lighter grey when the cursor enters the Canvas

  • The relief is set to "sunken" when the mouse button 1 is pressed

  • The command given in argument is executed when button 1 is released but only if the cursor is still on the Canvas and the relief is et back to "raised"

  • The relief is set back to "raised" and the normal background color is restored when the cursor leaves the Canvas

    from Tkinter import *
    
    class ButtonCanvas(Canvas):
        def __init__(self, master=None, command=lambda: None, **kw):
            Canvas.__init__(self, master, relief="raised", bd=1, **kw)
            self._bg = self.cget("bg")
            cWidth = 64
            cHeight = 64
            self.configure(width=64, height=64)
            self.Xshape((cWidth+2)/2,(cHeight+2)/2, cHeight/5,cHeight/2)
            self.command = command
    
            self.bind("<Enter>", self.on_enter)
            self.bind("<Leave>", self.on_leave)
            self.bind("<ButtonPress-1>", self.on_b1press)
            self.bind("<ButtonRelease-1>", self.on_b1release)
    
        def on_enter(self, event):
            self.configure(background="#ececec")
    
        def on_leave(self, event):
            self.configure(background=self._bg)
            self.configure(relief="raised")
    
        def on_b1press(self, event):
            self.configure(relief="sunken")
    
        def on_b1release(self, event):
            self.configure(relief="raised")
            if self.winfo_containing(event.x_root, event.y_root) == self:
                self.command()
    
        def Xshape (self,x,y,p,t,outline='black',fill='green',width=1):
    
            points =  []
            s = t - p
            for i in (1,-1):
                points.extend(( x,         y + (i*p) ))
                points.extend(( x + (i*s), y + (i*t) ))
                points.extend(( x + (i*t), y + (i*s) ))
                points.extend(( x + (i*p), y))
                points.extend(( x + (i*t), y - (i*s)  ))
                points.extend(( x + (i*s), y - (i*t)  ))
    
            self.create_polygon(points,outline=outline,
                                fill=fill,width=width)
    
    
    root = Tk()
    
    def cmd():
        print("ok")
    
    ButtonCanvas(root, command=cmd).pack()
    Button(root, command=cmd).pack()
    
    root.mainloop()
    

Upvotes: 1

Related Questions