Search code examples
pythonpython-3.xtkintertkinter-canvas

Make canvas size be the size of its content


i am trying to build a user interface with Tkinter. In doing so i used a canvas as a container (because a canvas is scrollable and a frame is not). However I noticed that a canvas does not behave like a frame in terms of size. A frame adjusts its size to the size of its content - a canvas does not (as far as I know).

I would really like the canvas to "shrink to"/be the size of its content. Is there an easy way I could do this? (mayby I missed something in the documentation and the canvas is able to do exactly that and you could point that out to me or the question was asked before already (i did not find such question myself however) and you could point me to the answer or you could provide a suggestion of how to create this effect myself)

To give an example: Here the canvas is bigger than the widget that is placed onto the canvas. (I colored the canvas black to make it more obvious). How can I make the size of the canvas be the same as the size of the item on the canvas (button1 in this example).

from tkinter import Tk, Canvas, Button



window = Tk()



my_canvas = Canvas(window, highlightthickness=0, borderwidth=0)
my_canvas.grid(column=1, row=1)
my_canvas.config(background="black")

button1 = Button(my_canvas, text="testest", width=10, height=10)
window_id = my_canvas.create_window((0, 0), window=button1, anchor="nw")


window.mainloop()

A possible solution I came up with:

from tkinter import Tk, Canvas, Button



window = Tk()



my_canvas = Canvas(window, highlightthickness=0, borderwidth=0)
my_canvas.grid(column=1, row=1)
my_canvas.config(background="black")

button1 = Button(my_canvas, text="testest", width=10, height=10)
window_id = my_canvas.create_window((0, 0), window=button1, anchor="nw")

window.update()
my_canvas.configure(width=button1.winfo_width(), height=button1.winfo_height())


window.mainloop()

This however shows the canvas with its undesired/old size for a split second before switching to the new adjusted size. It doesn't feel right. Is there a better way to do this? Any help is appreciated.

PS: Of course i could just add

window.update()
print(button1.winfo_width())
print(button1.winfo_height())

to my code and run it to get the pixel width and height of my button and adjust my code so that the canvas is initialized with these values but I want it to work even when the size of the widget that i place on the canvas is not predetermined (for example a frame which might grow when other widgets are placed into it).


Solution

  • You can use the bbox method to get the bounding box of the contents of the canvas and then use that information to compute and reconfigure the canvas.

    The bbox method returns the data in the form (x1,y1,x2,y2) such that the drawn areas of all the named elements are within the region bounded by x1 on the left, x2 on the right, y1 on the top, and y2 on the bottom.

    note: according to the canonical documentation, "The return value may overestimate the actual bounding box by a few pixels."

    x1,y1,x2,y2 = my_canvas.bbox("all")
    width = x2-x1
    height=y2-y1
    my_canvas.configure(width=width, height=height)