Layout for a Tkinter form

I am making a GUI application for a class. I have made the form layout (make_form()) using both Frame and grid, but I am not exactly sure that one should be mixing those two together. What is the best way to make a layout, using grid to incorporate not just the labels and Entry fields, but also the buttons?

import tkinter as tk

fields = "Dimensions", "Parent population size", "Children populatoin size", \
"Recombination population size", "Maximum iterations", "Low boundary domain", \
"High boundary domain", "Parameter size"

def quit():
root.destroy()

def clear_initial_conditions(initial_conditions):
for ic in initial_conditions:
ic[1].delete(0, "end")

def get_initial_conditions(initial_conditions):
for ic in initial_conditions:
field = ic[0]
text = ic[1].get()
if text.isdigit():
print("%s: %s" % (field, text))
else:
print("Field \"%s\" does not have a valid number, correct to integer" % field)

def make_form(root, fields):
initial_conditions = []
for field in fields:
row = tk.Frame(root)
row.pack(side = tk.TOP, fill = tk.X, padx = 10, pady = 10)
lab = tk.Label(row, width = 30, text = field, anchor = tk.W)
lab.grid(row = 0, column = 0)
ent = tk.Entry(row)
ent.grid(row = 0, column = 1, sticky = tk.W)
initial_conditions.append((field, ent))
return initial_conditions

if __name__ == "__main__":
root = tk.Tk()
root.title("Pack - Example 12")
ents = make_form(root, fields)

root.bind("<Return>", (lambda event, e = ents: get_initial_conditions(e)))
b1 = tk.Button(root, text = "Find minimum", command = (lambda e = ents: get_initial_conditions(e)))
b2 = tk.Button(root, text = "Clear", command = (lambda e = ents: clear_initial_conditions(e)))

root.mainloop()

• Your question is unclear. Frame and grid are explicitly designed to work together. Also, what do you mean by "using grid to incorporate not just the labels and Entry fields, but also the buttons?" If you want to use grid with buttons, just call grid on the buttons. – Bryan Oakley Dec 4 '17 at 15:26

Frames are specifically designed to be a tool in organizing the widgets in your GUI.

In the case of a simple form like this, I recommend using one frame for the labels and entries, and one for the buttons. grid is the natural choice for the form, and it looks like pack would be best for the buttons. pack is also best to put the form on top and the buttons on the bottom.

For the form, I would not use a frame for each row.

Example:

# the root window and its children
form = tk.Frame(root)
buttons = tk.Frame(root)

form.pack(side="top", fill="both", expand=True)
buttons.pack(side="bottom", fill="x")

# the buttons
b1 = tk.Button(buttons, ...)
b2 = tk.Button(buttons, ...)

b1.pack(side="left", ...)
b2.pack(side="left", ...)

# the form
for row, field in enumerate(fields):
lab = tk.Label(form, ...)
ent = tk.Entry(form, ...)

lab.grid(row=row, column=0, ...)
ent.grid(row=row, column=1, ...)


The form can be thought of as a single complex widget. You can do that by creating a class for the form, based off of the Frame widget:

class Form(tk.Frame):
def __init__(self, parent, fields):
tk.Frame.__init__(self, parent)
for row, field in enumerate(fields):
...


You would then use it like a normal frame when adding it to the window:

form = Form(root, fields=fields)
...
form.pack(side="top", fill="both", expand=True)


With that, you now have a general purpose form that could be reused for different purposes with different sets of fields.