Метод grid

Grid является одним из трех менеджеров геометрии в Tkinter (другими являются уже рассмотренный ранее Pack, а также Place). У всех виджетов есть соответствующий данному менеджеру метод grid. "Grid" с английского переводится как "сетка", однако по смыслу правильнее говорить о таблице.

Табличный способ размещения предпочтителен из-за его гибкости и удобства, когда дело доходит до разработки относительно сложных интерфейсов. Grid позволяет избежать использования множества фреймов, что неизбежно в случае упаковщика Pack.

При размещении виджетов методом grid родительский контейнер (обычно это окно) условно разделяется на ячейки подобно таблице. Адрес каждой ячейки состоит из номера строки и номера столбца. Нумерация начинается с нуля. Ячейки можно объединять как по вертикали, так и по горизонтали.

На рисунке пунктир обозначает объединение ячеек. Общая ячейка в таком случае обозначается адресом первой.

Никаких предварительных команд по разбиению родительского виджета на ячейки не выполняется. Tkinter делает это сам, исходя из указанных позиций виджетов.

Размещение виджета в той или иной ячейке задается через аргументы row и column, которым присваиваются соответственно номера строки и столбца. Чтобы объединить ячейки по горизонтали, используется атрибут columnspan, которому присваивается количество объединяемых ячеек. Опция rowspan объединяет ячейки по вертикали.

Пусть надо запрограммировать такой GUI:

Представим данный интерфейс в виде таблицы и пронумеруем ячейки, в которых будут располагаться виджеты:

Теперь пишем код:

from tkinter import *
root = Tk()
 
Label(text="Имя:")\
    .grid(row=0, column=0)
Entry(width=30)\
    .grid(row=0, column=1, columnspan=3)
 
Label(text="Столбцов:")\
    .grid(row=1, column=0)
Spinbox(width=7, from_=1, to=50)\
    .grid(row=1, column=1)
Label(text="Строк:")\
    .grid(row=1, column=2)
Spinbox(width=7, from_=1, to=100)\
    .grid(row=1, column=3)
 
Button(text="Справка").grid(row=2, column=0)
Button(text="Вставить").grid(row=2, column=2)
Button(text="Отменить").grid(row=2, column=3)
 
root.mainloop()

Примечание. В примере используются виджеты класса Spinbox, которые не рассматривались в курсе. Spinbox похож на Entry, но для него задается список принимаемых значений, и имеется подобие скроллера.

Выполнив приведенный выше программный код, получим:

Похоже, но не совсем то, что хотелось. Теперь на помощь должны прийти другие свойства метода grid. У него, также как у pack, имеются атрибуты для задания внешних и внутренних отступов (padx, pady, ipadx, ipady).

Кроме этого есть атрибут sticky (липкий), который принимает значения направлений сторон света (N, S, W, E, NW, NE, SW, SE). Если, например, указать NW, то виджет прибьет к верхнему левому углу ячейки. Виджеты можно растягивать на весь объем ячейки (sticky=N+S+W+E) или только по одной из осей (N+S или W+E). Эффект от "липучки" заметен, только если виджет меньше ячейки.

from tkinter import *
root = Tk()
 
Label(text="Имя:").grid(row=0, column=0,
                        sticky=W,
                        pady=10, padx=10)
table_name = Entry()
table_name.grid(row=0, column=1,
                columnspan=3,
                sticky=W+E, padx=10)
 
Label(text="Столбцов:").grid(
    row=1, column=0, sticky=W,
    padx=10, pady=10)
Spinbox(width=7, from_=1, to=50)\
    .grid(row=1, column=1, padx=10)
Label(text="Строк:")\
    .grid(row=1, column=2, sticky=E)
Spinbox(width=7, from_=1, to=100)\
    .grid(row=1, column=3, sticky=E, padx=10)
 
Button(text="Справка")\
    .grid(row=2, column=0, pady=10, padx=10)
Button(text="Вставить")\
    .grid(row=2, column=2)
Button(text="Отменить")\
    .grid(row=2, column=3, padx=10)
 
root.mainloop()

С помощью методов grid_remove и grid_forget можно сделать виджет невидимым. Отличие между этими методами лишь в том, что grid_remove запоминает прежнее положение виджета. Поэтому для его отображения в прежней ячейки достаточно применить grid без аргументов. После grid_forget потребуется заново конфигурировать положение виджета.

from tkinter import *
 
 
def rem():
    global l1_flag
    if l1_flag == 1:
        l1.grid_remove()
        l1_flag = 0
    else:
        l1.grid()
        l1_flag = 1
 
 
def forg():
    global l2_flag
    if l2_flag == 1:
        l2.grid_forget()
        l2_flag = 0
    else:
        l2.grid(row=1)
        l2_flag = 1
 
 
root = Tk()
l1_flag = 1
l2_flag = 1
l1 = Label(width=5, height=3, bg='blue')
l2 = Label(width=5, height=3, bg='green')
b1 = Button(bg='lightblue', command=rem)
b2 = Button(bg='lightgreen', command=forg)
 
l1.grid(row=0)
l2.grid(row=1)
b1.grid(row=2)
b2.grid(row=3)
 
root.mainloop()

В данной программе голубая метка после удаления будет появляться в том же месте, где была до этого, несмотря на то, что в функции rem к ней применяется метод grid без настроек. В отличие от нее зеленая метка, если ей не указать место размещения после удаления, появлялась бы под кнопками.

Скрытие виджетов бывает необходимо в тех случаях, когда, например, от выбора пользователя в одной части интерфейса зависит, какие виджеты появятся в другой.

Практическая работа

Перепрограммируйте второе окно из практической работы предыдущего урока, используя метод grid.

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Tkinter. Программирование GUI на Python