Как изменить интерфейс питона

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

В этом уроке мы узнаем, как разрабатывать графические пользовательские интерфейсы, с помощью разбора некоторых примеров графического интерфейса Python с использованием библиотеки Tkinter.

Библиотека Tkinter установлена в Python в качестве стандартного модуля, поэтому нам не нужно устанавливать что-либо для его использования. Tkinter — очень мощная библиотека. Если вы уже установили Python, можете использовать IDLE, который является интегрированной IDE, поставляемой в Python, эта IDE написана с использованием Tkinter. Звучит круто!

Мы будем использовать Python 3.7 поэтому, если вы все еще используете Python 2.x, настоятельно рекомендуем перейти на Python 3.x, если вы не в курсе нюансов изменения языка, с целью, чтобы вы могли настроить код для запуска без ошибок.

Давайте предположим, что у вас уже есть базовые знания по Python, которые помогут понять что мы будем делать.
Мы начнем с создания окна, в котором мы узнаем, как добавлять виджеты, такие, как кнопки, комбинированные поля и т. д. После этого поэкспериментируем со своими свойствами, поэтому предлагаю начать.

Создание своего первого графического интерфейса

Для начала, следует импортировать Tkinter и создать окно, в котором мы зададим его название:

from tkinter import *


window = Tk()
window.title("Добро пожаловать в приложение PythonRu")
window.mainloop()

Результат будет выглядеть следующим образом:
Обучение Python GUI (уроки по Tkinter)Прекрасно! Наше приложение работает.
Последняя строка вызывает функцию mainloop. Эта функция вызывает бесконечный цикл окна, поэтому окно будет ждать любого взаимодействия с пользователем, пока не будет закрыто.

В случае, если вы забудете вызвать функцию mainloop , для пользователя ничего не отобразится.

Создание виджета Label

Чтобы добавить текст в наш предыдущий пример, мы создадим lbl , с помощью класса Label, например:

lbl = Label(window, text="Привет")

Затем мы установим позицию в окне с помощью функции grid и укажем ее следующим образом:

lbl.grid(column=0, row=0)

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

from tkinter import *  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
lbl = Label(window, text="Привет")  
lbl.grid(column=0, row=0)  
window.mainloop()

И вот как будет выглядеть результат:
Обучение Python GUI (уроки по Tkinter)Если функция grid не будет вызвана, текст не будет отображаться.

Настройка размера и шрифта текста

Вы можете задать шрифт текста и размер. Также можно изменить стиль шрифта. Для этого передайте параметр font таким образом:

lbl = Label(window, text="Привет", font=("Arial Bold", 50))

Обучение Python GUI (уроки по Tkinter)Обратите внимание, что параметр font может быть передан любому виджету, для того, чтобы поменять его шрифт, он применяется не только к Label.

Отлично, но стандартное окно слишком мало. Как насчет настройки размера окна?

Настройка размеров окна приложения

Мы можем установить размер окна по умолчанию, используя функцию geometry следующим образом:

window.geometry('400x250')

В приведенной выше строке устанавливается окно шириной до 400 пикселей и высотой до 250 пикселей.

Попробуем добавить больше виджетов GUI, например, кнопки и посмотреть, как обрабатывается нажатие кнопок.

Добавление виджета Button

Начнем с добавления кнопки в окно. Кнопка создается и добавляется в окно так же, как и метка:

btn = Button(window, text="Не нажимать!")
btn.grid(column=1, row=0)

Наш код будет выглядеть вот так:

from tkinter import *  
  

window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
lbl = Label(window, text="Привет", font=("Arial Bold", 50))  
lbl.grid(column=0, row=0)  
btn = Button(window, text="Не нажимать!")  
btn.grid(column=1, row=0)  
window.mainloop()

Результат будет следующим:
Обучение Python GUI (уроки по Tkinter)Обратите внимание, что мы помещаем кнопку во второй столбец окна, что равно 1. Если вы забудете и поместите кнопку в том же столбце, который равен 0, он покажет только кнопку.

Изменение цвета текста и фона у Button

Вы можете поменять цвет текста кнопки или любого другого виджета, используя свойство fg.
Кроме того, вы можете поменять цвет фона любого виджета, используя свойство bg.

btn = Button(window, text="Не нажимать!", bg="black", fg="red")

Обучение Python GUI (уроки по Tkinter)Теперь, если вы попытаетесь щелкнуть по кнопке, ничего не произойдет, потому что событие нажатия кнопки еще не написано.

Кнопка Click

Для начала, мы запишем функцию, которую нужно выполнить при нажатии кнопки:

def clicked():
    lbl.configure(text="Я же просил...")

Затем мы подключим ее с помощью кнопки, указав следующую ​​функцию:

btn = Button(window, text="Не нажимать!", command=clicked)

Обратите внимание: мы пишем clicked, а не clicked()с круглыми скобками. Теперь полный код будет выглядеть так:

from tkinter import *  
  
  
def clicked():  
    lbl.configure(text="Я же просил...")  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
lbl = Label(window, text="Привет", font=("Arial Bold", 50))  
lbl.grid(column=0, row=0)  
btn = Button(window, text="Не нажимать!", command=clicked)  
btn.grid(column=1, row=0)  
window.mainloop()

При нажатии на кнопку, результат, как и ожидалось, будет выглядеть следующим образом:
Обучение Python GUI (уроки по Tkinter)Круто!

Получение ввода с использованием класса Entry (текстовое поле Tkinter)

В предыдущих примерах GUI Python мы ознакомились со способами добавления простых виджетов, а теперь попробуем получить пользовательский ввод, используя класс Tkinter Entry (текстовое поле Tkinter).
Вы можете создать текстовое поле с помощью класса Tkinter Entry следующим образом:

txt = Entry(window, width=10)

Затем вы можете добавить его в окно, используя функцию grid.
Наше окно будет выглядеть так:

from tkinter import *  
  
  
def clicked():  
    lbl.configure(text="Я же просил...")  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
lbl = Label(window, text="Привет")  
lbl.grid(column=0, row=0)  
txt = Entry(window,width=10)  
txt.grid(column=1, row=0)  
btn = Button(window, text="Не нажимать!", command=clicked)  
btn.grid(column=2, row=0)  
window.mainloop()

Полученный результат будет выглядеть так:
Обучение Python GUI (уроки по Tkinter)Теперь, если вы нажмете кнопку, она покажет то же самое старое сообщение, но что же будет с отображением введенного текста в виджет Entry?

Во-первых, вы можете получить текст ввода, используя функцию get. Мы можем записать код для выбранной функции таким образом:

def clicked():
    res = "Привет {}".format(txt.get())
    lbl.configure(text=res)

Если вы нажмете на кнопку — появится текст «Привет » вместе с введенным текстом в виджете записи. Вот полный код:

from tkinter import *  
  
  
def clicked():  
    res = "Привет {}".format(txt.get())  
    lbl.configure(text=res)  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
lbl = Label(window, text="Привет")  
lbl.grid(column=0, row=0)  
txt = Entry(window,width=10)  
txt.grid(column=1, row=0)  
btn = Button(window, text="Клик!", command=clicked)  
btn.grid(column=2, row=0)  
window.mainloop()

Запустите вышеуказанный код и проверьте результат:
Обучение Python GUI (уроки по Tkinter)Прекрасно!

Каждый раз, когда мы запускаем код, нам нужно нажать на виджет ввода, чтобы настроить фокус на ввод текста, но как насчет автоматической настройки фокуса?

Установка фокуса виджета ввода

Здесь все очень просто, ведь все, что нам нужно сделать, — это вызвать функцию focus:

txt.focus()

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

Отключить виджет ввода

Чтобы отключить виджет ввода, отключите свойство состояния:

txt = Entry(window,width=10, state='disabled')

Обучение Python GUI (уроки по Tkinter)Теперь вы не сможете ввести какой-либо текст.

Добавление виджета Combobox

Чтобы добавить виджет поля с выпадающем списком, используйте класс Combobox из ttk следующим образом:

from tkinter.ttk import Combobox


combo = Combobox(window)

Затем добавьте свои значения в поле со списком.

from tkinter import *  
from tkinter.ttk import Combobox  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
combo = Combobox(window)  
combo['values'] = (1, 2, 3, 4, 5, "Текст")  
combo.current(1)  # установите вариант по умолчанию  
combo.grid(column=0, row=0)  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Как видите с примера, мы добавляем элементы combobox, используя значения tuple.
Чтобы установить выбранный элемент, вы можете передать индекс нужного элемента текущей функции.
Чтобы получить элемент select, вы можете использовать функцию get вот таким образом:

combo.get()

Добавление виджета Checkbutton (чекбокса)

С целью создания виджета checkbutton, используйте класс Checkbutton:

from tkinter.ttk import Checkbutton


chk = Checkbutton(window, text='Выбрать')

Кроме того, вы можете задать значение по умолчанию, передав его в параметр var в Checkbutton:

from tkinter import *  
from tkinter.ttk import Checkbutton  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
chk_state = BooleanVar()  
chk_state.set(True)  # задайте проверку состояния чекбокса  
chk = Checkbutton(window, text='Выбрать', var=chk_state)  
chk.grid(column=0, row=0)  
window.mainloop()

Посмотрите на результат:
Обучение Python GUI (уроки по Tkinter)

Установка состояния Checkbutton

Здесь мы создаем переменную типа BooleanVar, которая не является стандартной переменной Python, это переменная Tkinter, затем передаем ее классу Checkbutton, чтобы установить состояние чекбокса как True в приведенном выше примере.

Вы можете установить для BooleanVar значение false, что бы чекбокс не был отмечен.
Так же, используйте IntVar вместо BooleanVar и установите значения 0 и 1.

chk_state = IntVar()
chk_state.set(0) # False
chk_state.set(1) # True

Эти примеры дают тот же результат, что и BooleanVar.

Добавление виджетов Radio Button

Чтобы добавить radio кнопки, используйте класс RadioButton:

rad1 = Radiobutton(window,text='Первый', value=1)

Обратите внимание, что вы должны установить value для каждой radio кнопки с уникальным значением, иначе они не будут работать.

from tkinter import *  
from tkinter.ttk import Radiobutton  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
rad1 = Radiobutton(window, text='Первый', value=1)  
rad2 = Radiobutton(window, text='Второй', value=2)  
rad3 = Radiobutton(window, text='Третий', value=3)  
rad1.grid(column=0, row=0)  
rad2.grid(column=1, row=0)  
rad3.grid(column=2, row=0)  
window.mainloop()

Результатом вышеприведенного кода будет следующий:
Обучение Python GUI (уроки по Tkinter)Кроме того, вы можете задать command любой из этих кнопок для определенной функции. Если пользователь нажимает на такую кнопку, она запустит код функции.
Вот пример:

rad1 = Radiobutton(window,text='Первая', value=1, command=clicked)

def clicked():
    # Делайте, что нужно

Достаточно легко!

Получение значения Radio Button (Избранная Radio Button)

Чтобы получить текущую выбранную radio кнопку или ее значение, вы можете передать параметр переменной и получить его значение.

from tkinter import *  
from tkinter.ttk import Radiobutton  
  
  
def clicked():  
    lbl.configure(text=selected.get())  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
selected = IntVar()  
rad1 = Radiobutton(window,text='Первый', value=1, variable=selected)  
rad2 = Radiobutton(window,text='Второй', value=2, variable=selected)  
rad3 = Radiobutton(window,text='Третий', value=3, variable=selected)  
btn = Button(window, text="Клик", command=clicked)  
lbl = Label(window)  
rad1.grid(column=0, row=0)  
rad2.grid(column=1, row=0)  
rad3.grid(column=2, row=0)  
btn.grid(column=3, row=0)  
lbl.grid(column=0, row=1)  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Каждый раз, когда вы выбираете radio button, значение переменной будет изменено на значение кнопки.

Добавление виджета ScrolledText (текстовая область Tkinter)

Чтобы добавить виджет ScrolledText, используйте класс ScrolledText:

from tkinter import scrolledtext


txt = scrolledtext.ScrolledText(window,width=40,height=10)

Здесь нужно указать ширину и высоту ScrolledText, иначе он заполнит все окно.

from tkinter import *  
from tkinter import scrolledtext  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
txt = scrolledtext.ScrolledText(window, width=40, height=10)  
txt.grid(column=0, row=0)  
window.mainloop()

Результат:
Обучение Python GUI (уроки по Tkinter)

Настройка содержимого Scrolledtext

Используйте метод insert, чтобы настроить содержимое Scrolledtext:

txt.insert(INSERT, 'Текстовое поле')

Удаление/Очистка содержимого Scrolledtext

Чтобы очистить содержимое данного виджета, используйте метод delete:

txt.delete(1.0, END)  # мы передали координаты очистки

Отлично!

Создание всплывающего окна с сообщением

Чтобы показать всплывающее окно с помощью Tkinter, используйте messagebox следующим образом:

from tkinter import messagebox


messagebox.showinfo('Заголовок', 'Текст')

Довольно легко! Давайте покажем окно сообщений при нажатии на кнопку пользователем.

from tkinter import *  
from tkinter import messagebox  
  
  
def clicked():  
    messagebox.showinfo('Заголовок', 'Текст')  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
btn = Button(window, text='Клик', command=clicked)  
btn.grid(column=0, row=0)  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Когда вы нажмете на кнопку, появится информационное окно.

Показ сообщений о предупреждениях и ошибках

Вы можете показать предупреждающее сообщение или сообщение об ошибке таким же образом. Единственное, что нужно изменить—это функция сообщения.

messagebox.showwarning('Заголовок', 'Текст')  # показывает предупреждающее сообщение
messagebox.showerror('Заголовок', 'Текст')  # показывает сообщение об ошибке

Показ диалоговых окон с выбором варианта

Чтобы показать пользователю сообщение “да/нет”, вы можете использовать одну из следующих функций messagebox:

from tkinter import messagebox


res = messagebox.askquestion('Заголовок', 'Текст')
res = messagebox.askyesno('Заголовок', 'Текст')
res = messagebox.askyesnocancel('Заголовок', 'Текст')
res = messagebox.askokcancel('Заголовок', 'Текст')
res = messagebox.askretrycancel('Заголовок', 'Текст')

Вы можете выбрать соответствующий стиль сообщения согласно вашим потребностям. Просто замените строку функции showinfo на одну из предыдущих и запустите скрипт. Кроме того, можно проверить, какая кнопка нажата, используя переменную результата.

Если вы кликнете OK, yes или retry, значение станет True, а если выберете no или cancel, значение будет False.
Единственной функцией, которая возвращает одно из трех значений, является функция askyesnocancel; она возвращает True/False/None.

Добавление SpinBox (Виджет спинбокс)

Для создания виджета спинбокса, используйте класс Spinbox:

spin = Spinbox(window, from_=0, to=100)

Таким образом, мы создаем виджет Spinbox, и передаем параметры from и to, чтобы указать диапазон номеров.
Кроме того, вы можете указать ширину виджета с помощью параметра width:

spin = Spinbox(window, from_=0, to=100, width=5)

Проверим пример полностью:

from tkinter import *  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
spin = Spinbox(window, from_=0, to=100, width=5)  
spin.grid(column=0, row=0)  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Вы можете указать числа для Spinbox, вместо использования всего диапазона следующим образом:

spin = Spinbox(window, values=(3, 8, 11), width=5)

Виджет покажет только эти 3 числа: 3, 8 и 11.

Задать значение по умолчанию для Spinbox

В случае, если вам нужно задать значение по умолчанию для Spinbox, вы можете передать значение параметру textvariable следующим образом:

var = IntVar()
var.set(36)
spin = Spinbox(window, from_=0, to=100, width=5, textvariable=var)

Теперь, если вы запустите программу, она покажет 36 как значение по умолчанию для Spinbox.

Добавление виджета Progressbar

Чтобы создать данный виджет, используйте класс progressbar :

from tkinter.ttk import Progressbar


bar = Progressbar(window, length=200)

Установите значение progressbar таким образом:

bar['value'] = 70

Вы можете установить это значение на основе любого процесса или при выполнении задачи.

Изменение цвета Progressbar

Изменение цвета Progressbar немного сложно. Сначала нужно создать стиль и задать цвет фона, а затем настроить созданный стиль на Progressbar. Посмотрите следующий пример:

from tkinter import *  
from tkinter.ttk import Progressbar  
from tkinter import ttk  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
style = ttk.Style()  
style.theme_use('default')  
style.configure("black.Horizontal.TProgressbar", background='black')  
bar = Progressbar(window, length=200, style='black.Horizontal.TProgressbar')  
bar['value'] = 70  
bar.grid(column=0, row=0)  
window.mainloop()

И в результате вы получите следующее:
Обучение Python GUI (уроки по Tkinter)

Добавление поля загрузки файла

Для добавления поля с файлом, используйте класс filedialog:

from tkinter import filedialog


file = filedialog.askopenfilename()

После того, как вы выберете файл, нажмите “Открыть”; переменная файла будет содержать этот путь к файлу. Кроме того, вы можете запросить несколько файлов:

files = filedialog.askopenfilenames()

Указание типа файлов (расширение фильтра файлов)

Возможность указания типа файлов доступна при использовании параметра filetypes, однако при этом важно указать расширение в tuples.

file = filedialog.askopenfilename(filetypes = (("Text files","*.txt"),("all files","*.*")))

Вы можете запросить каталог, используя метод askdirectory :

dir = filedialog.askdirectory()

Вы можете указать начальную директорию для диалогового окна файла, указав initialdir следующим образом:

from os import path
file = filedialog.askopenfilename(initialdir= path.dirname(__file__))

Легко!

Добавление панели меню

Для добавления панели меню, используйте класс menu:

from tkinter import Menu


menu = Menu(window)
menu.add_command(label='Файл')
window.config(menu=menu)

Сначала мы создаем меню, затем добавляем наш первый пункт подменю. Вы можете добавлять пункты меню в любое меню с помощью функции add_cascade() таким образом:

menu.add_cascade(label='Автор', menu=new_item)

Наш код будет выглядеть так:

from tkinter import *  
from tkinter import Menu  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
menu = Menu(window)  
new_item = Menu(menu)  
new_item.add_command(label='Новый')  
menu.add_cascade(label='Файл', menu=new_item)  
window.config(menu=menu)  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Таким образом, вы можете добавить столько пунктов меню, сколько захотите.

from tkinter import *  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
menu = Menu(window)  
new_item = Menu(menu)  
new_item.add_command(label='Новый')  
new_item.add_separator()  
new_item.add_command(label='Изменить')  
menu.add_cascade(label='Файл', menu=new_item)  
window.config(menu=menu)  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Теперь мы добавляем еще один пункт меню “Изменить” с разделителем меню. Вы можете заметить пунктирную линию в начале, если вы нажмете на эту строку, она отобразит пункты меню в небольшом отдельном окне.

Можно отключить эту функцию, с помощью tearoff подобным образом:

new_item = Menu(menu, tearoff=0)

Просто отредактируйте new_item, как в приведенном выше примере и он больше не будет отображать пунктирную линию.
Вы так же можете ввести любой код, который работает, при нажатии пользователем на любой элемент меню, задавая свойство команды.

new_item.add_command(label='Новый', command=clicked)

Добавление виджета Notebook (Управление вкладкой)

Для удобного управления вкладками реализуйте следующее:

  • Для начала, создается элемент управления вкладкой, с помощью класса Notebook .
  • Создайте вкладку, используя класс Frame.
  • Добавьте эту вкладку в элемент управления вкладками.
  • Запакуйте элемент управления вкладкой, чтобы он стал видимым в окне.
from tkinter import *  
from tkinter import ttk  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
tab_control = ttk.Notebook(window)  
tab1 = ttk.Frame(tab_control)  
tab_control.add(tab1, text='Первая')  
tab_control.pack(expand=1, fill='both')  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)Таким образом, вы можете добавлять столько вкладок, сколько нужно.

Добавление виджетов на вкладку

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

from tkinter import *  
from tkinter import ttk  
  
  
window = Tk()  
window.title("Добро пожаловать в приложение PythonRu")  
window.geometry('400x250')  
tab_control = ttk.Notebook(window)  
tab1 = ttk.Frame(tab_control)  
tab2 = ttk.Frame(tab_control)  
tab_control.add(tab1, text='Первая')  
tab_control.add(tab2, text='Вторая')  
lbl1 = Label(tab1, text='Вкладка 1')  
lbl1.grid(column=0, row=0)  
lbl2 = Label(tab2, text='Вкладка 2')  
lbl2.grid(column=0, row=0)  
tab_control.pack(expand=1, fill='both')  
window.mainloop()

Обучение Python GUI (уроки по Tkinter)

Добавление интервала для виджетов (Заполнение)

Вы можете добавить отступы для элементов управления, чтобы они выглядели хорошо организованными с использованием свойств padx иpady.

Передайте padx и pady любому виджету и задайте значение.

lbl1 = Label(tab1, text= 'label1', padx=5, pady=5)

Это очень просто!

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

В Python есть довольно много GUI фреймворков (graphical user interface), однако только Tkinter встроен в стандартную библиотеку языка. У Tkinter есть несколько преимуществ. Он кроссплатформенный, поэтому один и тот же код можно использовать на Windows, macOS и Linux.

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

Хотя Tkinter является популярным GUI фреймворком на Python, у него есть свои недостатки. Один из них заключается в том, что графические интерфейсы, созданные с использованием Tkinter, выглядят устаревшими. Если вам нужен современный, броский интерфейс, то Tkinter может оказаться не совсем тем, для этого есть PyQt5 который развивается сильнее в данном плане.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Тем не менее, в плане использования, Tkinter является относительно легким по сравнению с другими библиотеками. Это отличный выбор для создания GUI приложений в Python, особенно если современный облик не в приоритете для программы, а большую роль играет функциональность и кроссплатформенная скорость.

Другие статьи по Tkinter на сайте

  1. Создание окна по центру и кнопка выхода в Tkinter
  2. Разметка виджетов в Tkinter — pack, grid и place
  3. Виджеты Checkbutton, Label, Scale и Listbox в Tkinter
  4. Меню, подменю и панель инструментов в Tkinter
  5. Диалоговые окна в Tkinter
  6. Рисуем линии, прямоугольники, круг и текст в Tkinter
  7. Пишем игру змейка на Tkinter

Основные аспекты руководства

  • Введение и создание простого приложения «Hello, World!» в Tkinter;
  • Работа с виджетами вроде кнопок или текстовых боксов;
  • Управление макетом приложений через геометрические менеджеры;
  • Создание интерактивного приложения через связывание кнопок с функциями Python.

После освоения основных навыков в процессе изучения примеров, данных в руководстве, вы сможете создать два рабочих приложения. Одно будет конвертером температуры, а второе редактором текста. Что ж, приступим!

Создание простого GUI приложения на Tkinter

Главным элементом GUI Tkinter является окно. Окнами называют контейнеры, в которых находятся все GUI элементы. Данные GUI элементы, к числу которых относятся текстовые боксы, ярлыки и кнопки, называются виджетами. Виджеты помещаются внутри окон.

Сперва создадим окно с одним виджетом. Запустим новую сессию оболочки Python, следуя инструкции.

На заметку: Примеры кода, используемые в руководстве, были протестированы на Windows, macOS и Ubuntu Linux 18.04 через версии Python 3.6, 3.7 и 3.8.

В случае, если вы установили Python на Windows или macOS, скачав установщик c официального сайта python.org, проблем с запуском кода из примеров возникнуть не должно. Можете пропустить оставшуюся часть заметки и перейти к самому руководству.

В случае, если ввиду отсутствия официальных дистрибутивов Python для вашей системы, вы установили Python иным образом, тогда обратите внимание на следующие моменты.

Установка Python на macOS используя Homebrew:

Дистрибутив Python для macOS, доступный на Homebrew, поставляется без библиотеки Tcl/Tk с Tkinter. Вместо этого используется версия системы по умолчанию. Версия может быть устаревшей и не позволяющей импортировать модуль Tkinter в Python. Во избежание такой проблемы, используйте официальный установщик macOS.

Ubuntu Linux 16.04:

Последняя версия Python доступна на Ubuntu Linux 16.04, это 3.5. Вы можете установить последнюю версию через deadsnakes PPA. Далее даны команды для установки PPA и загрузки последней версии Python с правильной версией Tcl/Tk:

$ sudo addaptrepository ppa:deadsnakes/ppa

$ sudo aptget update

$ sudo aptget install python3.8 python3tk

Первые две команды добавляют deadsnakes PPA в ваш системный список репозитория, а последняя команда устанавливает Python 3.8 и модуль GUI Tkinter.

Ubuntu Linux 18.04:

Вы можете установить последнюю версию Python с соответствующей версией Tcl/Tk из универсального репозитория через следующую команду:

$ sudo aptget install python3.8 python3tk

Таким образом устанавливается Python 3.8, а также модуль Tkinter.

Прочие версии Linux:

Если на данный момент нет официального рабочего установщика Python для Linux дистрибутива, то можете поставить Python вместе с соответствующей версией Tcl/Tk через исходный код. С пошаговой инструкцией процесса можете ознакомиться на странице: Установка Python 3.8 из исходного кода на Linux

В открытой оболочке IDLE Python первым делом необходимо импортировать модуль Tkinter:

Окно, или window является экземпляром класса Tkinter. Попробуйте создать новое окно, присвоив его переменной окна window:

При выполнении вышеуказанного кода на экране появится новое окно. То, как оно будет выглядеть, зависит от вашей операционной системы:

tkinter os

Далее в руководстве будут представлены примеры из операционной системы Ubuntu и Windows.

Добавление нового виджета в приложении

Теперь, когда у вас есть окно, можно добавить на него виджет. Используем класс tk.Label и добавим какой либо текст на окно.

Создадим виджет ярлыка (label) с текстом "Привет, Tkinter!" и присвоим его переменной под названием greeting:

>>> greeting = tk.Label(text=«Привет, Tkinter!»)

Созданное ранее окно не меняется. Создается виджет ярлыка, однако пока не добавляется на окно. Есть несколько способов добавления виджетов на окно. Сейчас можно использовать метод .pack() от виджета ярлыка:

Теперь окно выглядит следующим образом:

Виджет Label

При использовании метода .pack() для размещения виджета в окне, Tkinter устанавливает размер окна настолько маленьким, насколько возможно, пока в него не влезает виджет. Теперь выполним следующее:

Кажется, будто ничего не случилось, но это не совсем так. window.mainloop() указывает Python, что нужно запустить цикл событий Tkinter. Данный метод требуется для событий вроде нажатий на клавиши или кнопки, он также блокирует запуск любого кода, что следует после, пока окно, на котором оно было вызвано, не будет закрыто. Попробуйте закрыть созданное окно, и вы увидите появившуюся в оболочке подсказку.

Внимание: При работе с Tkinter из Python REPL окна обновляются при выполнении каждой строки. Однако, если программа Tkinter выполняется из запущенного файла, такого не происходит.

Если не добавлять window.mainloop() в конец программы в Python файле, тогда приложение Tkinter не запустится вообще, и ничего не будет отображаться.

Для создания окна в Tkinter, нужно всего несколько строк кода. Тем не менее, в пустое окно нам ничем не поможет! В следующем разделе будут рассмотрены некоторые виджеты, доступные в Tkinter, а также способы их настройки при добавлении в приложение.

Выполните небольшое задание, чтобы проверить свои знания.

Напишите полный скрипт для создания окна в Tkinter с текстом «Python рулит!». Окно должно выглядеть следующим образом:

Tkinter примеры

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

Код с решением

import tkinter as tk

window = tk.Tk()

label = tk.Label(text=«Python рулит!»)

label.pack()

window.mainloop()

Работа с виджетами в Tkinter

Виджеты являются основой GUI фреймворка Tkinter в Python. Это элементы, через которые пользователи взаимодействуют с программой. В Tkinter каждый виджет определен классом. Далее представлен список популярных виджетов из Tkinter:

Класс виджета Описание
Label Используется для отображения текста или вставки изображения на окне приложения.
Button Кнопка, на которой может быть текст, совершает определенные действия при нажатии на нее.
Entry Виджет для ввода одной строчки текста. Эквивалент <input type="text"> в HTML.
Text Виджет для ввода большого текста. Эквивалент <textarea> в HTML.
Frame Прямоугольная область, что используется для группировки виджетов или для добавления расстояния между виджетами.

В дальнейшем, мы на практике рассмотрим, как действует каждый из представленных выше виджетов. С более подробными списками виджетов Tkinter можете ознакомиться на страницах Basic Widgets и More Widgets. А пока рассмотрим подробнее виджет ярлыка Label.

Виджет Label — Отображение текста и картинок

Виджеты Label используется для отображения текста или картинок. Текст на виджете Label, не может редактироваться пользователем. Он только показывается. Как было показано в примере в начале данного руководства, виджет Label можно создать через экземпляр класса Label и передачу строки в параметр text:

label = tk.Label(text=«Hello, Tkinter»)

Виджеты Label отображают текст с установленным по умолчанию системным цветом и фоном. Обычно это черный и белый цвета. Следовательно, если в вашей операционной системе указаны иные цвета, именно их вы и увидите.

Изменить цвет текста и фона виджета Label можно через параметры foreground и background:

label = tk.Label(

    text=«Привет, Tkinter!»,

    foreground=«white»,  # Устанавливает белый текст

    background=«black»  # Устанавливает черный фон

)

Некоторые доступные цвета:

  • red — красный;
  • orange — оранжевый;
  • yellow — желтый;
  • green — зеленый;
  • blue — синий;
  • purple — сиреневый.

Многие HTML-цвета имеют такое же название в Tkinter. Со списком доступных названий цветов можете ознакомиться здесь.

Все цвета в Tkinter

Если хотите подробнее разобраться в теме, особенно в системных цветах macOS и Windows, контролируемых текущей темой ОС, ознакомьтесь со страницей значений цветов.

Вы также можете указать цвет, используя шестнадцатеричные значения RGB такое часто используется в CSS для стилизации сайтов:

