tkinter gui layout using frames and grid
Solution 1:
I assumed that frames contain their own 'grid space'
That is a correct assumption.
You can just see one of the entry fields to the right of the green frame. Why is it going there ?
The problem starts here:
top_frame = Frame(root, ...).grid(row=0, ...)
In python, x = y().z()
will always set x
to the result of .z()
. In the case of top_frame = Frame(...).grid(...)
, grid(...)
always returns None
so top_frame
will be None
. That causes every widget that you think is going into the top frame to actually go in the root window.
Solution Overview
As a general rule of thumb, you should never call grid
, pack
or place
as part of the same statement that creates the widget. Partially it is for this exact behavior that you're experiencing, but also because I think it makes your code harder to write and harder to maintain over time.
Widget creation and widget layout are two different things. In my experience, layout problems are considerably easier to debug when you group your layout commands together.
Also, you should be consistent when using grid
and always put the options in the same order so you can more easily visualize the layout. And finally, when using grid
you should get in the habit of always specifying the sticky
option, and always give one row and one column in each containing frame a non-zero weight.
Solution Example
Here's how I would write your code. It's much longer, but much easier to understand.
from Tkinter import *
root = Tk()
root.title('Model Definition')
root.geometry('{}x{}'.format(460, 350))
# create all of the main containers
top_frame = Frame(root, bg='cyan', width=450, height=50, pady=3)
center = Frame(root, bg='gray2', width=50, height=40, padx=3, pady=3)
btm_frame = Frame(root, bg='white', width=450, height=45, pady=3)
btm_frame2 = Frame(root, bg='lavender', width=450, height=60, pady=3)
# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)
top_frame.grid(row=0, sticky="ew")
center.grid(row=1, sticky="nsew")
btm_frame.grid(row=3, sticky="ew")
btm_frame2.grid(row=4, sticky="ew")
# create the widgets for the top frame
model_label = Label(top_frame, text='Model Dimensions')
width_label = Label(top_frame, text='Width:')
length_label = Label(top_frame, text='Length:')
entry_W = Entry(top_frame, background="pink")
entry_L = Entry(top_frame, background="orange")
# layout the widgets in the top frame
model_label.grid(row=0, columnspan=3)
width_label.grid(row=1, column=0)
length_label.grid(row=1, column=2)
entry_W.grid(row=1, column=1)
entry_L.grid(row=1, column=3)
# create the center widgets
center.grid_rowconfigure(0, weight=1)
center.grid_columnconfigure(1, weight=1)
ctr_left = Frame(center, bg='blue', width=100, height=190)
ctr_mid = Frame(center, bg='yellow', width=250, height=190, padx=3, pady=3)
ctr_right = Frame(center, bg='green', width=100, height=190, padx=3, pady=3)
ctr_left.grid(row=0, column=0, sticky="ns")
ctr_mid.grid(row=0, column=1, sticky="nsew")
ctr_right.grid(row=0, column=2, sticky="ns")
root.mainloop()
Result:
Solution 2:
variable = Widget(...).grid()
assigns None
to variable because grid()
/pack()
/place()
return None
use
variable = Widget(...)
variable.grid() # .pack() .place()