0

I'm trying to build a Tkinter form in Python that has three "frames": a narrow frame across the top (for a title), a thin frame down the left (for buttons), and a main frame (to show content). The left frame will have buttons, and the top and main frames will have canvases that can be drawn on. I'd like to use "grid" placement of the Tkinter widgets to simplify layout.

The code below works perfectly if I don't include the canvas in the top panel: the frames, the buttons in the left frame, and the canvas in the main frame are all laid out exactly as I want (and note particularly that the canvas in the main frame sized itself to fit allotted space). (See Figure 1.)

However, when I add a canvas to the top panel (in exactly the same manner as the canvas in the main panel), it doesn't size itself properly. (The width is correct, but the height isn't.) (Figure 2.) How to I make the canvas in the top panel size itself properly (and/or what am I doing wrong)?

Figure 1: Without Canvas in the Top Panel Figure 1 - Without Canvas in Top Panel

Figure 2: With Canvas Added To the Top Panel Figure 2 - With Canvas Added to Top Panel

from tkinter import Tk
tk = Tk()

from tkinter import Frame, Canvas, Button

screenWidth = tk.winfo_screenwidth()
screenHeight = tk.winfo_screenheight()

formWidth = int(screenWidth * 0.8)
formHeight = int(screenHeight * 0.8)
formLeft = (screenWidth - formWidth) // 2
formTop = (screenHeight - formHeight) // 2
tk.geometry(f'{formWidth}x{formHeight}+{formLeft}+{formTop}')
tk.resizable(False, False)
tk.columnconfigure(0, weight=10)
tk.columnconfigure(1, weight=90)
tk.rowconfigure(0, weight=10)
tk.rowconfigure(1, weight=90)

topFrame = Frame(tk, bg="blue", highlightthickness=0)
topFrame.grid(column=0, row=0, columnspan=2, sticky="nwes")
topFrame.columnconfigure(0, weight=1)
topFrame.rowconfigure(0, weight=1)

leftFrame = Frame(tk, bg="green", highlightthickness=0)
leftFrame.grid(column=0, row=1, sticky="nwes")
leftFrame.columnconfigure(0, weight=1)
#leftFrame.rowconfigure(0, weight=1)

mainFrame = Frame(tk, bg="purple", highlightthickness=0)
mainFrame.grid(column=1, row=1, sticky="nwes")
mainFrame.columnconfigure(0, weight=1)
mainFrame.rowconfigure(0, weight=1)

startButton = Button(leftFrame, text="Start")
startButton.grid(column=0, row=0, sticky="ew")

stopButton = Button(leftFrame, text="Stop")
stopButton.grid(column=0, row=1, sticky="ew")

titleCanvas = Canvas(topFrame, bg="indigo", highlightthickness=0)
titleCanvas.grid(column=0, row=0, sticky="nwes")

mainCanvas = Canvas(mainFrame, bg="gray", highlightthickness=0)
mainCanvas.grid(column=0, row=0, sticky="nwes")

tk.update()

# Show that the main canvas has sized itself correctly.
mainCanvas.create_line(0, 0, mainCanvas.winfo_width(), mainCanvas.winfo_height(), width=2, fill="black")

tk.mainloop()

2 Answers 2

2

It is because the default height (if not specified) of titleCanvas is larger than the allocated height of its parent frame (topFrame), so the height of topFrame will be expanded. Note that weight option of rowconfigure() and columnconfigure() does not guarantee to allocate exact ratio on the specified values.

To achieve what you want, you can specify the height of titleCanvas to 1 and let the grid option sticky='nwes' to expand it to fit its parent frame.

titleCanvas = Canvas(topFrame, bg="indigo", highlightthickness=0, height=1)
Sign up to request clarification or add additional context in comments.

Comments

0

The problem can be fixed by the use of grid_propagate(). Specifically, adding...

topFrame.grid_propogate(False)

... immediately after the topFrame.grid() statement fixes the problem.

I'm not sure exactly why this works: I get that grid_propagate() prevents a widget's children from requesting a new size for the widget, but I'm not sure why this triggers the child widget (titleCanvas) to pick up the size of its parent (topFrame) when it wouldn't before.

I'm also not sure why the original approach worked for the mainCanvas child widget of mainFrame but not for titleCanvas.

Finally, as a test, I added ...

mainFrame.grid_propogate(False)

... immediately after the mainFrame.grid() statement and it works there as well (but doesn't seem to be needed).

1 Comment

normally parent automatically resizes its size to child sizes, grid_propagate stops parent to resize its size. Propably titleCanvas still has its original size but now Frame crops it and you see only some part of canvas.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.