Django-Admin.ru

Tkinter: графический интерфейс на Python

Tkinter в Python с нуля: первое окно, кнопки и поля ввода, размещение через pack и grid, обработчики событий, mainloop и установка python3-tk на Linux.

Редакция Django-Admin.ru
· обновлено · 9 мин чтения

Содержание · 8 разделов
  1. Установка и проверка Tkinter
  2. Окно и mainloop
  3. Основные виджеты
  4. Переменные Tkinter
  5. Виджеты ttk
  6. Размещение виджетов: pack и grid
  7. Обработчики событий
  8. command у кнопок
  9. Пример: форма с grid и обработчиком
  10. bind: клавиши и мышь
  11. after: таймеры без зависания
  12. Частые ошибки
  13. Функция срабатывает сразу при запуске
  14. TclError при смешивании pack и grid
  15. Виджет создан, но не виден
  16. TclError: no display name and no $DISPLAY environment variable
  17. Окно зависает при долгой операции
  18. Частые вопросы
  19. Как открыть окно по центру экрана?
  20. Как изменить шрифт, размер и цвет текста?
  21. Как удалить все виджеты из окна?
  22. Как показать картинку в окне?
  23. Как сделать из программы на Tkinter exe-файл?
  24. Итог

Tkinter — стандартная библиотека Python для создания графического интерфейса: окон, кнопок, полей ввода и списков. Она входит в поставку Python для Windows и macOS, поэтому ставить ничего не нужно. Минимальное приложение на python tkinter:

import tkinter as tk

root = tk.Tk()                      # главное окно
root.title("Привет, Tkinter")
root.geometry("300x150")            # ширина x высота

label = tk.Label(root, text="Hello, world!", font=("Arial", 14))
label.pack(pady=20)

def on_click():
    label.config(text="Кнопка нажата")

tk.Button(root, text="Нажми меня", command=on_click).pack()

root.mainloop()                     # запустить цикл обработки событий

Ниже разберём, из чего состоит приложение на Tkinter: окно, виджеты, размещение через pack и grid, обработчики событий и mainloop, а также установку на Linux и частые ошибки. Примеры проверены на Python 3.12 с Tk 8.6.

Установка и проверка Tkinter

На Windows Tkinter устанавливается вместе с Python из официального установщика (пункт «tcl/tk and IDLE» включён по умолчанию). На macOS он есть в установщике с python.org; для Python из Homebrew нужен отдельный пакет brew install python-tk — он соответствует основной версии python в Homebrew, для другой версии указывайте её явно, например brew install python-tk@3.14.

Через pip Tkinter не ставится — такого пакета на PyPI нет:

pip install tkinter
# ERROR: No matching distribution found for tkinter

На Linux Tkinter часто вынесен в отдельный системный пакет. Если импорт падает с ModuleNotFoundError: No module named 'tkinter', установите его менеджером пакетов дистрибутива:

# Debian, Ubuntu, Mint
sudo apt install python3-tk

# Fedora
sudo dnf install python3-tkinter

# Arch Linux
sudo pacman -S tk

Проверить установку можно двумя способами:

python3 -m tkinter      # откроется тестовое окно с версией Tk (на Windows — py -m tkinter)
import tkinter as tk

print(tk.TkVersion)  # → 8.6

Если Python собран вручную или через pyenv без библиотек Tk, ошибка будет No module named '_tkinter' — тогда нужно поставить пакет разработки Tk (например, tk-dev в Debian/Ubuntu) и пересобрать Python. Общий разбор этой ошибки — в статье про ModuleNotFoundError.

Окно и mainloop

Любое приложение начинается с главного окна tk.Tk() и заканчивается вызовом mainloop():

  • root.title("...") — заголовок окна;
  • root.geometry("400x300") — размер; можно указать и позицию: "400x300+100+50";
  • root.resizable(False, False) — запретить изменение размера;
  • root.destroy() — закрыть окно и выйти из mainloop().

mainloop() — бесконечный цикл: он ждёт действий пользователя (клик, нажатие клавиши, перерисовка) и вызывает ваши функции. Код после mainloop() выполнится только после закрытия окна. Без этого вызова окно в скрипте мелькнёт и сразу исчезнет.

Основные виджеты

Виджет — элемент интерфейса. Первым аргументом ему передают родителя (окно или рамку), остальные параметры задают текст, размер и поведение.

ВиджетНазначение
Labelтекст или картинка
Buttonкнопка, функция — в параметре command
Entryоднострочное поле ввода
Textмногострочный текст
Checkbuttonфлажок
Radiobuttonпереключатель — один вариант из нескольких
Listboxсписок строк
Scaleползунок
Frameконтейнер для группировки виджетов
ttk.Comboboxвыпадающий список

Параметры уже созданного виджета меняют методом config(), а читают через cget() или как словарь:

