В данном уроке по изучению Tkinter мы познакомимся с менеджерами разметки. Когда мы создаем графический интерфейс нашего приложения, мы определяем, какие виджеты будем использовать, и как они будут расположены в приложении. Для того, чтобы организовать виджеты в приложении, используются специальные невидимые объекты – менеджеры разметки.
Содержание курса
- Создание окна по центру и кнопка выхода в Tkinter
- Разметка виджетов в Tkinter — pack, grid и place
- Виджеты Checkbutton, Label, Scale и Listbox в Tkinter
- Меню, подменю и панель инструментов в Tkinter
- Диалоговые окна в Tkinter
- Рисуем линии, прямоугольники, круг и текст в Tkinter
- Пишем игру змейка на Tkinter
Содержание статьи
- Метод place() в Tkinter — Абсолютное позиционирование
- Tkinter pack() — размещение виджетов по горизонтали и вертикали
- Пример создания кнопок в Tkinter
- Создаем приложение для отзывов на Tkinter
- Разметка grid() в Tkinter для создания калькулятора
- Пример создания диалогового окна в Tkinter
Существует два вида виджетов:
- контейнеры;
- дочерние виджеты.
Контейнеры объединяют виджеты для формирования разметки. У Tkinter есть три встроенных менеджера разметки: pack, grid и place.
- Place – это менеджер геометрии, который размещает виджеты, используя абсолютное позиционирование.
- Pack – это менеджер геометрии, который размещает виджеты по горизонтали и вертикали.
- Grid – это менеджер геометрии, который размещает виджеты в двухмерной сетке.
Метод place() в Tkinter — Абсолютное позиционирование
В большинстве случаев разработчикам необходимо использовать менеджеры разметки. Есть несколько ситуаций, в которых следует использовать именно абсолютное позиционирование. В рамках абсолютного позиционирования разработчик определяет позицию и размер каждого виджета в пикселях. Во время изменения размеров окна размер и позиция виджетов не меняются.
Изображения из примера:
Сохраните их в папке рядом с файлом absolute.py
код для которого будет ниже.
Таким образом, на разных платформах приложения выглядят по-разному. То, что выглядит нормально на Linux, может отображаться некорректно на Mac OS. Изменение шрифтов в нашем приложении также может испортить разметку. Если мы переведем наше приложение на другой язык, мы должны доработать и разметку.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
from PIL import Image, ImageTk from tkinter import Tk, BOTH from tkinter.ttk import Frame, Label, Style class Example(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("Absolute positioning") self.pack(fill=BOTH, expand=1) Style().configure("TFrame", background="#333") bard = Image.open("bardejov.jpg") bardejov = ImageTk.PhotoImage(bard) label1 = Label(self, image=bardejov) label1.image = bardejov label1.place(x=20, y=20) rot = Image.open("rotunda.jpg") rotunda = ImageTk.PhotoImage(rot) label2 = Label(self, image=rotunda) label2.image = rotunda label2.place(x=40, y=160) minc = Image.open("mincol.jpg") mincol = ImageTk.PhotoImage(minc) label3 = Label(self, image=mincol) label3.image = mincol label3.place(x=170, y=50) def main(): root = Tk() root.geometry("300x280+300+300") app = Example() root.mainloop() if __name__ == '__main__': main() |
В этом примере мы расположили три изображения при помощи абсолютного позиционирования. Мы использовали менеджер геометрии place.
1 |
from PIL import Image, ImageTk |
Мы использовали Image и ImageTk из модуля PIL (Python Imaging Library).
1 2 |
style = Style() style.configure("TFrame", background="#333") |
При помощи стилей, мы изменили фон нашего окна на темно-серый.
1 2 |
bard = Image.open("bardejov.jpg") bardejov = ImageTk.PhotoImage(bard) |
Мы создали объект изображения и объект фото изображения из сохраненных ранее изображений в текущей рабочей директории.
1 |
label1 = Label(self, image=bardejov) |
Мы создали Label
(ярлык) с изображением. Данные ярлыки могут содержать как изображения, так и текст.
1 |
label1.image = bardejov |
Нам нужно сохранить ссылку на изображение, чтобы не потерять его если сборщик мусора (Garbage collector) его не закроет.
1 |
label1.place(x=20, y=20) |
Ярлык размещен в рамке по координатам x=20
и y=20
.
Tkinter pack() — размещение виджетов по горизонтали и вертикали
Менеджер геометрии pack()
упорядочивает виджеты в горизонтальные и вертикальные блоки. Макетом можно управлять с помощью параметров fill
, expand
и side
.
Пример создания кнопок в Tkinter
В следующем примере мы разместим две кнопки в нижнем правом углу нашего окна. Для этого мы воспользуемся менеджером pack.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from tkinter import Tk, RIGHT, BOTH, RAISED from tkinter.ttk import Frame, Button, Style class Example(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("Кнопки в kinter") self.style = Style() self.style.theme_use("default") frame = Frame(self, relief=RAISED, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) closeButton = Button(self, text="Закрыть") closeButton.pack(side=RIGHT, padx=5, pady=5) okButton = Button(self, text="Готово") okButton.pack(side=RIGHT) def main(): root = Tk() root.geometry("300x200+300+300") app = Example() root.mainloop() if __name__ == '__main__': main() |
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
У нас есть две рамки. Первая рамка – основная, а также вторая – дополнительная, которая растягивается в обе стороны и сдвигает две кнопки в нижнюю часть основной рамки. Кнопки находятся в горизонтальном контейнере и размещены в ее правой части.
1 2 |
frame = Frame(self, relief=RAISED, borderwidth=1) frame.pack(fill=BOTH, expand=True) |
Мы создали еще один виджет Frame. Этот виджет занимает практически все пространство окна. Мы изменяем границы рамки, чтобы сама рамка была видна. По умолчанию она плоская.
1 2 |
closeButton = Button(self, text="Закрыть") closeButton.pack(side=RIGHT, padx=5, pady=5) |
Кнопка closeButton создана. Она расположена в горизонтальном контейнере. Параметр side позволяет поместить кнопку в правой части горизонтальной полосы. Параметры padx
и pady
позволяют установить отступ между виджетами. Параметр padx
устанавливает пространство между виджетами кнопки closeButton
и правой границей корневого окна.
1 |
okButton.pack(side=RIGHT) |
Кнопка okButton размещена возле closeButton с установленным отступом (padding) в 5 пикселей.
Создаем приложение для отзывов на Tkinter
Менеджер pack – это простой менеджер разметки. Его можно использовать для простых задач разметки. Чтобы создать более сложную разметку, необходимо использовать больше рамок, каждая из которых имеет собственный менеджер разметки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
from tkinter import Tk, Text, BOTH, X, N, LEFT from tkinter.ttk import Frame, Label, Entry class Example(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("Оставить отзыв") self.pack(fill=BOTH, expand=True) frame1 = Frame(self) frame1.pack(fill=X) lbl1 = Label(frame1, text="Заголовок", width=10) lbl1.pack(side=LEFT, padx=5, pady=5) entry1 = Entry(frame1) entry1.pack(fill=X, padx=5, expand=True) frame2 = Frame(self) frame2.pack(fill=X) lbl2 = Label(frame2, text="Автор", width=10) lbl2.pack(side=LEFT, padx=5, pady=5) entry2 = Entry(frame2) entry2.pack(fill=X, padx=5, expand=True) frame3 = Frame(self) frame3.pack(fill=BOTH, expand=True) lbl3 = Label(frame3, text="Отзыв", width=10) lbl3.pack(side=LEFT, anchor=N, padx=5, pady=5) txt = Text(frame3) txt.pack(fill=BOTH, pady=5, padx=5, expand=True) def main(): root = Tk() root.geometry("300x300+300+300") app = Example() root.mainloop() if __name__ == '__main__': main() |
На этом примере видно, как можно создать более сложную разметку с многочисленными рамками и менеджерами pack().
1 |
self.pack(fill=BOTH, expand=True) |
Первая рамка является базовой. На ней располагаются все остальные рамки. Стоит отметить, что даже при организации дочерних виджетов в рамках, мы управляем ими на базовой рамке.
1 2 3 4 5 6 7 8 |
frame1 = Frame(self) frame1.pack(fill=X) lbl1 = Label(frame1, text="Заголовок", width=10) lbl1.pack(side=LEFT, padx=5, pady=5) entry1 = Entry(frame1) entry1.pack(fill=X, padx=5, expand=True) |
Первые два виджета размещены на первой рамке. Поле для ввода данных растянуто горизонтально с параметрами fill и expand.
1 2 3 4 5 6 7 8 |
frame3 = Frame(self) frame3.pack(fill=BOTH, expand=True) lbl3 = Label(frame3, text="Отзыв", width=10) lbl3.pack(side=LEFT, anchor=N, padx=5, pady=5) txt = Text(frame3) txt.pack(fill=BOTH, pady=5, padx=5, expand=True) |
В третьей рамке мы разместили ярлык и виджет для ввода текста. Ярлык закреплен по северной стороне anchor=N
, а виджет текста занимает все остальное пространство.
Разметка grid() в Tkinter для создания калькулятора
Менеджер геометрии grid() в Tkinter используется для создания сетки кнопок для калькулятора.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
from tkinter import Tk, W, E from tkinter.ttk import Frame, Button, Entry, Style class Example(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("Калькулятор на Tkinter") Style().configure("TButton", padding=(0, 5, 0, 5), font='serif 10') self.columnconfigure(0, pad=3) self.columnconfigure(1, pad=3) self.columnconfigure(2, pad=3) self.columnconfigure(3, pad=3) self.rowconfigure(0, pad=3) self.rowconfigure(1, pad=3) self.rowconfigure(2, pad=3) self.rowconfigure(3, pad=3) self.rowconfigure(4, pad=3) entry = Entry(self) entry.grid(row=0, columnspan=4, sticky=W+E) cls = Button(self, text="Очистить") cls.grid(row=1, column=0) bck = Button(self, text="Удалить") bck.grid(row=1, column=1) lbl = Button(self) lbl.grid(row=1, column=2) clo = Button(self, text="Закрыть") clo.grid(row=1, column=3) sev = Button(self, text="7") sev.grid(row=2, column=0) eig = Button(self, text="8") eig.grid(row=2, column=1) nin = Button(self, text="9") nin.grid(row=2, column=2) div = Button(self, text="/") div.grid(row=2, column=3) fou = Button(self, text="4") fou.grid(row=3, column=0) fiv = Button(self, text="5") fiv.grid(row=3, column=1) six = Button(self, text="6") six.grid(row=3, column=2) mul = Button(self, text="*") mul.grid(row=3, column=3) one = Button(self, text="1") one.grid(row=4, column=0) two = Button(self, text="2") two.grid(row=4, column=1) thr = Button(self, text="3") thr.grid(row=4, column=2) mns = Button(self, text="-") mns.grid(row=4, column=3) zer = Button(self, text="0") zer.grid(row=5, column=0) dot = Button(self, text=".") dot.grid(row=5, column=1) equ = Button(self, text="=") equ.grid(row=5, column=2) pls = Button(self, text="+") pls.grid(row=5, column=3) self.pack() def main(): root = Tk() app = Example() root.mainloop() if __name__ == '__main__': main() |
Менеджер grid() используется для организации кнопок в контейнере рамки.
1 2 |
Style().configure("TButton", padding=(0, 5, 0, 5), font='serif 10') |
Мы настроили виджет кнопки так, чтобы отображался специфический шрифт и применялся отступ (padding) в 3 пикселя.
1 2 3 |
self.columnconfigure(0, pad=3) ... self.rowconfigure(0, pad=3) |
Мы использовали методы columnconfigure() и rowconfigure() чтобы создать определенное пространство в сетке строк и столбцов. Благодаря этому шагу мы разделяем кнопки определенным пустым пространством.
1 2 |
entry = Entry(self) entry.grid(row=0, columnspan=4, sticky=W+E) |
Виджет графы ввода – это место, где будут отображаться цифры. Данный виджет расположен в первом ряду и охватывает все четыре столбца. Виджеты могут не занимать все пространство, которое выделяется клетками в созданной сетке.
Параметр sticky расширяет виджет в указанном направлении. В нашем случае, мы можем убедиться, что наш виджет графы ввода был расширен слева направо W+E (восток-запад).
1 2 |
cls = Button(self, text="Очистить") cls.grid(row=1, column=0) |
Кнопка очистки установлена во второй строке и первом столбце. Стоит отметить, что строки и столбцы начинаются с нуля.
1 |
self.pack() |
Метод pack() показывает виджет рамки и дает ей первоначальный размер. Если дополнительные параметры не указываются, размер будет таким, чтобы все дочерние виджеты могли поместиться. Этот метод компонует виджет рамки в верхнем корневом окне, которое также является контейнером. Менеджер grid() используется для организации кнопок в виджете рамки.
Пример создания диалогового окна в Tkinter
Следующий пример создает диалоговое окно, используя менеджер геометрии grid.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
from tkinter import Tk, Text, BOTH, W, N, E, S from tkinter.ttk import Frame, Button, Label, Style class Example(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("Диалоговое окно в Tkinter") self.pack(fill=BOTH, expand=True) self.columnconfigure(1, weight=1) self.columnconfigure(3, pad=7) self.rowconfigure(3, weight=1) self.rowconfigure(5, pad=7) lbl = Label(self, text="Окна") lbl.grid(sticky=W, pady=4, padx=5) area = Text(self) area.grid(row=1, column=0, columnspan=2, rowspan=4, padx=5, sticky=E+W+S+N) abtn = Button(self, text="Активир.") abtn.grid(row=1, column=3) cbtn = Button(self, text="Закрыть") cbtn.grid(row=2, column=3, pady=4) hbtn = Button(self, text="Помощь") hbtn.grid(row=5, column=0, padx=5) obtn = Button(self, text="Готово") obtn.grid(row=5, column=3) def main(): root = Tk() root.geometry("350x300+300+300") app = Example() root.mainloop() if __name__ == '__main__': main() |
В этом примере мы использовали виджет ярлыка, текстовой виджет и четыре кнопки.
1 2 3 4 |
self.columnconfigure(1, weight=1) self.columnconfigure(3, pad=7) self.rowconfigure(3, weight=1) self.rowconfigure(5, pad=7) |
Мы добавили небольшое пространство между виджетами в сетке. Параметр weight создает возможность расширения второго столбца и четвертого ряда. В этом ряду и столбце находится текстовой виджет, поэтому оставшееся пространство заполняет данный виджет.
1 2 |
lbl = Label(self, text="Окна") lbl.grid(sticky=W, pady=4, padx=5) |
Виджет ярлыка также создается и помещается в сетку. Если не указываются ряд и столбец, тогда он займет первый ряд и столбец. Ярлык закрепляется у западной части окна sticky=W
и имеет определенные отступы вокруг своих границ.
1 2 3 |
area = Text(self) area.grid(row=1, column=0, columnspan=2, rowspan=4, padx=5, sticky=E+W+S+N) |
Создается текстовый виджет и помещается во второй ряд и первый столбец. Он охватывает два столбца и четыре строки.
Между виджетом и левым краем корневого окна присутствует пространство в 4 пикселя. Также, виджет закреплен около всех четырех сторон. Поэтому, когда окно расширяется, виджеты текстов увеличиваются во всех направлениях.
1 2 3 4 5 |
abtn = Button(self, text="Активир.") abtn.grid(row=1, column=3) cbtn = Button(self, text="Закрыть") cbtn.grid(row=2, column=3, pady=4) |
Эти две кнопки находятся возле текстового виджета.
1 2 3 4 5 |
hbtn = Button(self, text="Помощь") hbtn.grid(row=5, column=0, padx=5) obtn = Button(self, text="Готово") obtn.grid(row=5, column=3) |
Эти две кнопки находятся под текстовым виджетом. Кнопка «Помощь» расположена в первом столбце, а кнопка «Готово» в последнем столбце.
В этой части изучения Tkinter мы рассказали о работе с разметкой виджетов.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»