label = tk.Label(text=«Привет, Tkinter!», background=«#34A2FE»)

Теперь фон стал приятного голубого цвета. Шестнадцатеричные значения RGB, в отличие от названий цветов, закодированы, но это делает их более управляемыми. К счастью, доступны инструменты для простого и быстрого получения шестнадцатеричных кодов цветов.

Выбор HEX для цвета

Если вам не хочется регулярно вводить foreground и background, можете использовать сокращенные версии параметров — fg и bg. Они точно также отвечают за установку цвета текста и цвета фона.

label = tk.Label(text=«Привет, Tkinter!», fg=«white», bg=«black»)

Вы также можете управлять шириной и высотой ярлыка с помощью параметров width и height:

import tkinter as tk

window = tk.Tk()

label = tk.Label(

    text=«Привет, Tkinter!»,

    fg=«white»,

    bg=«black»,

    width=20,

    height=20

)

label.pack()

window.mainloop()

В окне, ярлык будет выглядеть следующим образом:

Tkinter Label

Может показаться странным, что ярлык в окне не является квадратным, хотя параметр как ширины, так и высоты равен 20. Все оттого, что ширина и высота измеряются в текстовых юнитах.

Горизонтальный текстовый юнит определен шириной символа "0", или цифрой ноль, в шрифте системы по умолчанию. Аналогичным образом один вертикальный текстовый юнит определен высотой символа "0".

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

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

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

Создание кнопки через виджет Button

Виджеты Button нужны для создания кликабельных кнопок. Их можно настроить таким образом, чтобы при нажатии вызывалась определенная функция. В следующей части урока, будет рассмотрено, как именно вызвать функцию при нажатии на кнопку. А пока займемся стилизацией виджета кнопки.

Существует много сходств между виджетами Button и Label. По сути кнопка — это просто ярлык, на который можно кликнуть. Для создания стилей виджетов ярлыка и кнопки также используются одинаковые аргументы.

К примеру, следующий код создает кнопку с синим фоном и желтым текстом. Также устанавливается ширина и высота с показателями в 25 и 5 текстовых юнит:

import tkinter as tk

window = tk.Tk()

button = tk.Button(

    text=«Нажми на меня!»,

    width=25,

    height=5,

    bg=«blue»,

    fg=«yellow»,

)

button.pack()

window.mainloop()

Готовая кнопка будет выглядеть следующим образом:

Tkinter кнопки

Неплохо! Следующие два виджета Entry и Text используются для сбора вводных данных от пользователя.

Виджет Entry — Однострочное текстовое поле

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

Создание виджета Entry практически ничем не отличается от процесса создания ярлыка и кнопки. К примеру, следующий код создает виджет с синим фоном и желтым текстом длиной в 50 текстовых юнит:

entry = tk.Entry(fg=«yellow», bg=«blue», width=50)

В случае виджета однострочного текстового поля (Entry) интересен не процесс создания стиля, а то, как получить эти входные данные от пользователя. Есть три основные операции, что можно провести с виджетом однострочного текстового поля (Entry):

  • Получение всего текста через .get()
  • Удаление текста через .delete()
  • Вставка нового текста через .insert()

Для лучшего понимания принципа работы виджетов Entry создадим один экземпляр элемента и попробуем ввести в него текст. Откройте оболочку Python и выполните следующие действия. Сначала импортируем tkinter и создаем новое окно:

>>> import tkinter as tk

>>> window = tk.Tk()

Теперь создаем виджеты Label и Entry:

>>> label = tk.Label(text=«Имя»)

>>> entry = tk.Entry()

Здесь ярлык указывает, что именно пользователь должен записать в виджете Entry. Нет никаких ограничений для вводимых данных, это просто подсказка, что ввести нужно «Имя». Виджеты станут видимыми на окне после выполнения метода .pack() для каждого из них:

>>> label.pack()

>>> entry.pack()

Результат выглядит следующим образом:

Tkinter виджет Entry

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

На созданном в программе виджете мы ввели текст «Иван Иванов»:

Tkinter текстовое поле

Теперь в виджете текстового поля есть текст, только он еще не был отправлен в программу. Для получения текста и присваивания его значения переменной name используется метод .get():

>>> name = entry.get()

>>> name

‘Иван Иванов’

Текст также можно удалить, для этого используется метод .delete(). Данный метод принимает аргумент, который является целым числом и сообщает Python, какой символ нужно удалить. К примеру, во фрагменте кода ниже показано, как через метод .delete(0) можно удалить первый символ из текстового поля:

Теперь в виджете остался текст "ван Иванов":

Tkinter текстовое поле

Обратите внимание, что как и строки в Python, текст в виджете однострочного текстового поля индексируется,  и начинается индексирование с 0.

При необходимости удалить несколько символов из текстового поля нужно передать второй целочисленный аргумент в .delete(), указав индекс символа, на котором процесс удаления должен завершиться. К примеру, следующий код удаляет первые четыре буквы из текстового поля:

Теперь остался только текст "Иванов":

Tkinter удаление из Entry

Метод .delete() работает по аналогии с методом строк slice() для удаления части символов из строки. Первый аргумент определяет начальный индекс удаления, последний индекс указывает где именно процесс удаления должен остановиться.

Для удаления всего текста из текстового поля во втором аргументе метода .delete() используется специальная константа tk.END:

>>> entry.delete(0, tk.END)

Теперь остался пустой текстовый бокс:

Tkinter виджет Entry

Вы также можете вставить текст в виджете однострочного текстового поля с помощью метода .insert():

>>> entry.insert(0, «Иванов»)

Теперь окно стало таким:

Tkinter удаление из Entry

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

К примеру, при вызове .insert() с первым аргументом 100, а не 0, как было сделано выше, вывод будет тем же.

В том случае, если текстовое поле уже содержит какой-то текст, тогда .insert() вставить новый текст в указанную позицию и сдвинет существующий текст вправо:

>>> entry.insert(0, «Иван «)

В виджете с текстом теперь значится "Иван Иванов":

Tkinter текстовое поле

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

Виджет Text — ввод большого текста в Tkinter

Виджеты Text используются для ввода текста, как и виджеты Entry. Разница в том, что Text может содержать несколько строчек текста. С виджетом Text пользователь может вводить целые параграфы или страницы текста. Как и в случае с виджетами Entry, над виджетами Text можно провести три основные операции:

  • Получение текста через метод .get()
  • Удаление текста через метод .delete()
  • Вставка текста через метод .insert()

Несмотря на то, что названия методов совпадают с теми, что используются в Entry, процесс реализации несколько различен. Разберем пример создания виджета Text и посмотрим на деле, что он может делать.

На заметку: У вас все еще открыто предыдущее окно? Если это так, тогда закройте его, выполнив следующую команду:

Это также можно сделать вручную, просто нажав на кнопку «Закрыть».

В оболочке Python нужно создать новое пустое окно, а внутри нее с помощью метода .pack() разместить текстовой виджет:

>>> import tkinter as tk

>>> window = tk.Tk()

>>> text_box = tk.Text()

>>> text_box.pack()

По умолчанию текстовые боксы значительно больше виджетов однострочного ввода текста Entry. Созданное с помощью кода выше окно будет выглядеть следующим образом:

Форма для ввода текста в Tkinter

Для активации текстового бокса нажмите на любую точку внутри окна. Введите слово "Hello". Нажмите на клавиатуре кнопку Enter, после чего введите на второй строке слово "World". Окно будет выглядеть следующим образом:

text tkinter

Как и в случае с виджетами Entry, вы можете получить текст их виджета Text с помощью метода .get(). Однако, вызов .get() без аргументов не возвращает весь текст как это происходит с виджетами однострочного ввода текста Entry. Появится исключение TypeError:

>>> text_box.get()

Traceback (most recent call last):

  File «<pyshell#4>», line 1, in <module>

    text_box.get()

TypeError: get() missing 1 required positional argument: ‘index1’

Метод .get() для текстового виджета запрашивает хотя бы один аргумент. Вызов .get() с единственным индексом возвращает один символ. Для получения нескольких символов требуется передать начальный и конечный индексы.

Индексы в виджетах Text действуют иначе, чем в виджетах Entry. Так как виджеты Text могут получить несколько строк текста, индекс должен содержать следующие два пункта:

  • Номер строки где находится символа;
  • Позиция символа в строке.

Номера строк начинаются с 1, а позиция символа с 0. Для получения индекса создается строка формата "<line>.<char>", где <line> заменяется номером строки, а <char> номером символа. К примеру, "1.0" означает первый символ на первой строке, а "2.3" представляет четвертый символ на второй строке.

Используем индекс "1.0" для получения первой буквы созданного ранее текстового бокса:

>>> text_box.get(«1.0»)

‘H’

В слове "Hello" 5 букв, буква o стоит под индексом 4, так как отсчет символов начинается с 0, и слово "Hello" начинается с первой строки в текстовом боксе.

Как при слайсинге строк в Python, для получения целого слова "Hello" из текстового бокса, конечный индекс должен быть на один больше последнего читаемого символа.

Таким образом, для получения слова "Hello" из текстового бокса используется "1.0" для первого индекса, и "1.5" для второго индекса:

>>> text_box.get(«1.0», «1.5»)

‘Hello’

Для получения "World" на второй строке текстового бокса, измените номера строк каждого индекса на 2:

>>> text_box.get(«2.0», «2.5»)

‘World’

Для получения всего текста из текстового виджета установите индекс на "1.0" и используйте специальную константу tk.END для второго индекса:

>>> text_box.get(«1.0», tk.END)

‘HellonWorldn’

Обратите внимание, что текст, возвращаемый через метод .get(), включает символы перехода на новую строку n. Вы также можете увидеть в данном примере, что каждая строка в виджете Text содержит в конце символ новой строки, включая последнюю строку текста в текстовом боксе.

Метод .delete() используется для удаления символов из текстового виджета. Работает точно так же, как и метод .delete() для виджетов Entry. Есть два способа использования .delete():

  • С одним аргументом;
  • С двумя аргументами.

Используя вариант с одним аргументом, вы передаете индекс символа для удаления в .delete(). К примеру, следующий код удаляет первый символ H из текстового бокса:

>>> text_box.delete(«1.0»)

Теперь в первой строке текста значится "ello":

delete tkinter

В версии с двумя аргументами передается два индекса для удаления группы символов, начиная с первого символа и заканчивая вторым, но не включая его.

К примеру, для удаления оставшейся части "ello" из первой строки текстового бокса используются индексы "1.0" и "1.4":

>>> text_box.delete(«1.0», «1.4»)

Обратите внимание, что текст из первой строки удалился, но осталась пустая строка, за которой следует слово World на второй строке:

delete tkinter

Хотя вам этого не видно, но на первой строке остался один символ — это символ новой строки n. Вы можете сами убедиться в этом, вызвав метод .get():

>>> text_box.get(«1.0»)

‘n’

При удалении данного символа оставшаяся часть содержимого текстового виджета сдвинется на строку вверх:

>>> text_box.delete(«1.0»)

Теперь слово "World" переместилось на первую строку текстового виджета:

delete tkinter

Попробуем очистить оставшуюся часть из текстового виджета. Установим "1.0" в качестве стартового индекса и tk.END в качестве второго индекса:

>>> text_box.delete(«1.0», tk.END)

Теперь наш текстовый виджет совершенно пуст:

empty textbox tkinter

Вы также можете вставить текст в текстовый виджет с помощью метода  .insert():

>>> text_box.insert(«1.0», «Hello»)

Метод вставляет слово "Hello" в начало текстового виджета. Используется уже знакомый формат "<line>.<column>" как и у метода .get() для уточнения позиции вставки текста:

insert tkinter

Проверьте, что произойдет при попытке вставить слово "World" во вторую строчку:

>>> text_box.insert(«2.0», «World»)

Вместо вставки слова на вторую строку текст ставится в конце первой строки:

insert tkinter

Если вы хотите поставить текст на новую строку, тогда понадобится вставить символ новой строки n вручную:

>>> text_box.insert(«2.0», «nWorld»)

Теперь слово "World" находится на второй строке текстового виджета:

insert tkinter new line

Метод .insert() делает следующие две вещи:

  • Вставляет текст в указанной позиции, если в этой позиции или после неё уже находится текст;
  • Добавляет текст в указанную строку, если номер символа превышает индекс последнего символа в текстовом боксе.

Обычно нецелесообразно пытаться отследить индекс последнего символа. Лучший способ вставить текст в конец виджета Text — передать tk.END как первый параметр в методе .insert():

text_box.insert(tk.END, «Вставь меня в самом конце!»)

Не забудьте включить символ новой строки (n) в начале текста, если вы хотите расположить его на новой строке:

text_box.insert(tk.END, «nВставь меня в новую строку!»)

Виджеты Label, Button, Entry и Text являются только небольшой частью виджетов, доступных в Tkinter. Среди прочих виджетов есть чекбоксы, радио кнопки, скролл бары и прогресс бары.

Использование виджета Frame в Tkinter

В данном руководстве мы рассмотрим только пять виджетов. Это описанные ранее четыре виджета плюс виджет рамки Frame. Виджеты рамок важны для организации макета виджетов в приложении.

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

import tkinter as tk

window = tk.Tk()

frame = tk.Frame()

frame.pack()

window.mainloop()

Метод frame.pack() помещает рамку в окно, таким образом размер окна становится таким маленьким, каким он может быть для того, чтобы поместился рамку. При запуске скрипта выше будет получен крайне скучный вывод:

frame tkinter

Пустой виджет рамки практически невидим.

Рамки лучше всего рассматривать как контейнеры для других виджетов.

Вы можете вставить любой виджет в рамку, установив атрибут master данному виджету:

frame = tk.Frame()

label = tk.Label(master=frame)

Чтобы посмотреть, как это работает, напишем скрипт для создания двух виджетов Frame под названиями frame_a и frame_b. В данном скрипте frame_a содержит ярлык с текстом "I'm in Frame A", а frame_b содержит ярлык с текстом "I'm in Frame B". Вот как это можно сделать:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()

frame_b = tk.Frame()

label_a = tk.Label(master=frame_a, text=«I’m in Frame A»)

label_a.pack()

label_b = tk.Label(master=frame_b, text=«I’m in Frame B»)

label_b.pack()

frame_a.pack()

frame_b.pack()

window.mainloop()

Обратите внимание, что рамка frame_a помещается в окно перед рамкой frame_b. Открывающееся окно показывает ярлык в рамке frame_a над ярлыком в рамке frame_b:

frame tkinter

Теперь посмотрим, что произойдет при изменении порядка вставки frame_a.pack() и frame_b.pack():

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()

label_a = tk.Label(master=frame_a, text=«I’m in Frame A»)

label_a.pack()

frame_b = tk.Frame()

label_b = tk.Label(master=frame_b, text=«I’m in Frame B»)

label_b.pack()

# Вставка рамок в окне поменялись местами.

frame_b.pack()

frame_a.pack()

window.mainloop()

Вывод выглядит следующим образом:

frame tkinter

Теперь ярлык label_b сверху. Так как ярлык label_b назначен к рамке frame_b, он перемещается, куда позиционируется рамка  frame_b.

У всех четырех рассмотренных типов виджетов — Label, Button, Entry и Text — есть атрибут master, который устанавливается при их создании. Таким образом, вы можете управлять тем, к какой именно рамки назначен виджет.

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

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

Атрибут relief в Tkinter — Меняем стиль рамки

Рамки могут менять свой стиль с помощью атрибута relief, который создает границу вокруг рамки. Можно присвоить relief любое из следующих значений:

  • tk.FLAT: Никакого эффекта рамки (по умолчанию);
  • tk.SUNKEN: Создается эффект углубления элемента;
  • tk.RAISED: Создается эффект выпуклости элемента;
  • tk.GROOVE: Создается эффект врезанной в текстуру рамки, своего рода выемки;
  • tk.RIDGE: Создается эффект выпуклой выемки.

Для применения эффекта рамки нужно установить значение атрибута borderwidth выше, чем 1. Данный атрибут корректирует ширину рамки в пикселях. Лучший способ понять, как выглядит каждый эффект — проверить на практике.

Вот скрипт, который помещает пять рамок в окно, где каждая из которых имеет свое значение аргумента relief:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import tkinter as tk

border_effects = {

    «flat»: tk.FLAT,

    «sunken»: tk.SUNKEN,

    «raised»: tk.RAISED,

    «groove»: tk.GROOVE,

    «ridge»: tk.RIDGE,

}

window = tk.Tk()

for relief_name, relief in border_effects.items():

    frame = tk.Frame(master=window, relief=relief, borderwidth=5)

    frame.pack(side=tk.LEFT)

    label = tk.Label(master=frame, text=relief_name)

    label.pack()

window.mainloop()

Разбор скрипта по строкам кода:

  • Строки с 3 по 9 создают словарь, ключами которого являются названия различных эффектов от relief, доступных в Tkinter. Значения являются соответствующими объектами Tkinter. Этот словарь назначен переменной border_effects
  • Строка 13 запускает цикл for для каждого элемента в словаре border_effects
  • Строка 14 создает новый виджет Frame и назначает его объекту window. Атрибут relief установлен на соответствующий элемент рельефа в словаре border_effects, а атрибут border установлен на 5 пикселей, чтобы эффект был видимым
  • Строка 15 помещает виджет рамки в окно с помощью метода .pack(). Ключевое слово side указывает Tkinter, где поместить поместить рамку. Подробнее о том, как это работает, вы узнаете в следующем разделе.
  • Строки 16 и 17 создаем виджет текстового ярлыка для отображения названия рельефа и помещаем его в только что созданную рамку.

Окно, созданное вышеуказанным скриптом, выглядит следующим образом:

frame tkinter

На данной картинке представлены следующие эффекты:

  • tk.FLAT создает простую рамку без видимой границы;
  • tk.SUNKEN добавляет границу, которая создает эффект, будто элемент провалился в окне;
  • tk.RAISED создает рамке границу в виде эффекта выпуклости;
  • tk.GROOVE добавляет эффект врезанной, будто утопающей в текстуру, рамки;
  • tk.RIDGE создает эффект, будто врезанной с другой стороны выпуклой рамки.

Данные эффекты могут сделать ваше приложение на Tkinter более интересным с визуальной точки зрения.

Правила именования виджетов в Tkinter

При создании виджета можно дать ему любое имя при условии, что данное имя уже не зарегистрировано самим Python, вроде with, for, range и т.д.

Обычно хорошей идеей является включение имени класса виджета в имя переменной, которую вы назначаете экземпляру виджета. Например, если виджет Label используется для отображения имени пользователя, вы можете назвать виджет label_user_name. Виджет Entry, используемый для определения возраста пользователя, может называться entry_age.

Когда вы включаете имя класса виджета в имя переменной, вы помогаете себе (и всем, кому необходимо прочитать ваш код), чтобы понять, к какому типу виджетов относится имя переменной.

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

Класс виджета Префикс названия переменной Пример
Label lbl lbl_name
Button btn btn_submit
Entry ent ent_age
Text txt txt_notes
Frame frm frm_address

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

В следующем разделе мы познакомимся с управлением макетом приложений с помощью мощных менеджеров геометрии от Tkinter.

Задание #2 — Создать текстовое поле и вставить в нее текст

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

Задание: Создать однострочное текстовое поле и вставить на нее какой-то текст.

  1. Напишите скрипт для отображения виджета однострочного текстового поля Entry шириной в 40 текстовых юнитов с белым фоном и черным текстом;
  2. Используйте метод .insert() для вставки текста: "What is your name?".

Результат должен выглядеть следующим образом:

tkinter entry

Попробуйте выполнить задание самостоятельно.

Ниже представлен вариант кода для выполнения данного задания. В следующем варианте для установки фона и цвета шрифта текстового виджета, используются параметры bg и fg. Помните, что ваша программа может отличаться от нашей, у каждого разработчика свой стиль программирования.

Код с решением задачи #2

import tkinter as tk

window = tk.Tk()

entry = tk.Entry(width=40, bg=«white», fg=«black»)

entry.pack()

entry.insert(0, «What is your name?»)

window.mainloop()

Это хорошее решение, цвет фона и цвет шрифта для виджета однострочного текстового поля Entry указываются довольно просто.

В большинстве систем по умолчанию цвет фона для виджета Entry — белый, а цвет содержимого — черный. Таким образом, вы можете создать то же самое окно, не уточняя при этом параметры bg и fg:

Измененный код с решением задачи #2

import tkinter as tk

window = tk.Tk()

entry = tk.Entry(width=40)

entry.pack()

entry.insert(0, «What is your name?»)

window.mainloop()

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

Настройка макета приложения через менеджеры геометрии

До данного момента мы добавляли виджеты на окно с помощью метода  .pack(), однако что именно делает данный метод нам пока неизвестно.

Давайте все проясним. Макет приложения в Tkinter управляется через менеджеры геометрии. Одним из менеджеров геометрии является .pack(). Помимо него в Tkinter есть еще двое таких менеджеров:

  • .place()
  • .grid()

Каждое окно и рамка в приложении может использовать только один менеджер геометрии. Однако разные рамки могут использовать разные менеджеры геометрии, даже если они назначены окну или рамики, что использует другой менеджер геометрии. Начнем с более подробного разбора менеджера .pack().

Менеджер геометрии pack() в Tkinter

Менеджер .pack() используется для размещения виджетов на рамку или на окне приложения в определенном порядке использует алгоритм упаковки. Для заданного виджета у алгоритма упаковки есть два основных этапа:

  • Расчет прямоугольной области, называемой участком. Размер области подходит для вмещения виджета, а оставшаяся ширина (или высота) окна заполняется пустым пространством;
  • Центрирование виджета в участке, если не указано другое местоположение в окне.

Менеджер геометрии .pack() является мощным инструментом, но его может быть трудно визуализировать. Лучший способ понять .pack() — это разобрать примеры.

Посмотрите, что происходит при размещении трех виджетов ярлыков с текстом в рамку через менеджер .pack():

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=100, height=100, bg=«red»)

frame1.pack()

frame2 = tk.Frame(master=window, width=50, height=50, bg=«yellow»)

frame2.pack()

frame3 = tk.Frame(master=window, width=25, height=25, bg=«blue»)

frame3.pack()

window.mainloop()

По умолчанию .pack() помещает каждую рамку друг под другом в том порядке, в котором они добавлены на окно приложения:

pack tkinter

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

Есть три невидимых участка в каждой рамки. Ширина каждого участка совпадает с шириной окна, а высота с высотой рамки внутри. Поскольку, при каждом вызове метода .pack() для каждой рамки не было указано никакой точки привязки, все они центрированы внутри своих участков. Вот почему каждая рамка находится в центре окна.

Менеджер .pack() принимает некоторые ключевые аргументы для более точной настройки размещения виджетов. К примеру, можно установить ключевой аргумент fill для уточнения в какое направление рамки будут пополняться. Доступная опция tk.X для горизонтального направления, tk.Y для вертикального направления и tk.BOTH для обоих. Далее представлен код для горизонтального заполнения окна тремя рамками:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, height=100, bg=«red»)

frame1.pack(fill=tk.X)

frame2 = tk.Frame(master=window, height=50, bg=«yellow»)

frame2.pack(fill=tk.X)

frame3 = tk.Frame(master=window, height=25, bg=«blue»)

frame3.pack(fill=tk.X)

window.mainloop()

Обратите внимание, что для виджета рамки ширина width не установлена. Аргумент width больше не требуется, так как каждая рамка задействует .pack() для горизонтального заполнения, переписывая любой индивидуальный параметр ширины.

Результат вышеуказанного скрипта будет выглядеть следующим образом:

frame tkinter

Одна из приятных особенностей заполнения окна с помощью .pack() заключается в том, что содержимое реагирует на изменение размера окна. Попробуйте расширить окно, созданное предыдущим скриптом, чтобы увидеть, как это работает. Когда вы расширяете окно, ширина трех рамок внутри также увеличивается, заполняя окно:

tkinter response

Обратите внимание, что виджеты рамок не растягиваются в вертикальном направлении.

Аргумент side от метода .pack() уточняет, на какую сторону окна виджет должен размещаться. Доступные варианты:

  • tk.TOP сверху;
  • tk.BOTTOM снизу;
  • tk.LEFT слева;
  • tk.RIGHT  справа.

В случае, если аргумент side не указан, тогда метод .pack() автоматически использует tk.TOP и ставит новые виджеты в верхнюю часть окна, или в самую верхнюю часть окна, которая еще не занята виджетом. К примеру, следующий скрипт размещает три рамки рядом друг с другом слева направо и расширяет каждую рамку, чтобы заполнить окно по вертикали:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg=«red»)

frame1.pack(fill=tk.Y, side=tk.LEFT)

frame2 = tk.Frame(master=window, width=100, bg=«yellow»)

frame2.pack(fill=tk.Y, side=tk.LEFT)

frame3 = tk.Frame(master=window, width=50, bg=«blue»)

frame3.pack(fill=tk.Y, side=tk.LEFT)

window.mainloop()

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

Полученное окно выглядит следующим образом:

tkinter frames

При установке аргумента fill=tk.X рамки адаптируются при горизонтальном изменении размера окна, а при указании аргумента fill=tk.Y они адаптируются при изменении размера окна по вертикали:

tkinter vertical

Для того чтобы сделать макет по-настоящему адаптированным, можно установить начальный размер фреймов, используя атрибуты width и height. Затем нужно установить значение аргумента fill от метода .pack() на tk.BOTH, а также установить значение аргумента expand на True:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg=«red»)

frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame2 = tk.Frame(master=window, width=100, bg=«yellow»)

frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame3 = tk.Frame(master=window, width=50, bg=«blue»)

frame3.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

window.mainloop()

При запуске вышеуказанного кода результатом будет окно, что поначалу будет выглядеть как и предыдущий пример. Разница в том, что теперь можно менять размер окна в любом направлении, и рамки будут соответственно адаптироваться при изменении размеров окна:

tk full responsive

Вышло неплохо!

Менеджер геометрии place() в Tkinter

Для управления точным местом расположения виджета в окне или в рамке используется менеджер .place(). Вы должны указать два ключевых аргумента x и y, которые определяют координаты x и y для верхнего левого угла виджета. Аргументы x и y измеряются в пикселях, а не в текстовых юнитах.

Имейте в виду, что начало координат (где x и y равны 0) — это верхний левый угол рамки или окна. Таким образом, вы можете рассматривать аргумент y в методе .place() как количество пикселей в верхней части окна, а аргумент x — в качестве количества пикселей в левой части окна.

Вот пример того, как работает менеджер геометрии .place():

import tkinter as tk

window = tk.Tk()

frame = tk.Frame(master=window, width=150, height=150)

frame.pack()

label1 = tk.Label(master=frame, text=«I’m at (0, 0)», bg=«red»)

label1.place(x=0, y=0)

label2 = tk.Label(master=frame, text=«I’m at (75, 75)», bg=«yellow»)

label2.place(x=75, y=75)

window.mainloop()

Разберем код:

  • Строки 5 и 6 создают новый виджет рамки Frame под названием frame1, его ширина 150 пикселей и высота также 150 пикселей, и упаковывают его в окно с помощью .pack();
  • Строки 8 и 9 создают новый ярлык с текстом под названием label1 с желтым фоном и помещает его в рамку frame1 на позицию (0, 0);
  • Строки 11 и 12 создают второй ярлык с текстом под названием label2 на красном фоне и помещают его в рамку frame1 на позицию (75, 75).

Вот окно, которое создалась при выполнении данного кода:

tk place

Менеджер геометрии .place() используется не очень часто. У него есть два значимых недостатка:

  • Через метод .place() макет сложно настраивать, особенно в тех случаях, когда у приложений много виджетов;
  • Макеты, созданные при помощи менеджера .place(), не адаптируются. Они не меняются вместе с изменением размера главного окна.

Одной из основных проблем при разработке кроссплатформенного графического интерфейса является создание макетов, которые хорошо выглядят независимо от того, на какой платформе они просматриваются.

В таком случае менеджер геометрии .place() — плохой выбор для создания адаптивных и кроссплатформенных макетов.

Однако это не значит, что нужно отказаться от использования менеджера  .place()!

В некоторых случаях это оптимальный выбор. Например, если вы создаете графический интерфейс для географической карты, то .place() прекрасно подойдет. Он обеспечит размещение виджетов на правильном расстоянии друг от друга на карте.

Как правило, лучше использовать менеджер .pack(), а не .place(). Тем не менее, и у .pack() есть определенные недостатки. Расположение виджетов зависит от порядка, в котором вызывается метод .pack(), поэтому может быть сложно модифицировать существующие приложения без полного понимания кода, управляющего макетом. Менеджер геометрии .grid() решает многие из этих проблем.

Поговорим об этом в следующем разделе.

Менеджер геометрии grid() в Tkinter

Самым популярным менеджером геометрии в Tkinter является .grid(). Он обладает всей мощностью .pack(), будучи намного более простым в использовании.

Менеджер .grid() работает путем разделения окна или рамки на строки и столбцы (на сетку). Вы указываете местоположение виджета, вызывая метод .grid() и передавая индексы row и column (строки и столбца) в ключевые аргументы строки и столбца соответственно.

Индексы строк и столбцов начинаются с 0, поэтому индекс строки 1 и индекс столбца 2 указывают методу .grid(), что виджет нужно разместить в третьем столбце второй строки.

Следующий скрипт создает сетку из рамок 3 × 3 с находящимися в них ярлыками с текстом:

import tkinter as tk

window = tk.Tk()

for i in range(3):

    for j in range(3):

        frame = tk.Frame(

            master=window,

            relief=tk.RAISED,

            borderwidth=1

        )

        frame.grid(row=i, column=j)

        label = tk.Label(master=frame, text=f«Row {i}nColumn {j}»)

        label.pack()

window.mainloop()

Результат выглядит следующим образом:

tk grid

В данном примере использовались два менеджера геометрии. Каждая рамка привязана к определенному окну через менеджер геометрии grid():

import tkinter as tk

window = tk.Tk()

for i in range(3):

    for j in range(3):

        frame = tk.Frame(

            master=window,

            relief=tk.RAISED,

            borderwidth=1

        )

        frame.grid(row=i, column=j)

        label = tk.Label(master=frame, text=f«Row {i}nColumn {j}»)

        label.pack()

window.mainloop()

Каждый ярлык с текстом привязан к определенной рамки через менеджер .pack():

import tkinter as tk

window = tk.Tk()

for i in range(3):

    for j in range(3):

        frame = tk.Frame(

            master=window,

            relief=tk.RAISED,

            borderwidth=1

        )

        frame.grid(row=i, column=j)

        label = tk.Label(master=frame, text=f«Row {i}nColumn {j}»)

        label.pack()

window.mainloop()

Важно понимать, что метод .grid() вызывается для каждого объекта рамки, но менеджер геометрии применяется к окну. Аналогично, расположение каждой рамки контролируется менеджером геометрии .pack().

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

Отступ, или padding — это просто пустое пространство, которое окружает виджет и визуально отделяет его от его содержимого.

Есть два вида отступов — внешние и внутренние. Внешнее отступы добавляют пространство вокруг ячейки сетки. Управление осуществляется через два ключевые аргумента метода .grid():

  • padx добавляет отступ в горизонтальном направлении;
  • pady добавляет отступы в вертикальном направлении.

Аргументы padx и pady измеряются в пикселях, а не в текстовых юнитах, поэтому установка их обоих на одно и то же значение создаст одинаковое количество отступов в обоих направлениях. Попробуем добавить отступы вокруг рамок из предыдущего примера:

import tkinter as tk

window = tk.Tk()

for i in range(3):

    for j in range(3):

        frame = tk.Frame(

            master=window,

            relief=tk.RAISED,

            borderwidth=1

        )

        frame.grid(row=i, column=j, padx=5, pady=5)

        label = tk.Label(master=frame, text=f«Row {i}nColumn {j}»)

        label.pack()

window.mainloop()

Вот результат:

padding tkinter

У менеджера геометрии .pack() также есть параметры padx и pady. Следующий код почти идентичен предыдущему коду, за исключением того, что добавляется 5 пикселей дополнительного отступа вокруг каждого ярлыка с текстом в направлениях x и y:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import tkinter as tk

window = tk.Tk()

for i in range(3):

    for j in range(3):

        frame = tk.Frame(

            master=window,

            relief=tk.RAISED,

            borderwidth=1

        )

        frame.grid(row=i, column=j, padx=5, pady=5)

        label = tk.Label(master=frame, text=f«Row {i}nColumn {j}»)

        label.pack(padx=5, pady=5)

window.mainloop()

Дополнительные отступы вокруг ярлыков с текстом немного расширяют место для каждой ячейки в сетке между рамкой Frame и текстом на ярлыке Label:

padding tkinter

Выглядит неплохо! Однако при попытке расширить окно в каком бы то ни было направлении можно заметить, что макет не адаптируется:

tk grid unresponsive

При расширении окна сетка с виджетами остается в верхнем левом углу.

Можно настроить так, чтобы строки и столбы сетки будут реагировать на изменение размера окна с помощью метода .columnconfigure() и .rowconfigure() для объекта окна приложения. Помните, что сетка привязана к окну, даже если вы вызываете метод .grid() для каждой рамки. Метод .columnconfigure() и .rowconfigure() принимают три важных аргумента:

  1. Индекс столбца или строки сетки, которого нужно настроить (или список индексов для настройки нескольких строк или столбцов одновременно);
  2. Ключевой аргумент под названием weight, который определяет, как столбец или строка должны реагировать на изменение размера окна относительно других столбцов и строк;
  3. Ключевой аргумент под названием minsize, который устанавливает минимальный размер высоты строки или ширины столбца в пикселях.

Аргумент weight по умолчанию равен 0 . Это означает, что столбец или строка не расширяются при изменении размера окна. Если каждому столбцу и строке присвоен weight=1, то все они будут меняться одинаково. Если вес (weight) одного столбца 1, а у другого 2, то второй столбец расширяется вдвое быстрее первого. Настроим предыдущий скрипт для лучшей обработки изменения размера окна:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import tkinter as tk

window = tk.Tk()

for i in range(3):

    window.columnconfigure(i, weight=1, minsize=75)

    window.rowconfigure(i, weight=1, minsize=50)

    for j in range(0, 3):

        frame = tk.Frame(

            master=window,

            relief=tk.RAISED,

            borderwidth=1

        )

        frame.grid(row=i, column=j, padx=5, pady=5)

        label = tk.Label(master=frame, text=f«Row {i}nColumn {j}»)

        label.pack(padx=5, pady=5)

window.mainloop()

Метод .columnconfigure() и .rowconfigure() помещаются в тело внешнего цикла for. Можно точно настроить каждый столбец и строку за пределами цикла for, однако для этого нужно будет написать дополнительные шесть строк кода.

В каждой итерации цикла i-ые столбцы и строки настраиваются таким образом, чтобы значение их weight было равно 1. Это является гарантией того, что каждая строка и столбец расширяются с одинаковой скоростью при изменении размера окна. Аргумент minsize имеет значение 75 для каждого столбца и 50 для каждой строки. Это гарантирует, что ярлык с текстом всегда отображает свой текст без обрезания каких-либо символов, даже если размер окна очень мал.

Результатом является макет сетки, который плавно расширяется и сжимается при изменении размера окна:

tk grid resize

Попробуйте самим изменить размер окна. Поэкспериментируйте с параметрами weight и minsize, чтобы посмотреть, как они влияют на сетку.

По умолчанию виджеты центрируются в своих ячейках сетки. К примеру, следующий код создает два ярлыка с текстом и помещает их в сетку с одним столбцом и двумя строками:

import tkinter as tk

window = tk.Tk()

window.columnconfigure(0, minsize=250)

window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text=«A»)

label1.grid(row=0, column=0)

label2 = tk.Label(text=«B»)

label2.grid(row=1, column=0)

window.mainloop()

Ширина каждой ячейка сетки 250 пикселей, высота 100 пикселей. Ярлыки с текстом помещаются в центре каждой ячейки, как показано в следующей фигуре:

tk grid

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

  • «n» или «N»Север — выравнивает по верхней центральной части ячейки;
  • «e» или «E»Запад — выравнивает по правой центральной части ячейки;
  • «s» или «S»Юг — выравнивает по нижней центральной части ячейки;
  • «w» или «W»Восток — выравнивает по левой центральной части ячейки.

Буквы «n», «s», «e» и «w» идут по траектории с севера, на юг, затем восток и запад. Установка параметра sticky со значением «n» для обоих ярлыков из предыдущего кода позиционируют каждый ярлык с текстом в верхнюю центральную ячейку сетки:

import tkinter as tk

window = tk.Tk()

window.columnconfigure(0, minsize=250)

window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text=«A»)

label1.grid(row=0, column=0, sticky=«n»)

label2 = tk.Label(text=«B»)

label2.grid(row=1, column=0, sticky=«n»)

window.mainloop()

Результат:

tk grid

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

import tkinter as tk

window = tk.Tk()

window.columnconfigure(0, minsize=250)

window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text=«A»)

label1.grid(row=0, column=0, sticky=«ne»)

label2 = tk.Label(text=«B»)

label2.grid(row=1, column=0, sticky=«sw»)

window.mainloop()

В данном примере параметр sticky ярлыка label1 настроен на "ne", который позиционируется ярлык в верхний правый угол ячейки сетки. Ярлык label2 позиционируется в нижнем левом углу, указывая "sw" в аргументе sticky.

Вот как будет выглядеть окно:

tk grid

Когда виджет позиционируется через sticky, размера самого виджета достаточно для размещения любого текста и другого содержимого внутри него. Он не заполнить всю ячейку сетки. Для заполнения всей сетки можно указать "ns", заставив виджет заполнить ячейку в вертикальном направлении, или "ew" для заполнения ячейки в горизонтальном направлении. Для заполнения всей ячейки в sticky требуется указать "nsew".

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

import tkinter as tk

window = tk.Tk()

window.rowconfigure(0, minsize=50)

window.columnconfigure([0, 1, 2, 3], minsize=50)

label1 = tk.Label(text=«1», bg=«black», fg=«white»)

label2 = tk.Label(text=«2», bg=«black», fg=«white»)

label3 = tk.Label(text=«3», bg=«black», fg=«white»)

label4 = tk.Label(text=«4», bg=«black», fg=«white»)

label1.grid(row=0, column=0)

label2.grid(row=0, column=1, sticky=«ew»)

label3.grid(row=0, column=2, sticky=«ns»)

label4.grid(row=0, column=3, sticky=«nsew»)

window.mainloop()

Результат выглядит следующим образом:

tk grid

Приведенный выше пример демонстрирует то, что параметр sticky от менеджера геометрии .grid() может использоваться для достижения тех же эффектов, что и параметр fill от менеджера геометрии .pack(). Параллели между параметрами sticky и fill представлены в следующей таблице:

Метод grid() Метод pack()
sticky="ns" fill=tk.Y
sticky="ew" fill=tk.X
sticky="nsew" fill=tk.BOTH

Метод .grid() является довольно мощным менеджером геометрии. Зачастую его проще понять, чем менеджер .pack(). Он также универсальнее, нежели менеджер .place(). При создании новых приложений в Tkinter, в качестве основного менеджера геометрии лучше рассматривать .grid().

На заметку: .grid() очень гибок в плане управления. Например, вы можете настроить ячейки на несколько строк и столбцов. Для получения дополнительной информации просмотрите раздел Grid Geometry Manager.

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

Задание #3 Создайте анкету с контактными данными клиента

Ниже дано еще одно задание для проверки понимания всего изученного.

Задание: Создать форму для ввода полного адреса клиента.

Ниже представлено изображение формы для ввода полного домашнего адреса клиента, которая была создана через Tkinter.

Форма ввода адреса в Tkinter

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

Ниже представлен наш вариант кода. Есть много разных способов выполнить данное задание. Если ваш вариант воспроизводит такое же окно как на картинке, то поздравляем! Вы справились! Ниже можете также ознакомиться с двумя вариантами кода, где используется менеджер .grid().

В следующем варианте создаются виджеты ярлыка с текстом и однострочное поле для ввода текста для каждого поля с желаемыми данными:

Полный код с решением задачи #3

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

84

85

86

87

88

89

import tkinter as tk

# Создается новое окно с заголовком «Введите домашний адрес»

window = tk.Tk()

window.title(«Введите домашний адрес»)

# Создается новая рамка `frm_form` для ярлыков с текстом и

# Однострочных полей для ввода информации об адресе.

frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)

# Помещает рамку в окно приложения.

frm_form.pack()

# Создает ярлык и текстовок поле для ввода имени.

lbl_first_name = tk.Label(master=frm_form, text=«Имя:»)

