Раскладка
Виджеты сами по себе в окне не появляются: их расставляет менеджер геометрии. В Tkinter их три — pack, grid и place.
pack — друг за другом
pack складывает виджеты в стопку: по умолчанию сверху вниз.
tk.Label(root, text="Заголовок").pack(pady=10)
tk.Entry(root).pack(fill="x", padx=10) # растянуть по ширине
tk.Button(root, text="OK").pack(side="right") # прижать вправо
side— к какой стороне прижимать:"top"(по умолчанию),"bottom","left","right";fill="x","y"или"both"— растянуть по оси;expand=True— забрать свободное место при увеличении окна;padx,pady— внешние отступы.
grid — таблица
grid кладёт виджеты в ячейки таблицы. Строки и столбцы нумеруются с нуля:
tk.Label(root, text="Имя").grid(row=0, column=0, sticky="w")
tk.Entry(root).grid(row=0, column=1)
tk.Label(root, text="Пароль").grid(row=1, column=0, sticky="w")
tk.Entry(root, show="*").grid(row=1, column=1)
tk.Button(root, text="Войти").grid(row=2, column=0, columnspan=2, sticky="ew")
columnspan,rowspan— сколько ячеек занимает виджет;sticky— к каким краям ячейки прижать:"n","s","e","w"и их сочетания;"nsew"— растянуть на всю ячейку;grid_columnconfigure(c, weight=1)— столбец растягивается вместе с окном;minsize— минимальная ширина.
Когда кнопки создаются в цикле, номер кнопки i легко превратить в ячейку: строка i // cols, столбец i % cols.
Frame — рамка-группа
В одном контейнере нельзя смешивать pack и grid. Зато можно положить через pack рамку Frame, а внутри неё раскладывать через grid:
screen = tk.Label(root, text="0")
screen.pack(fill="x")
pad = tk.Frame(root)
pad.pack()
for i, key in enumerate("789456123"):
tk.Button(pad, text=key, width=4).grid(row=i // 3, column=i % 3)
place — точные координаты
place(x=20, y=40) ставит виджет в точку в пикселях, place(relx=0.5, rely=0.5, anchor="center") — в долях размера окна (здесь — ровно в центр). Используют редко: при изменении окна такие виджеты сами не подстраиваются.
Кнопки из цикла и lambda
У кнопок, созданных в цикле, command часто пишут через lambda с параметром по умолчанию:
for i in range(9):
tk.Button(root, command=lambda i=i: play(i)).grid(row=i // 3, column=i % 3)
i=i запоминает текущее значение i. Без него все кнопки вызвали бы play(8) — последнее значение переменной.
Итог
pack— стопкой (side,fill,expand),grid— таблицей (row,column,columnspan,sticky),place— координатами;- один менеджер на контейнер, группы — через
Frame; - номер в сетке:
(i // cols, i % cols); lambda i=i: ...для кнопок из цикла.