Работа с Tkinter: виджеты, компоновка и события

Раздел: Python -> GUI библиотеки

Основы Tkinter

Эффективное решение для быстрого старта

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


import tkinter as tk

def change_label():
    label.config(text="Текст изменён")

root = tk.Tk()
root.title("Простое окно")
root.geometry("300x200")

label = tk.Label(root, text="Начальный текст", font=("Arial", 14))
label.pack(pady=20)

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

root.mainloop()
  

Python графическая библиотека tkinter (графическая библиотека tkinter в python)

Пояснение:

  • tk.Tk() создаёт главное окно.
  • tk.Label - статический текст.
  • tk.Button - кнопка с вызовом функции command.
  • pack() - менеджер геометрии, размещает виджеты сверху вниз.
  • mainloop() запускает цикл обработки событий, без него окно не появится.

Типичные ошибки:

  • Забытый вызов mainloop() - окно не отображается.
  • Указание неверного имени функции в command (без скобок, только имя).
  • Использование pack и grid одновременно в одном контейнере - приведёт к ошибке cannot use geometry manager pack with grid.
  • Проблемы с локалью (лат.) - если система настроена не на UTF-8, возможны кракозябры. Рекомендуется явно указывать кодировку или использовать латинские символы.

Как расположить виджеты с помощью менеджера grid?

Менеджер grid размещает виджеты в таблице. Укажите строку (row) и столбец (column).


import tkinter as tk

root = tk.Tk()
root.title("Сетка")

label1 = tk.Label(root, text="Метка 1", bg="red", fg="white")
label1.grid(row=0, column=0, padx=5, pady=5)

label2 = tk.Label(root, text="Метка 2", bg="blue", fg="white")
label2.grid(row=0, column=1, padx=5, pady=5)

label3 = tk.Label(root, text="Метка 3", bg="green", fg="white")
label3.grid(row=1, column=0, columnspan=2, sticky="we", padx=5, pady=5)

root.mainloop()
  

Параметр sticky определяет привязку к сторонам ячейки ("nsew" - растянуть на всю ячейку). columnspan объединяет несколько столбцов.

Ошибка: если не задать sticky, виджет может не растянуться при изменении размера окна. Используйте grid_rowconfigure и grid_columnconfigure для весов.

Как использовать менеджер place для точного позиционирования?

place позволяет задавать абсолютные координаты в пикселях.


import tkinter as tk

root = tk.Tk()
root.geometry("300x200")

btn = tk.Button(root, text="Кнопка")
btn.place(x=100, y=50, width=80, height=30)

root.mainloop()
  

Подходит для фиксированных макетов, но плохо адаптируется к изменению размера окна.

Проблема: при изменении размеров окна виджеты остаются на месте. Решение - использовать относительное позиционирование: relx=0.5, rely=0.5, anchor="center".

Как добавить меню в окно Tkinter?

Создайте объект Menu и привяжите его к окну.


import tkinter as tk

def new_file():
    print("Новый файл")

root = tk.Tk()
menubar = tk.Menu(root)
root.config(menu=menubar)

file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="Новый", command=new_file)
file_menu.add_separator()
file_menu.add_command(label="Выход", command=root.quit)
menubar.add_cascade(label="Файл", menu=file_menu)

root.mainloop()
  

Параметр tearoff=0 убирает пунктирную линию отрыва.

Как рисовать на холсте Canvas?

Холст поддерживает фигуры: линии, прямоугольники, овалы, текст.


import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()

canvas.create_rectangle(50, 50, 150, 150, fill="blue", outline="black")
canvas.create_oval(200, 50, 300, 150, fill="red")
canvas.create_line(10, 10, 390, 290, fill="green", width=3)

root.mainloop()
  

Координаты задаются как (x1, y1, x2, y2).

Ошибка: забыли обновить холст после изменений? Canvas обновляется автоматически при каждом вызове метода create_*.

Как открыть диалог выбора файла?

Используйте модуль filedialog из Tkinter.


import tkinter as tk
from tkinter import filedialog

def open_file():
    file_path = filedialog.askopenfilename(
        title="Выберите файл",
        filetypes=(("Текстовые файлы", "*.txt"), ("Все файлы", "*.*"))
    )
    if file_path:
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()
            text.insert("1.0", content)