ent_first_name = tk.Entry(master=frm_form, width=50)

# Использует менеджер геометрии grid для размещения ярлыка и

# однострочного поля для ввода текста в первый и второй столбец

# первой строки сетки.

lbl_first_name.grid(row=0, column=0, sticky=«e»)

ent_first_name.grid(row=0, column=1)

# Создает ярлык и текстовок поле для ввода фамилии.

lbl_last_name = tk.Label(master=frm_form, text=«Фамилия:»)

ent_last_name = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на вторую строку сетки

lbl_last_name.grid(row=1, column=0, sticky=«e»)

ent_last_name.grid(row=1, column=1)

# Создает ярлык и текстовок поле для ввода первого адреса.

lbl_address1 = tk.Label(master=frm_form, text=«Адрес 1:»)

ent_address1 = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на третьей строке сетки.

lbl_address1.grid(row=2, column=0, sticky=«e»)

ent_address1.grid(row=2, column=1)

# Создает ярлык и текстовок поле для ввода второго адреса.

lbl_address2 = tk.Label(master=frm_form, text=«Адрес 2:»)

ent_address2 = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на четвертой строке сетки.

lbl_address2.grid(row=3, column=0, sticky=tk.E)

ent_address2.grid(row=3, column=1)

# Создает ярлык и текстовок поле для ввода города.

lbl_city = tk.Label(master=frm_form, text=«Город:»)

ent_city = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на пятой строке сетки.

lbl_city.grid(row=4, column=0, sticky=tk.E)

ent_city.grid(row=4, column=1)

# Создает ярлык и текстовок поле для ввода региона.

lbl_state = tk.Label(master=frm_form, text=«Регион:»)

ent_state = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на шестой строке сетки.

lbl_state.grid(row=5, column=0, sticky=tk.E)

ent_state.grid(row=5, column=1)

# Создает ярлык и текстовок поле для ввода почтового индекса

lbl_postal_code = tk.Label(master=frm_form, text=«Почтовый индекс:»)

ent_postal_code = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на седьмой строке сетки.

lbl_postal_code.grid(row=6, column=0, sticky=tk.E)

ent_postal_code.grid(row=6, column=1)

# Создает ярлык и текстовок поле для ввода страны.

lbl_country = tk.Label(master=frm_form, text=«Страна:»)

ent_country = tk.Entry(master=frm_form, width=50)

# Размещает виджеты на восьмой строке сетки.

lbl_country.grid(row=7, column=0, sticky=tk.E)

ent_country.grid(row=7, column=1)

# Создает новую рамку `frm_buttons` для размещения

# кнопок «Отправить» и «Очистить». Данная рамка заполняет

# все окно в горизонтальном направлении с

# отступами в 5 пикселей горизонтально и вертикально.

frm_buttons = tk.Frame()

frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5)

# Создает кнопку «Отправить» и размещает ее

# справа от рамки `frm_buttons`.

btn_submit = tk.Button(master=frm_buttons, text=«Отправить»)

btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10)

# Создает кнопку «Очистить» и размещает ее

# справа от рамки `frm_buttons`.

btn_clear = tk.Button(master=frm_buttons, text=«Очистить»)

btn_clear.pack(side=tk.RIGHT, ipadx=10)

# Запуск приложения.

window.mainloop()

Это довольно неплохое решение. Оно немного длинное, зато понятное. При желании что-то изменить сразу ясно, где это можно сделать.

Есть и более короткий вариант решения. Учитывая, что у каждого текстового поля одинаковая ширина width, понадобится только указать текст для ярлыков:

Обновленный код с решением задачи #3

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

import tkinter as tk

# Создается новое окно с заголовком «Введите домашний адрес».

window = tk.Tk()

window.title(«Address Entry Form»)

# Создается новая рамка `frm_form` для ярлыков с текстом и

# Однострочных полей для ввода информации об адресе.

frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)

# Помещает рамку на окно приложения.

frm_form.pack()

# Список ярлыков полей.

labels = [

    «Имя:»,

    «Фамилия:»,

    «Адрес 1:»,

    «Адрес 2:»,

    «Город:»,

    «Регион:»,

    «Почтовый индекс:»,

    «Страна:»,

]

# Цикл для списка ярлыков полей.

for idx, text in enumerate(labels):

    # Создает ярлык с текстом из списка ярлыков.

    label = tk.Label(master=frm_form, text=text)

    # Создает текстовое поле которая соответствует ярлыку.

    entry = tk.Entry(master=frm_form, width=50)

    # Использует менеджер геометрии grid для размещения ярлыков и

    # текстовых полей в строку, чей индекс равен idx.

    label.grid(row=idx, column=0, sticky=«e»)

    entry.grid(row=idx, column=1)

# Создает новую рамку `frm_buttons` для размещения в ней

# кнопок «Отправить» и «Очистить». Данная рамка заполняет

# все окно в горизонтальном направлении с

# отступами в 5 пикселей горизонтально и вертикально.

frm_buttons = tk.Frame()

frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5)

# Создает кнопку «Отправить» и размещает ее

# справа от рамки `frm_buttons`.

btn_submit = tk.Button(master=frm_buttons, text=«Submit»)

btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10)

# Создает кнопку «Очистить» и размещает ее

# справа от рамки `frm_buttons`.

btn_clear = tk.Button(master=frm_buttons, text=«Clear»)

btn_clear.pack(side=tk.RIGHT, ipadx=10)

# Запуск приложения.

window.mainloop()

В данном варианте список используется для хранения текста для каждого ярлыка формы. Они хранятся в том порядке, в котором поля формы должно появляться. После этого функция enumerate() возвращает индекс и строку каждого значения из списка labels.

Разобравшись с заданием, можете переходить к следующей части урока.

Создание интерактивного приложения  в Tkinter

Теперь у вас есть довольно хорошее представление о том, как создать окно с Tkinter, мы уже можем добавлять некоторые виджеты и управлять макетом приложения. Это здорово, но приложения должны не просто хорошо выглядеть — им нужно что-то делать! В этом разделе вы узнаете, как создать интерактивное приложение, которая будет выполнять какие-то действия при возникновении определенных событий.

Обработчика событий в Tkinter

При создании приложения в Tkinter, для старта цикла событий требуется вызвать метод window.mainloop(). Во время цикла событий приложение проверяет, произошло ли какое либо событие. Если это так, то в ответ может быть выполнен какой-то метод или функция.

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

На заметку: Событие — это любое действие, которое происходит во время цикла событий. Оно может вызвать некоторое поведение в приложении. Например, при нажатии клавиши с клавиатуры или кнопки мышки.

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

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

Предположим, что есть список с названием events_list, который содержит объекты событий. Новый объект события автоматически добавляется в список events_list каждый раз, когда в программе происходит событие. Вам не нужно реализовывать механизм обновления. Это просто происходит автоматически в этом концептуальном примере. Используя бесконечный цикл, вы можете постоянно проверять, есть ли какие-либо объекты событий в events_list:

# Предположим, что список автоматически обновляется

events_list = []

# Запускаем цикл событий

while True:

    # Если events_list пуст, тогда никаких событий не происходит

    # и можно пропустить следующую итерацию цикла.

    if events_list == []:

        continue

    # Если выполнение доходит до данной точки, значит,

    # есть по крайней мере одно событие в events_list.

    event = events_list[0]

Прямо сейчас созданный цикл событий с самим событием event ничего не делает. Давайте изменим это. Предположим, приложение должно реагировать на нажатия клавиш. Вам нужно проверить, что событие event было создано пользователем при нажатии клавиши на клавиатуре, и, если это так, передать event в функцию обработчика событий для нажатия клавиш.

Предположим, что event имеет атрибут .type, установленный на строку "keypress", если событие является объектом события нажатия клавиши, а также атрибут .char, содержащий символ нажатой клавиши. Создадим новую функцию handle_keypress() и обновим код цикла событий:

events_list = []

# Создает обработчик событий

def handle_keypress(event):

    «»»Выводит символ, связанный с нажатой клавишей»»»

    print(event.char)

while True:

    if events_list == []:

        continue

    event = events_list[0]

    # Если событие event является объектом нажатия клавиши keypress

    if event.type == «keypress»:

        # Вызывает обработчик события нажатия клавиши

        handle_keypress(event)

При вызове метода window.mainloop() запускается похожий цикл. Данный метод отвечает за две части цикла:

  • Он поддерживает список событий, которые произошли в приложении;
  • Он запускает обработчик событий каждый раз, когда новое событие добавляется в список.

Обновим цикл событий, чтобы использовать window.mainloop() вместо вашего собственного цикла событий:

import tkinter as tk

# Создает объект окна.

window = tk.Tk()

# Создает обработчик событий.

def handle_keypress(event):

    «»»Выводит символ, связанный с нажатой клавишей»»»

    print(event.char)

# Запускает обработчик событий.

window.mainloop()

Метод .mainloop() отвечает за многое, но в вышеуказанном коде все-таки кое-что отсутствует. Как Tkinter узнает, когда использовать handle_keypress()? У виджетов в Tkinter есть метод под названием .bind(), предназначенный только для этой цели.

Метод .bind() для виджетов в Tkinter

Для вызова обработчика событий во время возникновения события, связанного с виджетом, используется метод .bind(). Обработчик событий напрямую связан с событием. Продолжим использовать предыдущий пример с нажатием клавиши и задействуем метод .bind(), чтобы связать handle_keypress() с событием нажатия клавиши:

import tkinter as tk

window = tk.Tk()

def handle_keypress(event):

    «»»Выводит символ, связанный с нажатой клавишей»»»

    print(event.char)

# Связывает событие нажатия клавиши с handle_keypress()

window.bind(«<Key>», handle_keypress)

window.mainloop()

Здесь обработчик событий handle_keypress() связывается с событием "<Key>", используя window.bind(). Если во время работы приложения нажата определенная клавиша, тогда программа выведет символ этой нажатой клавиши.

На заметку: Результат вышеуказанной программы не выводится в окне приложения Tkinter. Он выводится в stdout (терминал).

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

Метод .bind() всегда принимает по крайней мере два аргумента:

  1. Событие, представленное строкой в форме "<event_name>", где event_name может быть любым событием;
  2. Обработчик событий, что является названием функции, вызываемой во время срабатывания события.

Обработчик события связан с виджетом, для которого вызывается метод  .bind(). Когда вызывается обработчик события, объект события передается в функцию обработчика события.

В приведенном выше примере обработчик событий привязан к самому окну, но вы можете привязать обработчик событий к любому виджету в приложении. Например, вы можете привязать обработчик событий к виджету кнопки Button, который будет выполнять некоторые действия при каждом нажатии на данную кнопку:

def handle_click(event):

    print(«Нажата кнопка!»)

button = tk.Button(text=«Кликни!»)

button.bind(«<Button-1>», handle_click)

В данном примере событие "<Button-1>" над виджетом кнопки связано с обработчиком handle_click. Событие  "<Button-1>", происходит когда при направлении курсора на виджет нажимается левая кнопка мыши.

Есть и другие события для кнопок мыши, включая "<Button-2>" для средней кнопки мыши (в современных мышках это нажатая колесо для скролла) и "<Button-3>" и правой кнопки мыши.

При помощи метода .bind() с виджетом можно связать любой обработчик событий. Однако есть более простой способ связать обработчики событий с нажатием кнопки при помощи использования атрибута command от виджета кнопки.

Атрибут command для виджета кнопки в Tkinter

У каждого виджета кнопки есть атрибут command, который привязывает какую либо функцию к данной кнопки. Во время нажатия кнопки функция выполняется.

Взглянем на следующий пример. Сначала создается окно с ярлыком который содержит числовое значение. Справа и слева от ярлыка помещаются кнопки. Левая кнопка будет использоваться для уменьшения значения из ярлыка, а вторая кнопка для увеличения значения. Ниже представлен код для окна:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import tkinter as tk

window = tk.Tk()

window.rowconfigure(0, minsize=50, weight=1)

window.columnconfigure([0, 1, 2], minsize=50, weight=1)

btn_decrease = tk.Button(master=window, text=«-«)

btn_decrease.grid(row=0, column=0, sticky=«nsew»)

lbl_value = tk.Label(master=window, text=«0»)

lbl_value.grid(row=0, column=1)

btn_increase = tk.Button(master=window, text=«+»)

btn_increase.grid(row=0, column=2, sticky=«nsew»)

window.mainloop()

Окно выглядит следующим образом:

tk counter

Макет приложения определен, можно вдохнуть в него жизнь, назначив кнопкам определенные команды. Начнем с левой кнопки. При нажатии на нее значение должно увеличиваться на 1. Для этого нам нужно знать две вещи:

  • Как получить текст из ярлыка?
  • Как обновить текст в ярлыке?

У виджетов ярлыка нет метода .get() в отличие от виджетов в возможностью ввода текста. Однако можно получить текста из ярлыка, через ключ text как при работе с обычным словарем:

label = Tk.Label(text=«Hello»)

# Получение текста из ярлыка

text = label[«text»]

# Установка нового текста для ярлыка

label[«text»] = «Good bye»

Теперь, когда мы знаем, как получить и установить текст ярлыка, напишем функцию increase(), которая увеличивает значение ярлыка lbl_value на 1:

def increase():

    value = int(lbl_value[«text»])

    lbl_value[«text»] = f«{value + 1}»

Функция increase() получает текст из ярлыка lbl_value и конвертирует его в целое число через int(). Затем его значение увеличивает на 1, присваивая атрибуту text из ярлыка это новое значение. В примере выше, мы использовали f-строки для форматирование строки.

Функция decrease() нужна для уменьшения значения value_label на 1:

def decrease():

    value = int(lbl_value[«text»])

    lbl_value[«text»] = f«{value — 1}»

Функции increase() и decrease() помещаются в код сразу после оператора import.

Для связи кнопки с функцией нужно назначить атрибуту command определенную функцию. Вы можете сделать это, когда создаете экземпляр этой кнопки. Например, чтобы назначить функцию increase() для кнопки increase_button, обновите код, в котором создается кнопка, следующим образом:

btn_increase = tk.Button(

    master=window,

    text=«+»,

    command=increase

)

Теперь присваиваем функцию decrease() для кнопки decrease_button:

btn_decrease = tk.Button(

    master=window,

    text=«-«,

    command=decrease

)

Это все, что вам нужно сделать, чтобы связать кнопки с функциями increase() и decrease() и сделать программу работоспособной. Попробуйте сохранить изменения и запустить приложение. Нажмите на кнопки, чтобы увеличить или уменьшить значение в центре окна:

tk counter

Ниже представлен код данного приложения:

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

import tkinter as tk

def increase():

    value = int(lbl_value[«text»])

    lbl_value[«text»] = f«{value + 1}»

def decrease():

    value = int(lbl_value[«text»])

    lbl_value[«text»] = f«{value — 1}»

window = tk.Tk()

window.rowconfigure(0, minsize=50, weight=1)

window.columnconfigure([0, 1, 2], minsize=50, weight=1)

btn_decrease = tk.Button(

    master=window,

    text=«-«,

    command=decrease

)

btn_decrease.grid(row=0, column=0, sticky=«nsew»)

lbl_value = tk.Label(master=window, text=«0»)

lbl_value.grid(row=0, column=1)

btn_increase = tk.Button(

    master=window,

    text=«+»,

    command=increase

)

btn_increase.grid(row=0, column=2, sticky=«nsew»)

window.mainloop()

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

  • Использование виджетов для создания компонентов пользовательского интерфейса;
  • Использование менеджеров геометрии для управления макетом приложения;
  • Создание функций для взаимодействия с различными компонентами для принятия и трансформирования пользовательского ввода.

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

Задание #4 — Бросаем игровые кости в Tkinter

Игра в кости на Tkinter

Ниже дано еще одно задание для проверки понимания всего изученного.

Задание: Создать симулятор броска игральных костей.

Напишите программу, которая является симулятором результата броска шестигранной игральной кости. В приложении должна быть кнопка с текстом «Бросить». При нажатии на эту кнопку должно генерироваться и выводиться на экран случайное число от 1 до 6.

Подсказка: Случайное число можно сгенерировать с помощью randint() из модуля random.

В конечном итоге, приложение должно выглядеть следующим образом:

Бросаем кости в Tkinter

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

Решение задачи #4 — Бросаем кости

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import random

import tkinter as tk

def roll():

    lbl_result[«text»] = str(random.randint(1, 6))

window = tk.Tk()

window.columnconfigure(0, minsize=150)

window.rowconfigure([0, 1], minsize=50)

btn_roll = tk.Button(text=«Бросить», command=roll)

lbl_result = tk.Label()

btn_roll.grid(row=0, column=0, sticky=«nsew»)

lbl_result.grid(row=1, column=0)

window.mainloop()

Когда разберетесь с заданием, переходите к следующей части.

Приложение по конвертированию температуры в Tkinter

В этом разделе вы создадите приложение для конвертирования температуры, которое позволит пользователю вводить температуру в градусах Фаренгейта и нажимать кнопку, чтобы преобразовать эту температуру в градусы Цельсия. Мы разберем код шаг за шагом. Полный код программы будет дан в конце раздела.

Перед написанием кода нужно задуматься о дизайне приложения. Нам потребуются следующие элементы:

  1. Виджет однострочного поля для ввода текста под названием ent_temperature для ввода значения температуры по Фаренгейту;
  2. Виджет ярлыка под названием lbl_result для отображения итогового значения температуры по Цельсию;
  3. Виджет кнопки под названием btn_convert, что читает значение текстового поля и конвертирует его из Фаренгейта в Цельсий, и после нажатия помещает результат в виде текста в ярлыке.

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

Ярлык помещается справа от текстового поля ent_temperature с символом Фаренгейта (℉), чтобы пользователь знал, что значением в текстовом поле ent_temperature нужно ввести в градусы Фаренгейта. Устанавливаем для ярлыка данный текст "N{DEGREE FAHRENHEIT}", это именованный Unicode символ в Python для отображения символа Фаренгейта.

Можно вставить кнопку btn_convert немного изящества, установив для нее значение "N{RIGHTWARDS BLACK ARROW}", которое отображает черную стрелку, указывающую вправо. Также нужно, чтобы у ярлыка с результатом lbl_result был символ Цельсия (℃), ярлык будет иметь следующее значение "N{DEGREE CELSIUS}" для указания того, что результат в градусах Цельсия. Вот как будет выглядеть итоговое окно:

app converter

Теперь, когда перечень виджетов и внешний вид финального результата известны, можно приступить к делу. Для начала импортирует tkinter и создаем новое окно.

import tkinter as tk

window = tk.Tk()

window.title(«Конвертер температуры»)

Метод window.title() устанавливает заголовок для существующего окна. При запуске приложения у окна будет заголовок «Конвертер температуры». Далее создаем виджет однострочного текстового поля ent_temperature с ярлыком lbl_temp, вставляем их в рамку под названием frm_entry:

frm_entry = tk.Frame(master=window)

ent_temperature = tk.Entry(master=frm_entry, width=10)

lbl_temp = tk.Label(master=frm_entry, text=«N{DEGREE FAHRENHEIT}»)

Однострочное текстовое поле ent_temperature является местом, куда пользователь вводит значение по Фаренгейту. Перемнная lbl_temp содержит ярлык для текстового поля ent_temperature с символом Фаренгейта. Рамка frm_entry является контейнером, что группирует текстовое поле ent_temperature и ярлык lbl_temp вместе.

Нам нужно, чтобы ярлык lbl_temp находился справа от тектсового поля ent_temperature. Их можно поместить в рамку frm_entry, используя менеджер геометрии .grid() с одной строкой и двумя столбцами:

ent_temperature.grid(row=0, column=0, sticky=«e»)

lbl_temp.grid(row=0, column=1, sticky=«w»)

Мы установили параметр sticky на "e" для текстового поля ent_temperature таким образом, чтобы она всегда будет с правой стороны ячейки сетки. Также установили sticky на "w" для ярлыка  lbl_temp для закрепления с левой стороны его ячейки сетки.

Теперь ярлык lbl_temp будет находиться сразу справа от текстового поля ent_temperature.

Теперь заставим кнопку btn_convert и ярлык с результатом lbl_result конвертировать введенную в текстовом поле ent_temperature температуру, после чего отобразить результат на экране:

btn_convert = tk.Button(

    master=window,

    text=«N{RIGHTWARDS BLACK ARROW}»

)

lbl_result = tk.Label(master=window, text=«N{DEGREE CELSIUS}»)

Как и в случае с рамкой frm_entry, виджеты кнопки btn_convert и ярлык с результатом lbl_result назначаются окну.

Вместе эти три виджета являются ячейками сетки приложения. Используем метод .grid(), чтобы разместить их должным образом:

frm_entry.grid(row=0, column=0, padx=10)

btn_convert.grid(row=0, column=1, pady=10)

lbl_result.grid(row=0, column=2, padx=10)

Теперь запускаем приложение:

Выглядит неплохо. Однако кнопки пока ничего не делают. В верхней части скрипта, прямо под строкой import, добавим функцию под названием fahrenheit_to_celsius():

def fahrenheit_to_celsius():

    «»»

    Конвертирует значение Фаренгейта в Цельсий и вставляет

    результат в ярлык lbl_result.

    «»»

    fahrenheit = ent_temperature.get()

    celsius = (5/9) * (float(fahrenheit) 32)

    lbl_result[«text»] = f«{round(celsius, 2)} N{DEGREE CELSIUS}»

Данная функция читает значение из текстового поля ent_temperature, конвертирует его из Фаренгейтов в Цельсий, после чего отображает результат в ярлыке lbl_result.

Вернемся к строке создания кнопки btn_convert и назначим параметр command на функцию fahrenheit_to_celsius:

btn_convert = tk.Button(

    master=window,

    text=«N{RIGHTWARDS BLACK ARROW}»,

    command=fahrenheit_to_celsius  # <— Добавим его тут

)

Вот и все! Мы создали полностью функционирующий конвертер температуры длиной всего в 26 строк кода. Неплохо, правда?

Ниже дан полный код программы конвертера температуры:

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

import tkinter as tk

def fahrenheit_to_celsius():

    «»»

    Конвертирует значение из градусов по Фаренгейту в градусы

    по Цельсию и выводит результат в ярлык lbl_result.

    «»»

    fahrenheit = ent_temperature.get()

    celsius = (5/9) * (float(fahrenheit) 32)

    lbl_result[«text»] = f«{round(celsius, 2)} N{DEGREE CELSIUS}»

# Создание окна.

window = tk.Tk()

window.title(«Конвертер температуры»)

window.resizable(width=False, height=False)

# Создание рамки для ввода значения по Фаренгейту через виджет

# однострочного текстового поля вместе с ярлыком.

frm_entry = tk.Frame(master=window)

ent_temperature = tk.Entry(master=frm_entry, width=10)

lbl_temp = tk.Label(master=frm_entry, text=«N{DEGREE FAHRENHEIT}»)

# Макет для рамки ввода температуры и ярлыка с символом Фаренгейта

# использует менеджер геометрии .grid().

ent_temperature.grid(row=0, column=0, sticky=«e»)

lbl_temp.grid(row=0, column=1, sticky=«w»)

# Создание кнопки-конвертера и ярлыка для вывода результата.

btn_convert = tk.Button(

    master=window,

    text=«N{RIGHTWARDS BLACK ARROW}»,

    command=fahrenheit_to_celsius

)

lbl_result = tk.Label(master=window, text=«N{DEGREE CELSIUS}»)

# Настройка макета через менеджер геометрии .grid().

frm_entry.grid(row=0, column=0, padx=10)

btn_convert.grid(row=0, column=1, pady=10)

lbl_result.grid(row=0, column=2, padx=10)

# Запуск приложения.

window.mainloop()

В следующем разделе рассмотрим процесс создания текстового редактора для файлов.

Создание текстового редактора на Tkinter

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

  • Виджет кнопки под названием btn_open для открытия файла, которого нужно отредактировать;
  • Виджет кнопки под названием btn_save для сохранения изменений;
  • Виджет для ввода большого текста под названием txt_edit для создания и редактирования текстового файла.

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

Все окно должно быть высотой минимум в 800 пикселей, и ширина текстового поля txt_edit также должна быть минимум 800 пикселей. Весь макет должен быть адаптивным, так что если размер окна изменяется, то также изменяется размер текстового поля txt_edit. Однако ширина рамки с кнопками не должна меняться.

Вот примерный набросок того, как будет выглядеть окно:

Python Text Editor

Добиться желаемого макета можно через менеджер геометрии .grid(). В макете одна строка и два столбца:

  1. Узкий столбец слева от кнопок;
  2. Широкий столбец справа от текстового поля.

Чтобы установить минимальные размеры для окна и текстового поля txt_edit, вы можете установить параметры minsize используя методы окна .rowconfigure() и .columnconfigure() указав значение в 800 пикселей. Для обработки изменения размера окна, можно установить параметр weight для выше перечисленных методов на 1.

Для размещения обеих кнопок в один и тот же столбец нужно создать виджет рамки под названием fr_buttons. В соответствии с макетом, две кнопки должны быть расположены вертикально внутри рамки, с кнопкой btn_open сверху. Это можно сделать с помощью менеджера геометрии .grid() или .pack(). Будем использовать .grid(), так как с ним немного легче работать.

Теперь, когда у нас есть план, можно приступить к написанию приложения. Первый шаг — создать все нужные виджеты:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import tkinter as tk

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window)

btn_open = tk.Button(fr_buttons, text=«Открыть»)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…»)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

Разберем код поэтапно:

  • Строка 1 импортирует библиотеку tkinter;
  • Строки 3 и 4 создают новое окно с заголовком «Простой текстовый редактор»;
  • Строки 6 и 7 устанавливают конфигурацию строк и столбцов;
  • Строки 9 и 12 создают четыре виджета — текстовый бокс, рамку, кнопка для открытия и кнопка для сохранения файла.

Подробнее рассмотрим строку 6. Параметр minsize для метода .rowconfigure() ставится на 800 пикселей, weight имеет значение 1:

window.rowconfigure(0, minsize=800, weight=1)

Первый аргумент — 0, устанавливает высоту первой строки на 800 пикселей, гарантируя, что высота строки увеличивается пропорционально высоте окна. В макете приложения только одна строка, поэтому эти параметры применяются ко всему окну.

Давайте также подробнее рассмотрим строку 7. Здесь используется метод .columnconfigure(), чтобы установить атрибуты width и weight столбца с индексами от 1 до 800 и 1 соответственно:

window.columnconfigure(1, minsize=800, weight=1)

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

Теперь можно работать над макетом приложения. Сначала назначим две кнопки в рамку fr_buttons с помощью менеджера геометрии .grid():

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

Эти две строки кода создают сетку с двумя строками и одним столбцом в рамке fr_buttons, поскольку для кнопки btn_open и кнопки btn_save атрибут master указывает на рамку fr_buttons. Кнопка btn_open помещается в первый ряд, а кнопка btn_save — во второй ряд, так что кнопка btn_open отображается над кнопкой btn_save в макете, как и было запланировано в изначальном макете.

Для кнопок btn_open и btn_save атрибуты sticky установлены на "ew", что заставляет кнопки расширяться горизонтально в обоих направлениях и заполнять всю рамку. Это гарантирует, что обе кнопки имеют одинаковый размер.

Указывается 5 пикселей отступа вокруг каждой кнопки, устанавливая параметры padx и pady на 5. Только у кнопки btn_open есть вертикальный отступ. Поскольку он находится сверху, вертикальный отступ немного смещает кнопку вниз от верхней части окна и обеспечивает небольшой зазор между ним и кнопкой btn_save.

Теперь, когда рамка fr_buttons разложена и готова к работе, вы можете настроить макет сетки для остальной части окна:

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

Эти две строки кода создают сетку с одной строкой и двумя столбцами для окна. Вы помещаете рамку fr_buttons в первый столбец и текстовое поле txt_edit во второй столбец, так что рамка с кнопками  fr_buttons появляется слева от тектсового поля txt_edit в макете окна приложения.

Параметр sticky для рамки с кнопками fr_buttons имеет значение "ns", что заставляет всю рамку расширяться по вертикали и заполнять всю высоту его столбца. Текстовое поле txt_edit заполняет всю ячейку сетки, потому что вы установили для ее параметра sticky значение "nsew", что заставляет его расширяться во всех направлениях.

Теперь, когда макет приложения готов, добавьте метод  window.mainloop() в конец программы, сохраните и запустите скрипт. Появится следующее окно:

Tkinter текстовой редактор

Выглядит неплохо! Но приложение пока ничего не делает, поэтому нужно создать команды для кнопок. Кнопка btn_open должена показать диалоговое окно выбора файла и позволить пользователю выбрать файл с компьютера. Затем он должен открыть файл и вставить его содержимое в текстом поле txt_edit. Для этого используем функцию open_file():

def open_file():

    «»»Открывает файл для редактирования»»»

    filepath = askopenfilename(

        filetypes=[(«Text Files», «*.txt»), («All Files», «*.*»)]

    )

    if not filepath:

        return

    txt_edit.delete(«1.0», tk.END)

    with open(filepath, «r») as input_file:

        text = input_file.read()

        txt_edit.insert(tk.END, text)

    window.title(f«Simple Text Editor — {filepath}»)

Разберем данную функцию поэтапно:

  • Строки с 3 по 5 используют диалоговое окно askopenfilename из модуля tkinter.filedialog, чтобы отобразить диалоговое окно открытия файла и сохранить выбранный путь к файлу в переменную filepath;
  • Строки 6 и 7 проверяют, закрывает ли пользователь диалоговое окно или нажимает кнопку отмены. Если это так, то filepath будет иметь значение None, и функция вернется без выполнения какого-либо кода для чтения содержимого файла и вставки текста в текстовом поле txt_edit;
  • Строка 8 очищает текущее содержимое из текстового поля txt_edit, используя метод .delete();
  • Строки 9 и 10 открывают выбранный файл и читают через .read() его содержимое перед сохранением текста в виде строки;
  • Строка 11 вставляем текст в текстовом поле txt_edit, используя метод .insert();
  • Строка 12 устанавливает заголовок окна так, чтобы он содержал путь к открытому файлу.

Теперь вы можете обновить программу так, чтобы кнопка btn_open вызывала функцию open_file() при каждом нажатии. Есть несколько вещей, которые нужно сделать, чтобы обновить программу.

Сначала импортируйте askopenfilename из модуля  tkinter.filedialog, добавив следующий импорт в начало вашей программы:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import tkinter as tk

from tkinter.filedialog import askopenfilename

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window)

btn_open = tk.Button(fr_buttons, text=«Открыть»)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…»)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

Затем добавляется тело функции open_file() прямо под оператором импорта:

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

import tkinter as tk

from tkinter.filedialog import askopenfilename

def open_file():

    «»»Открываем файл для редактирования»»»

    filepath = askopenfilename(

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)]

    )

    if not filepath:

        return

    txt_edit.delete(«1.0», tk.END)

    with open(filepath, «r») as input_file:

        text = input_file.read()

        txt_edit.insert(tk.END, text)

    window.title(f«Простой текстовый редактор — {filepath}»)

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window)

btn_open = tk.Button(fr_buttons, text=«Открыть»)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…»)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

Теперь указываем атрибуту command от кнопки btn_open на функцию  open_file:

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

import tkinter as tk

from tkinter.filedialog import askopenfilename

def open_file():

    «»»Открываем файл для редактирования»»»

    filepath = askopenfilename(

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)]

    )

    if not filepath:

        return

    txt_edit.delete(«1.0», tk.END)

    with open(filepath, «r») as input_file:

        text = input_file.read()

        txt_edit.insert(tk.END, text)

    window.title(f«Простой текстовый редактор — {filepath}»)

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window)

btn_open = tk.Button(fr_buttons, text=«Открыть», command=open_file)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…»)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

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

Теперь, наша кнопка btn_open работает и успешно открывает текстовые файлы, можно уже заняться функцией для кнопки btn_save. Для этого необходимо открыть диалоговое окно сохранения файла, чтобы пользователь мог выбрать, куда он хотел бы сохранить файл.

Для этого вы будете использовать диалог asksaveasfilename из модуля tkinter.filedialog. Эта функция также должна извлечь все содержимое из текстового поля txt_edit и записать его в файл в выбранном месте. Вот функция, которая делает именно это:

def save_file():

    «»»Сохраняем текущий файл как новый файл.»»»

    filepath = asksaveasfilename(

        defaultextension=«txt»,

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)],

    )

    if not filepath:

        return

    with open(filepath, «w») as output_file:

        text = txt_edit.get(«1.0», tk.END)

        output_file.write(text)

    window.title(f«Простой текстовый редактор — {filepath}»)

Разберем код:

  • Строки с 3 по 6 используют диалоговое окно asksaveasfilename, чтобы выбрать желаемое место сохранения файла. Выбранный путь к файлу сохраняется в переменную filepath;
  • Строки 7 и 8 проверяют, закрывает ли пользователь диалоговое окно или нажимает кнопку отмены. Если это так, то переменная filepath будет иметь значение None, и функция завершиться без выполнения какого-либо кода для сохранения текста в файле;
  • Строка 9 создает новый файл по выбранному пути;
  • Строка 10 извлекает текст из текстового поля txt_edit с помощью метода .get() и присваивает его переменной text;
  • Строка 11 записывает содержимое из переменной text в выбранном файле;
  • Строка 12 обновляет заголовок окна приложения, чтобы новый путь к файлу отображался в заголовке окна.

Теперь вы можете обновить программу, чтобы кнопка btn_save вызывала функцию save_file() при нажатии. Опять же, есть несколько вещей, которые вам нужно сделать, чтобы обновить программу.

Сначала импортируйте asksaveasfilename из модуля tkinter.filedialog, обновив импорт в верхней части вашего скрипта, например так:

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

import tkinter as tk

from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file():

    «»»Открываем файл для редактирования»»»

    filepath = askopenfilename(

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)]

    )

    if not filepath:

        return

    txt_edit.delete(«1.0», tk.END)

    with open(filepath, «r») as input_file:

        text = input_file.read()

        txt_edit.insert(tk.END, text)

    window.title(f«Простой текстовый редактор — {filepath}»)

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)

btn_open = tk.Button(fr_buttons, text=«Открыть», command=open_file)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…»)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

Затем добавляем функцию save_file() прямо под функцию open_file():

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

import tkinter as tk

from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file():

    «»»Открываем файл для редактирования»»»

    filepath = askopenfilename(

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)]

    )

    if not filepath:

        return

    txt_edit.delete(«1.0», tk.END)

    with open(filepath, «r») as input_file:

        text = input_file.read()

        txt_edit.insert(tk.END, text)

    window.title(f«Простой текстовый редактор — {filepath}»)

def save_file():

    «»»Сохраняем текущий файл как новый файл.»»»

    filepath = asksaveasfilename(

        defaultextension=«txt»,

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)],

    )

    if not filepath:

        return

    with open(filepath, «w») as output_file:

        text = txt_edit.get(«1.0», tk.END)

        output_file.write(text)

    window.title(f«Простой текстовый редактор — {filepath}»)

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)

btn_open = tk.Button(fr_buttons, text=«Открыть», command=open_file)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…»)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

Наконец, указываем атрибуту command от кнопки btn_save на функцию save_file:

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

import tkinter as tk

