Модуль tkinter: создание оконных приложений на Python
Основы работы с модулем tkinter
Основное решение: создание простого окна
tkinter - стандартная библиотека Python для создания графических интерфейсов. Первый шаг - импорт модуля и создание главного окна. Минимальный код:
import tkinter as tk
root = tk.Tk() # создаем главное окно
root.mainloop() # запускаем цикл обработки событийPython module attributes (атрибуты модуля в python)
После вызова mainloop() окно остается открытым до закрытия пользователем. Без этого вызова окно мгновенно закроется.
Типичная ошибка: забыть вызвать mainloop() - окно не появится. Еще одна проблема: повторный импорт модуля с переопределением имени - избегайте конфликтов.
Как создать окно с заданным размером и заголовком?
Методы geometry и title позволяют настроить окно:
import tkinter as tk
root = tk.Tk()
root.title("Мое приложение") # заголовок
root.geometry("400x300+100+50") # ширина x высота + смещение от левого края + от верхнего
root.mainloop()Python module version (версия модуля python)
Формат строки: "widthxheight+x+y". Если не указать смещение, окно появится в центре экрана (по умолчанию).
Ошибка: неверный формат (например, пробелы вместо x) вызывает исключение TclError. Проверяйте строку.
Как добавить кнопку и обработать нажатие?
Виджет Button создает кнопку. Параметр command связывает функцию с событием клика:
import tkinter as tk
def say_hello():
print("Привет!")
root = tk.Tk()
btn = tk.Button(root, text="Нажми меня", command=say_hello)
btn.pack() # размещаем кнопку
root.mainloop()
Python cpp module (взаимодействие python с модулями c++)
Функция say_hello выполняется при каждом нажатии. Чтобы передать аргументы, используют lambda или functools.partial.
Проблема: если передать command=func() (с круглыми скобками), функция выполнится один раз при создании кнопки, а не по клику. Всегда используйте ссылку на функцию без скобок или обертку lambda.
Как разместить несколько виджетов с помощью pack, grid, place?
Три менеджера геометрии: pack (упаковка в контейнер), grid (сетка), place (абсолютное позиционирование). Пример с grid:
import tkinter as tk
root = tk.Tk()
tk.Label(root, text="Имя:").grid(row=0, column=0)
tk.Entry(root).grid(row=0, column=1)
tk.Button(root, text="Войти").grid(row=1, columnspan=2)
root.mainloop()Python module cv2 (модуль cv2 (opencv) в python)
Строки и столбцы нумеруются с 0. columnspan объединяет ячейки. Менеджеры не должны смешиваться для одного родительского виджета - это вызовет ошибку.
Типичная ошибка: использовать одновременно pack и grid для разных виджетов с одним родителем - программа зависает или выдает исключение. Выбирайте один способ на контейнер.
Как создать текстовое поле и получить введенный текст?
Виджет Entry для однострочного ввода. Чтобы получить текст, используется метод get():
import tkinter as tk
def show_text():
content = entry.get()
print(f"Вы ввели: {content}")
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
btn = tk.Button(root, text="Показать", command=show_text)
btn.pack()
root.mainloop()Python encodings module (модуль encodings в python)
Если поле пустое, get() вернет пустую строку. Для многострочного текста используют Text и get("1.0", "end-1c").
Ошибка: попытка получить текст до того, как окно создано - виджет уже существует, но пользователь еще не ввел данные. Логично вызывать get() только по событию (например, нажатию кнопки).
Как создать метку и изменить ее текст?
Label отображает статический или динамический текст. Для обновления используйте метод config или label["text"] = ...:
import tkinter as tk
def update_label():
label.config(text="Текст изменен")
root = tk.Tk()
label = tk.Label(root, text="Исходный текст")
label.pack()
btn = tk.Button(root, text="Изменить", command=update_label)
btn.pack()
root.mainloop()Метка может содержать не только текст, но и изображения (параметр image). Для обновления картинки потребуется пересоздать объект PhotoImage.
Проблема: если изменить текст до запуска mainloop(), окно все равно отобразится с новым текстом. Однако стандартная практика - изменять виджеты во время работы приложения через события.
Расширенные примеры использования tkinter
Рисование на холсте (Canvas)
Виджет Canvas позволяет рисовать линии, фигуры, текст и изображения. Пример создания простой анимации круга:
import tkinter as tk
def move_ball():
canvas.move(ball, 2, 0) # сдвигаем мяч на 2 пикселя вправо
root.after(50, move_ball) # повторяем через 50 мс
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
ball = canvas.create_oval(10, 10, 40, 40, fill="red")
move_ball()
root.mainloop()Метод after планирует следующий вызов функции, создавая анимацию. Координаты фигур задаются как (x1, y1, x2, y2).
Красный круг плавно движется слева направо по белому холсту.
Таблица с использованием Treeview (ttk)
Виджет Treeview из модуля ttk (тематические виджеты) поддерживает таблицы с колонками и иерархические данные. Пример таблицы контактов:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
tree = ttk.Treeview(root, columns=("name", "phone"), show="headings")
tree.heading("name", text="Имя")
tree.heading("phone", text="Телефон")
tree.insert("", "end", values=("Иван", "+7-123-456-78-90"))
tree.insert("", "end", values=("Мария", "+7-098-765-43-21"))
tree.pack()
root.mainloop()Параметр show="headings" скрывает пустой первый столбец (обычно используемый для иерархии). Для добавления строк используется insert с родительским элементом (пустая строка - корень).
Отображается таблица с двумя колонками и двумя строками.
Вкладки Notebook (ttk)
Виджет Notebook создает набор вкладок. Каждая вкладка содержит отдельный фрейм. Пример:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
notebook = ttk.Notebook(root)
notebook.pack(fill="both", expand=True)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Вкладка 1")
notebook.add(frame2, text="Вкладка 2")
ttk.Label(frame1, text="Содержимое первой вкладки").pack()
ttk.Label(frame2, text="Содержимое второй вкладки").pack()
root.mainloop()Фреймы добавляются через add, параметр text задает заголовок вкладки. fill и expand растягивают виджет при изменении окна.
Окно с двумя вкладками, при переключении отображается соответствующее содержимое.
Работа с изображениями (PhotoImage)
tkinter поддерживает формат GIF и PNG (через PhotoImage, но PNG только в Python 3.11+). Для других форматов используют Pillow. Пример отображения изображения на метке:
import tkinter as tk
root = tk.Tk()
img = tk.PhotoImage(file="example.gif") # замените на существующий файл
label = tk.Label(root, image=img)
label.pack()
root.mainloop()Важно: переменная img должна жить, пока окно открыто. Если присвоить image в локальной переменной функции, изображение может не отобразиться (сборщик мусора). Решение - сохранить ссылку (например, в атрибуте виджета: label.image = img).
Метка отображает выбранное изображение из файла.
Создание собственного диалогового окна (Toplevel)
Виджет Toplevel создает дополнительное окно, независимое от главного. Пример окна с пользовательским вводом:
import tkinter as tk
def open_dialog():
dialog = tk.Toplevel(root)
dialog.title("Введите данные")
tk.Label(dialog, text="Имя:").pack()
entry = tk.Entry(dialog)
entry.pack()
def submit():
print(f"Введено: {entry.get()}")
dialog.destroy()
tk.Button(dialog, text="OK", command=submit).pack()
root = tk.Tk()
tk.Button(root, text="Открыть диалог", command=open_dialog).pack()
root.mainloop()Метод destroy закрывает дочернее окно. Если нужно сделать модальным (блокировать взаимодействие с главным окном), используйте grab_set().
При нажатии кнопки открывается новое окно, после ввода текста и нажатия OK данные выводятся в консоль.
Использование StringVar для автоматического обновления
Переменные tkinter (StringVar, IntVar и т.д.) позволяют синхронизировать значение между виджетами. Пример с меткой и полем ввода:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar()
tk.Entry(root, textvariable=var).pack()
tk.Label(root, textvariable=var).pack() # автоматически отображает введенный текст
root.mainloop()Любое изменение в Entry немедленно меняет текст метки. Можно также подписаться на изменения через trace.
При вводе текста в поле он сразу дублируется в метке.
Привязка клавиш (bind) и событий мыши
Метод bind связывает любое событие (нажатие клавиши, движение мыши) с функцией-обработчиком. Пример обработки нажатия Enter в Entry:
import tkinter as tk
def on_enter(event):
print("Нажат Enter")
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
entry.bind("", on_enter) # событие Return - клавиша Enter
root.mainloop() Событие передает объект event с информацией (координаты, код клавиши). Для мыши используют <Button-1> (левая кнопка), <Motion> (движение).
При нажатии Enter в поле ввода в консоль выводится сообщение.