label.config(text="Новый текст", fg="red")
print(label["text"])  # → Новый текст

Поле Entry читают методом get(), очищают — delete(0, tk.END), заполняют — insert(0, "текст"). У Text индексы вида "строка.символ", начиная с "1.0":

text = tk.Text(root, height=3, width=20)
text.insert("1.0", "строка 1\nстрока 2")
print(repr(text.get("1.0", tk.END)))  # → 'строка 1\nстрока 2\n'

Text всегда добавляет в конец перевод строки — уберите его через .strip() или читайте до "end-1c".

Переменные Tkinter

Флажки, переключатели и надписи удобно связывать со специальными переменными StringVar, IntVar, BooleanVar. Значение читают get(), меняют set(), а виджет обновляется сам:

remember = tk.BooleanVar()
tk.Checkbutton(root, text="Запомнить меня", variable=remember).pack()

theme = tk.StringVar(value="light")
tk.Radiobutton(root, text="Светлая", variable=theme, value="light").pack()
tk.Radiobutton(root, text="Тёмная", variable=theme, value="dark").pack()

print(remember.get(), theme.get())  # → False light

Виджеты ttk

Модуль tkinter.ttk содержит те же виджеты (ttk.Button, ttk.Label, ttk.Entry) в оформлении текущей ОС и несколько дополнительных: Combobox, Progressbar, Treeview, Notebook. Внешний вид у них настраивается через стили, а не параметрами bg и fg.

from tkinter import ttk

combo = ttk.Combobox(root, values=["Python", "Django", "FastAPI"], state="readonly")
combo.current(0)
combo.pack()
print(combo.get())  # → Python

Размещение виджетов: pack и grid

Созданный виджет не появится в окне, пока вы не вызовете менеджер геометрии.

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

frame = tk.Frame(root, padx=10, pady=10)
frame.pack(fill="both", expand=True)

tk.Button(frame, text="OK").pack(side="left", padx=5)
tk.Button(frame, text="Отмена").pack(side="right")

Параметры: side ("top", "bottom", "left", "right"), fill ("x", "y", "both"), expand=True — занять свободное место, padx/pady — отступы.

grid() размещает виджеты по таблице строк и столбцов — это удобно для форм:

  • row, column — ячейка;
  • columnspan, rowspan — объединение ячеек;
  • sticky — прижать к краям ячейки: "w" (влево), "e", "nsew" (растянуть);
  • root.columnconfigure(0, weight=1) — столбец растягивается вместе с окном.

Есть и третий менеджер, place(), — он ставит виджет по точным координатам x и y, но при изменении размера окна такая вёрстка «ломается».

Важно: внутри одного контейнера нельзя смешивать pack и grid. Нужно и то и другое — разделите окно на Frame и в каждом используйте один менеджер.

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

command у кнопок

Самый простой обработчик — функция в параметре command. Передавайте саму функцию, без скобок. Если нужны аргументы, оберните вызов в lambda:

tk.Button(root, text="Сохранить", command=save)                     # правильно
tk.Button(root, text="Удалить 5", command=lambda: delete_item(5))   # с аргументом

Пример: форма с grid и обработчиком

Небольшое приложение складывает два числа и показывает ошибку, если введены не числа:

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()
root.title("Сумма")