from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file():

    «»»Открываем файл для редактирования»»»

    filepath = askopenfilename(

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)]

    )

    if not filepath:

        return

    txt_edit.delete(«1.0», tk.END)

    with open(filepath, «r») as input_file:

        text = input_file.read()

        txt_edit.insert(tk.END, text)

    window.title(f«Простой текстовый редактор — {filepath}»)

def save_file():

    «»»Сохраняем текущий файл как новый файл.»»»

    filepath = asksaveasfilename(

        defaultextension=«txt»,

        filetypes=[(«Текстовые файлы», «*.txt»), («Все файлы», «*.*»)],

    )

    if not filepath:

        return

    with open(filepath, «w») as output_file:

        text = txt_edit.get(«1.0», tk.END)

        output_file.write(text)

    window.title(f«Простой текстовый редактор — {filepath}»)

window = tk.Tk()

window.title(«Простой текстовый редактор»)

window.rowconfigure(0, minsize=800, weight=1)

window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)

fr_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)

btn_open = tk.Button(fr_buttons, text=«Открыть», command=open_file)

btn_save = tk.Button(fr_buttons, text=«Сохранить как…», command=save_file)

btn_open.grid(row=0, column=0, sticky=«ew», padx=5, pady=5)

btn_save.grid(row=1, column=0, sticky=«ew», padx=5)

fr_buttons.grid(row=0, column=0, sticky=«ns»)

txt_edit.grid(row=0, column=1, sticky=«nsew»)

window.mainloop()

Сохраните файл и запустите его. Теперь у вас есть небольшой, но полностью функциональный текстовый редактор написанный на Python используя библиотеку Tkinter.

Tkinter текстовой редактор

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

Заключение

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

В этом руководстве вы узнали несколько важных концепций от Tkinter:

  • Как работать с виджетами;
  • Как управлять макетом приложения с помощью менеджеров геометрии;
  • Как сделать ваши приложения интерактивными;
  • Как использовать пять основных виджетов из Tkinter (Label, Button, Entry, Text и Frame).

Теперь, когда вы освоили основы программирования на Python используя графический интерфейс при помощью Tkinter, следующим шагом будет создание ваших собственных приложений. Что это будет? Поделитесь идеями для своих проектов в комментариях ниже.

Дополнительные материалы для изучения Tkinter

В этом уроке вы затронули только основы создания приложений с графическим интерфейсом в Python используя Tkinter. Есть ряд дополнительных тем, которые здесь не рассматривались. В этом разделе вы найдете некоторые из лучших доступных ресурсов, которые помогут продолжить путешествие в мир Tkinter.

Вот некоторые официальные ресурсы, которые можно просмотреть:

  • Официальное руководство Python Tkinter, в котором подробно описан данный модуль. Текст предназначен для более продвинутых пользователей, новичкам будет сложновато;
  • Справочник по Tkinter 8.5: GUI для Python — это обширный справочник, охватывающий большинство модулей от Tkinter. Он исчерпывающий, но написан кратко и без комментариев и примеров.
  • Справочник по Tk — это полное руководство по командам в библиотеке Tk. Он написан для языка Tcl, но отвечает на множество вопросов о том, почему все работает так, как они работают в Tkinter. В официальной документации по Python есть раздел, посвященный отображению базового Tk в Tkinter, который необходим при чтении документации по командам Tk.

Дополнительные виджеты

В этом руководстве вы узнали о виджетах Label, Button, Entry, Text и Frame . В Tkinter есть несколько других виджетов, каждый из которых необходим для создания реальных приложений. Вот несколько ресурсов, чтобы продолжить изучение виджетов:

  • TkDocs Tkinter Tutorial — это достаточно обширное руководство для Tk, базовой библиотеки кода, используемой Tkinter. Примеры представлены на ткаих языка программирования как Python, Ruby, Perl и Tcl. Вы можете найти несколько примеров виджетов, помимо описанных в данной статье;
  • Базовые виджеты включают те же виджеты, что и в этом руководстве, а также некоторые другие;
  • Раздел Больше виджетов охватывает несколько дополнительных виджетов.

В официальной документации Python есть три раздела, охватывающих дополнительные виджеты:

  • Тематические виджеты ttk набор тематических виджетов Tk;
  • Расширение виджетов для Tk охватывает виджеты в наборе расширений интерфейса Tk;
  • Виджет прокрутки текста расширяет возможности виджета Text в сочетании с вертикальной полосой прокрутки.

Поделиться приложением на Tkinter

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

  • Использование PyInstaller для компиляции приложений
  • 4 попытки упаковки Python как исполняемого файла
  • Создание автономных приложений Python с помощью PyOxidizer 

Другие GUI фреймворки 

Tkinter — не единственный выбор для создания GUI приложений на Python. Если Tkinter не соответствует потребностям вашего проекта, задумайтесь об использовании другого фреймворка:

  • Полный курс уроков по wxPython
  • Python и PyQt5: создание калькулятора на PyQt5
  • Создание мобильного приложения с Kivy Python Framework

Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.

E-mail: vasile.buldumac@ati.utm.md

Образование
Universitatea Tehnică a Moldovei (utm.md)

  • 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
  • 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»

Существует множество пакетов для создания графических интерфейсов пользователя в Python, но есть только один такой пакет, который считается стандартом де-факто и распространяется со всеми установками Python по умолчанию. Этот пакет называется Tkinter, это привязка Python к Tk – кроссплатформенному набору графических интерфейсов с открытым исходным кодом.

Создание первого окна

Как упоминалось ранее, Tkinter доступен со стандартными установками Python, поэтому независимо от вашей операционной системы создание вашего первого окна должно быть очень быстрым. Все, что вам нужно, это 3 строки кода:

import tkinter

root = tkinter.Tk()

root.mainloop()

Вывод:

Создание первого окна

После импорта пакета tkinter в строке 1, в строке 3 мы создаем виджет главного (корневого) окна приложения. Чтобы программа работала должным образом, в нашем интерфейсе должен быть только один виджет корневого окна, и, поскольку все остальные виджеты будут ниже в иерархии, чем корневые, он должен быть создан перед любыми другими виджетами.

В строке 5 мы инициализируем главный цикл корня. Благодаря этой строке окно остается в цикле, который ожидает событий (например, взаимодействия с пользователем) и соответствующим образом обновляет интерфейс. Цикл заканчивается, когда пользователь закрывает окно или вызывается метод quit().

Добавление простых виджетов в корневое окно

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

На втором этапе мы должны использовать один из доступных методов для размещения нового виджета внутри другого уже существующего виджета (родительского). Самый простой виджет, который вы можете поместить в свой интерфейс Tkinter, – это метка, которая просто отображает некоторый текст. В следующем примере создается простой виджет метки:

import tkinter

root = tkinter.Tk()

simple_label = tkinter.Label(root, text="Easy, right?")

simple_label.pack()

root.mainloop()

Вывод:

Добавление простых виджетов в корневое окно

Мы создаем экземпляр класса Label в строке 5 приведенного выше кода. В первом аргументе мы указываем на желаемый родительский виджет метки, которым в этом примере является наше корневое окно. Во втором аргументе мы указываем текст, который должна отображать метка.

Затем в строке 7 мы применяем метод ориентации нашей метки внутри корневого окна. Самый простой метод ориентирования виджетов, который предлагает Tkinter, – это pack(). Метка – единственный виджет внутри окна, поэтому он просто отображается в середине окна.

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

Добавление функциональной кнопки

Теперь давайте добавим то, с чем пользователь может взаимодействовать. Самый очевидный выбор – простая кнопка. Давайте поместим кнопку в наше окно, которая даст нам дополнительный способ закрыть наше окно.

import tkinter

root = tkinter.Tk()

root.title("Hello!")

simple_label = tkinter.Label(root, text="Easy, right?")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)

simple_label.pack()
closing_button.pack()

root.mainloop()

Вывод:

Добавление функциональной кнопки

В строке 8 мы создаем экземпляр класса Button аналогично тому, как мы создавали метку. Однако, как вы, вероятно, видите, мы добавили аргумент команды, в котором мы сообщаем программе, что должно произойти после нажатия кнопки. В этом случае вызывается метод destroy() root, который закрывает наше окно при выполнении.

В строках 10 и 11 мы снова используем метод pack(). На этот раз мы можем понять это немного лучше, поскольку теперь мы используем его для размещения двух виджетов внутри окна. В зависимости от порядка, в котором мы упаковываем наши виджеты, метод просто помещает их один поверх другого по центру по горизонтали. Высота и ширина окна подстраиваются под размеры виджетов.

Вы, наверное, заметили еще одну новую строчку. В строке 5 мы указываем заголовок корневого окна. К сожалению, самый широкий виджет нашего интерфейса недостаточно широк, чтобы заголовок окна стал видимым.

Управление размером окна

Давайте взглянем на три новые строки, которые позволят нам легко изменять размер нашего окна.

import tkinter

root = tkinter.Tk()

root.title("Hello!")

root.resizable(width="false", height="false")

root.minsize(width=300, height=50)
root.maxsize(width=300, height=50)

simple_label = tkinter.Label(root, text="Easy, right?")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)

simple_label.pack()
closing_button.pack()

root.mainloop()

Вывод:

Разработка графического интерфейса с помощью Tkinter в Python

В строке 7 мы определяем, должен ли пользователь программы изменять ширину и высоту окна. В этом случае для обоих аргументов установлено значение «false», поэтому размер окна зависит только от нашего кода. Если бы не строки 9 и 10, это зависело бы от размеров виджетов, ориентированных внутри окна.

Однако в этом примере мы используем методы root minsize и maxsize для управления максимальными и минимальными значениями ширины и высоты нашего окна. Здесь мы точно определяем, насколько широким и высоким должно быть окно, но я рекомендую вам поиграть с этими тремя строками, чтобы увидеть, как изменение размера работает в зависимости от размера наших виджетов и от того, какие минимальные и максимальные значения мы определяем.

Подробнее об ориентации виджета

Как вы, наверное, уже заметили, использование метода pack() не дает нам слишком много контроля над тем, где находятся виджеты после их упаковки в родительские контейнеры. Нельзя сказать, что метод pack() непредсказуем – просто очевидно, что иногда размещение виджетов в окне в одном столбце, где один виджет помещается поверх предыдущего, не обязательно согласуется с нашим изощренным чувством эстетики. . В этих случаях мы можем либо использовать pack() с некоторыми умными аргументами, либо использовать grid() – еще один метод ориентации виджетов внутри контейнеров.

Изменив строки 15 и 16 из предыдущего примера, мы можем немного улучшить наш интерфейс:

simple_label.pack(fill="x")
closing_button.pack(fill="x")

Вывод:

Ориентация виджета

Таким простым способом мы говорим методу pack() растянуть метку и кнопку полностью вдоль горизонтальной оси. Мы также можем изменить способ, которым pack() создает новые виджеты внутри окна. Например, используя следующий аргумент:

simple_label.pack(side="left")
closing_button.pack(side="left")

Вывод:

Упаковка виджетов

Мы можем упаковать виджеты в один ряд, начиная с левой стороны окна. Однако pack() – не единственный метод ориентации виджетов внутри их родительских виджетов. Метод, который дает лучшие результаты, – это, вероятно, метод grid(), который позволяет нам упорядочивать виджеты по строкам и столбцам. Взгляните на следующий пример.

import tkinter

root = tkinter.Tk()

simple_label = tkinter.Label(root, text="Easy, right?")
another_label = tkinter.Label(root, text="More text")
closing_button = tkinter.Button(root, text="Close window", command=root.destroy)
another_button = tkinter.Button(root, text="Do nothing")

simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")
closing_button.grid(column=1, row=0, sticky="ew")
another_button.grid(column=1, row=1, sticky="ew")

root.mainloop()

Вывод:

Использование функции pack()

Чтобы сделать этот пример более понятным, мы избавились от строк, которые меняли заголовок и размер корневого окна. В строках 6 и 8 мы добавили еще одну метку и еще одну кнопку (обратите внимание, что щелчок по ней ничего не сделает, поскольку мы не добавили к ней никаких команд).

Однако, что наиболее важно, во всех случаях pack() был заменен на grid(). Как вы, вероятно, легко догадываетесь, столбец и строка аргументов позволяют нам определить, какую ячейку сетки будет занимать наш виджет. Имейте в виду, что если вы определяете одинаковые координаты для двух разных виджетов, тот, который отображается далее в вашем коде, будет отображаться поверх другого.

Привязанный аргумент, вероятно, не так очевиден. Используя эту опцию, мы можем прикрепить края наших виджетов к краям их соответствующих ячеек сетки – северного (вверху), южного (внизу), восточного (справа) и западного (слева). Мы делаем это, передавая простую строку, содержащую конфигурацию букв n, s, e и w.

В нашем примере мы приклеиваем края всех четырех виджетов к восточным и западным краям их ячеек, поэтому строка имеет вид ew. В результате виджеты растягиваются по горизонтали. Вы можете играть с разными конфигурациями этих четырех букв. Их порядок в строке не имеет значения.

Теперь, когда вы знаете два разных метода ориентации виджетов, имейте в виду, что никогда не следует смешивать grid() и pack() внутри одного контейнера.

Frames

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

Давайте попробуем сделать это с помощью наших четырех простых виджетов:

import tkinter

root = tkinter.Tk()

frame_labels = tkinter.Frame(root, borderwidth="2", relief="ridge")
frame_buttons = tkinter.Frame(root, borderwidth="2", relief="ridge")

simple_label = tkinter.Label(frame_labels, text="Easy, right?")
another_label = tkinter.Label(frame_labels, text="More text")

closing_button = tkinter.Button(frame_buttons, text="Close window", command=root.destroy)
another_button = tkinter.Button(frame_buttons, text="Do nothing")

frame_labels.grid(column=0, row=0, sticky="ns")
frame_buttons.grid(column=1, row=0)

simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")

closing_button.pack(fill="x")
another_button.pack(fill="x")

root.mainloop()

Вывод:

Виджет Frame

Давайте внимательно рассмотрим пример, показанный выше. В строках 5 и 6 мы определяем два новых виджета Frame. Очевидно, что в первом аргументе мы указываем на их родительский виджет, которым является корневое окно.

По умолчанию границы Frame невидимы, но, допустим, мы хотели бы видеть, где именно они расположены. Чтобы показать их границы, мы должны дать им определенную ширину (в нашем примере 2 пикселя) и стиль рельефа (своего рода трехмерный эффект), в котором будет отображаться граница. На выбор предлагается 5 различных стилей relief – в нашем примере мы используем ridge.

Определения Label и Button также были немного изменены (строки 8-12). Мы хотели разместить наши метки в нашем фрейме frame_labels, а наши кнопки – в фрейме frame_buttons. Таким образом, нам пришлось заменить их предыдущий родительский элемент, root, на их соответствующие новые родительские элементы фрейма.

В строках 14 и 15 мы ориентируем фреймы внутри корневого окна с помощью метода grid(). Затем мы используем метод grid() для ориентации меток (строки 17–18) и метод pack() для ориентации кнопок (строки 20–21). Метки и кнопки теперь находятся в отдельных контейнерах, поэтому ничто не мешает нам ориентировать виджеты разными методами.

Окна верхнего уровня

Ваш интерфейс не должен содержать более одного корневого окна, но вы можете создать множество окон, которые являются дочерними по отношению к корневому окну. Лучший способ сделать это – использовать класс Toplevel.

import tkinter

root = tkinter.Tk()

new_window = tkinter.Toplevel()
new_window.withdraw()

frame_labels = tkinter.Frame(root, borderwidth="2", relief="ridge")
frame_buttons = tkinter.Frame(root, borderwidth="2", relief="ridge")

simple_label = tkinter.Label(frame_labels, text="Easy, right?")
another_label = tkinter.Label(frame_labels, text="More text")

closing_button = tkinter.Button(frame_buttons, text="Close window", command=root.destroy)
window_button = tkinter.Button(frame_buttons, text="Show new window", command=new_window.deiconify)

frame_labels.grid(column=0, row=0, sticky="ns")
frame_buttons.grid(column=1, row=0)

simple_label.grid(column=0, row=0, sticky="ew")
another_label.grid(column=0, row=1, sticky="ew")

closing_button.pack(fill="x")
window_button.pack(fill="x")

root.mainloop()

В приведенном выше примере мы создаем наше новое окно в строке 5. Поскольку окно – это объект, который не привязан к какому-либо другому виджету, нам не нужно указывать на его родительский элемент или ориентировать его внутри родительского виджета.

Мы хотели бы показывать новое окно после нажатия кнопки. В строке 5 он отображается сразу, поэтому мы используем метод remove() в строке 6, чтобы скрыть его. Затем мы изменяем определение кнопки в строке 15.

Помимо нового имени переменной и текста, кнопка теперь выполняет команду – метод объекта new_window, deiconify, который заставит окно снова появиться после того, как пользователь щелкнет кнопку window_button.

Заключение

Как видите, с помощью Tkinter вы можете легко и быстро создавать графические интерфейсы для непрофессиональных пользователей вашего программного обеспечения. Библиотека включена во все установки Python, поэтому создание вашего первого простого окна – это всего лишь пара строк кода.

Введение

Этой библиотеке посвящено мало внимания, и найти в рунете курс, книгу или FAQ по ней довольно-таки сложно. Стоит отметить, что здесь отражены только основы этой библиотеки, и надеюсь, что более опытные люди дополнят эту статью.

Что такое Tkinter?

Tkinter (от англ. tk interface) — это графическая библиотека, позволяющая создавать программы с оконным интерфейсом. Эта библиотека является интерфейсом к популярному языку программирования и инструменту создания графических приложений tcl/tk. Tkinter, как и tcl/tk, является кроссплатформенной библиотекой и может быть использована в большинстве распространённых операционных систем (Windows, Linux, Mac OS X и др.).

Так как Tkinter является достаточно прозрачным интерфейсом к tcl/tk, то основным источником информации для неё являются man-страницы tcl/tk. Эти страницы имеются в любой Unix-системе (в разделе n или 3tk). Также они доступны онлайн на сайте http://tcl.tk. Основные:

  • Список разделов
  • Список виджетов и команд
  • Список общих опций для виджетов

Начиная с версии python-3.0 библиотека переименована в соответствии с PEP 8 в tkinter (с маленькой буквы).

Импортируется она как и любая другая библиотека:

# для версии python 2.7 и ниже
import Tkinter

# для версии python 3.0 и выше
import tkinter

или

# для версии python 2.7 и ниже
from Tkinter import *

# для версии python 3.0 и выше
from tkinter import *

В Tkinter визуальные контроллы называются виджетами (widget, от англ. window gadget) — стандартизированный компонент графического интерфейса, с которым взаимодействует пользователь.

Класс Tk

Tk является базовым классом любого Tkinter приложения. При создании объекта этого класса запускается интерпретатор tcl/tk и создаётся базовое окно приложения.

Tkinter является событийно-ориентированной библиотекой. В приложениях такого типа имеется главный цикл обработки событий. В Tkinter такой цикл запускается методом mainloop. Для явного выхода из интерпретатора и завершения цикла обработки событий используется метод quit.

Таким образом минимальное приложение на Tkinter будет таким:

from tkinter import *
root = Tk()
root.mainloop()

В приложении можно использовать несколько интерпретаторов tcl/tk. Так как после вызова метода mainloop дальнейшие команды python исполняться не будут до выхода из цикла обработки событий, необходимо метод mainloop всех интерпретаторов кроме последнего осуществлять в фоновом режиме. Пример запуска двух интерпретаторов:

from tkinter import *
root1 = Tk()
root2 = Tk()
root1.after(500, root1.mainloop) # первый цикл запускаем в фоне
root2.mainloop()

При использовании двух и более интерпретаторов необходимо следить, чтобы объекты, созданные в одном интерпретаторе, использовались только в нём. Например, изображение, созданное в первом интерпретаторе, может быть использовано много раз в этом же интерпретаторе, но не может быть использовано в других интерпретаторах. Необходимость в запуске нескольких интерпретаторов в одном приложении возникает крайне редко. Для создания дополнительного окна приложения в большинстве случаев достаточно виджета Toplevel.

Общее для всех виджетов

Все виджеты в Tkinter обладают некоторыми общими свойствами. Опишем их, перед тем как перейти к рассмотрению конкретных виджетов. Виджеты создаются вызовом конструктора соответствующего класса. Первый аргумент (как правило неименованный, но можно использовать имя master) это родительский виджет, в который будет упакован (помещён) наш виджет. Родительский виджет можно не указывать, в таком случае будет использовано главное окно приложения. Далее следуют именованные аргументы, конфигурирующие виджет. Это может быть используемый шрифт (font=…), цвет виджета (bg=…), команда, выполняющаяся при активации виджета (command=…) и т.д. Полный список всех аргументов можно посмотреть в man options и man-странице соответствующего виджета (например man button, см. разделы «STANDARD OPTIONS» и «WIDGET-SPECIFIC OPTIONS»). Пример кода:

from Tkinter import *
def button_clicked():
    print (u"Клик!")
root=Tk()
# кнопка по умолчанию
button1 = Button()
button1.pack()
# кнопка с указанием родительского виджета и несколькими аргументами
button2 = Button(root, bg="red", text=u"Кликни меня!", command=button_clicked)
button2.pack()
root.mainloop()

Памятуя о Zen Python (явное лучше неявного) указываю: данный код написан и работает для Python v 2. В случае использования Python v 3 код немного изменится.
1. tkinter с маленькой буквы.
2. print в круглых скобках () и без u.

Методы виджетов

configure, config

Виджеты могут быть сконфигурированы во время создания, но иногда необходимо изменить конфигурацию виджета во время исполнения программы. Для этого используется метод configure (или его синоним config). Также можно использовать квадратные скобки (widget[‘option’] = new_value). Пример, программа выводит текущее время, после клика по кнопке:

from Tkinter import *
import time
def button_clicked():
    # изменяем текст кнопки
    button['text'] = time.strftime('%H:%M:%S')
root=Tk()
# создаём виджет
button = Button(root)
# конфигурируем виджет после создания
button.configure(text=time.strftime('%H:%M:%S'), command=button_clicked)
# также можно использовать квадратные скобки:
# button['text'] = time.strftime('%H:%M:%S')
# button['command'] = button_clicked
button.pack()
root.mainloop()

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

cget

Метод cget является обратным к методу configure. Он предназначен для получения информации о конфигурации виджета. Здесь как и в случае с configure можно использовать квадратные скобки (value = widget[‘option’]). Пример, после клика на кнопку программа показывает цвет кнопки и меняет его на другой:

from Tkinter import *
from random import random
def button_clicked():
    button['text'] = button['bg'] # показываем предыдущий цвет кнопки
    bg = '#%0x%0x%0x' % (int(random()*16), int(random()*16), int(random()*16))
    button['bg'] = bg
    button['activebackground'] = bg
root=Tk()
button = Button(root, command=button_clicked)
button.pack()
root.mainloop()
destroy

Уничтожение виджета и всех его потомков. Стоит отметить, что если необходимо только на время спрятать какой-либо виджет, то лучше пользоваться упаковщиком grid и методом grid_remove:

from Tkinter import *
def hide_show():
    if label.winfo_viewable():
        label.grid_remove()
    else:
        label.grid()
root=Tk()
label = Label(text=u'Я здесь!')
label.grid()
button = Button(command=hide_show, text=u"Спрятать/показать")
button.grid()
root.mainloop()

Использование grid_remove позволяет сохранять взаимное расположение виджетов.

grab_*[1]

Методы семейства grab_ предназначены для управления потоком события. Виджет, захвативший поток, будет получать все события окна или приложения.

  • grab_set — передать поток данному виджету
  • grab_set_global — передать глобальный поток данному виджету. В этом случае все события на дисплее будут передаваться этому виджету. Следует пользоваться очень осторожно, т.к. остальные виджеты всех приложений не будут получать события.
  • grab_release — освободить поток
  • grab_status — узнать текущий статус потока событий для виджета. Возможные значения: None, «local» или «global».
  • grab_current — получить виджет, который получает поток

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

from Tkinter import *
root=Tk()
root.after(200, root.grab_set_global)
root.after(10000, root.grab_release)
root.mainloop()
focus_*[2]

Методы семейства focus_ используются для управления фокусом ввода с клавиатуры. Виджет, имеющий фокус, получает все события с клавиатуры.

  • focus (синоним focus_set) — передать фокус виджету.
  • focus_force — передать фокус, даже если приложение не имеет фокуса. Используйте осторожно, поскольку это может раздражать пользователей.
  • focus_get — возвращает виджет, на который направлен фокус, либо None, если такой отсутствует.
  • focus_displayof — возвращает виджет, на который направлен фокус на том дисплее, на котором размещён виджет, либо None, если такой отсутствует.
  • focus_lastfor — возвращает виджет, на который будет направлен фокус, когда окно с этим виджетом получит фокус.
  • tk_focusNext — возвращает виджет, который получит фокус следующим (обычно смена фокуса происходит при нажатии клавиши Tab). Порядок следования определяется последовательностью упаковки виджетов.
  • tk_focusPrev — то же, что и focusNext, но в обратном порядке.
  • tk_focusFollowsMouse — устанавливает, что виджет будет получать фокус при наведении на него мышью. Вернуть нормальное поведение достаточно сложно.

«Системные» методы

Эти методы не являются виджет-специфичными, т.е. хотя они являются методами виджетов они влияют на работу интерпретатора tcl/tk.

after, after_idle и after_cancel[3]

Таймеры. С помощью этих методов вы можете отложить выполнение какого-нибудь кода на определённое время.

after — принимает два аргумента: время в миллисекундах и функцию, которую надо выполнить через указанное время. Возвращает идентификатор, который может быть использован в after_cancel.

after_idle — принимает один аргумент — функцию. Эта функция будет выполнена после завершения всех отложенных операций (после того, как будут обработаны все события). Возвращает идентификатор, который может быть использован в after_cancel.

after_cancel — принимает один аргумент: идентификатор задачи, полученный предыдущими функциями, и отменяет это задание.

Пример, часы:

from Tkinter import *
import time
def tick():
    label.after(200, tick)
    label['text'] = time.strftime('%H:%M:%S')
root=Tk()
label = Label(font='sans 20')
label.pack()
label.after_idle(tick)
root.mainloop()

update и update_idletasks[4]

Две функции, для работы с очередью задач. Их выполнение вызывает обработку отложенных задач.

update_idletasks выполняет задачи, обычно откладываемые «на потом», когда приложение будет простаивать. Это приводит к прорисовке всех виджетов, расчёту их расположения и т.д. Обычно эта функция используется если были внесены изменения в состояние приложения, и вы хотите, чтобы эти изменения были отображены на экране немедленно, не дожидаясь завершения сценария.

update обрабатывает все задачи, стоящие в очереди. Обычно эта функция используется во время «тяжёлых» расчётов, когда необходимо чтобы приложение оставалось отзывчивым на действия пользователя.

Пример:

from Tkinter import *
import math
def hard_job():
    x = 1000
    while True:
        x = math.log(x) ** 2.8
        root.update()
root=Tk()
button = Button()
button.pack()
root.after(500, hard_job)
root.mainloop()

eval и evalfile

Две недокументированные функции для выполнения кода на tcl. eval позволяет выполнить строку на языке программирования tcl, а evalfile — выполнить код, записанный в файл. В качестве аргументов принимают соответственно строку и путь к файлу. Данные функции полезны при использовании дополнительных модулей, написанных на tcl. Пример:

from Tkinter import *
root=Tk()
root.eval('package require tile; ttk::style theme use clam')
root.eval('ttk::button .b -text {ttk button}; pack .b')
root.mainloop()

Основные виджеты

Toplevel

Toplevel[5] — окно верхнего уровня. Обычно используется для создания многооконных программ, а также для диалоговых окон.

Методы виджета
  • title — заголовок окна
  • overrideredirect — указание оконному менеджеру игнорировать это окно. Аргументом является True или False. В случае, если аргумент не указан — получаем текущее значение. Если аргумент равен True, то такое окно будет показано оконным менеджером без обрамления (без заголовка и бордюра). Может быть использовано, например, для создания splashscreen при старте программы.
  • iconify / deiconify — свернуть / развернуть окно
  • withdraw — «спрятать» (сделать невидимым) окно. Для того, чтобы снова показать его, надо использовать метод deiconify.
  • minsize и maxsize — минимальный / максимальный размер окна. Методы принимают два аргумента — ширина и высота окна. Если аргументы не указаны — возвращают текущее значение.
  • state — получить текущее значение состояния окна. Может возвращать следующие значения: normal (нормальное состояние), icon (показано в виде иконки), iconic (свёрнуто), withdrawn (не показано), zoomed (развёрнуто на полный экран, только для Windows и Mac OS X)
  • resizable — может ли пользователь изменять размер окна. Принимает два аргумента — возможность изменения размера по горизонтали и по вертикали. Без аргументов возвращает текущее значение.
  • geometry — устанавливает геометрию окна в формате ширинаxвысота+x+y (пример: geometry(«600×400+40+80») — поместить окно в точку с координатам 40,80 и установить размер в 600×400). Размер или координаты могут быть опущены (geometry(«600×400») — только изменить размер, geometry(«+40+80») — только переместить окно).
  • transient — сделать окно зависимым от другого окна, указанного в аргументе. Будет сворачиваться вместе с указанным окном. Без аргументов возвращает текущее значение.
  • protocol — получает два аргумента: название события и функцию, которая будет вызываться при наступлении указанного события. События могут называться WM_TAKE_FOCUS (получение фокуса), WM_SAVE_YOURSELF (необходимо сохраниться, в настоящий момент является устаревшим), WM_DELETE_WINDOW (удаление окна).
  • tkraise (синоним lift) и lower — поднимает (размещает поверх всех других окон) или опускает окно. Методы могут принимать один необязательный аргумент: над/под каким окном разместить текущее.
  • grab_set — устанавливает фокус на окно, даже при наличии открытых других окон
  • grab_release — снимает монопольное владение фокусом ввода с окна

Эти же методы могут быть использованы для корневого (root) окна.

Пример:

from Tkinter import *
def window_deleted():
    print u'Окно закрыто'
    root.quit() # явное указание на выход из программы
root=Tk()
root.title(u'Пример приложения')
root.geometry('500x400+300+200') # ширина=500, высота=400, x=300, y=200
root.protocol('WM_DELETE_WINDOW', window_deleted) # обработчик закрытия окна
root.resizable(True, False) # размер окна может быть изменён только по горизонтали
root.mainloop()

Таким способом можно предотвратить закрытие окна (например, если закрытие окна приведёт к потере введённых пользователем данных).

Button

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

from Tkinter import *
root=Tk()
button1=Button(root,text='ok',width=25,height=5,bg='black',fg='red',font='arial 14')
button1.pack()
root.mainloop()

Разберем этот небольшой код. За создание, собственно, окна, отвечает класс Tk(), и первым делом нужно создать экземпляр этого класса. Этот экземпляр принято называть root, хотя вы можете назвать его как угодно. Далее создаётся кнопка, при этом мы указываем её свойства (начинать нужно с указания окна, в примере — root). Здесь перечислены некоторые из них:

  • text — какой текст будет отображён на кнопке (в примере — ок)
  • width,height — соответственно, ширина и длина кнопки.
  • bg — цвет кнопки (сокращенно от background, в примере цвет — чёрный)
  • fg — цвет текста на кнопке (сокращённо от foreground, в примере цвет — красный)
  • font — шрифт и его размер (в примере — arial, размер — 14)

Далее, нашу кнопку необходимо разместить на окне. Для этого, в Tkinter используются специальные упаковщики( pack(), place(), grid() ). Поподробнее об упаковщиках узнаем позже. Пока, чтобы разместить несколько виджетов на окне, будем применять самый простой упаковщик pack(). В конце программы, нужно использовать функцию mainloop (см. пример), иначе окно не будет создано.

Label

Label — это виджет, предназначенный для отображения какой-либо надписи без возможности редактирования пользователем. Имеет те же свойства, что и перечисленные свойства кнопки.

Entry

Entry — это виджет, позволяющий пользователю ввести одну строку текста. Имеет дополнительное свойство bd (сокращённо от borderwidth), позволяющее регулировать ширину границы.

  • borderwidth — ширина бордюра элемента
  • bd — сокращение от borderwidth
  • width — задаёт длину элемента в знакоместах.
  • show — задает отображаемый символ.

Text

Text — это виджет, который позволяет пользователю ввести любое количество текста. Имеет дополнительное свойство wrap, отвечающее за перенос (чтобы, например, переносить по словам, нужно использовать значение WORD).Например:

from Tkinter import *
root=Tk()
text1=Text(root,height=7,width=7,font='Arial 14',wrap=WORD)
text1.pack()
root.mainloop()

Методы insert, delete и get добавляют, удаляют или извлекают текcт. Первый аргумент — место вставки в виде ‘x.y’, где x – это строка, а y – столбец. Например:

text1.insert(1.0,'Добавить Текстn в начало первой строки')
text1.delete('1.0', END)   # Удалить все
text1.get('1.0', END)      # Извлечь все

Listbox

Listbox — это виджет, который представляет собой список, из элементов которого пользователь может выбирать один или несколько пунктов. Имеет дополнительное свойство selectmode, которое, при значении SINGLE, позволяет пользователю выбрать только один элемент списка, а при значении EXTENDED — любое количество. Пример:

from Tkinter import *
root=Tk()
listbox1=Listbox(root,height=5,width=15,selectmode=EXTENDED)
listbox2=Listbox(root,height=5,width=15,selectmode=SINGLE)
list1=[u"Москва",u"Санкт-Петербург",u"Саратов",u"Омск"]
list2=[u"Канберра",u"Сидней",u"Мельбурн",u"Аделаида"]
for i in list1:
    listbox1.insert(END,i)
for i in list2:
    listbox2.insert(END,i)
listbox1.pack()
listbox2.pack()
root.mainloop()

Стоит заметить, что в этой библиотеке для того, чтобы использовать русские буквы в строках, нужно использовать Unicode-строки. В Python 2.x для этого нужно перед строкой поставить букву u, в Python 3.x этого делать вообще не требуется, т.к. все строки в нем изначально Unicode. Кроме того в первой или второй строке файла необходимо указать кодировку файла (в комментарии): coding: utf-8. Чаще всего используется формат в стиле текстового редактора emacs:

В Python 3.x кодировку файла можно не указывать, в этом случае по умолчанию предполагается utf-8.

Frame

Виджет Frame (рамка) предназначен для организации виджетов внутри окна. Рассмотрим пример:

from tkinter import *
root=Tk()
frame1=Frame(root,bg='green',bd=5)
frame2=Frame(root,bg='red',bd=5)
button1=Button(frame1,text=u'Первая кнопка')
button2=Button(frame2,text=u'Вторая кнопка')
frame1.pack()
frame2.pack()
button1.pack()
button2.pack()
root.mainloop()

Свойство bd отвечает за толщину края рамки.

Checkbutton

Checkbutton — это виджет, который позволяет отметить „галочкой“ определенный пункт в окне. При использовании нескольких пунктов нужно каждому присвоить свою переменную. Разберем пример:

from tkinter import *
root=Tk()
var1=IntVar()
var2=IntVar()
check1=Checkbutton(root,text=u'1 пункт',variable=var1,onvalue=1,offvalue=0)
check2=Checkbutton(root,text=u'2 пункт',variable=var2,onvalue=1,offvalue=0)
check1.pack()
check2.pack()
root.mainloop()