root = tk.Tk()
text = tk.Text(root)
text.pack(fill="both", expand=True)
btn = tk.Button(root, text="Открыть", command=open_file)
btn.pack()
root.mainloop()
  

Функция возвращает путь к файлу или пустую строку, если пользователь нажал Отмена.

Как избежать заморозки интерфейса при длительных операциях?

Используйте метод after для планирования задач.


import tkinter as tk
import time

def long_task():
    # Имитация долгой работы
    for i in range(10):
        time.sleep(0.5)
        progress_var.set(i+1)
        root.update()  # принудительное обновление

def start_task():
    # Запускаем в отдельном потоке? Нет, используем after для прерывания
    root.after(100, long_task)  # Неправильно! long_task блокирует цикл

root = tk.Tk()
progress_var = tk.IntVar()
progress = tk.Progressbar(root, variable=progress_var, maximum=10)
progress.pack(pady=20)
btn = tk.Button(root, text="Старт", command=start_task)
btn.pack()
root.mainloop()
  

Правильный подход - разбить задачу на маленькие шаги и выполнять их через after.

Проблема: прямой вызов time.sleep() в обработчике блокирует главный цикл. Решение - использовать root.after() для планирования следующего шага.

Расширенные примеры использования Tkinter

Пример 1: Простой калькулятор с Grid

Пример

import tkinter as tk

def calculate():
    try:
        result = eval(entry.get())
        entry.delete(0, tk.END)
        entry.insert(tk.END, str(result))
    except Exception as e:
        entry.delete(0, tk.END)
        entry.insert(tk.END, "Ошибка")

root = tk.Tk()
root.title("Калькулятор")

entry = tk.Entry(root, width=16, font=("Arial", 14), justify="right")
entry.grid(row=0, column=0, columnspan=4, padx=5, pady=5)

buttons = [
    '7', '8', '9', '/',
    '4', '5', '6', '*',
    '1', '2', '3', '-',
    '0', '.', '=', '+'
]

row = 1
col = 0
for text in buttons:
    if text == '=':
        btn = tk.Button(root, text=text, width=5, height=2, command=calculate)
    else:
        btn = tk.Button(root, text=text, width=5, height=2, command=lambda t=text: entry.insert(tk.END, t))
    btn.grid(row=row, column=col, padx=2, pady=2)
    col += 1
    if col > 3:
        col = 0
        row += 1

root.mainloop()
Результат: окно калькулятора с кнопками и полем ввода. При нажатии '=' выполняется eval и отображается результат.

Пример 2: Анимация на Canvas с использованием after

Пример

import tkinter as tk

def move_ball():
    global x, y, dx, dy
    canvas.move(ball, dx, dy)
    x += dx
    y += dy
    if x <= 0 or x >= 350:
        dx = -dx
    if y <= 0 or y >= 250:
        dy = -dy
    root.after(20, move_ball)

root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg="lightgray")
canvas.pack()

x, y, dx, dy = 50, 50, 2, 3
ball = canvas.create_oval(x, y, x+30, y+30, fill="red")

move_ball()
root.mainloop()
Результат: красный круг движется по холсту, отскакивая от границ. Анимация плавная.

Пример 3: Приложение с вкладками (ttk.Notebook)

Пример

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.title("Вкладки")

notebook = ttk.Notebook(root)
notebook.pack(fill="both", expand=True)

tab1 = ttk.Frame(notebook)
notebook.add(tab1, text="Первая вкладка")
label1 = ttk.Label(tab1, text="Содержимое первой вкладки")
label1.pack(pady=20)

tab2 = ttk.Frame(notebook)
notebook.add(tab2, text="Вторая вкладка")
entry2 = ttk.Entry(tab2)
entry2.pack(pady=20)

root.mainloop()
Результат: окно с двумя вкладками, переключение между ними.

Пример 4: Использование StringVar и trace для реакции на изменение

Пример

import tkinter as tk

def on_text_change(*args):
    label.config(text="Вы печатаете: " + var.get())

root = tk.Tk()
var = tk.StringVar()
var.trace_add("write", on_text_change)

entry = tk.Entry(root, textvariable=var)
entry.pack(pady=10)

label = tk.Label(root, text="Вы печатаете: ")
label.pack(pady=10)

root.mainloop()
Результат: при каждом вводе символа в поле, метка обновляется.

Графическая библиотека Tkinter в Python - comments

En
Python графическая библиотека tkinter (python)