質問

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.

役に立ちましたか?

解決

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.

他のヒント

What you can do is bind the canvas to the mouse:

import Tkinter

DIM = 100

root = Tkinter.Tk()
frame = Tkinter.Frame(root)

circle = Tkinter.Canvas(frame)
circle.create_oval(5, 5, DIM-5, DIM-5, fill="red")

frame.grid()
circle.grid(row=1, column=1)

##################################
def click(event):
    root.quit()

circle.bind("<Button-1>", click)
##################################

root.mainloop()

Now, if a user clicks inside the canvas, function click will be called (essentially, the canvas has now been made a button).

Notice though that function click will be called if a user clicks anywhere in the canvas. If you want to make it so that click is only called when a user clicks in the circle, you can use event.x and event.y to get a hold of the x and y coordinates of the click. Once you have those, you can run a calculation to determine whether those coordinates are within the circle. Here is a reference on that.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top