IntVar() — специальный класс библиотеки для работы с целыми числами. variable — свойство, отвечающее за прикрепление к виджету переменной. onvalue, offvalue — свойства, которые присваивают прикреплённой к виджету переменной значение, которое зависит от состояния(onvalue — при выбранном пункте, offvalue — при невыбранном пункте).

Radiobutton

Виджет Radiobutton выполняет функцию, схожую с функцией виджета Checkbutton. Разница в том, что в виджете Radiobutton пользователь может выбрать лишь один из пунктов. Реализация этого виджета несколько иная, чем виджета Checkbutton:

from tkinter import *
root=Tk()
var=IntVar()
rbutton1=Radiobutton(root,text='1',variable=var,value=1)
rbutton2=Radiobutton(root,text='2',variable=var,value=2)
rbutton3=Radiobutton(root,text='3',variable=var,value=3)
rbutton1.pack()
rbutton2.pack()
rbutton3.pack()
root.mainloop()

В этом виджете используется уже одна переменная. В зависимости от того, какой пункт выбран, она меняет своё значение. Самое интересное, что если присвоить этой переменной какое-либо значение, поменяется и выбранный виджет. На этом мы прервём изучение типов виджетов (потом мы к ним обязательно вернёмся).

Scale

Scale (шкала) — это виджет, позволяющий выбрать какое-либо значение из заданного диапазона. Свойства:

  • orient — как расположена шкала на окне. Возможные значения: HORIZONTAL, VERTICAL (горизонтально, вертикально).
  • length — длина шкалы.
  • from_ — с какого значения начинается шкала.
  • to — каким значением заканчивается шкала.
  • tickinterval — интервал, через который отображаются метки шкалы.
  • resolution — шаг передвижения (минимальная длина, на которую можно передвинуть движок)

Пример кода:

from tkinter import *
root = Tk()
def getV(root):
    a = scale1.get()
    print "Значение", a 
scale1 = Scale(root,orient=HORIZONTAL,length=300,from_=50,to=80,tickinterval=5,
               resolution=5)
button1 = Button(root,text=u"Получить значение")
scale1.pack()
button1.pack()
button1.bind("<Button-1>",getV)
root.mainloop()

Здесь используется специальный метод get(), который позволяет снять с виджета определенное значение, и используется не только в Scale.

Scrollbar

Этот виджет даёт возможность пользователю «прокрутить» другой виджет (например текстовое поле) и часто бывает полезен. Использование этого виджета достаточно нетривиально. Необходимо сделать две привязки: command полосы прокрутки привязываем к методу xview/yview виджета, а xscrollcommand/yscrollcommand виджета привязываем к методу set полосы прокрутки.

Рассмотрим на примере:

from tkinter import *
root = Tk()
text = Text(root, height=3, width=60)
text.pack(side='left')
scrollbar = Scrollbar(root)
scrollbar.pack(side='left')
# первая привязка
scrollbar['command'] = text.yview
# вторая привязка
text['yscrollcommand'] = scrollbar.set
root.mainloop()

Упаковщики

Упаковщик (менеджер геометрии, менеджер расположения) это специальный механизм, который размещает (упаковывает) виджеты на окне. В Tkinter есть три упаковщика: pack, place, grid. Обратите внимание, что в одном виджете можно использовать только один тип упаковки, при смешивании разных типов упаковки программа, скорее всего, не будет работать.

Разберем каждый из них по порядку:

pack()[6]

Упаковщик pack() является самым интеллектуальным (и самым непредсказуемым). При использовании этого упаковщика с помощью свойства side нужно указать к какой стороне родительского виджета он должен примыкать. Как правило этот упаковщик используют для размещения виджетов друг за другом (слева направо или сверху вниз). Пример:

from tkinter import *
root=Tk()
button1 = Button(text="1")
button2 = Button(text="2")
button3 = Button(text="3")
button4 = Button(text="4")
button5 = Button(text="5")
button1.pack(side='left')
button2.pack(side='top')
button3.pack(side='left')
button4.pack(side='bottom')
button5.pack(side='right')
root.mainloop()

Tkinter-pack.png

Результат работы можно увидеть на скриншоте справа.

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

Аргументы

При применении этого упаковщика можно указать следующие аргументы:

  • side («left»/»right»/»top»/»bottom») — к какой стороне должен примыкать размещаемый виджет.
  • fill (None/»x»/»y»/»both») — необходимо ли расширять пространство предоставляемое виджету.
  • expand (True/False) — необходимо ли расширять сам виджет, чтобы он занял всё предоставляемое ему пространство.
  • in_ — явное указание в какой родительский виджет должен быть помещён.
Дополнительные функции

Кроме основной функции у виджетов есть дополнительные методы для работы с упаковщиками.

  • pack_configure — синоним для pack.
  • pack_slaves (синоним slaves) — возвращает список всех дочерних упакованных виджетов.
  • pack_info — возвращает информацию о конфигурации упаковки.
  • pack_propagate (синоним propagate) (True/False) — включает/отключает распространении информации о геометрии дочерних виджетов. По умолчанию виджет изменяет свой размер в соответствии с размером своих потомков. Этот метод может отключить такое поведение (pack_propagate(False)). Это может быть полезно, если необходимо, чтобы виджет имел фиксированный размер и не изменял его по прихоти потомков.[7]
  • pack_forget (синоним forget) — удаляет виджет и всю информацию о его расположении из упаковщика. Позднее этот виджет может быть снова размещён.

grid()[8]

Этот упаковщик представляет собой таблицу с ячейками, в которые помещаются виджеты.

Аргументы
  • row — номер строки, в который помещаем виджет.
  • rowspan — сколько строк занимает виджет
  • column — номер столбца, в который помещаем виджет.
  • columnspan — сколько столбцов занимает виджет.
  • padx / pady — размер внешней границы (бордюра) по горизонтали и вертикали.
  • ipadx / ipady — размер внутренней границы (бордюра) по горизонтали и вертикали. Разница между pad и ipad в том, что при указании pad расширяется свободное пространство, а при ipad расширяется помещаемый виджет.
  • sticky («n», «s», «e», «w» или их комбинация) — указывает к какой границе «приклеивать» виджет. Позволяет расширять виджет в указанном направлении. Границы названы в соответствии со сторонами света. «n» (север) — верхняя граница, «s» (юг) — нижняя, «w» (запад) — левая, «e» (восток) — правая.
  • in_ — явное указание в какой родительский виджет должен быть помещён.

Для каждого виджета указываем, в какой он находится строке, и в каком столбце. Если нужно, указываем, сколько ячеек он занимает (если, например, нам нужно разместить три виджета под одним, необходимо «растянуть» верхний на три ячейки). Пример:

entry1.grid(row=0,column=0,columnspan=3)
button1.grid(row=1,column=0)
button2.grid(row=1,column=1)
button3.grid(row=1,column=2)
Дополнительные функции
  • grid_configure — синоним для grid.
  • grid_slaves (синоним slaves) — см. pack_slaves.
  • grid_info — см. pack_info.
  • grid_propagate (синоним propagate) — см. pack_propagate.
  • grid_forget (синоним forget) — см. pack_forget.
  • grid_remove — удаляет виджет из-под управления упаковщиком, но сохраняет информацию об упаковке. Этот метод удобно использовать для временного удаления виджета (см. пример в описании метода destroy).
  • grid_bbox (синоним bbox) — возвращает координаты (в пикселях) указанных столбцов и строк.[9]
  • grid_location (синоним location) — принимает два аргумента: x и y (в пикселях). Возвращает номер строки и столбца в которые попадают указанные координаты, либо -1 если координаты попали вне виджета.
  • grid_size (синоним size) — возвращает размер таблицы в строках и столбцах.
  • grid_columnconfigure (синоним columnconfigure) и grid_rowconfigure (синоним rowconfigure) — важные функции для конфигурирования упаковщика. Методы принимают номер строки/столбца и аргументы конфигурации. Список возможных аргументов:
    • minsize — минимальная ширина/высота строки/столбца.
    • weight — «вес» строки/столбца при увеличении размера виджета. 0 означает, что строка/столбец не будет расширяться. Строка/столбец с «весом» равным 2 будет расширяться вдвое быстрее, чем с весом 1.
    • uniform — объединение строк/столбцов в группы. Строки/столбцы имеющие одинаковый параметр uniform будут расширяться строго в соответствии со своим весом.
    • pad — размер бордюра. Указывает, сколько пространства будет добавлено к самому большому виджету в строке/столбце.

Пример, текстовый виджет с двумя полосами прокрутки:

from tkinter import *
root=Tk()
text = Text(wrap=NONE)
vscrollbar = Scrollbar(orient='vert', command=text.yview)
text['yscrollcommand'] = vscrollbar.set
hscrollbar = Scrollbar(orient='hor', command=text.xview)
text['xscrollcommand'] = hscrollbar.set
# размещаем виджеты
text.grid(row=0, column=0, sticky='nsew')
vscrollbar.grid(row=0, column=1, sticky='ns')
hscrollbar.grid(row=1, column=0, sticky='ew')
# конфигурируем упаковщик, чтобы текстовый виджет расширялся
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
root.mainloop()

place()[10]

place представляет собой простой упаковщик, позволяющий размещать виджет в фиксированном месте с фиксированным размером. Также он позволяет указывать координаты размещения в относительных единицах для реализации «резинового» размещения. При использовании этого упаковщика, нам необходимо указывать координаты каждого виджета. Например:

Этот упаковщик, хоть и кажется неудобным, предоставляет полную свободу в размещении виджетов на окне.

Аргументы
  • anchor («n», «s», «e», «w», «ne», «nw», «se», «sw» или «center») — какой угол или сторона размещаемого виджета будет указана в аргументах x/y/relx/rely. По умолчанию «nw» — левый верхний
  • bordermode («inside», «outside», «ignore») — определяет в какой степени будут учитываться границы при размещении виджета.
  • in_ — явное указание в какой родительский виджет должен быть помещён.
  • x и y — абсолютные координаты (в пикселях) размещения виджета.
  • width и height — абсолютные ширина и высота виджета.
  • relx и rely — относительные координаты (от 0.0 до 1.0) размещения виджета.
  • relwidth и relheight — относительные ширина и высота виджета.

Относительные и абсолютные координаты (а также ширину и высоту) можно комбинировать. Так например, relx=0.5, x=-2 означает размещение виджета в двух пикселях слева от центра родительского виджета, relheight=1.0, height=-2 — высота виджета на два пикселя меньше высоты родительского виджета.

Дополнительные функции

place_slaves, place_forget, place_info — см. описание аналогичных методов упаковщика pack.

Привязка событий

«Всё это хорошо» — наверное, подумали вы. — «Но как сделать так, чтобы мои виджеты что-то делали, а не просто красовались на окне?».

command

Для большинства виджетов, реагирующих на действие пользователя, активацию виджета (например нажатие кнопки) можно привязать с использованием опции command. К таким виджетам относятся: Button, Checkbutton, Radiobutton, Spinbox, Scrollbar, Scale. Выше мы уже неоднократно пользовались этим способом:

button = Button(command=callback)

Такой способ является предпочтительным и наиболее удобным способом привязки.

bind()

Метод bind[1] привязывает событие к какому-либо действию (нажатие кнопки мыши, нажатие клавиши на клавиатуре и т.д.). bind принимает три аргумента:

  • название события
  • функцию, которая будет вызвана при наступлении события
  • третий аргумент (необязательный) — строка «+» — означает, что эта привязка добавляется к уже существующим. Если третий аргумент опущен или равен пустой строке — привязка замещает все другие привязки данного события к виджету.

Метод bind возвращает идентификатор привязки, который может быть использован в функции unbind.

Обратите внимание, что если bind привязан к окну верхнего уровня, то Tkinter будет обрабатывать события всех виджетов этого окна (см. также bind_all ниже).

Функция, которая вызывается при наступлении события, должна принимать один аргумент. Это объект класса Event, в котором описано наступившее событие. Объект класса Event имеет следующие атрибуты (в скобках указаны события, для которых этот атрибут установлен):

  • serial — серийный номер события (все события)
  • num — номер кнопки мыши (ButtonPress, ButtonRelease)
  • focus — имеет ли окно фокус (Enter, Leave)
  • height и width — ширина и высота окна (Configure, Expose)
  • keycode — код нажатой клавиши (KeyPress, KeyRelease)
  • state — состояние события (для ButtonPress, ButtonRelease, Enter, KeyPress, КeyRelease, Leave, Motion — в виде числа; для Visibility — в виде строки)
  • time — время наступления события (все события)
  • x и y — координаты мыши
  • x_root и y_root — координаты мыши на экране (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion)
  • char — набранный на клавиатуре символ (KeyPress, KeyRelease)
  • send_event — см. документацию по X/Windows
  • keysym — набранный на клавиатуре символ (KeyPress, KeyRelease)
  • keysym_num — набранный на клавиатуре символ в виде числа (KeyPress, KeyRelease)
  • type — тип события в виде числа (все события)
  • widget — виджет, который получил событие (все события)
  • delta — изменение при вращении колеса мыши (MouseWheel)

Эта функция может возвращать строки «continue» и «break». Если функция возвращает «continue» то Tkinter продолжит обработку других привязок этого события, если «break» — обработка этого события прекращается. Если функция ничего не возвращает (если возвращает None), то обработка событий продолжается (т.е. это эквивалентно возвращению «continue»).

Названия событий

Есть три формы названия событий. Самый простой случай это символ ASCII. Так описываются события нажатия клавиш на клавиатуре:

widget.bind("z", callback)

callback вызывается каждый раз, когда будет нажата клавиша «z».

Второй способ длиннее, но позволяет описать больше событий. Он имеет следующий синтаксис:

<modifier-modifier-type-detail>

Название события заключено в угловые скобки. Внутри имеются ноль или более модификаторов, тип события и дополнительная информация (номер нажатой клавиши мыши или символ клавиатуры) Поля разделяются дефисом или пробелом. Пример (привязываем одновременное нажатие Ctrl+Shift+q):

widget.bind("<Control-Shift-KeyPress-q>", callback)

(в данном примере KeyPress можно убрать).

Третий способ позволяет привязывать виртуальные события — события, которые генерируются самим приложением. Такие события можно создавать самим, а потом привязывать их. Имена таких событий помещаются в двойные угловые скобки: <<Paste>>. Есть некоторое количество уже определённых виртуальных событий.

Список модификаторов
  • Return — Enter
  • Escape — Esc
  • Control — Ctrl
  • Alt
  • Shift
  • Lock
  • Extended
  • Prior — PgUp
  • Next — PgDown
  • Button1, B1 — нажата первая (левая) кнопка мыши
  • Button2, B2 — вторая (средняя) кнопка мыши
  • Button3, B3 — третья (правая)
  • Button4, B4 — четвёртая
  • Button5, B5 — пятая
  • Mod1, M1, Command
  • Mod2, M2, Option
  • Mod3, M3
  • Mod4, M4
  • Mod5, M5
  • Meta, M
  • Double — двойной щелчок мыши (например, <Double-Button-1>)
  • Triple — тройной
  • Quadruple — четверной
Типы событий

Здесь перечислены все возможные типы событий, для самых часто используемых дано описание. Более подробно см. man bind.

  • Activate, Deactivate
  • MouseWheel — прокрутка колесом мыши
  • KeyPress, KeyRelease — нажатие и отпускание клавиши на клавиатуре
  • ButtonPress, ButtonRelease, Motion — нажатие, отпускание клавиши мыши, движение мышью
  • Configure — изменение положения или размера окна
  • Map, Unmap — показывание или сокрытие окна (например, в случае сворачивания/разворачивания окна пользователем)
  • Visibility
  • Expose — событие генерируется, когда необходимо всё окно или его часть перерисовать
  • Destroy — закрытие окна
  • FocusIn, FocusOut — получение или лишение фокуса
  • Enter, Leave — Enter генерируется когда курсор мыши «входит» в окно, Leave — когда «уходит» из окна
  • Property
  • Colormap
  • MapRequest, CirculateRequest, ResizeRequest, ConfigureRequest, Create
  • Gravity, Reparent, Circulate
Клавиатурные символы

Полный список см. man keysyms.

Примеры

<Button-1> или <1> — нажата левая клавиша мыши.

<Alt-Motion> — движение мышью с нажатой на клавиатуре клавишей Alt.

<Key> — нажатие любой клавиши на клавиатуре.

Пример:

from Tkinter import *
root=Tk()
def leftclick(event):
    print u'Вы нажали левую кнопку мыши'
def rightclick(event):
    print u'Вы нажали правую кнопку мыши'
button1=Button(root, text=u'Нажми')
button1.pack()
button1.bind('<Button-1>', leftclick)
button1.bind('<Button-3>', rightclick)
root.mainloop()
Дополнительные методы
  • bind_all — создаёт привязку для всех виджетов приложения. Отличие от привязки к окну верхнего уровня заключается в том, что в случае привязки к окну привязываются все виджеты этого окна, а этот метод привязывает все виджеты приложения (у приложения может быть несколько окон).
  • bind_class — создаёт привязку для всех виджетов данного класса

Пример:

from Tkinter import *
def callback(e):
    print u'Нажата кнопка', e.widget['text']
root=Tk()
button1 = Button(root, text='1')
button1.pack()
button2 = Button(root, text='2')
button2.pack()
root.bind_class('Button', '<1>', callback)
root.mainloop()
  • bindtags — позволяет изменить порядок обработки привязок. По умолчанию порядок следующий: виджет, класс, окно, all; где виджет — привязка к виджету (bind), класс — привязка к классу (bind_class), окно — привязка к окну (root.bind), all — привязка всех виджетов (bind_all).

Пример, меняем порядок обработки привязок на обратный:

from Tkinter import *
def callback1(e): print 'callback1'
def callback2(e): print 'callback2'
def callback3(e): print 'callback3'
def callback4(e): print 'callback4'
root=Tk()
button = Button(root)
button.pack()
button.bind('<1>', callback1)
root.bind_class('Button', '<1>', callback2)
root.bind('<1>', callback3)
root.bind_all('<1>', callback4)
button.bindtags(('all', root, 'Button', button))
root.mainloop()
  • unbind — отвязать виджет от события. В качестве аргумента принимает идентификатор, полученный от метода bind.
  • unbind_all — то же, что и unbind, только для метода bind_all.
  • unbind_class — то же, что и unbind, только для метода bind_class.

Изображения

Для работы с изображениями в Tkinter имеется два класса BitmapImage и PhotoImage. BitmapImage представляет собой простое двухцветное изображение, PhotoImage — полноцветное изображение.

BitmapImage

Конструктор класса принимает следующие аргументы:

  • background и foreground — цвета фона и переднего плана для изображения. Поскольку изображение двухцветное, то эти параметры определяют соответственно чёрный и белый цвет.
  • file и maskfile — пути к файлу с изображением и к маске (изображению, указывающему какие пиксели будут прозрачными).
  • data и maskdata — вместо пути к файлу можно указать уже загруженные в память данные изображения. Данная возможность удобна для встраивания изображения в программу.

Пример:

from Tkinter import *
data = '''#define image_width 15
#define image_height 15
static unsigned char image_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x30, 0x0c, 0x60, 0x06,
   0x60, 0x06, 0xc0, 0x03, 0xc0, 0x03, 0x60, 0x06, 0x60, 0x06, 0x30, 0x0c,
   0x38, 0x1c, 0x00, 0x00, 0x00, 0x00 };'''
root=Tk()
image = BitmapImage(data=data, background='red', foreground='green')
button=Button(root, image=image)
button.pack()
root.mainloop()

PhotoImage

PhotoImage позволяет использовать полноцветное изображение. Кроме того у этого класса есть несколько (достаточно примитивных) методов для работы с изображениями. PhotoImage гарантированно понимает формат GIF.

Аргументы конструктора:

  • file — путь к файлу с изображением.
  • data — вместо пути к файлу можно указать уже загруженные в память данные изображения. Изображения в формате GIF могут быть закодированы с использованием base64. Данная возможность удобна для встраивания изображения в программу.
  • format — явное указание формата изображения.
  • width, height — ширина и высота изображения.
  • gamma — коррекция гаммы.
  • palette — палитра изображения.

ttk

ttk (themed tk) это расширение tcl/tk с новым набором виджетов. В ttk используется новый движок для создания виджетов. Этот движок обладает поддержкой тем и стилей оформления. Благодаря этому виджеты ttk выглядят более естественно в различных операционных системах.

Начиная с версий python 2.7 и 3.1.2 в Tkinter включён модуль для работы с ttk.

Внешний вид некоторых виджетов ttk, используется тема clam

В ttk включены следующие виджеты, которые можно использовать вместо соответствующих виджетов tk: Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton, PanedWindow, Radiobutton, Scale и Scrollbar. Кроме того имеется несколько новых виджетов: Combobox, Notebook, Progressbar, Separator, Sizegrip и Treeview.

Поскольку это относительно новая возможность Tkinter, несколько слов об установке этого модуля. Версии python 2.7/3.1.2 и старше уже имеют в своём составе этот модуль. Для использования ttk с более ранними версиями python, его нужно установить самостоятельно. Домашняя страница модуля. В данный момент python-ttk хостится в svn python.org. Модуль представляет собой один файл — ttk.py, который нужно положить в каталог, где его сможет найти python. Прямые ссылки для скачивания: для версии 2.x, для версии 3.x.

С точки зрения программиста главное отличие новых виджетов от старых заключается в том, что у виджетов ttk отсутствуют опции для конфигурирования его внешнего вида. Сравните, например, количество STANDARD OPTIONS для старого и нового виджета button. Конфигурация внешнего вида виджетов ttk осуществляется через темы и стили. В остальном использование виджетов ttk аналогично соответствующим виджетам tk.

ttk имеет четыре встроенных темы: default, classic, alt, clam. Кроме того дополнительно под Windows есть темы winnative, xpnative и vista, а под Mac OS X — aqua.

Style

Style это класс для работы со стилями и темами. Именно этот класс надо использовать для конфигурирования внешнего вида виджетов. Основные методы класса:

configure

Конфигурирование внешнего вида виджетов. В качестве аргументов принимает название стиля виджета (например «TButton») и список опций конфигурирования. Пример:

style.configure("TButton", padding=6, relief="flat", background="#ccc")
map

Конфигурирование внешнего вида виджетов в зависимости от их состояний (active, pressed, disabled и т.д.). В качестве аргументов принимает название стиля виджета и список опций конфигурирования, где опции представлены в виде списка. Пример:

style.map("C.TButton",
    foreground=[('pressed', 'red'), ('active', 'blue')],
    background=[('pressed', '!disabled', 'black'), ('active', 'white')]
    )
lookup

Возвращает соответствующую опцию конфигурирования. Пример:

style.lookup("TButton", "font")
layout

Изменяет layout (схему) виджета. Виджеты ttk состоят из отдельных элементов, опций конфигурирования и других вложенных layouts. Следующий пример иллюстрирует применение метода layout:

style.layout("TMenubutton", [
   ("Menubutton.background", None),
   ("Menubutton.button", {"children":
       [("Menubutton.focus", {"children":
           [("Menubutton.padding", {"children":
               [("Menubutton.label", {"side": "left", "expand": 1})]
           })]
       })]
   }),
])
element_create

Создаёт новый элемент темы.

element_names

Возвращает список элементов текущей темы.

element_options

Возвращает список опций (конфигурацию), указанного в аргументе элемента.

theme_create

Создаёт новую тему. Аргументы те же, что и в theme_settings.

theme_settings

Конфигурирует существующую тему. Первый аргумент — название темы, второй аргумент — словарь, ключами которого являются названия стилей (TButton и т.п.), а значениями — layout соответствующего стиля.

theme_names

Возвращает список доступных тем.

theme_use

Изменяет текущую тему на указанную в аргументе.

Combobox

Виджет Combobox предназначен для отображения списка значений, их выбора или изменения пользователем. В версии tk ему подобен виджет Listbox. Разница заключается в том, что Combobox имеет возможность сворачиваться подобно свитку, а Listbox будет отображаться всегда открытым.
Что бы отобразить Combobox с заранее заданными значениями в форме, достаточно сделать следующее:

import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
frame = tk.Frame(root)
frame.grid()
combobox = ttk.Combobox(frame,values = [u"ОДИН",u"ДВА",u"ТРИ"],height=3)
#frame - задает родительский виджет, на его территории будет располагаться Combobox
#values - задает набор значений, которые будут содержаться в Combobox изначально
#height - задает высоту выпадающего списка. Если число элементов списка меньше 11, то можно не задавать.
#Если не задано при количестве элементов больше 10, то с правой стороны появится полоса прокрутки.
#Если в нашем примере задать значение height меньше трех, то с правой стороны появится полоса прокрутки,
#но она будет недоступна, а все элементы будут отображаться одновременно.
combobox.set(u"ОДИН")#с помощью этой строчки мы установим Combobox в значение ОДИН изначально
combobox.grid(column=0,row=0)#Позиционируем Combobox на форме
root.mainloop()

Progressbar

Виджет отображает уровень загрузки.

  • length — длина полосы.
start

Запускает бесконечный цикл загрузки. Шаг длиною 1 выполняется один раз в указанное время (в миллисекундах).

stop

Останавливает цикл загрузки.

step

Продвигает загрузку на заданное количество шагов.

import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
pb = ttk.Progressbar(root, length=100)
pb.pack()
pb.start(100)
root.mainloop()

Примечания

  1. http://www.tcl.tk/man/tcl8.5/TkCmd/grab.htm
  2. http://www.tcl.tk/man/tcl8.5/TkCmd/focus.htm
  3. http://www.tcl.tk/man/tcl8.5/TclCmd/after.htm
  4. http://www.tcl.tk/man/tcl8.5/TclCmd/update.htm
  5. http://www.tcl.tk/man/tcl8.5/TkCmd/toplevel.htm
  6. http://www.tcl.tk/man/tcl8.5/TkCmd/pack.htm
  7. http://www.tcl.tk/man/tcl8.5/TkCmd/pack.htm#M28
  8. http://www.tcl.tk/man/tcl8.5/TkCmd/grid.htm
  9. http://www.tcl.tk/man/tcl8.5/TkCmd/grid.htm#M7
  10. http://www.tcl.tk/man/tcl8.5/TkCmd/place.htm

Ссылки

  • An Introduction to Tkinter (англ.) — хорошее дополнение к документации
  • Роман Сузи. Tkinter в рамках интерактивного курса «Язык программирования Python»
  • Несколько статей на русском

The end

В этом руководстве мы научимся разрабатывать графические интерфейсы пользователя (GUI), написав несколько примеров GUI на Python с использованием пакета Tkinter.

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

Я предполагаю, что у вас есть некоторые базовые знания Python, которые помогут вам понять, что мы делаем.

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

Содержание

  • 1 Создайте свое первое приложение с графическим интерфейсом
  • 2 Создание виджета label
  • 3 Установка размера шрифта метки
  • 4 Установка размера окна
  • 5 Добавление виджета кнопки
  • 6 Изменение цвета фона и шрифта кнопки.
  • 7 Обработка события нажатия кнопки
  • 8 Ввод данных с помощью класса Entry (текстовое поле Tkinter)
  • 9 Установка фокуса на виджете ввода
  • 10 Отключение виджета ввода
  • 11 Добавление виджета combobox
  • 12 Добавление виджета Checkbutton (Tkinter checkbox)
  • 13 Установка состояния Checkbutton
  • 14 Добавление виджета radio button
  • 15 Получить значение радиокнопки (выбранный вариант)
  • 16 Добавление виджета ScrolledText (Tkinter textarea)
  • 17 Установка содержимого scrolledtext.
  • 18 Удаление/очистка содержимого scrolledtext
  • 19 Создание MessageBox
  • 20 Отображение сообщений об ошибках и предупреждений
  • 21 Отображение диалоговых окон с вопросами и ответами
  • 22 Добавление SpinBox (виджет чисел)
  • 23 Установка значения по умолчанию для Spinbox
  • 24 Добавление виджета Progressbar
  • 25 Изменение цвета Progressbar
  • 26 Окно добавления файла (выбор файла и каталога).
  • 27 Указание типов файлов (фильтр по расширению)
  • 28 Добавление меню
  • 29 Добавление виджета Notebook (элемент управления вкладками)
  • 30 Добавление виджетов на вкладки
  • 31 Добавление отступов у виджетов (spacing или padding)
    • 31.1 Похожие записи

Создайте свое первое приложение с графическим интерфейсом

Сначала мы импортируем пакет Tkinter, создадим окно и зададим его заголовок:

from tkinter import * window = Tk() window.title("Добро пожаловать!") window.mainloop()

Code language: JavaScript (javascript)

Невероятно! Наше приложение работает.

Последняя строка вызывает функцию mainloop. Эта функция запускает бесконечный цикл окна, поэтому оно будет открыто, пока мы его сами не закроем.

Если вы забудете вызвать функцию mainloop, пользователю ничего не будет показано.

Создание виджета label

Чтобы добавить метку к нашему предыдущему примеру, мы создадим ее с помощью класса label:

lbl = Label(window, text="Привет!")

Code language: JavaScript (javascript)

Затем мы установим его положение с помощью функции grid вот так:

lbl.grid(column=0, row=0)

Итоговый код будет выглядеть следующим образом:

from tkinter import * window = Tk() window.title("Добро пожаловать!") lbl = Label(window, text="Привет!") lbl.grid(column=0, row=0) window.mainloop()

Code language: JavaScript (javascript)

Вот результат:

Без вызова функции grid для label метка не появится.

Установка размера шрифта метки

Вы можете настроить шрифт метки, чтобы сделать его более крупным или, возможно, жирным. Кроме того, можно изменить стиль шрифта.

Для этого передайте дополнительный параметр font:

lbl = Label(window, text="Привет!", font=("Arial Bold", 50))

Code language: JavaScript (javascript)

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

Все замечательно. Но окно настолько маленькое, что мы даже не видим заголовок. Как установить размер окна?

Установка размера окна

Мы можем установить размер окна по умолчанию, используя функцию geometry:

window.geometry('350x200')

Code language: JavaScript (javascript)

Приведенная выше строка устанавливает ширину окна в 350 пикселей и высоту в 200 пикселей.

Давайте попробуем добавить больше виджетов в виде кнопок и посмотрим, как обрабатывать событие нажатия на кнопку.

Добавление виджета кнопки

Давайте начнем с добавления кнопки на окно. Кнопка создается и добавляется так же, как и метка:

btn = Button(window, text="Нажми меня") btn.grid(column=1, row=0)

Code language: JavaScript (javascript)

В результате код будет выглядеть следующим образом:

from tkinter import * window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') lbl = Label(window, text="Привет!") lbl.grid(column=0, row=0) btn = Button(window, text="Нажми меня") btn.grid(column=1, row=0) window.mainloop()

Code language: JavaScript (javascript)

Результат будет выглядеть так:

Обратите внимание, что мы размещаем кнопку во втором столбце окна, то есть в столбце 1. Если забыть об этом и поместить кнопку в тот же столбец (в данном случае 0), то будет отображаться только кнопка, так как кнопка будет находиться над меткой.

Изменение цвета фона и шрифта кнопки.

Вы можете изменить цвет текста кнопки или любого другого виджета с помощью свойства fg.

Также с помощью свойства bg можно изменить цвет фона любого виджета.

btn = Button(window, text="Click Me", bg="orange", fg="red")

Code language: JavaScript (javascript)

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

Обработка события нажатия кнопки

Сначала мы напишем функцию, которая должна выполняться при нажатии на кнопку:

def clicked(): lbl.configure(text="Кнопка нажата!!")

Code language: JavaScript (javascript)

Затем мы свяжем её с кнопкой, указав функцию следующим образом:

btn = Button(window, text="Нажми меня", command=clicked)

Code language: JavaScript (javascript)

Обратите внимание, что мы пишем просто clicked, а не clicked() со скобками.

Теперь весь код будет выглядеть следующим образом:

from tkinter import * def clicked(): lbl.configure(text="Кнопка нажата!!") window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') lbl = Label(window, text="Привет!") lbl.grid(column=0, row=0) btn = Button(window, text="Нажми меня", command=clicked) btn.grid(column=1, row=0) window.mainloop()

Code language: JavaScript (javascript)

Когда мы жмем на кнопку, то получаем нужный результат:

Отлично!

Ввод данных с помощью класса Entry (текстовое поле Tkinter)

В предыдущих примерах графического интерфейса Python мы рассмотрели, как добавлять простые виджеты. Теперь давайте попробуем получить данные от пользователя, используя класс Tkinter Entry (текстовое поле Tkinter).

Вы можете создать текстовое поле с помощью класса Tkinter Entry следующим образом:

Далее вы можете добавить виджет в окно с помощью функции grid, как и раньше.

Наш код будет выглядеть так::

from tkinter import * def clicked(): lbl.configure(text="Кнопка нажата!!") window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') lbl = Label(window, text="Привет!") lbl.grid(column=0, row=0) txt = Entry(window,width=10) txt.grid(column=1, row=0) btn = Button(window, text="Нажми меня", command=clicked) btn.grid(column=2, row=0) window.mainloop()

Code language: JavaScript (javascript)

Результат будет следующим:

Теперь, если вы нажмете на кнопку, будет выведено то же сообщение, что и выше. Как насчет показа текста, набранного в виджете ввода?

Мы можем получить введенный текст с помощью функции get. Затем мы можем добавить его в нашу функцию clicked, например, так:

def clicked(): res = "Добро пожаловать, " + txt.get() lbl.configure(text= res)

Code language: JavaScript (javascript)

Если вы нажмете на кнопку и в виджете ввода будет текст, он покажет “Добро пожаловать, “, соединенный с введенным текстом.

Вот полный код:

from tkinter import * def clicked(): res = "Добро пожаловать, " + txt.get() lbl.configure(text= res) window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') lbl = Label(window, text="Привет!") lbl.grid(column=0, row=0) txt = Entry(window,width=10) txt.grid(column=1, row=0) btn = Button(window, text="Нажми меня", command=clicked) btn.grid(column=2, row=0) window.mainloop()

Code language: JavaScript (javascript)

Запустите приведенный выше код и проверьте результат:

Потрясающе!

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

Установка фокуса на виджете ввода

Это очень просто, все, что нам нужно сделать, это вызвать функцию focus:

txt.focus()

Code language: CSS (css)

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

Отключение виджета ввода

Чтобы отключить виджет ввода, можно установить свойство state в значение disabled:

txt = Entry(window,width=10, state='disabled')

Code language: JavaScript (javascript)

Теперь вы больше не можете вводить никакой текст.

Добавление виджета combobox

Чтобы добавить виджет combobox, можно использовать класс Combobox из библиотеки ttk, как показано ниже:

from tkinter.ttk import * combo = Combobox(window)

Code language: JavaScript (javascript)

Затем вы можете добавить значения в список.

from tkinter import * from tkinter.ttk import * window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') combo = Combobox(window) combo['values']= (1, 2, 3, 4, 5, "Какой-то текст") combo.current(1) #Элемент выбранный по умолчанию combo.grid(column=0, row=0) window.mainloop()

Code language: PHP (php)

Как вы видите, мы добавляем элементы combobox, используя кортеж значений.