tk.Label(root, text="Первое число:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
tk.Label(root, text="Второе число:").grid(row=1, column=0, sticky="w", padx=5, pady=5)

entry_a = tk.Entry(root, width=10)
entry_a.grid(row=0, column=1, padx=5, pady=5)
entry_b = tk.Entry(root, width=10)
entry_b.grid(row=1, column=1, padx=5, pady=5)

result = tk.StringVar(value="Результат: —")


def calculate():
    try:
        total = float(entry_a.get()) + float(entry_b.get())
    except ValueError:
        messagebox.showerror("Ошибка", "Введите числа")
        return
    result.set(f"Результат: {total}")


tk.Button(root, text="Сложить", command=calculate).grid(row=2, column=0, columnspan=2, pady=5)
tk.Label(root, textvariable=result).grid(row=3, column=0, columnspan=2)

root.bind("<Return>", lambda event: calculate())  # Enter тоже запускает расчёт
root.mainloop()

Если ввести 2 и 3.5, надпись сменится на «Результат: 5.5». Диалоги messagebox (showinfo, showwarning, showerror, askyesno) открываются модальным окном; askyesno возвращает True или False. Перехват исключения разобран в статье про try/except.

bind: клавиши и мышь

Метод bind() связывает событие с функцией. Функция получает объект event с подробностями: event.keysym — имя клавиши, event.x и event.y — координаты курсора.

def on_key(event):
    print("Нажата клавиша:", event.keysym)

entry = tk.Entry(root)
entry.pack()
entry.bind("<KeyRelease>", on_key)       # отпускание клавиши в поле
root.bind("<Escape>", lambda e: root.destroy())

canvas = tk.Canvas(root, width=200, height=100, bg="white")
canvas.pack()
canvas.bind("<Button-1>", lambda e: print(e.x, e.y))  # клик левой кнопкой

Частые события: <Button-1> (левая кнопка мыши), <Double-Button-1>, <Return>, <Escape>, <KeyPress>, <Motion>.

after: таймеры без зависания

Обработчики выполняются в том же потоке, что и mainloop(). Пока функция работает, окно не перерисовывается и «зависает». Для периодических действий используйте root.after(миллисекунды, функция) вместо time.sleep():

import time

clock = tk.Label(root, font=("Arial", 24))
clock.pack()

def tick():
    clock.config(text=time.strftime("%H:%M:%S"))
    root.after(1000, tick)  # запланировать следующий вызов через секунду

tick()

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

Частые ошибки

Функция срабатывает сразу при запуске

tk.Button(root, text="Нажми", command=hello())  # ошибка: скобки

Со скобками hello() вызывается при создании кнопки, а в command попадает её результат (None). Уберите скобки или используйте lambda.

TclError при смешивании pack и grid

_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack

В одном контейнере (здесь . — главное окно) использованы оба менеджера. Оставьте один или разнесите виджеты по разным Frame.

Виджет создан, но не виден

Не вызван pack(), grid() или place(). Ещё одна ловушка — цепочка entry = tk.Entry(root).grid(...): grid() возвращает None, и в entry окажется None. Создавайте виджет и размещайте его отдельными строками.

TclError: no display name and no $DISPLAY environment variable

Ошибка _tkinter.TclError с таким текстом появляется на сервере или в Docker без графической среды. Tkinter нужен экран: запускайте программу на компьютере с рабочим столом или используйте виртуальный дисплей (например, Xvfb).

Окно зависает при долгой операции

Обработчик кнопки выполняет долгую работу, и mainloop() не может обработать события. Разбейте работу на части через after() или вынесите её в поток.

Частые вопросы

Как открыть окно по центру экрана?

Размер экрана возвращают winfo_screenwidth() и winfo_screenheight(). Вычтите размер окна, поделите пополам и передайте отступы в geometry():

w, h = 400, 300
x = (root.winfo_screenwidth() - w) // 2
y = (root.winfo_screenheight() - h) // 2
root.geometry(f"{w}x{h}+{x}+{y}")

Как изменить шрифт, размер и цвет текста?

Шрифт задаётся кортежем «семейство, размер, стиль», цвет текста — параметром fg, фон — bg:

label = tk.Label(root, text="Заголовок", font=("Arial", 16, "bold"), fg="white", bg="#1f77b4")

Стиль может быть "bold", "italic" или "bold italic". У виджетов ttk параметров fg и bg нет — их цвет меняют через ttk.Style.

Как удалить все виджеты из окна?

Метод winfo_children() возвращает дочерние виджеты окна или рамки, destroy() удаляет каждый:

for widget in root.winfo_children():
    widget.destroy()

Если нужно лишь временно скрыть виджет, вызовите pack_forget() или grid_remove() — потом его можно показать снова.

Как показать картинку в окне?

tk.PhotoImage в Tk 8.6 читает PNG и GIF. Сохраните ссылку на картинку в переменной, иначе сборщик мусора удалит её и вместо изображения останется пустое место:

img = tk.PhotoImage(file="bg.png")
label = tk.Label(root, image=img)
label.image = img                                      # держим ссылку
label.place(x=0, y=0, relwidth=1, relheight=1)         # картинка во всё окно, как фон

Для JPEG и изменения размера используйте Pillow: ImageTk.PhotoImage(Image.open("photo.jpg")).

Как сделать из программы на Tkinter exe-файл?

Соберите её через PyInstaller:

pip install pyinstaller
pyinstaller --onefile --windowed app.py

Готовый файл появится в папке dist. Флаг --windowed убирает чёрное окно консоли, --onefile собирает всё в один файл. Собирать нужно на той ОС, для которой предназначен файл: exe для Windows делают на Windows.

Итог

Приложение на Tkinter состоит из окна tk.Tk(), виджетов, размещённых через pack или grid, обработчиков в command и bind и вызова mainloop(). Если интерфейс нужен для своих скриптов, посмотрите идеи в статье про автоматизацию на Python. Для игр с графикой и анимацией лучше подходит Pygame, идеи первых программ с интерфейсом — в подборке проектов на Python. Другие популярные пакеты — в обзоре библиотек Python, полный справочник — в документации tkinter.

  • #Python
  • #Tkinter
  • #GUI
  • #Интерфейс
↑ ↓ выборEnter открытьEsc закрыть