15

I created a Frame and then a Canvas.
What I want to do next is to add a Button on the Canvas.
However, when I packed the Button I cannot see the Canvas!

Here is what I tried:

from Tkinter import Tk, Canvas, Frame, Button
from Tkinter import BOTH, W, NW, SUNKEN, TOP, X, FLAT, LEFT

class Example(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.initUI()

    def initUI(self):
        self.parent.title("Layout Test")
        self.config(bg = '#F0F0F0')
        self.pack(fill = BOTH, expand = 1)
                #create canvas
        canvas1 = Canvas(self, relief = FLAT, background = "#D2D2D2",
                                            width = 180, height = 500)
        canvas1.pack(side = TOP, anchor = NW, padx = 10, pady = 10)
        #add quit button
        button1 = Button(canvas1, text = "Quit", command = self.quit,
                                                            anchor = W)
        button1.configure(width = 10, activebackground = "#33B5E5",
                                                        relief = FLAT)
        button1.pack(side = TOP)

def main():
    root = Tk()
    root.geometry('800x600+10+50')
    app = Example(root)
    app.mainloop()

if __name__ == '__main__':
    main()
Honest Abe
  • 8,430
  • 4
  • 49
  • 64
Tong
  • 2,057
  • 2
  • 15
  • 20

3 Answers3

20

The Tkinter pack manager tries to resize the parent widget to the correct size to contain its child widgets, and no larger, by default. So the canvas is there - but it's precisely the same size as the button, and thus invisible.

If you want to place a widget on a canvas without causing the canvas to dynamically resize, you want the Canvas.create_window() function:

# ... snip ...
button1 = Button(self, text = "Quit", command = self.quit, anchor = W)
button1.configure(width = 10, activebackground = "#33B5E5", relief = FLAT)
button1_window = canvas1.create_window(10, 10, anchor=NW, window=button1)

This will create your button with upper-left corner at (10, 10) relative to the canvas, without resizing the canvas itself.

Note that you could replace the window argument with a reference to any other Tkinter widget. One caveat, though: the named widget must be a child of the top-level window that contains the canvas, or a child of some widget located in the same top-level window.

atomicinf
  • 3,596
  • 19
  • 17
3

you can use button1.place(x=0,y=0) geometry manager instead of pack(side =TOP) pack resize the master widget to makes it large enough to hold the child widget

http://effbot.org/tkinterbook/pack.htm#Tkinter.Pack.pack_propagate-method

http://effbot.org/tkinterbook/place.htm

Younes
  • 47
  • 2
3

I had the exact same problem. There isn't an official way that I know, but here's a way around it:

from Tkinter import *
root = Tk()
def clicked(event):
    print("pressed")
canvas1 = Canvas(root, relief = FLAT, background = "#D2D2D2")
canvas1.pack()
buttonBG = canvas1.create_rectangle(0, 0, 100, 30, fill="grey40", outline="grey60")
buttonTXT = canvas1.create_text(50, 15, text="click")
canvas1.tag_bind(buttonBG, "<Button-1>", clicked) ## when the square is clicked runs function "clicked".
canvas1.tag_bind(buttonTXT, "<Button-1>", clicked) ## same, but for the text.
root.mainloop()