Чтобы установить выбранный элемент, можно передать индекс нужного элемента функцию current.

Чтобы получить выбранный элемент, можно использовать функцию get, как показано ниже:

combo.get()

Code language: CSS (css)

Добавление виджета Checkbutton (Tkinter checkbox)

Чтобы создать виджет Checkbutton, вы можете использовать класс Checkbutton:

chk = Checkbutton(window, text='Выбери меня')

Code language: JavaScript (javascript)

Кроме того, вы можете установить состояние “checked”, передав значение активации кнопке Checkbutton, как показано ниже:

from tkinter import * from tkinter.ttk import * window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') chk_state = BooleanVar() chk_state.set(True) #Устанавливаем состояние по умолчанию checked chk = Checkbutton(window, text='Выбери меня', var=chk_state) chk.grid(column=0, row=0) window.mainloop()

Code language: PHP (php)

Проверка результата:

Установка состояния Checkbutton

Здесь мы создаем переменную типа BooleanVar, которая не является стандартной переменной Python, это переменная Tkinter. Затем мы передаем его в класс Checkbutton для установки состояния галочки, как показано выше. Вы можете установить булево значение false, чтобы отключить отметку.

Также вы можете использовать IntVar вместо BooleanVar и установить значение 0 или 1.

chk_state = IntVar() chk_state.set(0) #Галочки нет chk_state.set(1) #Галочка есть

Code language: PHP (php)

Добавление виджета radio button

Чтобы их добавить, вы можете использовать класс RadioButton, как показано ниже:

rad1 = Radiobutton(window,text='Первый пункт', value=1)

Code language: JavaScript (javascript)

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

from tkinter import * from tkinter.ttk import * window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') rad1 = Radiobutton(window,text='Первый пункт', value=1) rad2 = Radiobutton(window,text='Второй пункт', value=2) rad3 = Radiobutton(window,text='Третий пункт', value=3) rad1.grid(column=0, row=0) rad2.grid(column=1, row=0) rad3.grid(column=2, row=0) window.mainloop()

Code language: JavaScript (javascript)

Результат выполнения приведенного выше кода выглядит следующим образом:

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

Вот пример:

rad1 = Radiobutton(window,text='Первый пункт', value=1, command=clicked) def clicked(): # Код функции

Code language: PHP (php)

Все очень просто!

Получить значение радиокнопки (выбранный вариант)

Чтобы получить текущую выбранную радиокнопку или значение радиокнопки, вы можете передать радиокнопкам параметр variable, а затем взять его значение.

from tkinter import * from tkinter.ttk import * window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') selected = IntVar() rad1 = Radiobutton(window,text='Первый пункт', value=1, variable=selected) rad2 = Radiobutton(window,text='Второй пункт', value=2, variable=selected) rad3 = Radiobutton(window,text='Третий пункт', value=3, variable=selected) def clicked(): print(selected.get()) btn = Button(window, text="Нажми меня!", command=clicked) rad1.grid(column=0, row=0) rad2.grid(column=1, row=0) rad3.grid(column=2, row=0) btn.grid(column=3, row=0) window.mainloop()

Code language: JavaScript (javascript)

Добавление виджета ScrolledText (Tkinter textarea)

Чтобы добавить виджет ScrolledText, вы можете использовать класс ScrolledText, как показано ниже:

from tkinter import scrolledtext txt = scrolledtext.ScrolledText(window,width=40,height=10)

Code language: JavaScript (javascript)

Мы указываем ширину и высоту виджета, иначе он заполнит все окно.

from tkinter import * from tkinter.ttk import * from tkinter import scrolledtext window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') txt = scrolledtext.ScrolledText(window,width=40,height=10) txt.grid(column=0,row=0) window.mainloop()

Code language: JavaScript (javascript)

Результат:

Установка содержимого scrolledtext.

Чтобы задать содержимое у scrolledtext, можно использовать метод insert, как показано ниже:

txt.insert(INSERT,'Какой-то текст')

Code language: JavaScript (javascript)

Удаление/очистка содержимого scrolledtext

Чтобы удалить содержимое виджета с прокруткой текста, можно использовать метод delete, как показано ниже:

txt.delete(1.0,END)

Code language: CSS (css)

Отлично!

Создание MessageBox

Чтобы отобразить окно сообщения с помощью Tkinter, вы можете использовать библиотеку messagebox, как показано ниже:

from tkinter import messagebox messagebox.showinfo('Заголовок всплывающего окна','Содержимое всплывающего окна')

Code language: JavaScript (javascript)

Достаточно просто!

Покажем сообщение, когда пользователь нажмет на кнопку.

from tkinter import * from tkinter import messagebox window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') def clicked(): messagebox.showinfo('Заголовок всплывающего окна','Содержимое всплывающего окна') btn = Button(window,text='Нажми меня!', command=clicked) btn.grid(column=0,row=0) window.mainloop()

Code language: JavaScript (javascript)

Когда вы нажмете на кнопку, появится информационное окно с сообщением.

Отображение сообщений об ошибках и предупреждений

Таким же можно отобразить предупреждающее сообщение или сообщение об ошибке. Единственное, что необходимо изменить messagebox.showinfo на messagebox.showwarning или messagebox.showerror.

messagebox.showwarning('Заголовок окна', 'Содержимое окна') #Окно с иконкой предупрждения messagebox.showerror('Заголовок окна', 'Содержимое окна') #Окно с иконкой ошибки

Code language: PHP (php)

Отображение диалоговых окон с вопросами и ответами

Чтобы отобразить пользователю окно сообщения “да/нет”, можно использовать одну из следующих функций messagebox:

res = messagebox.askquestion('Заголовок окна','Содержимое окна') res = messagebox.askyesno('Заголовок окна','Содержимое окна') res = messagebox.askyesnocancel('Заголовок окна','Содержимое окна') res = messagebox.askokcancel('Заголовок окна','Содержимое окна') res = messagebox.askretrycancel('Заголовок окна','Содержимое окна')

Code language: JavaScript (javascript)

Вы можете выбрать подходящий стиль сообщения в соответствии с вашей задачей. Просто замените функцию showinfo в приведенном выше коде и запустите ее.

Вы также можете проверить, какую кнопку нажал пользователь, используя переменную result.

Если вы нажмете OK, или “да”, или “повторить попытку”, то вернется True, а если вы выберете “нет” или “отмена”, то вернется False.

Единственной функцией, которая возвращает одно из трех значений, является функция askyesnocancel , которая возвращает True, False или None .

Добавление SpinBox (виджет чисел)

Чтобы создать данный виджет, вы можете использовать класс Spinbox, как показано ниже:

spin = Spinbox(window, from_=0, to=100)

Code language: JavaScript (javascript)

Здесь мы создаем виджет Spinbox и передаем параметры from_ и to, чтобы указать диапазон чисел для Spinbox.

Кроме того, вы можете указать ширину виджета с помощью параметра width:

spin = Spinbox(window, from_=0, to=100, width=5)

Code language: JavaScript (javascript)

Давайте посмотрим весь пример:

from tkinter import * from tkinter import messagebox window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') spin = Spinbox(window, from_=0, to=100, width=5) spin.grid(column=0,row=0) window.mainloop()

Code language: JavaScript (javascript)

Вы можете указать числа для Spinbox вместо использования всего диапазона, например, так:

spin = Spinbox(window, values=(3, 8, 11), width=5)

Code language: JavaScript (javascript)

В результате виджет Spinbox покажет только 3 числа: 3, 8 и 11.

Установка значения по умолчанию для Spinbox

Чтобы установить значение по умолчанию для Spinbox, вы можете передать его в параметр textvariable, как показано ниже:

var =IntVar() var.set(36) spin = Spinbox(window, from_=0, to=100, width=5, textvariable=var)

Code language: JavaScript (javascript)

Теперь, если вы запустите программу, Spinbox отобразит значение по умолчанию 36.

Добавление виджета Progressbar

Чтобы создать прогресс-бар, вы можете использовать класс progressbar, как показано ниже:

from tkinter.ttk import Progressbar bar = Progressbar(window, length=200)

Code language: JavaScript (javascript)

Значение шкалы можно задать следующим образом:

bar['value'] = 70

Code language: JavaScript (javascript)

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

Изменение цвета Progressbar

Изменение цвета прогрессбара сначала кажется немного сложным, но на самом деле это очень просто.

Сперва мы создадим стиль, затем в этом стиле установим цвет фона, и, наконец, присвоим стиль Progressbar’у.

Посмотрите следующий пример:

from tkinter import * from tkinter.ttk import Progressbar from tkinter import ttk window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') style = ttk.Style() style.theme_use('default') style.configure("black.Horizontal.TProgressbar", background='black') bar = Progressbar(window, length=200, style='black.Horizontal.TProgressbar') bar['value'] = 70 bar.grid(column=0, row=0) window.mainloop()

Code language: JavaScript (javascript)

Результат будет выглядеть так:

Окно добавления файла (выбор файла и каталога).

Для создания такого окна вы можете использовать класс filedialog, как показано ниже:

from tkinter import filedialog file = filedialog.askopenfilename()

Code language: JavaScript (javascript)

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

Вы также можете выбрать несколько файлов:

files = filedialog.askopenfilenames()

Указание типов файлов (фильтр по расширению)

Вы можете указать типы файлов с помощью параметра filetypes, который задает расширение в кортежах.

file = filedialog.askopenfilename(filetypes = (("Text files","*.txt"),("all files","*.*")))

Code language: JavaScript (javascript)

Директорию можно получить с помощью метода askdirectory:

dir = filedialog.askdirectory()

Вы можете указать начальный каталог, указав initialdir:

from os import path file = filedialog.askopenfilename(initialdir= path.dirname(__file__))

Code language: JavaScript (javascript)

Добавление меню

Чтобы добавить меню, вы можете использовать класс menu:

from tkinter import Menu menu = Menu(window) menu.add_command(label='Файл') window.config(menu=menu)

Code language: JavaScript (javascript)

Сначала мы создадим меню. Затем мы добавляем нашу первую метку. После этого мы назначаем меню окну.

Вы можете добавить пункты в любое меню с помощью функции add_cascade(), например, так:

menu.add_cascade(label='Файл', menu=new_item)

Code language: JavaScript (javascript)

Наш код будет выглядеть так:

from tkinter import * from tkinter import Menu window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') menu = Menu(window) new_item = Menu(menu) new_item.add_command(label='Новый') menu.add_cascade(label='Файл', menu=new_item) window.config(menu=menu) window.mainloop()

Code language: JavaScript (javascript)

Вы можете добавить столько пунктов, сколько захотите.

from tkinter import * from tkinter import Menu window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') menu = Menu(window) new_item = Menu(menu) new_item.add_command(label='Новый') new_item.add_separator() new_item.add_command(label='Редактировать') menu.add_cascade(label='Файл', menu=new_item) window.config(menu=menu) window.mainloop()

Code language: JavaScript (javascript)

Здесь мы добавим еще один пункт меню под названием “Редактировать” с разделителем.

Вы можете отключить пунктирную линию в верхней части выпадающего списка:

new_item = Menu(menu, tearoff=0)

Вы можете написать любой код, который срабатывает при нажатии пользователем на любой пункт меню, указав свойство command.

new_item.add_command(label='Новый', command=clicked)

Code language: JavaScript (javascript)

Добавление виджета Notebook (элемент управления вкладками)

Чтобы создать блок управления вкладками, необходимо выполнить три шага.

  • Сначала создадим блок управления вкладками с помощью класса Notebook
  • Создадим вкладку с помощью класса Frame.
  • Добавим вкладку в блок управления вкладками.
  • Расположим блок управления вкладками так, чтобы он отображался в окне.

from tkinter import * from tkinter import ttk window = Tk() window.title("Добро пожаловать!") window.geometry('350x200') tab_control = ttk.Notebook(window) tab1 = ttk.Frame(tab_control) tab_control.add(tab1, text='Первая вкладка') tab_control.pack(expand=1, fill='both') window.mainloop()

Code language: JavaScript (javascript)

Вы можете добавить столько вкладок, сколько захотите.

Добавление виджетов на вкладки

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

Добавление отступов у виджетов (spacing или padding)

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

Просто передайте padx и pady любому виджету и присвойте им значение.

lbl1 = Label(tab1, text= 'Метка', padx=5, pady=5)

Code language: JavaScript (javascript)

Все просто!

В этом руководстве мы рассмотрели множество примеров графических интерфейсов Python, использующих библиотеку Tkinter, и увидели, насколько просто разрабатывать GUI с ее помощью.

Надеюсь, эти примеры будут вам полезны.

Python has a lot of GUI frameworks, but Tkinter is the only framework that’s built into the Python standard library. Tkinter has several strengths. It’s cross-platform, so the same code works on Windows, macOS, and Linux. Visual elements are rendered using native operating system elements, so applications built with Tkinter look like they belong on the platform where they’re run.

Although Tkinter is considered the de facto Python GUI framework, it’s not without criticism. One notable criticism is that GUIs built with Tkinter look outdated. If you want a shiny, modern interface, then Tkinter may not be what you’re looking for.

However, Tkinter is lightweight and relatively painless to use compared to other frameworks. This makes it a compelling choice for building GUI applications in Python, especially for applications where a modern sheen is unnecessary, and the top priority is to quickly build something that’s functional and cross-platform.

In this tutorial, you’ll learn how to:

  • Get started with Tkinter with a Hello, World application
  • Work with widgets, such as buttons and text boxes
  • Control your application layout with geometry managers
  • Make your applications interactive by associating button clicks with Python functions

Once you’ve mastered these skills by working through the exercises at the end of each section, you’ll tie everything together by building two applications. The first is a temperature converter, and the second is a text editor. It’s time to dive right in and learn how to build an application with Tkinter!

Building Your First Python GUI Application With Tkinter

The foundational element of a Tkinter GUI is the window. Windows are the containers in which all other GUI elements live. These other GUI elements, such as text boxes, labels, and buttons, are known as widgets. Widgets are contained inside of windows.

First, create a window that contains a single widget. Start up a new Python shell session and follow along!

With your Python shell open, the first thing you need to do is import the Python GUI Tkinter module:

>>>

>>> import tkinter as tk

A window is an instance of Tkinter’s Tk class. Go ahead and create a new window and assign it to the variable window:

When you execute the above code, a new window pops up on your screen. How it looks depends on your operating system:

A blank Tkinter application window on Windows 10, macOS, and Ubuntu Linux

Throughout the rest of this tutorial, you’ll see Windows screenshots.

Adding a Widget

Now that you have a window, you can add a widget. Use the tk.Label class to add some text to a window. Create a Label widget with the text "Hello, Tkinter" and assign it to a variable called greeting:

>>>

>>> greeting = tk.Label(text="Hello, Tkinter")

The window you created earlier doesn’t change. You just created a Label widget, but you haven’t added it to the window yet. There are several ways to add widgets to a window. Right now, you can use the Label widget’s .pack() method:

The window now looks like this:

Example "Hello, world" Tkinter application on Windows 10

When you pack a widget into a window, Tkinter sizes the window as small as it can be while still fully encompassing the widget. Now execute the following:

Nothing seems to happen, but notice that no new prompt appears in the shell.

window.mainloop() tells Python to run the Tkinter event loop. This method listens for events, such as button clicks or keypresses, and blocks any code that comes after it from running until you close the window where you called the method. Go ahead and close the window you’ve created, and you’ll see a new prompt displayed in the shell.

Creating a window with Tkinter only takes a couple of lines of code. But blank windows aren’t very useful! In the next section, you’ll learn about some of the widgets available in Tkinter, and how you can customize them to meet your application’s needs.

Check Your Understanding

Expand the code blocks below to check your understanding:

Write a full Python script that creates a Tkinter window with the text "Python rocks!".

The window should look like this:

A Tkinter window containing the text "Python rocks!"

Try this exercise now.

You can expand the code block below to see a solution:

Here’s one possible solution:

import tkinter as tk

window = tk.Tk()
label = tk.Label(text="Python rocks!")
label.pack()

window.mainloop()

Keep in mind your code may look different.

When you’re ready, you can move on to the next section.

Working With Widgets

Widgets are the bread and butter of the Python GUI framework Tkinter. They’re the elements through which users interact with your program. Each widget in Tkinter is defined by a class. Here are some of the widgets available:

Widget Class Description
Label A widget used to display text on the screen
Button A button that can contain text and can perform an action when clicked
Entry A text entry widget that allows only a single line of text
Text A text entry widget that allows multiline text entry
Frame A rectangular region used to group related widgets or provide padding between widgets

You’ll see how to work with each of these in the following sections, but keep in mind that Tkinter has many more widgets than those listed here. The widget’s choice gets even more complicated when you account for a whole new set of themed widgets. In the remaining part of this tutorial, you’re only going to use Tkinter’s classic widgets, though.

If you’d like to learn more about the two widget types, then you can expand the collapsible section below:

It’s worth noting that there are currently two broad categories of widgets in Tkinter:

  1. Classic widgets: Available in the tkinter package, for example tkinter.Label
  2. Themed widgets: Available in the ttk submodule, for example tkinter.ttk.Label

Tkinter’s classic widgets are highly customizable and straightforward, but they tend to appear dated or somewhat foreign on most platforms today. If you’d like to take advantage of widgets with a native look and feel familiar to users of a given operating system, then you might want to check out the themed widgets.

Most of the themed widgets are near drop-in replacements for their legacy counterparts, but with a more modern look. You can also use a few brand-new widgets, such as the progress bar, which weren’t available in Tkinter before. At the same time, you’ll need to continue using some of the classic widgets that don’t have a themed alternative.

When working with regular and themed widgets in Tkinter, it’s customary to declare the following aliases for the Tkinter packages and modules:

>>>

>>> import tkinter as tk
>>> import tkinter.ttk as ttk

Aliases like this let you explicitly refer to either tk.Label or ttk.Label, for example, in one program depending on your needs:

>>>

>>> tk.Label()
<tkinter.Label object .!label>

>>> ttk.Label()
<tkinter.ttk.Label object .!label2>

However, you may sometimes find it more convenient to use a wildcard import (*) to automatically override all legacy widgets with the themed ones where possible, like so:

>>>

>>> from tkinter import *
>>> from tkinter.ttk import *

>>> Label()
<tkinter.ttk.Label object .!label>

>>> Text()
<tkinter.Text object .!text>

Now, you don’t have to prefix the widget’s class name with its corresponding Python module. You’ll always create a themed widget as long as it’s available, or you’ll fall back to the classic widget otherwise. The two import statements above must be placed in the specified order to have an effect. Because of that, wildcard imports are considered a bad practice, which should generally be avoided unless used consciously.

For a full list of Tkinter widgets, check out Basic Widgets and More Widgets in the TkDocs tutorial. Even though it describes themed widgets introduced in Tcl/Tk 8.5, most of the information there should also apply to the classic widgets.

For now, take a closer look at the Label widget.

Displaying Text and Images With Label Widgets

Label widgets are used to display text or images. The text displayed by a Label widget can’t be edited by the user. It’s for display purposes only. As you saw in the example at the beginning of this tutorial, you can create a Label widget by instantiating the Label class and passing a string to the text parameter:

label = tk.Label(text="Hello, Tkinter")

Label widgets display text with the default system text color and the default system text background color. These are typically black and white, respectively, but you may see different colors if you’ve changed these settings in your operating system.

You can control Label text and background colors using the foreground and background parameters:

label = tk.Label(
    text="Hello, Tkinter",
    foreground="white",  # Set the text color to white
    background="black"  # Set the background color to black
)

There are numerous valid color names, including:

  • "red"
  • "orange"
  • "yellow"
  • "green"
  • "blue"
  • "purple"

Many of the HTML color names work with Tkinter. For a full reference, including macOS- and Windows-specific system colors that the current system theme controls, check out the colors manual page.

You can also specify a color using hexadecimal RGB values:

label = tk.Label(text="Hello, Tkinter", background="#34A2FE")

This sets the label background to a nice, light blue color. Hexadecimal RGB values are more cryptic than named colors, but they’re also more flexible. Fortunately, there are tools available that make getting hexadecimal color codes relatively painless.

If you don’t feel like typing out foreground and background all the time, then you can use the shorthand fg and bg parameters to set the foreground and background colors:

label = tk.Label(text="Hello, Tkinter", fg="white", bg="black")

You can also control the width and height of a label with the width and height parameters:

label = tk.Label(
    text="Hello, Tkinter",
    fg="white",
    bg="black",
    width=10,
    height=10
)

Here’s what this label looks like in a window:

A Tkinter window containing a button with a black background and white text that reads "Hello, Tkinter"

It may seem strange that the label in the window isn’t square even though the width and height are both set to 10. This is because the width and height are measured in text units. One horizontal text unit is determined by the width of the character 0, or the number zero, in the default system font. Similarly, one vertical text unit is determined by the height of the character 0.

Labels are great for displaying some text, but they don’t help you get input from a user. The next three widgets that you’ll learn about are all used to get user input.

Displaying Clickable Buttons With Button Widgets

Button widgets are used to display clickable buttons. You can configure them to call a function whenever they’re clicked. You’ll cover how to call functions from button clicks in the next section. For now, take a look at how to create and style a button.

There are many similarities between Button and Label widgets. In many ways, a button is just a label that you can click! The same keyword arguments that you use to create and style a Label will work with Button widgets. For example, the following code creates a button with a blue background and yellow text. It also sets the width and height to 25 and 5 text units, respectively:

button = tk.Button(
    text="Click me!",
    width=25,
    height=5,
    bg="blue",
    fg="yellow",
)

Here’s what the button looks like in a window:

A Tkinter window containing a button with a blue background and yellow text that reads "Click me!"

Pretty nifty! You can use the next two widgets to collect text input from a user.

Getting User Input With Entry Widgets

When you need to get a little bit of text from a user, like a name or an email address, use an Entry widget. It’ll display a small text box that the user can type some text into. Creating and styling an Entry widget works pretty much exactly like with Label and Button widgets. For example, the following code creates a widget with a blue background, some yellow text, and a width of 50 text units:

entry = tk.Entry(fg="yellow", bg="blue", width=50)

The interesting bit about Entry widgets isn’t how to style them, though. It’s how to use them to get input from a user. There are three main operations that you can perform with Entry widgets:

  1. Retrieving text with .get()
  2. Deleting text with .delete()
  3. Inserting text with .insert()

The best way to get an understanding of Entry widgets is to create one and interact with it. Open up a Python shell and follow along with the examples in this section. First, import tkinter and create a new window:

>>>

>>> import tkinter as tk
>>> window = tk.Tk()

Now create a Label and an Entry widget:

>>>

>>> label = tk.Label(text="Name")
>>> entry = tk.Entry()

The Label describes what sort of text should go in the Entry widget. It doesn’t enforce any sort of requirements on the Entry, but it tells the user what your program expects them to put there. You need to .pack() the widgets into the window so that they’re visible:

>>>

>>> label.pack()
>>> entry.pack()

Here’s what that looks like:

A Tkinter window containing an Entry widget withe Label "Name"

Notice that Tkinter automatically centers the label above the Entry widget in the window. This is a feature of .pack(), which you’ll learn more about in later sections.

Click inside the Entry widget with your mouse and type Real Python:

A Tkinter window containing an Entry widget with the text "Real Python"

Now you’ve got some text entered into the Entry widget, but that text hasn’t been sent to your program yet. You can use .get() to retrieve the text and assign it to a variable called name:

>>>

>>> name = entry.get()
>>> name
'Real Python'

You can delete text as well. This .delete() method takes an integer argument that tells Python which character to remove. For example, the code block below shows how .delete(0) deletes the first character from Entry:

The text remaining in the widget is now eal Python:

A Tkinter window containing an Entry widget with the text "eal Python"

Note that, just like Python string objects, text in an Entry widget is indexed starting with 0.

If you need to remove several characters from an Entry, then pass a second integer argument to .delete() indicating the index of the character where deletion should stop. For example, the following code deletes the first four letters in Entry:

>>>

>>> entry.delete(0, 4)

The remaining text now reads Python:

A Tkinter window containing an Entry widget with the text "Python"

Entry.delete() works just like string slicing. The first argument determines the starting index, and the deletion continues up to but not including the index passed as the second argument. Use the special constant tk.END for the second argument of .delete() to remove all text in Entry:

>>>

>>> entry.delete(0, tk.END)

You’ll now see a blank text box:

A Tkinter window containing an Entry widget withe Label "Name"

On the opposite end of the spectrum, you can also insert text into an Entry widget:

>>>

>>> entry.insert(0, "Python")

The window now looks like this:

A Tkinter window containing an Entry widget with the text "Python"

The first argument tells .insert() where to insert the text. If there’s no text in Entry, then the new text will always be inserted at the beginning of the widget, no matter what value you pass as the first argument. For example, calling .insert() with 100 as the first argument instead of 0, as you did above, would’ve generated the same output.

If Entry already contains some text, then .insert() will insert the new text at the specified position and shift all existing text to the right:

>>>

>>> entry.insert(0, "Real ")

The widget text now reads Real Python:

A Tkinter window containing an Entry widget with the text "Real Python"

Entry widgets are great for capturing small amounts of text from a user, but because they’re only displayed on a single line, they’re not ideal for gathering large amounts of text. That’s where Text widgets come in!

Getting Multiline User Input With Text Widgets

Text widgets are used for entering text, just like Entry widgets. The difference is that Text widgets may contain multiple lines of text. With a Text widget, a user can input a whole paragraph or even several pages of text! Just like with Entry widgets, you can perform three main operations with Text widgets:

  1. Retrieve text with .get()
  2. Delete text with .delete()
  3. Insert text with .insert()

Although the method names are the same as the Entry methods, they work a bit differently. It’s time to get your hands dirty by creating a Text widget and seeing what it can do.

In your Python shell, create a new blank window and pack a Text() widget into it:

>>>

>>> window = tk.Tk()
>>> text_box = tk.Text()
>>> text_box.pack()

Text boxes are much larger than Entry widgets by default. Here’s what the window created above looks like:

A Tkinter window containing a Text Box widget

Click anywhere inside the window to activate the text box. Type in the word Hello. Then press Enter and type World on the second line. The window should now look like this:

A Tkinter window containing a Text Box widget with the text "Hello World"

Just like with Entry widgets, you can retrieve the text from a Text widget using .get(). However, calling .get() with no arguments doesn’t return the full text in the text box like it does for Entry widgets. It raises an exception:

>>>

>>> text_box.get()
Traceback (most recent call last):
  ...
TypeError: get() missing 1 required positional argument: 'index1'

Text.get() requires at least one argument. Calling .get() with a single index returns a single character. To retrieve several characters, you need to pass a start index and an end index. Indices in Text widgets work differently than in Entry widgets. Since Text widgets can have several lines of text, an index must contain two pieces of information:

  1. The line number of a character
  2. The position of a character on that line

Line numbers start with 1, and character positions start with 0. To make an index, you create a string of the form "<line>.<char>", replacing <line> with the line number and <char> with the character number. For example, "1.0" represents the first character on the first line, and "2.3" represents the fourth character on the second line.

Use the index "1.0" to get the first letter from the text box that you created earlier:

>>>

>>> text_box.get("1.0")
'H'

There are five letters in the word Hello, and the character number of o is 4, since character numbers start from 0, and the word Hello starts at the first position in the text box. Just like with Python string slices, in order to get the entire word Hello from the text box, the end index must be one more than the index of the last character to be read.

So, to get the word Hello from the text box, use "1.0" for the first index and "1.5" for the second index:

>>>

>>> text_box.get("1.0", "1.5")
'Hello'

To get the word World on the second line of the text box, change the line numbers in each index to 2:

>>>

>>> text_box.get("2.0", "2.5")
'World'

To get all of the text in a text box, set the starting index in "1.0" and use the special tk.END constant for the second index:

>>>

>>> text_box.get("1.0", tk.END)
'HellonWorldn'

Notice that text returned by .get() includes any newline characters. You can also see from this example that every line in a Text widget has a newline character at the end, including the last line of text in the text box.

.delete() is used to delete characters from a text box. It works just like .delete() for Entry widgets. There are two ways to use .delete():

  1. With a single argument
  2. With two arguments

Using the single-argument version, you pass to .delete() the index of a single character to be deleted. For example, the following deletes the first character, H, from the text box:

>>>

>>> text_box.delete("1.0")

The first line of text in the window now reads ello:

A Tkinter window containing a Text Box widget with the text "ello World"

With the two-argument version, you pass two indices to delete a range of characters starting at the first index and up to, but not including, the second index.

For example, to delete the remaining ello on the first line of the text box, use the indices "1.0" and "1.4":

>>>

>>> text_box.delete("1.0", "1.4")

Notice that the text is gone from the first line. This leaves a blank line followed the word World on the second line:

A Tkinter window containing a Text Box widget with a blank first line and the text "World" on the second line

Even though you can’t see it, there’s still a character on the first line. It’s a newline character! You can verify this using .get():

>>>

>>> text_box.get("1.0")
'n'

If you delete that character, then the rest of the contents of the text box will shift up a line:

>>>

>>> text_box.delete("1.0")

Now, World is on the first line of the text box:

A Tkinter window containing a Text Box widget with the text "World"

Try to clear out the rest of the text in the text box. Set "1.0" as the start index and use tk.END for the second index:

>>>

>>> text_box.delete("1.0", tk.END)

The text box is now empty:

A Tkinter window containing a Text Box widget

You can insert text into a text box using .insert():

>>>

>>> text_box.insert("1.0", "Hello")

This inserts the word Hello at the beginning of the text box, using the same "<line>.<column>" format used by .get() to specify the insertion position:

A Tkinter window containing a Text Box widget with the text "Hello"

Check out what happens if you try to insert the word World on the second line:

>>>

>>> text_box.insert("2.0", "World")

Instead of inserting the text on the second line, the text is inserted at the end of the first line:

A Tkinter window containing a Text Box widget with the text "HelloWorld"

If you want to insert text onto a new line, then you need to insert a newline character manually into the string being inserted:

>>>

>>> text_box.insert("2.0", "nWorld")

Now World is on the second line of the text box:

A Tkinter window containing a Text Box widget with the text "Hello World"

.insert() will do one of two things:

  1. Insert text at the specified position if there’s already text at or after that position.
  2. Append text to the specified line if the character number is greater than the index of the last character in the text box.

It’s usually impractical to try and keep track of what the index of the last character is. The best way to insert text at the end of a Text widget is to pass tk.END to the first parameter of .insert():

>>>

>>> text_box.insert(tk.END, "Put me at the end!")

Don’t forget to include the newline character (n) at the beginning of the text if you want to put it on a new line:

>>>

>>> text_box.insert(tk.END, "nPut me on a new line!")

Label, Button, Entry, and Text widgets are just a few of the widgets available in Tkinter. There are several others, including widgets for checkboxes, radio buttons, scroll bars, and progress bars. For more information on all of the available widgets, see the Additional Widgets list in the Additional Resources section.

Assigning Widgets to Frames With Frame Widgets

In this tutorial, you’re going to work with only five widgets:

  1. Label
  2. Button
  3. Entry
  4. Text
  5. Frame

These are the four you’ve seen so far plus the Frame widget. Frame widgets are important for organizing the layout of your widgets in an application.

Before you get into the details about laying out the visual presentation of your widgets, take a closer look at how Frame widgets work, and how you can assign other widgets to them. The following script creates a blank Frame widget and assigns it to the main application window:

import tkinter as tk

window = tk.Tk()
frame = tk.Frame()
frame.pack()

window.mainloop()

frame.pack() packs the frame into the window so that the window sizes itself as small as possible to encompass the frame. When you run the above script, you get some seriously uninteresting output:

A Tkinter window containing an empty Frame widget

An empty Frame widget is practically invisible. Frames are best thought of as containers for other widgets. You can assign a widget to a frame by setting the widget’s master attribute:

frame = tk.Frame()
label = tk.Label(master=frame)

To get a feel for how this works, write a script that creates two Frame widgets called frame_a and frame_b. In this script, frame_a contains a label with the text "I'm in Frame A", and frame_b contains the label "I'm in Frame B". Here’s one way to do this:

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()
frame_b = tk.Frame()

label_a = tk.Label(master=frame_a, text="I'm in Frame A")
label_a.pack()

label_b = tk.Label(master=frame_b, text="I'm in Frame B")
label_b.pack()

frame_a.pack()
frame_b.pack()

window.mainloop()

Note that frame_a is packed into the window before frame_b. The window that opens shows the label in frame_a above the label in frame_b:

A Tkinter window containg two Frame widgets stacked vertically, with the text "I'm in Frame A" in the top Frame, and "I'm in Frame B" in the bottom Frame

Now see what happens when you swap the order of frame_a.pack() and frame_b.pack():

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()
label_a = tk.Label(master=frame_a, text="I'm in Frame A")
label_a.pack()

frame_b = tk.Frame()
label_b = tk.Label(master=frame_b, text="I'm in Frame B")
label_b.pack()

# Swap the order of `frame_a` and `frame_b`
frame_b.pack()
frame_a.pack()

window.mainloop()

The output looks like this:

A Tkinter window containg two Frame widgets stacked vertically, with the text "I'm in Frame B" in the top Frame, and "I'm in Frame A" in the bottom Frame

Now label_b is on top. Since label_b is assigned to frame_b, it moves to wherever frame_b is positioned.

All four of the widget types that you’ve learned about—Label, Button, Entry, and Text—have a master attribute that’s set when you instantiate them. That way, you can control which Frame a widget is assigned to. Frame widgets are great for organizing other widgets in a logical manner. Related widgets can be assigned to the same frame so that, if the frame is ever moved in the window, then the related widgets stay together.

In addition to grouping your widgets logically, Frame widgets can add a little flare to the visual presentation of your application. Read on to see how to create various borders for Frame widgets.

Adjusting Frame Appearance With Reliefs

Frame widgets can be configured with a relief attribute that creates a border around the frame. You can set relief to be any of the following values:

  • tk.FLAT: Has no border effect (the default value)
  • tk.SUNKEN: Creates a sunken effect
  • tk.RAISED: Creates a raised effect
  • tk.GROOVE: Creates a grooved border effect
  • tk.RIDGE: Creates a ridged effect

To apply the border effect, you must set the borderwidth attribute to a value greater than 1. This attribute adjusts the width of the border in pixels. The best way to get a feel for what each effect looks like is to see them for yourself. Here’s a script that packs five Frame widgets into a window, each with a different value for the relief argument:

 1import tkinter as tk
 2
 3border_effects = {
 4    "flat": tk.FLAT,
 5    "sunken": tk.SUNKEN,
 6    "raised": tk.RAISED,
 7    "groove": tk.GROOVE,
 8    "ridge": tk.RIDGE,
 9}
10
11window = tk.Tk()
12
13for relief_name, relief in border_effects.items():
14    frame = tk.Frame(master=window, relief=relief, borderwidth=5)
15    frame.pack(side=tk.LEFT)
16    label = tk.Label(master=frame, text=relief_name)
17    label.pack()
18
19window.mainloop()

Here’s a breakdown of this script:

  • Lines 3 to 9 create a dictionary whose keys are the names of the different relief effects available in Tkinter. The values are the corresponding Tkinter objects. This dictionary is assigned to the border_effects variable.

  • Line 13 starts a for loop to loop over each item in the border_effects dictionary.

  • Line 14 creates a new Frame widget and assigns it to the window object. The relief attribute is set to the corresponding relief in the border_effects dictionary, and the border attribute is set to 5 so that the effect is visible.

  • Line 15 packs the Frame into the window using .pack(). The side keyword argument tells Tkinter in which direction to pack the frame objects. You’ll see more about how this works in the next section.

  • Lines 16 and 17 create a Label widget to display the name of the relief and pack it into the frame object you just created.

The window produced by the above script looks like this:

A Tkinter window containing 5 Frame widgets, each with one of the five relief values: tk.FLAT, tk.SUNKET, tk.RAISED, tk.GROOVE, and tk.RIDGE

In this image, you can see the following effects:

  • tk.FLAT creates a frame that appears to be flat.
  • tk.SUNKEN adds a border that gives the frame the appearance of being sunken into the window.
  • tk.RAISED gives the frame a border that makes it appear to stick out from the screen.
  • tk.GROOVE adds a border that appears as a sunken groove around an otherwise flat frame.
  • tk.RIDGE gives the appearance of a raised lip around the edge of the frame.

These effects give your Python GUI Tkinter application a bit of visual appeal.

Understanding Widget Naming Conventions

When you create a widget, you can give it any name you like, as long as it’s a valid Python identifier. It’s usually a good idea to include the name of the widget class in the variable name that you assign to the widget instance. For example, if a Label widget is used to display a user’s name, then you might name the widget label_user_name. An Entry widget used to collect a user’s age might be called entry_age.

When you include the widget class name in the variable name, you help yourself and anyone else who needs to read your code to understand what type of widget the variable name refers to. However, using the full name of the widget class can lead to long variable names, so you may want to adopt a shorthand for referring to each widget type. For the rest of this tutorial, you’ll use the following shorthand prefixes to name widgets:

Widget Class Variable Name Prefix Example
Label lbl lbl_name
Button btn btn_submit
Entry ent ent_age
Text txt txt_notes
Frame frm frm_address

In this section, you learned how to create a window, use widgets, and work with frames. At this point, you can make some plain windows that display messages, but you’ve yet to create a full-blown application. In the next section, you’ll learn how to control the layout of your applications using Tkinter’s powerful geometry managers.

Check Your Understanding

Expand the code block below for an exercise to check your understanding:

Write a complete script that displays an Entry widget that’s 40 text units wide and has a white background and black text. Use .insert() to display text in the widget that reads What is your name?.

The output window should look like this:

A Tkinter window containing an Entry widget with the text "What is your name?"

Try this exercise now.

You can expand the code block below to see a solution:

There are a couple of ways to solve this exercise. Here’s one solution that uses the bg and fg parameters to set the Entry widget’s background and foreground colors:

import tkinter as tk

window = tk.Tk()

entry = tk.Entry(width=40, bg="white", fg="black")
entry.pack()

entry.insert(0, "What is your name?")

window.mainloop()

This solution is great because it explicitly sets the background and foreground colors for the Entry widget.

On most systems, the default background color for an Entry widget is white, and the default foreground color is black. So, you might be able to generate the same window with the bg and fg parameters left out:

import tkinter as tk

window = tk.Tk()

entry = tk.Entry(width=40)
entry.pack()

entry.insert(0, "What is your name?")

window.mainloop()

Keep in mind your code may look different.

When you’re ready, you can move on to the next section.

Controlling Layout With Geometry Managers

Up until now, you’ve been adding widgets to windows and Frame widgets using .pack(), but you haven’t learned what exactly this method does. Let’s clear things up! Application layout in Tkinter is controlled with geometry managers. While .pack() is an example of a geometry manager, it isn’t the only one. Tkinter has two others:

  • .place()
  • .grid()

Each window or Frame in your application can use only one geometry manager. However, different frames can use different geometry managers, even if they’re assigned to a frame or window using another geometry manager. Start by taking a closer look at .pack().

The .pack() Geometry Manager

The .pack() geometry manager uses a packing algorithm to place widgets in a Frame or window in a specified order. For a given widget, the packing algorithm has two primary steps:

  1. Compute a rectangular area called a parcel that’s just tall (or wide) enough to hold the widget and fills the remaining width (or height) in the window with blank space.
  2. Center the widget in the parcel unless a different location is specified.

.pack() is powerful, but it can be difficult to visualize. The best way to get a feel for .pack() is to look at some examples. See what happens when you .pack() three Label widgets into a Frame:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=100, height=100, bg="red")
frame1.pack()

frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow")
frame2.pack()

frame3 = tk.Frame(master=window, width=25, height=25, bg="blue")
frame3.pack()

window.mainloop()

.pack() places each Frame below the previous one by default, in the order that they’re assigned to the window:

A Tkinter window with three colored squares packed vertically

Each Frame is placed at the topmost available position. Therefore, the red Frame is placed at the top of the window. Then the yellow Frame is placed just below the red one and the blue Frame just below the yellow one.

There are three invisible parcels, each containing one of the three Frame widgets. Each parcel is as wide as the window and as tall as the Frame that it contains. Because no anchor point was specified when .pack() was called for each Frame, they’re all centered inside of their parcels. That’s why each Frame is centered in the window.

.pack() accepts some keyword arguments for more precisely configuring widget placement. For example, you can set the fill keyword argument to specify in which direction the frames should fill. The options are tk.X to fill in the horizontal direction, tk.Y to fill vertically, and tk.BOTH to fill in both directions. Here’s how you would stack the three frames so that each one fills the whole window horizontally:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, height=100, bg="red")
frame1.pack(fill=tk.X)

frame2 = tk.Frame(master=window, height=50, bg="yellow")
frame2.pack(fill=tk.X)

frame3 = tk.Frame(master=window, height=25, bg="blue")
frame3.pack(fill=tk.X)

window.mainloop()

Notice that the width is not set on any of the Frame widgets. width is no longer necessary because each frame sets .pack() to fill horizontally, overriding any width you may set.

The window produced by this script looks like this:

A Tkinter window containing three colored frames packed vertically and expanded horizontally to fill the entire window

One of the nice things about filling the window with .pack() is that the fill is responsive to window resizing. Try widening the window generated by the previous script to see how this works. As you widen the window, the width of the three Frame widgets grow to fill the window:

A Tkinter window that expands horizontally with window resizing

Notice, though, that the Frame widgets don’t expand in the vertical direction.

The side keyword argument of .pack() specifies on which side of the window the widget should be placed. These are the available options:

  • tk.TOP
  • tk.BOTTOM
  • tk.LEFT
  • tk.RIGHT

If you don’t set side, then .pack() will automatically use tk.TOP and place new widgets at the top of the window, or at the topmost portion of the window that isn’t already occupied by a widget. For example, the following script places three frames side by side from left to right and expands each frame to fill the window vertically:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg="red")
frame1.pack(fill=tk.Y, side=tk.LEFT)

frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.Y, side=tk.LEFT)

frame3 = tk.Frame(master=window, width=50, bg="blue")
frame3.pack(fill=tk.Y, side=tk.LEFT)

window.mainloop()

This time, you have to specify the height keyword argument on at least one of the frames to force the window to have some height.

The resulting window looks like this:

A Tkinter window containing three colored frames packed horizontally and expanded vertically to fill the entire window

Just like when you set fill=tk.X to make the frames responsive when you resized the window horizontally, you can set fill=tk.Y to make the frames responsive when you resize the window vertically:

A Tkinter window that expands vertically with window resizing

To make the layout truly responsive, you can set an initial size for your frames using the width and height attributes. Then, set the fill keyword argument of .pack() to tk.BOTH and set the expand keyword argument to True:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg="red")
frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame3 = tk.Frame(master=window, width=50, bg="blue")
frame3.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

window.mainloop()

When you run the above script, you’ll see a window that initially looks the same as the one you generated in the previous example. The difference is that now you can resize the window however you want, and the frames will expand and fill the window responsively:

A Tkinter window that expands both horizontally and vertically with window resizing

Pretty cool!

The .place() Geometry Manager

You can use .place() to control the precise location that a widget should occupy in a window or Frame. You must provide two keyword arguments, x and y, which specify the x- and y-coordinates for the top-left corner of the widget. Both x and y are measured in pixels, not text units.

Keep in mind that the origin, where x and y are both 0, is the top-left corner of the Frame or window. So, you can think of the y argument of .place() as the number of pixels from the top of the window, and the x argument as the number of pixels from the left edge of the window.

Here’s an example of how the .place() geometry manager works:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4
 5frame = tk.Frame(master=window, width=150, height=150)
 6frame.pack()
 7
 8label1 = tk.Label(master=frame, text="I'm at (0, 0)", bg="red")
 9label1.place(x=0, y=0)
10
11label2 = tk.Label(master=frame, text="I'm at (75, 75)", bg="yellow")
12label2.place(x=75, y=75)
13
14window.mainloop()

Here’s how this code works:

  • Lines 5 and 6 create a new Frame widget called frame, measuring 150 pixels wide and 150 pixels tall, and pack it into the window with .pack().
  • Lines 8 and 9 create a new Label called label1 with a red background and place it in frame1 at position (0, 0).
  • Lines 11 and 12 create a second Label called label2 with a yellow background and place it in frame1 at position (75, 75).

Here’s the window that the code produces:

A Tkinter window containing two Label widgets laid out using the .place() geometry manager

Note that if you run this code on a different operating system that uses different font sizes and styles, then the second label might become partially obscured by the window’s edge. That’s why .place() isn’t used often. In addition to this, it has two main drawbacks:

  1. Layout can be difficult to manage with .place(). This is especially true if your application has lots of widgets.
  2. Layouts created with .place() aren’t responsive. They don’t change as the window is resized.

One of the main challenges of cross-platform GUI development is making layouts that look good no matter which platform they’re viewed on, and .place() is a poor choice for making responsive and cross-platform layouts.

That’s not to say you should never use .place()! In some cases, it might be just what you need. For example, if you’re creating a GUI interface for a map, then .place() might be the perfect choice to ensure widgets are placed at the correct distance from each other on the map.

.pack() is usually a better choice than .place(), but even .pack() has some downsides. The placement of widgets depends on the order in which .pack() is called, so it can be difficult to modify existing applications without fully understanding the code controlling the layout. The .grid() geometry manager solves a lot of these issues, as you’ll see in the next section.

The .grid() Geometry Manager

The geometry manager you’ll likely use most often is .grid(), which provides all the power of .pack() in a format that’s easier to understand and maintain.

.grid() works by splitting a window or Frame into rows and columns. You specify the location of a widget by calling .grid() and passing the row and column indices to the row and column keyword arguments, respectively. Both row and column indices start at 0, so a row index of 1 and a column index of 2 tells .grid() to place a widget in the third column of the second row.

The following script creates a 3 × 3 grid of frames with Label widgets packed into them:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}nColumn {j}")
        label.pack()

window.mainloop()

Here’s what the resulting window looks like:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them

You’re using two geometry managers in this example. Each frame is attached to window with the .grid() geometry manager:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}nColumn {j}")
        label.pack()

window.mainloop()

Each label is attached to its master Frame with .pack():

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}nColumn {j}")
        label.pack()

window.mainloop()

The important thing to realize here is that even though .grid() is called on each Frame object, the geometry manager applies to the window object. Similarly, the layout of each frame is controlled with the .pack() geometry manager.

The frames in the previous example are placed tightly next to one another. To add some space around each frame, you can set the padding of each cell in the grid. Padding is just some blank space that surrounds a widget and visually sets its content apart.

The two types of padding are external and internal padding. External padding adds some space around the outside of a grid cell. It’s controlled with two keyword arguments to .grid():

  1. padx adds padding in the horizontal direction.
  2. pady adds padding in the vertical direction.

Both padx and pady are measured in pixels, not text units, so setting both of them to the same value will create the same amount of padding in both directions. Try to add some padding around the outside of the frames from the previous example:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}nColumn {j}")
        label.pack()

window.mainloop()

Here’s the resulting window:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them. Each grid cell has 5 pixels of exterior padding.

.pack() also has padx and pady parameters. The following code is nearly identical to the previous code, except that you add five pixels of additional padding around each label in both the x and y directions:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}nColumn {j}")
        label.pack(padx=5, pady=5)

window.mainloop()

The extra padding around the Label widgets gives each cell in the grid a little bit of breathing room between the Frame border and the text in the label:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them. Each grid cell and Label widget has 5 pixels of exterior padding.

That looks pretty nice! But if you try and expand the window in any direction, then you’ll notice that the layout isn’t very responsive:

A Tkinter window containing a 3 x 3 grid that does not expand with window resizing

The whole grid stays at the top-left corner as the window expands.

By using .columnconfigure() and .rowconfigure() on the window object, you can adjust how the rows and columns of the grid grow as the window is resized. Remember, the grid is attached to window, even though you’re calling .grid() on each Frame widget. Both .columnconfigure() and .rowconfigure() take three essential arguments:

  1. Index: The index of the grid column or row that you want to configure or a list of indices to configure multiple rows or columns at the same time
  2. Weight: A keyword argument called weight that determines how the column or row should respond to window resizing, relative to the other columns and rows
  3. Minimum Size: A keyword argument called minsize that sets the minimum size of the row height or column width in pixels

weight is set to 0 by default, which means that the column or row doesn’t expand as the window resizes. If every column or row is given a weight of 1, then they all grow at the same rate. If one column has a weight of 1 and another a weight of 2, then the second column expands at twice the rate of the first. Adjust the previous script to better handle window resizing:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    window.columnconfigure(i, weight=1, minsize=75)
    window.rowconfigure(i, weight=1, minsize=50)

    for j in range(0, 3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}nColumn {j}")
        label.pack(padx=5, pady=5)

window.mainloop()

.columnconfigure() and .rowconfigure() are placed in the body of the outer for loop. You could explicitly configure each column and row outside of the for loop, but that would require writing an additional six lines of code.

On each iteration of the loop, the i-th column and row are configured to have a weight of 1. This ensures that the row and column expand at the same rate whenever the window is resized. The minsize argument is set to 75 for each column and 50 for each row. This ensures that the Label widget always displays its text without chopping off any characters, even if the window size is extremely small.

The result is a grid layout that expands and contracts smoothly as the window is resized:

A Tkinter window containing a fully responsive 3 x 3 grid layout

Try it yourself to get a feel for how it works! Play around with the weight and minsize parameters to see how they affect the grid.

By default, widgets are centered in their grid cells. For example, the following code creates two Label widgets and places them in a grid with one column and two rows:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0)

label2 = tk.Label(text="B")
label2.grid(row=1, column=0)

window.mainloop()

Each grid cell is 250 pixels wide and 100 pixels tall. The labels are placed in the center of each cell, as you can see in the following figure:

A Tkinter window with grid geometry manager and custom row and column sizes

You can change the location of each label inside of the grid cell using the sticky parameter, which accepts a string containing one or more of the following letters:

  • "n" or "N" to align to the top-center part of the cell
  • "e" or "E" to align to the right-center side of the cell
  • "s" or "S" to align to the bottom-center part of the cell
  • "w" or "W" to align to the left-center side of the cell

The letters "n", "s", "e", and "w" come from the cardinal directions north, south, east, and west. Setting sticky to "n" on both labels in the previous code positions each label at the top-center of its grid cell:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0, sticky="n")

label2 = tk.Label(text="B")
label2.grid(row=1, column=0, sticky="n")

window.mainloop()

Here’s the output:

A Tkinter window with grid geometry manager and sticky set to "North"

You can combine multiple letters in a single string to position each label in the corner of its grid cell:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0, sticky="ne")

label2 = tk.Label(text="B")
label2.grid(row=1, column=0, sticky="sw")

window.mainloop()

In this example, the sticky parameter of label1 is set to "ne", which places the label at the top-right corner of its grid cell. label2 is positioned in the bottom-left corner by passing "sw" to sticky. Here’s what that looks like in the window:

A Tkinter window with grid geometry manager and sticky set to "Northeast" and "Southwest"

When a widget is positioned with sticky, the size of the widget itself is just big enough to contain any text and other contents inside of it. It won’t fill the entire grid cell. In order to fill the grid, you can specify "ns" to force the widget to fill the cell in the vertical direction, or "ew" to fill the cell in the horizontal direction. To fill the entire cell, set sticky to "nsew". The following example illustrates each of these options:

import tkinter as tk

window = tk.Tk()

window.rowconfigure(0, minsize=50)
window.columnconfigure([0, 1, 2, 3], minsize=50)

label1 = tk.Label(text="1", bg="black", fg="white")
label2 = tk.Label(text="2", bg="black", fg="white")
label3 = tk.Label(text="3", bg="black", fg="white")
label4 = tk.Label(text="4", bg="black", fg="white")

label1.grid(row=0, column=0)
label2.grid(row=0, column=1, sticky="ew")
label3.grid(row=0, column=2, sticky="ns")
label4.grid(row=0, column=3, sticky="nsew")

window.mainloop()

Here’s what the output looks like:

A Tkinter window with grid geometry manager and sticky used to fill horizontally, vertically, and along both axes.

What the above example illustrates is that the .grid() geometry manager’s sticky parameter can be used to achieve the same effects as the .pack() geometry manager’s fill parameter. The correspondence between the sticky and fill parameters is summarized in the following table:

.grid() .pack()
sticky="ns" fill=tk.Y
sticky="ew" fill=tk.X
sticky="nsew" fill=tk.BOTH

.grid() is a powerful geometry manager. It’s often easier to understand than .pack() and is much more flexible than .place(). When you’re creating new Tkinter applications, you should consider using .grid() as your primary geometry manager.

Now that you’ve got the fundamentals of geometry managers down for the Python GUI framework Tkinter, the next step is to assign actions to buttons to bring your applications to life.

Check Your Understanding

Expand the code block below for an exercise to check your understanding:

Below is an image of an address entry form made with Tkinter:

An address entry form window built with Tkinter

Write a complete script that re-creates the window. You may use any geometry manager you like.

You can expand the code block below to see a solution:

There are many different ways to solve this exercise. If your solution generates a window identical to the one in the exercise statement, then congratulations! You’ve successfully solved the exercise! Below, you can look at two solutions that use the .grid() geometry manager.

One solution creates a Label and Entry widget with the desired settings for each field:

import tkinter as tk

# Create a new window with the title "Address Entry Form"
window = tk.Tk()
window.title("Address Entry Form")

# Create a new frame `frm_form` to contain the Label
# and Entry widgets for entering address information
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
# Pack the frame into the window
frm_form.pack()

# Create the Label and Entry widgets for "First Name"
lbl_first_name = tk.Label(master=frm_form, text="First Name:")
ent_first_name = tk.Entry(master=frm_form, width=50)
# Use the grid geometry manager to place the Label and
# Entry widgets in the first and second columns of the
# first row of the grid
lbl_first_name.grid(row=0, column=0, sticky="e")
ent_first_name.grid(row=0, column=1)

# Create the Label and Entry widgets for "Last Name"
lbl_last_name = tk.Label(master=frm_form, text="Last Name:")
ent_last_name = tk.Entry(master=frm_form, width=50)
# Place the widgets in the second row of the grid
lbl_last_name.grid(row=1, column=0, sticky="e")
ent_last_name.grid(row=1, column=1)

# Create the Label and Entry widgets for "Address Line 1"
lbl_address1 = tk.Label(master=frm_form, text="Address Line 1:")
ent_address1 = tk.Entry(master=frm_form, width=50)
# Place the widgets in the third row of the grid
lbl_address1.grid(row=2, column=0, sticky="e")
ent_address1.grid(row=2, column=1)

# Create the Label and Entry widgets for "Address Line 2"
lbl_address2 = tk.Label(master=frm_form, text="Address Line 2:")
ent_address2 = tk.Entry(master=frm_form, width=50)
# Place the widgets in the fourth row of the grid
lbl_address2.grid(row=3, column=0, sticky=tk.E)
ent_address2.grid(row=3, column=1)

# Create the Label and Entry widgets for "City"
lbl_city = tk.Label(master=frm_form, text="City:")
ent_city = tk.Entry(master=frm_form, width=50)
# Place the widgets in the fifth row of the grid
lbl_city.grid(row=4, column=0, sticky=tk.E)
ent_city.grid(row=4, column=1)

# Create the Label and Entry widgets for "State/Province"
lbl_state = tk.Label(master=frm_form, text="State/Province:")
ent_state = tk.Entry(master=frm_form, width=50)
# Place the widgets in the sixth row of the grid
lbl_state.grid(row=5, column=0, sticky=tk.E)
ent_state.grid(row=5, column=1)

# Create the Label and Entry widgets for "Postal Code"
lbl_postal_code = tk.Label(master=frm_form, text="Postal Code:")
ent_postal_code = tk.Entry(master=frm_form, width=50)
# Place the widgets in the seventh row of the grid
lbl_postal_code.grid(row=6, column=0, sticky=tk.E)
ent_postal_code.grid(row=6, column=1)

# Create the Label and Entry widgets for "Country"
lbl_country = tk.Label(master=frm_form, text="Country:")
ent_country = tk.Entry(master=frm_form, width=50)
# Place the widgets in the eight row of the grid
lbl_country.grid(row=7, column=0, sticky=tk.E)
ent_country.grid(row=7, column=1)

# Create a new frame `frm_buttons` to contain the
# Submit and Clear buttons. This frame fills the
# whole window in the horizontal direction and has
# 5 pixels of horizontal and vertical padding.
frm_buttons = tk.Frame()
frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5)

# Create the "Submit" button and pack it to the
# right side of `frm_buttons`
btn_submit = tk.Button(master=frm_buttons, text="Submit")
btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10)

# Create the "Clear" button and pack it to the
# right side of `frm_buttons`
btn_clear = tk.Button(master=frm_buttons, text="Clear")
btn_clear.pack(side=tk.RIGHT, ipadx=10)

# Start the application
window.mainloop()

There’s nothing wrong with this solution. It’s a bit long, but everything is very explicit. If you want to change something, then it’s clear to see exactly where to do so.

That said, the solution can be considerably shortened by recognizing that each Entry has the same width, and that all you need for each Label is the text:

import tkinter as tk

# Create a new window with the title "Address Entry Form"
window = tk.Tk()
window.title("Address Entry Form")

# Create a new frame `frm_form` to contain the Label
# and Entry widgets for entering address information
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
# Pack the frame into the window
frm_form.pack()

# List of field labels
labels = [
    "First Name:",
    "Last Name:",
    "Address Line 1:",
    "Address Line 2:",
    "City:",
    "State/Province:",
    "Postal Code:",
    "Country:",
]

# Loop over the list of field labels
for idx, text in enumerate(labels):
    # Create a Label widget with the text from the labels list
    label = tk.Label(master=frm_form, text=text)
    # Create an Entry widget
    entry = tk.Entry(master=frm_form, width=50)
    # Use the grid geometry manager to place the Label and
    # Entry widgets in the row whose index is idx
    label.grid(row=idx, column=0, sticky="e")
    entry.grid(row=idx, column=1)

# Create a new frame `frm_buttons` to contain the
# Submit and Clear buttons. This frame fills the
# whole window in the horizontal direction and has
# 5 pixels of horizontal and vertical padding.
frm_buttons = tk.Frame()
frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5)

# Create the "Submit" button and pack it to the
# right side of `frm_buttons`
btn_submit = tk.Button(master=frm_buttons, text="Submit")
btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10)

# Create the "Clear" button and pack it to the
# right side of `frm_buttons`
btn_clear = tk.Button(master=frm_buttons, text="Clear")
btn_clear.pack(side=tk.RIGHT, ipadx=10)

# Start the application
window.mainloop()

In this solution, a list is used to store the strings for each label in the form. They’re stored in the order that each form field should appear. Then, enumerate() gets both the index and string from each value in the labels list.

When you’re ready, you can move on to the next section.

Making Your Applications Interactive

By now, you have a pretty good idea of how to create a window with Tkinter, add some widgets, and control the application layout. That’s great, but applications shouldn’t just look good—they actually need to do something! In this section, you’ll learn how to bring your applications to life by performing actions whenever certain events occur.

Using Events and Event Handlers

When you create a Tkinter application, you must call window.mainloop() to start the event loop. During the event loop, your application checks if an event has occurred. If so, then it’ll execute some code in response.

The event loop is provided for you with Tkinter, so you don’t have to write any code that checks for events yourself. However, you do have to write the code that will be executed in response to an event. In Tkinter, you write functions called event handlers for the events that you use in your application.

You’ll write your own event loop in order to better understand how Tkinter’s event loop works. That way, you can see how Tkinter’s event loop fits into your application, and which parts you need to write yourself.

Assume there’s a list called events that contains event objects. A new event object is automatically appended to events every time an event occurs in your program. You don’t need to implement this updating mechanism. It just automatically happens for you in this conceptual example. Using an infinite loop, you can continually check if there are any event objects in events:

# Assume that this list gets updated automatically
events = []

# Run the event loop
while True:
    # If the event list is empty, then no events have occurred
    # and you can skip to the next iteration of the loop
    if events == []:
        continue

    # If execution reaches this point, then there is at least one
    # event object in the event list
    event = events[0]

Right now, the event loop that you’ve created doesn’t do anything with event. Let’s change that. Suppose your application needs to respond to keypresses. You need to check that event was generated by a user pressing a key on their keyboard, and if so, pass event to an event handler function for keypresses.

Assume that event has a .type attribute set to the string "keypress" if the event is a keypress event object, and a .char attribute containing the character of the key that was pressed. Create a new handle_keypress() function and update your event loop code:

events = []

# Create an event handler
def handle_keypress(event):
    """Print the character associated to the key pressed"""
    print(event.char)

while True:
    if events == []:
        continue

    event = events[0]

    # If event is a keypress event object
    if event.type == "keypress":
        # Call the keypress event handler
        handle_keypress(event)

When you call window.mainloop(), something like the above loop is run for you. This method takes care of two parts of the loop for you:

  1. It maintains a list of events that have occurred.
  2. It runs an event handler any time a new event is added to that list.

Update your event loop to use window.mainloop() instead of your own event loop:

import tkinter as tk

# Create a window object
window = tk.Tk()

# Create an event handler
def handle_keypress(event):
    """Print the character associated to the key pressed"""
    print(event.char)

# Run the event loop
window.mainloop()

.mainloop() takes care of a lot for you, but there’s something missing from the above code. How does Tkinter know when to use handle_keypress()? Tkinter widgets have a method called .bind() for just this purpose.

Using .bind()

To call an event handler whenever an event occurs on a widget, use .bind(). The event handler is said to be bound to the event because it’s called every time the event occurs. You’ll continue with the keypress example from the previous section and use .bind() to bind handle_keypress() to the keypress event:

import tkinter as tk

window = tk.Tk()

def handle_keypress(event):
    """Print the character associated to the key pressed"""
    print(event.char)

# Bind keypress event to handle_keypress()
window.bind("<Key>", handle_keypress)

window.mainloop()

Here, the handle_keypress() event handler is bound to a "<Key>" event using window.bind(). Whenever a key is pressed while the application is running, your program will print the character of the key pressed.

.bind() always takes at least two arguments:

  1. An event that’s represented by a string of the form "<event_name>", where event_name can be any of Tkinter’s events
  2. An event handler that’s the name of the function to be called whenever the event occurs

The event handler is bound to the widget on which .bind() is called. When the event handler is called, the event object is passed to the event handler function.

In the example above, the event handler is bound to the window itself, but you can bind an event handler to any widget in your application. For example, you can bind an event handler to a Button widget that will perform some action whenever the button is pressed:

def handle_click(event):
    print("The button was clicked!")

button = tk.Button(text="Click me!")

button.bind("<Button-1>", handle_click)

In this example, the "<Button-1>" event on the button widget is bound to the handle_click event handler. The "<Button-1>" event occurs whenever the left mouse button is pressed while the mouse is over the widget. There are other events for mouse button clicks, including "<Button-2>" for the middle mouse button and "<Button-3>" for the right mouse button.

You can bind any event handler to any kind of widget with .bind(), but there’s a more straightforward way to bind event handlers to button clicks using the Button widget’s command attribute.

Using command

Every Button widget has a command attribute that you can assign to a function. Whenever the button is pressed, the function is executed.

Take a look at an example. First, you’ll create a window with a Label widget that holds a numeric value. You’ll put buttons on the left and right side of the label. The left button will be used to decrease the value in the Label, and the right one will increase the value. Here’s the code for the window:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4
 5window.rowconfigure(0, minsize=50, weight=1)
 6window.columnconfigure([0, 1, 2], minsize=50, weight=1)
 7
 8btn_decrease = tk.Button(master=window, text="-")
 9btn_decrease.grid(row=0, column=0, sticky="nsew")
10
11lbl_value = tk.Label(master=window, text="0")
12lbl_value.grid(row=0, column=1)
13
14btn_increase = tk.Button(master=window, text="+")
15btn_increase.grid(row=0, column=2, sticky="nsew")
16
17window.mainloop()

The window looks like this:

A Tkinter application with increase and decrease buttons that increase and decrease a counter

With the app layout defined, you can bring it to life by giving the buttons some commands. Start with the left button. When this button is pressed, it should decrease the value in the label by one. In order to do this, you first need to get answers to two questions:

  1. How do you get the text in Label?
  2. How do you update the text in Label?

Label widgets don’t have .get() like Entry and Text widgets do. However, you can retrieve the text from the label by accessing the text attribute with a dictionary-style subscript notation:

label = tk.Label(text="Hello")

# Retrieve a label's text
text = label["text"]

# Set new text for the label
label["text"] = "Good bye"

Now that you know how to get and set a label’s text, write an increase() function that increases the value in lbl_value by one:

 1import tkinter as tk
 2
 3def increase():
 4    value = int(lbl_value["text"])
 5    lbl_value["text"] = f"{value + 1}"
 6
 7# ...

increase() gets the text from lbl_value and converts it to an integer with int(). Then, it increases this value by one and sets the label’s text attribute to this new value.

You’ll also need decrease() to decrease the value in value_label by one:

 5# ...
 6
 7def decrease():
 8    value = int(lbl_value["text"])
 9    lbl_value["text"] = f"{value - 1}"
10
11# ...

Put increase() and decrease() in your code just after the import statement.

To connect the buttons to the functions, assign the function to the button’s command attribute. You can do this when you instantiate the buttons. For example, update the two lines that instantiate the buttons to the following:

14# ...
15
16btn_decrease = tk.Button(master=window, text="-", command=decrease)
17btn_decrease.grid(row=0, column=0, sticky="nsew")
18
19lbl_value = tk.Label(master=window, text="0")
20lbl_value.grid(row=0, column=1)
21
22btn_increase = tk.Button(master=window, text="+", command=increase)
23btn_increase.grid(row=0, column=2, sticky="nsew")
24
25window.mainloop()

That’s all you need to do to bind the buttons to increase() and decrease() and make the program functional. Try saving your changes and running the application! Click the buttons to increase and decrease the value in the center of the window:

A counter app built with Tkinter

Here’s the full application code for your reference:

import tkinter as tk

def increase():
    value = int(lbl_value["text"])
    lbl_value["text"] = f"{value + 1}"

def decrease():
    value = int(lbl_value["text"])
    lbl_value["text"] = f"{value - 1}"

window = tk.Tk()

window.rowconfigure(0, minsize=50, weight=1)
window.columnconfigure([0, 1, 2], minsize=50, weight=1)

btn_decrease = tk.Button(master=window, text="-", command=decrease)
btn_decrease.grid(row=0, column=0, sticky="nsew")

lbl_value = tk.Label(master=window, text="0")
lbl_value.grid(row=0, column=1)

btn_increase = tk.Button(master=window, text="+", command=increase)
btn_increase.grid(row=0, column=2, sticky="nsew")

window.mainloop()

This app isn’t particularly useful, but the skills you learned here apply to every app you’ll make:

  • Use widgets to create the components of the user interface.
  • Use geometry managers to control the layout of the application.
  • Write event handlers that interact with various components to capture and transform user input.

In the next two sections, you’ll build more useful apps. First, you’ll build a temperature converter that converts a temperature value from Fahrenheit to Celsius. After that, you’ll build a text editor that can open, edit, and save text files!

Check Your Understanding

Expand the code block below for an exercise to check your understanding:

Write a program that simulates rolling a six-sided die. There should be one button with the text Roll. When the user clicks the button, a random integer from 1 to 6 should be displayed.

The application window should look something like this:

A Tkinter application with a "Roll" button that produces a random number between 1 and 6

Try this exercise now.

You can expand the code block below to see a solution:

Here’s one possible solution:

import random
import tkinter as tk

def roll():
    lbl_result["text"] = str(random.randint(1, 6))

window = tk.Tk()
window.columnconfigure(0, minsize=150)
window.rowconfigure([0, 1], minsize=50)

btn_roll = tk.Button(text="Roll", command=roll)
lbl_result = tk.Label()

btn_roll.grid(row=0, column=0, sticky="nsew")
lbl_result.grid(row=1, column=0)

window.mainloop()

Keep in mind that your code may look different.

When you’re ready, you can move on to the next section.

Building a Temperature Converter (Example App)

In this section, you’ll build a temperature converter application that allows the user to input temperature in degrees Fahrenheit and push a button to convert that temperature to degrees Celsius. You’ll walk through the code step by step. You can also find the full source code at the end of this section for your reference.

Before you start coding, you’ll first design the app. You need three elements:

  1. Entry: A widget called ent_temperature for entering the Fahrenheit value
  2. Label: A widget called lbl_result to display the Celsius result
  3. Button: A widget called btn_convert that reads the value from the Entry widget, converts it from Fahrenheit to Celsius, and sets the text of the Label widget to the result when clicked

You can arrange these in a grid with a single row and one column for each widget. That gets you a minimally working application, but it isn’t very user-friendly. Everything needs to have labels.

You’ll put a label directly to the right of the ent_temperature widget containing the Fahrenheit symbol (℉) so that the user knows that the value ent_temperature should be in degrees Fahrenheit. To do this, set the label text to "N{DEGREE FAHRENHEIT}", which uses Python’s named Unicode character support to display the Fahrenheit symbol.

You can give btn_convert a little flair by setting its text to the value "N{RIGHTWARDS BLACK ARROW}", which displays a black arrow pointing to the right. You’ll also make sure that lbl_result always has the Celsius symbol (℃) following the label text "N{DEGREE CELSIUS}" to indicate that the result is in degrees Celsius. Here’s what the final window will look like:

A temperature conversion application built with Tkinter

Now that you know what widgets you need and what the window is going to look like, you can start coding it up! First, import tkinter and create a new window:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4window.title("Temperature Converter")
 5window.resizable(width=False, height=False)

window.title() sets the title of an existing window, while window.resizable() with both arguments set to False makes the window have a fixed size. When you finally run this application, the window will have the text Temperature Converter in its title bar. Next, create the ent_temperature widget with a label called lbl_temp and assign both to a Frame widget called frm_entry:

 5# ...
 6
 7frm_entry = tk.Frame(master=window)
 8ent_temperature = tk.Entry(master=frm_entry, width=10)
 9lbl_temp = tk.Label(master=frm_entry, text="N{DEGREE FAHRENHEIT}")

The user will enter the Fahrenheit value in ent_temperature, and lbl_temp is used to label ent_temperature with the Fahrenheit symbol. The frm_entry container groups ent_temperature and lbl_temp together.

You want lbl_temp to be placed directly to the right of ent_temperature. You can lay them out in frm_entry using the .grid() geometry manager with one row and two columns:

 9# ...
10
11ent_temperature.grid(row=0, column=0, sticky="e")
12lbl_temp.grid(row=0, column=1, sticky="w")

You’ve set the sticky parameter to "e" for ent_temperature so that it always sticks to the rightmost edge of its grid cell. You also set sticky to "w" for lbl_temp to keep it stuck to the leftmost edge of its grid cell. This ensures that lbl_temp is always located immediately to the right of ent_temperature.

Now, make the btn_convert and the lbl_result for converting the temperature entered into ent_temperature and displaying the results:

12# ...
13
14btn_convert = tk.Button(
15    master=window,
16    text="N{RIGHTWARDS BLACK ARROW}"
17)
18lbl_result = tk.Label(master=window, text="N{DEGREE CELSIUS}")

Like frm_entry, both btn_convert and lbl_result are assigned to window. Together, these three widgets make up the three cells in the main application grid. Use .grid() to go ahead and lay them out now:

18# ...
19
20frm_entry.grid(row=0, column=0, padx=10)
21btn_convert.grid(row=0, column=1, pady=10)
22lbl_result.grid(row=0, column=2, padx=10)

Finally, run the application:

22# ...
23
24window.mainloop()

That looks great! But the button doesn’t do anything just yet. At the top of your script file, just below the import line, add a function called fahrenheit_to_celsius():

 1import tkinter as tk
 2
 3def fahrenheit_to_celsius():
 4    """Convert the value for Fahrenheit to Celsius and insert the
 5    result into lbl_result.
 6    """
 7    fahrenheit = ent_temperature.get()
 8    celsius = (5 / 9) * (float(fahrenheit) - 32)
 9    lbl_result["text"] = f"{round(celsius, 2)} N{DEGREE CELSIUS}"
10
11# ...

This function reads the value from ent_temperature, converts it from Fahrenheit to Celsius, and then displays the result in lbl_result.

Now go down to the line where you define btn_convert and set its command parameter to fahrenheit_to_celsius:

20# ...
21
22btn_convert = tk.Button(
23    master=window,
24    text="N{RIGHTWARDS BLACK ARROW}",
25    command=fahrenheit_to_celsius  # <--- Add this line
26)
27
28# ...

That’s it! You’ve created a fully functional temperature converter app in just twenty-six lines of code! Pretty cool, right?

You can expand the code block below to see the full script:

Here’s the full script for your reference:

import tkinter as tk

def fahrenheit_to_celsius():
    """Convert the value for Fahrenheit to Celsius and insert the
    result into lbl_result.
    """
    fahrenheit = ent_temperature.get()
    celsius = (5 / 9) * (float(fahrenheit) - 32)
    lbl_result["text"] = f"{round(celsius, 2)} N{DEGREE CELSIUS}"

# Set up the window
window = tk.Tk()
window.title("Temperature Converter")
window.resizable(width=False, height=False)

# Create the Fahrenheit entry frame with an Entry
# widget and label in it
frm_entry = tk.Frame(master=window)
ent_temperature = tk.Entry(master=frm_entry, width=10)
lbl_temp = tk.Label(master=frm_entry, text="N{DEGREE FAHRENHEIT}")

# Layout the temperature Entry and Label in frm_entry
# using the .grid() geometry manager
ent_temperature.grid(row=0, column=0, sticky="e")
lbl_temp.grid(row=0, column=1, sticky="w")

# Create the conversion Button and result display Label
btn_convert = tk.Button(
    master=window,
    text="N{RIGHTWARDS BLACK ARROW}",
    command=fahrenheit_to_celsius
)
lbl_result = tk.Label(master=window, text="N{DEGREE CELSIUS}")

# Set up the layout using the .grid() geometry manager
frm_entry.grid(row=0, column=0, padx=10)
btn_convert.grid(row=0, column=1, pady=10)
lbl_result.grid(row=0, column=2, padx=10)

# Run the application
window.mainloop()

It’s time to kick things up a notch! Read on to learn how to build a text editor.

Building a Text Editor (Example App)

In this section, you’ll build a text editor application that can create, open, edit, and save text files. There are three essential elements in the application:

  1. A Button widget called btn_open for opening a file for editing
  2. A Button widget called btn_save for saving a file
  3. A TextBox widget called txt_edit for creating and editing the text file

The three widgets will be arranged so that the two buttons are on the left-hand side of the window, and the text box is on the right-hand side. The whole window should have a minimum height of 800 pixels, and txt_edit should have a minimum width of 800 pixels. The whole layout should be responsive so that if the window is resized, then txt_edit is resized as well. The width of the frame holding the buttons should not change, however.

Here’s a sketch of how the window will look:

A design sketch for a text editor application

You can achieve the desired layout using the .grid() geometry manager. The layout contains a single row and two columns:

  1. A narrow column on the left for the buttons
  2. A wider column on the right for the text box

To set the minimum sizes for the window and txt_edit, you can set the minsize parameters of the window methods .rowconfigure() and .columnconfigure() to 800. To handle resizing, you can set the weight parameters of these methods to 1.

In order to get both buttons into the same column, you’ll need to create a Frame widget called frm_buttons. According to the sketch, the two buttons should be stacked vertically inside of this frame, with btn_open on top. You can do that with either the .grid() or .pack() geometry manager. For now, you’ll stick with .grid() since it’s a little easier to work with.

Now that you have a plan, you can start coding the application. The first step is to create all of the widgets you need:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4window.title("Simple Text Editor")
 5
 6window.rowconfigure(0, minsize=800, weight=1)
 7window.columnconfigure(1, minsize=800, weight=1)
 8
 9txt_edit = tk.Text(window)
10frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
11btn_open = tk.Button(frm_buttons, text="Open")
12btn_save = tk.Button(frm_buttons, text="Save As...")

Here’s a breakdown of this code:

  • Line 1 imports tkinter.
  • Lines 3 and 4 create a new window with the title "Simple Text Editor".
  • Lines 6 and 7 set the row and column configurations.
  • Lines 9 to 12 create the four widgets you’ll need for the text box, the frame, and the open and save buttons.

Take a look at line 6 more closely. The minsize parameter of .rowconfigure() is set to 800, and weight is set to 1:

window.rowconfigure(0, minsize=800, weight=1)

The first argument is 0, which sets the height of the first row to 800 pixels and makes sure that the height of the row grows proportionally to the height of the window. There’s only one row in the application layout, so these settings apply to the entire window.

Let’s also take a closer look at line 7. Here, you use .columnconfigure() to set the width and weight attributes of the column with index 1 to 800 and 1, respectively:

window.columnconfigure(1, minsize=800, weight=1)

Remember, row and column indices are zero-based, so these settings apply only to the second column. By configuring just the second column, the text box will expand and contract naturally when the window is resized, while the column containing the buttons will remain at a fixed width.

Now you can work on the application layout. First, assign the two buttons to the frm_buttons frame using the .grid() geometry manager:

12# ...
13
14btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
15btn_save.grid(row=1, column=0, sticky="ew", padx=5)

These two lines of code create a grid with two rows and one column in the frm_buttons frame since both btn_open and btn_save have their master attribute set to frm_buttons. btn_open is put in the first row and btn_save in the second row so that btn_open appears above btn_save in the layout, just you planned in your sketch.

Both btn_open and btn_save have their sticky attributes set to "ew", which forces the buttons to expand horizontally in both directions and fill the entire frame. This ensures that both buttons are the same size.

You place five pixels of padding around each button by setting the padx and pady parameters to 5. Only btn_open has vertical padding. Since it’s on top, the vertical padding offsets the button down from the top of the window a bit and makes sure that there’s a small gap between it and btn_save.

Now that frm_buttons is laid out and ready to go, you can set up the grid layout for the rest of the window:

15# ...
16
17frm_buttons.grid(row=0, column=0, sticky="ns")
18txt_edit.grid(row=0, column=1, sticky="nsew")

These two lines of code create a grid with one row and two columns for window. You place frm_buttons in the first column and txt_edit in the second column so that frm_buttons appears to the left of txt_edit in the window layout.

The sticky parameter for frm_buttons is set to "ns", which forces the whole frame to expand vertically and fill the entire height of its column. txt_edit fills its entire grid cell because you set its sticky parameter to "nsew", which forces it to expand in every direction.

Now that the application layout is complete, add window.mainloop() to the bottom of the program and save and run the file:

18# ...
19
20window.mainloop()

The following window is displayed:

A text editor application made with Tkinter

That looks great! But it doesn’t do anything just yet, so you need to start writing the commands for the buttons. btn_open needs to show a file open dialog and allow the user to select a file. It then needs to open that file and set the text of txt_edit to the contents of the file. Here’s an open_file() function that does just this:

 1import tkinter as tk
 2
 3def open_file():
 4    """Open a file for editing."""
 5    filepath = askopenfilename(
 6        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
 7    )
 8    if not filepath:
 9        return
10    txt_edit.delete("1.0", tk.END)
11    with open(filepath, mode="r", encoding="utf-8") as input_file:
12        text = input_file.read()
13        txt_edit.insert(tk.END, text)
14    window.title(f"Simple Text Editor - {filepath}")
15
16# ...

Here’s a breakdown of this function:

  • Lines 5 to 7 use the askopenfilename() dialog from the tkinter.filedialog module to display a file open dialog and store the selected file path to filepath.
  • Lines 8 and 9 check to see if the user closes the dialog box or clicks the Cancel button. If so, then filepath will be None, and the function will return without executing any of the code to read the file and set the text of txt_edit.
  • Line 10 clears the current contents of txt_edit using .delete().
  • Lines 11 and 12 open the selected file and .read() its contents before storing the text as a string.
  • Line 13 assigns the string text to txt_edit using .insert().
  • Line 14 sets the title of the window so that it contains the path of the open file.

Now you can update the program so that btn_open calls open_file() whenever it’s clicked. There are a few things that you need to do to update the program. First, import askopenfilename() from tkinter.filedialog by adding the following import to the top of your program:

 1import tkinter as tk
 2from tkinter.filedialog import askopenfilename
 3
 4# ...

Next, set the command attribute of btn_opn to open_file:

 1import tkinter as tk
 2from tkinter.filedialog import askopenfilename
 3
 4def open_file():
 5    """Open a file for editing."""
 6    filepath = askopenfilename(
 7        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
 8    )
 9    if not filepath:
10        return
11    txt_edit.delete("1.0", tk.END)
12    with open(filepath, mode="r", encoding="utf-8") as input_file:
13        text = input_file.read()
14        txt_edit.insert(tk.END, text)
15    window.title(f"Simple Text Editor - {filepath}")
16
17window = tk.Tk()
18window.title("Simple Text Editor")
19
20window.rowconfigure(0, minsize=800, weight=1)
21window.columnconfigure(1, minsize=800, weight=1)
22
23txt_edit = tk.Text(window)
24frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
25btn_open = tk.Button(frm_buttons, text="Open", command=open_file)
26btn_save = tk.Button(frm_buttons, text="Save As...")
27
28# ...

Save the file and run it to check that everything is working. Then try opening a text file!

With btn_open working, it’s time to work on the function for btn_save. This needs to open a save file dialog box so that the user can choose where they would like to save the file. You’ll use the asksaveasfilename() dialog in the tkinter.filedialog module for this. This function also needs to extract the text currently in txt_edit and write this to a file at the selected location. Here’s a function that does just this:

15# ...
16
17def save_file():
18    """Save the current file as a new file."""
19    filepath = asksaveasfilename(
20        defaultextension=".txt",
21        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
22    )
23    if not filepath:
24        return
25    with open(filepath, mode="w", encoding="utf-8") as output_file:
26        text = txt_edit.get("1.0", tk.END)
27        output_file.write(text)
28    window.title(f"Simple Text Editor - {filepath}")
29
30# ...

Here’s how this code works:

  • Lines 19 to 22 use the asksaveasfilename() dialog box to get the desired save location from the user. The selected file path is stored in the filepath variable.
  • Lines 23 and 24 check to see if the user closes the dialog box or clicks the Cancel button. If so, then filepath will be None, and the function will return without executing any of the code to save the text to a file.
  • Line 25 creates a new file at the selected file path.
  • Line 26 extracts the text from txt_edit with .get() method and assigns it to the variable text.
  • Line 27 writes text to the output file.
  • Line 28 updates the title of the window so that the new file path is displayed in the window title.

Now you can update the program so that btn_save calls save_file() when it’s clicked. Again, there are a few things you need to do in order to update the program. First, import asksaveasfilename() from tkinter.filedialog by updating the import at the top of your script, like so:

 1import tkinter as tk
 2from tkinter.filedialog import askopenfilename, asksaveasfilename
 3
 4# ...

Finally, set the command attribute of btn_save to save_file:

28# ...
29
30window = tk.Tk()
31window.title("Simple Text Editor")
32
33window.rowconfigure(0, minsize=800, weight=1)
34window.columnconfigure(1, minsize=800, weight=1)
35
36txt_edit = tk.Text(window)
37frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
38btn_open = tk.Button(frm_buttons, text="Open", command=open_file)
39btn_save = tk.Button(frm_buttons, text="Save As...", command=save_file)
40
41btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
42btn_save.grid(row=1, column=0, sticky="ew", padx=5)
43
44frm_buttons.grid(row=0, column=0, sticky="ns")
45txt_edit.grid(row=0, column=1, sticky="nsew")
46
47window.mainloop()

Save the file and run it. You’ve now got a minimal yet fully functional text editor!

You can expand the code block below to see the full script:

Here’s the full script for your reference:

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file():
    """Open a file for editing."""
    filepath = askopenfilename(
        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
    )
    if not filepath:
        return
    txt_edit.delete("1.0", tk.END)
    with open(filepath, mode="r", encoding="utf-8") as input_file:
        text = input_file.read()
        txt_edit.insert(tk.END, text)
    window.title(f"Simple Text Editor - {filepath}")

def save_file():
    """Save the current file as a new file."""
    filepath = asksaveasfilename(
        defaultextension=".txt",
        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
    )
    if not filepath:
        return
    with open(filepath, mode="w", encoding="utf-8") as output_file:
        text = txt_edit.get("1.0", tk.END)
        output_file.write(text)
    window.title(f"Simple Text Editor - {filepath}")

window = tk.Tk()
window.title("Simple Text Editor")

window.rowconfigure(0, minsize=800, weight=1)
window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)
frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
btn_open = tk.Button(frm_buttons, text="Open", command=open_file)
btn_save = tk.Button(frm_buttons, text="Save As...", command=save_file)

btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
btn_save.grid(row=1, column=0, sticky="ew", padx=5)

frm_buttons.grid(row=0, column=0, sticky="ns")
txt_edit.grid(row=0, column=1, sticky="nsew")

window.mainloop()

You’ve now built two GUI applications in Python and applied many of the skills that you’ve learned throughout this tutorial. That’s no small achievement, so take some time to feel good about what you’ve done. You’re now ready to tackle some applications on your own!

Conclusion

In this tutorial, you learned how to get started with Python GUI programming. Tkinter is a compelling choice for a Python GUI framework because it’s built into the Python standard library, and it’s relatively painless to make applications with this framework.

Throughout this tutorial, you’ve learned several important Tkinter concepts:

  • How to work with widgets
  • How to control your application layout with geometry managers
  • How to make your applications interactive
  • How to use five basic Tkinter widgets: Label, Button, Entry, Text, and Frame

Now that you’ve mastered the foundations of Python GUI programming with Tkinter, the next step is to build some of your own applications. What will you create? Share your fun projects down in the comments below!

Additional Resources

In this tutorial, you touched on just the foundations of creating Python GUI applications with Tkinter. There are a number of additional topics that aren’t covered here. In this section, you’ll find some of the best resources available to help you continue on your journey.

Tkinter References

Here are some official resources to check out:

  • The official Python Tkinter reference documentation covers Python’s Tkinter module in moderate depth. It’s written for more advanced Python developers and isn’t the best resource for beginners.
  • Tkinter 8.5 reference: a GUI for Python is an extensive reference covering the majority of the Tkinter module. It’s exhaustive, but it’s written in the reference style without commentary or examples.
  • The Tk Commands reference is the definitive guide to commands in the Tk library. It’s written for the Tcl language, but it answers a lot of questions about why things work the way they do in Tkinter.

Additional Widgets

In this tutorial, you learned about the Label, Button, Entry, Text, and Frame widgets. There are several other widgets in Tkinter, all of which are essential for building real-world applications. Here are some resources to continue learning about widgets:

  • The TkDocs Tkinter Tutorial is a fairly comprehensive guide for Tk, the underlying code library used by Tkinter. Examples are presented in Python, Ruby, Perl, and Tcl. You can find several examples of widgets beyond those covered here in two sections:
    • Basic Widgets covers the same widgets as this tutorial, plus a few more.
    • More Widgets covers several additional widgets.
  • The official Python docs cover additional widgets:
    • ttk themed widgets covers the Tk themed widget set.
    • Scrolled Text Widget details a Text widget combined with a vertical scroll bar.

Application Distribution

Once you’ve created an application with Tkinter, you probably want to distribute it to your colleagues and friends. Here are some tutorials to get you going with that process:

  • Using PyInstaller to Easily Distribute Python Applications
  • 4 Attempts at Packaging Python as an Executable
  • Building Standalone Python Applications with PyOxidizer

Other GUI Frameworks

Tkinter isn’t your only choice for a Python GUI framework. If Tkinter doesn’t meet the needs of your project, then here are some other frameworks to consider:

  • How to Build a Python GUI Application With wxPython
  • Python and PyQt: Building a GUI Desktop Calculator
  • Building a Mobile Application With the Kivy Python Framework
  • PySimpleGUI: The Simple Way to Create a GUI With Python

Графический интерфейс – неотъемлемая составляющая любого современного приложения. Даже во времена MS DOS, несмотря на тотальное доминирование командной строки в те времена, все равно начали появляться первые графические интерфейсы. Чего только стоит легендарный файловый менеджер Norton Commander? А для создания игр работа с графическими интерфейсами вообще является приоритетной.

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

Пример приложения, созданного с применением Tkinter – IDLE. Это очень популярная среда разработки приложений для Python.

Рекомендуется пользоваться Python 3.7 для того, чтобы проверять нашу инструкцию. Дело в том, что третья версия претерпела настолько существенные изменения, что использование второй версии уже далеко от реального программирования.

В этой инструкции мы будем подразумевать, что базовое представление о Python у Вас уже есть. Мы же будем говорить о том, как создавать окно, что такое виджеты и как их добавить и редактировать. Давайте начинать.

Содержание

  1. Создаем свой первый графический интерфейс
  2. Создаем виджет Label
  3. Настраиваем размер и шрифт текста
  4. Настраиваем размеры окна приложения
  5. Добавляем виджет Button
  6. Как изменить цвет текста и фона у Button?
  7. Кнопка Click
  8. Получение ввода при помощи класса Entry (текстовое поле Tkinter)
  9. Фокус виджета ввода
  10. Отключение виджета ввода
  11. Добавляем виджет Combobox
  12. Добавляем виджет Checkbutton (чекбокс)
  13. Установка состояния с помощью intvar()
  14. Установка состояния с помощью booleanvar()
  15. Добавляем виджеты RadioButton
  16. Получаем значение Radio Button (избранная Radio Button)
  17. Добавляем виджет ScrolledText (текстовая область Tkinter)
  18. Настраиваем содержимое
  19. Удаление/Очистка содержимого Scrolledtext
  20. Создаем всплывающее окно с сообщением
  21. Показ сообщений о предупреждениях и ошибках
  22. Показ диалоговых окон с выбором варианта
  23. Добавление SpinBox (Виджет спинбокс)
  24. Значение по умолчанию для Spinbox
  25. Добавляем виджет Progressbar
  26. Меняем цвет Progressbar
  27. Добавляем поле загрузки файла
  28. Указываем тип файлов (расширение фильтра файлов)
  29. Добавление панели меню
  30. Добавляем виджет Notebook (управление вкладкой)
  31. Добавляем виджет на вкладку
  32. Добавляем интервал для виджетов (заполнение)

Создаем свой первый графический интерфейс

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

from tkinter import *

window = Tk()

window.title(«Добро пожаловать в приложение Python»)

window.mainloop()

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

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

Обратите внимание на последнюю строку. Она содержит метод mainloop(), который принадлежит объекту window. Она нужна для того, чтобы зациклить отображение окна. Если ее не использовать, то окно сразу закроется, и пользователь ничего не увидит.

Создаем виджет Label

Окно – это, конечно, прекрасно. Но что делать, если нам надо отобразить в нем какую-то надпись? Например, слово «Привет». Как это сделать?

Сначала необходимо добавить виджет. Для этого объявляется переменная (в нашем случае – lbl) с использованием следующего кода.

lbl = Label(window, text=»Привет»)

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

lbl.grid(column=0, row=0)

После того, как мы добавим эти строки в приведенный выше код (а добавлять их надо после той строки, где задается заголовок), получится следующий.

from tkinter import *  

window = Tk()  

window.title(«Добро пожаловать в приложение Python»)  

lbl = Label(window, text=»Привет»)  

lbl.grid(column=0, row=0)  

window.mainloop()

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Вызов метода grid – обязательный. Иначе пользователь не увидит текста, который мы захотели.

Настраиваем размер и шрифт текста

Tkinter – гибкая библиотека. В том числе, в ней можно редактировать текст, задавая произвольный тип шрифта и его размер. Для этого в скобках объекта Label задается свойство font. Характеристики в скобках записываются в следующей последовательности: сначала – начертание шрифта, а потом – его размер. Например, если указать шрифт «Arial Bold» 50 размера, то окно с нашей надписью будет уже поинтереснее.

lbl = Label(window, text=»Привет», font=(«Arial Bold», 50))

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Этот параметр может использоваться, в том числе, и для других виджетов. Его можно применять не только к надписи. Если в виджете есть шрифт, его можно изменять.

Настраиваем размеры окна приложения

Программа у нас получилась отличная, конечно. Но, скажите, чего-то не хватает. Все правильно, окошко хотелось бы побольше. Что можно сделать для того, чтобы увеличить его?

Чтобы это сделать, используется метод geometry с аргументом в виде размеров окна, записанных в виде строки в формате «ширина»х«высота».

Например, так.

window.geometry(‘500×350’)

Эта строка делает размер окна по ширине 500 пикселей, а по высоте – 250.

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

Добавляем виджет Button

Теперь нам нужно добавить кнопку. Чтобы это сделать, необходимо создать объект соответствующего класса, а в свойствах задать текст, который будет на ней отображаться.

btn = Button(window, text=»Не нажимать!»)

btn.grid(column=1, row=0)

Мы добавили кнопку после метки в коде. В целом, у нас получилась такая программа.

from tkinter import *  

window = Tk()  

window.title(«Добро пожаловать в приложение Python»)  

window.geometry(‘400×250’)  

lbl = Label(window, text=»Привет», font=(«Arial Bold», 50))  

lbl.grid(column=0, row=0)  

btn = Button(window, text=»Не нажимать!»)  

btn.grid(column=1, row=0)  

window.mainloop()

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

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Заметьте, что в коде выше мы указали позиционирование кнопки во втором столбце. Но поскольку индексация начинается с нуля, то, соответственно, мы написали индекс столбца 1. Если же его разместить с координатами 0,0, то он просто заменит надпись.

Как изменить цвет текста и фона у Button?

Кастомизация кнопок не ограничивается одним лишь изменением размера и начертания шрифтов. Например, можно сделать кнопку черной, а буквы на ней – красными. Для этого надо прописать соответствующие свойства: fg и bg. Они позволяют настроить цвет надписи и фон соответственно.

Точно так же, как и с размером и начертанием шрифта, с помощью этих свойств можно редактировать внешний вид любого другого элемента, не только кнопки. 

А теперь приведем пример кода, с помощью которого мы сделали надпись кнопки красной на черном фоне.

btn = Button(window, text=»Не нажимать!», bg=»black», fg=»red»)

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Хорошо, а что сейчас будет, если нажать на кнопку? Если вы попробовали у себя написать аналогичный код, то вы увидите, что ничего не произойдет. Дело в том, что не был настроен обработчик события. Это такая функция, которая активируется при наступлении определенного события.

Кнопка Click

Давайте попробуем прописать действия приложения на случай, если пользователь таки попробует нажать на эту кнопку. Как это делается?

Для начала необходимо прописать ту функцию, которая активируется при нажатии кнопки (то есть, будет служить обработчиком событий).

def clicked():

    lbl.configure(text=»Я же просил…»)

После этого она прописывается в качестве свойства command таким образом:

btn = Button(window, text=»Не нажимать!», command=clicked)

Заметьте, что мы используем название функции обработчика событий без скобок.

Если мы запустим этот код, то увидим, что надпись изменилась на «Я же просил».

Вот, что уже умеет наша программа. Но это еще не все.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Получение ввода при помощи класса Entry (текстовое поле Tkinter)

Раньше мы рассматривали самые простые виджеты, которые работали на вывод и простейший ввод (в виде нажатия кнопки). Теперь же мы рассмотрим, как вводить конкретную информацию в поле ввода, используя класс Entry. С его помощью можно добавить в программу такое текстовое поле, которое вы привыкли видеть в других приложениях.

Чтобы его добавить, необходимо создать объект соответствующего класса и присвоить его переменной. Для этого используется следующая строка кода.

txt = Entry(window, width=10)

Но, как вы уже поняли, этого недостаточно. Текстовое поле, равно как и любой другой объект, необходимо добавить к окну. Для этого используется функция grid. Например, так будет выглядеть поле ввода, размещенное во втором столбце (то есть, столбце, имеющем индекс 1). Соответственно, пришлось переместить кнопку немного правее.

Итоговый код получился следующим.

from tkinter import *    

def clicked():  

    lbl.configure(text=»Я же просил…»)  

window = Tk()  

window.title(«Добро пожаловать в приложение Python»)  

window.geometry(‘400×250’)  

lbl = Label(window, text=»Привет»)  

lbl.grid(column=0, row=0)  

txt = Entry(window,width=10)  

txt.grid(column=1, row=0)  

btn = Button(window, text=»Не нажимать!», command=clicked)  

btn.grid(column=2, row=0)  

window.mainloop()

Ну, соответственно, программа будет выглядеть так.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Тем не менее, в этом коде есть недостаток. Попробуйте вчитаться в него и понять, в чем он заключается. Дело в том, что при нажатии кнопки будет выдаваться старое сообщение, текст в поле не обрабатывается. Как же сделать так, чтобы приложение могло «считывать» его?

Эта задача достигается с помощью функции get. Соответственно, запишем следующий код.

def clicked():

    res = «Привет {}».format(txt.get())

    lbl.configure(text=res)

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

Ну и небольшая демонстрация окна программы, в которой реализован этот функционал.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Фокус виджета ввода

А знаете, в чем еще проблема этого приложения? В том, что нужно вручную настраивать фокус ввода. То есть, нельзя сразу вводить текст после того, как программа запустится. Как же осуществить автоматическую настройку фокуса? Для этого необходимо вызвать одноименную функцию.

txt.focus()

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

Отключение виджета ввода

Отключение виджета ввода – это операция, вследствие которой он отображается серым цветом, а пользователь не имеет возможности вводить туда текст. Для того, чтобы деактивировать виджет, необходимо выставить его состояние, как «disabled». Приведем фрагмент кода, как это делается.

txt = Entry(window,width=10, state=’disabled’)

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

Добавляем виджет Combobox

Combobox – это выпадающий список. Чтобы его добавить, необходимо создать объект соответствующего класса.

from tkinter.ttk import Combobox

combo = Combobox(window)

Далее необходимо присвоить значения для поля ввода и разместить поле ввода в подходящее место окна.

combo[‘values’] = (1, 2, 3, 4, 5, «Текст»)  

combo.current(1)  # установите вариант по умолчанию  

combo.grid(column=0, row=0)  

Возможно также использование функции get, чтобы получить содержимое списка. 

Добавляем виджет Checkbutton (чекбокс)

Чтобы добавить виджет Checkbutton, необходимо добавить объект соответствующего класса.

from tkinter.ttk import Checkbutton

chk = Checkbutton(window, text=’Выбрать’)

Если передать объекту var=chk_state, то можно задать значение по умолчанию.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Установка состояния с помощью intvar()

Чтобы чекбокс сделать с поставленной галочкой или пустым, используется метод set с соответствующим индексом. Если нужно сделать так, чтобы он был активирован, то в качестве аргумента этой функции используется 1. Если же надо, чтобы он был деактивирован, то используется 0 в качестве аргумента. Перед тем, как это использовать, необходимо присвоить соответствующему чекбоксу функцию intvar().

chk_state = IntVar()

chk_state.set(0) # False

chk_state.set(1) # True

Установка состояния с помощью booleanvar()

Устанавливать состояние чекбокса можно и с помощью функции BooleanVar. В этом случае метод set устанавливается на значение True или False.

chk_state = BooleanVar()  

chk_state.set(True)  # задайте проверку состояния чекбокса 

Результат от использования BooleanVar и IntVar одинаковые. Можно использовать тот, который больше нравится.

Добавляем виджеты RadioButton

Радиокнопки – один из любимых виджетов пользователей любых программ. Выглядят они так.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

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

rad1 = Radiobutton(window, text=’Первый’, value=1)  

rad2 = Radiobutton(window, text=’Второй’, value=2)  

rad3 = Radiobutton(window, text=’Третий’, value=3)

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

Также можно настроить радиокнопку так, чтобы она работала, как обычная кнопка. То есть, можно сделать обработчик события на нажатие. Делается это таким образом.

rad1 = Radiobutton(window,text=’Первая’, value=1, command=clicked)

def clicked():

Получаем значение Radio Button (избранная Radio Button)

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

Для этого прописывается сама переменная, а ей присваивается функция IntVar. А номер кнопки передается в качестве свойства value.

Добавляем виджет ScrolledText (текстовая область Tkinter)

Чтобы добавить большое текстовое поле, необходимо присвоить соответствующей переменной объект класса ScrolledText:

from tkinter import scrolledtext

txt = scrolledtext.ScrolledText(window,width=40,height=10)

Видим, что в свойствах необходимо указать ширину и высоту. Можно этого не делать. В таком случае текстовое поле займет область всего окна.

Результат получится приблизительно следующим.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Настраиваем содержимое

Настройка содержимого большого текстового поля осуществляется с помощью метода insert. Смотрите, как это работает.

txt.insert(INSERT, ‘Поле для ввода текста’)

Удаление/Очистка содержимого Scrolledtext

Очистить содержимое текстового поля очень просто. Для этого используется функция delete, которая работает вместе с объектом. В скобках указываются координаты, начиная с какой позиции и до какой требуется убрать содержимое текстового поля.

txt.delete(1.0, END)

Создаем всплывающее окно с сообщением

Всплывающие окна – важные составляющие любого программного обеспечения. Они позволяют быстро проинформировать пользователя о чем-то, не изменяя содержимого основного окна. 

Они создаются таким образом.

from tkinter import messagebox

messagebox.showinfo(‘Заголовок’, ‘Текст’)

Например, можно прописать обработчик клика по кнопке, в котором предусмотрено всплывающее окно.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Показ сообщений о предупреждениях и ошибках

Также возможна настройка показа предупреждающего окна или же уведомления об ошибках. Отличается только функция, которая отображает окно.

messagebox.showwarning(‘Заголовок’, ‘Текст’)  # показывает предупреждающее сообщение

messagebox.showerror(‘Заголовок’, ‘Текст’)  # показывает сообщение об ошибке

Показ диалоговых окон с выбором варианта

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

res = messagebox.askquestion(‘Заголовок’, ‘Текст’)

res = messagebox.askyesno(‘Заголовок’, ‘Текст’)

res = messagebox.askyesnocancel(‘Заголовок’, ‘Текст’)

res = messagebox.askokcancel(‘Заголовок’, ‘Текст’)

res = messagebox.askretrycancel(‘Заголовок’, ‘Текст’)

Кнопка, нажатая пользователем, возвращает значение в переменную результата. Значение истина возвращается в случаях, если было нажато на «ОК», «Да», «Повторить». Во всех остальных случаях возвращается значение false.

Но есть еще функция, которая возвращает три значения – True/False/None в зависимости от выбранной кнопки. Это метод askyesnocancel.

Добавление SpinBox (Виджет спинбокс)

Spinbox – это такое поле ввода со стрелками. Добавляется он с помощью команды

spin = Spinbox(window, from_=0, to=100)

Соответственно, параметры from и to отвечают за диапазон допустимых значений.

Создание графического интерфейса на Python с Tkinter. Обучение Python GUI

Значение по умолчанию для Spinbox

Чтобы указать значение по умолчанию, нужно передать параметр textvariable.

spin = Spinbox(window, from_=0, to=100, width=5, textvariable=var)

Добавляем виджет Progressbar

Как и с предыдущими вариантами, для добавления шкалы прогресса, нужно использовать соответствующий класс.

from tkinter.ttk import Progressbar

bar = Progressbar(window, length=200)

Меняем цвет Progressbar

Схема изменения цвета Progressbar довольно сложная и требует отдельного рассмотрения. В целом, механика следующая: нужно создать стиль, задать цвет фона, а потом перенести его на Progressbar.

Добавляем поле загрузки файла

Поле открытия файла относится к классу filedialog. Соответственно, добавить его можно с помощью следующего кода.

from tkinter import filedialog

file = filedialog.askopenfilename()

Все эти фрагменты приводятся, как примеры. Вы подставляете свои значения, исходя из ваших задач.

Указываем тип файлов (расширение фильтра файлов)

Для этого нужно использовать параметр filetypes. При этом требуется указать расширение в tuples.

file = filedialog.askopenfilename(filetypes = ((«Text files»,»*.txt»),(«all files»,»*.*»)))

Добавление панели меню

Для работы с меню существует соответствующий класс menu

menu = Menu(window)

menu.add_command(label=’Файл’)

window.config(menu=menu)

Видим, что сначала создается меню, а потом добавляются команды. А добавление подменю осуществляется так.

menu.add_cascade(label=’Автор’, menu=new_item)

Добавляем виджет Notebook (управление вкладкой)

Также Python позволяет создавать вкладки. Последовательность действий довольно сложная, и эта тема требует отдельного рассмотрения:

  1. Создается объект класса Notebook.
  2. Создается вкладка.
  3. Вкладка добавляется в элемент управления вкладками и осуществляется запаковка с целью отображения его в окне.

Добавляем виджет на вкладку

Чтобы добавить виджет на вкладку, необходимо назначить родительское свойство той, на которую осуществляется добавление.

Добавляем интервал для виджетов (заполнение)

Для этого используются свойства padx и pady.

lbl1 = Label(tab1, text= ‘label1’, padx=5, pady=5)

Как видим, тема очень обширная, и работа с каждым действием, описанным выше, требует отдельной статьи. Тем не менее, хочется надеяться, что Вы получили общее представление о том, как работать с графическим интерфейсом в Python.

Оцените качество статьи. Нам важно ваше мнение:

Понравилась статья? Поделить с друзьями:
  • Как изменить интерфейс папок на windows 10
  • Как изменить интерфейс панели задач
  • Как изменить интерфейс осу
  • Как изменить интерфейс одноклассников
  • Как изменить интерфейс нанокад