
Привет, Хабр! Меня зовут Леша Жиряков, я техлид бэкенд-команды витрины KION, а еще в МТС я возглавляю Python-гильдию.
В 2025 году Python остается одним из самых популярных языков программирования, а его возможности для создания графических интерфейсов (GUI) продолжают радовать разработчиков. В этой подборке — шесть лучших инструментов, которые выделяются функциями, активностью сообщества и фишками. Погружаемся в мир кнопок, окон и виджетов — от проверенной классики до ярких новичков GitHub.
Tkinter

Tkinter как Python-интерфейс разработали в начале 1990-х, в основном при участии Гвидо ван Россума (Guido van Rossum) и других ранних разработчиков Python. В апреле 2025 года стандартная версия Python (3.12+) использует Tcl/Tk 8.6.13.
Что касается возможностей, то это встроенная библиотека Python для создания графических интерфейсов. Поэтому, в отличие от других библиотек из подборки, я не указываю версию, коммиты и звезды. Она дает вам набор виджетов (кнопки, текстовые поля, метки, меню и так далее) и менеджеры геометрии (pack, grid, place) для организации интерфейса. Поскольку база — Tcl/Tk, то библиотека кроссплатформенная (Windows, macOS, Linux). Tkinter прост в использовании, дополнительных установок нет, а это делает его идеальным для начинающих юзеров и быстрых прототипов. Еще включает модуль ttk (Themed Tk), который добавляет современные виджеты с нативным стилем.
Достоинства:
Встроенность: не нужно ничего устанавливать дополнительно, идет с Python.
Простота: легко освоить, минималистичный синтаксис для базовых приложений.
Кроссплатформенность: работает везде, где есть Python и Tcl/Tk.
Стабильность: зрелая и проверенная временем библиотека.
Мощные виджеты: текстовые поля и канвас (Canvas) предлагают гибкость для сложных задач.
Недостатки:
Устаревший внешний вид: классические виджеты Tkinter выглядят старомодно без ttk.
Несколько ограниченные возможности: нет продвинутых современных виджетов — например, встроенного браузера или комплексных графиков.
В целом, это надежный выбор для не очень сложных GUI-проектов в 2025 году. Особенно если вы цените скорость разработки и не хотите возиться с зависимостями. Для новичков или прототипирования — идеально, но для сложных современных интерфейсов стоит рассмотреть альтернативы. О них — ниже.
В каких проектах используется:
IDLE (Python's Integrated Development Environment) — встроенная IDE Python, которая полностью создана с использованием Tkinter.
Thonny — это IDE для Python, ориентированная на образовательные цели, полностью построенная с использованием Tkinter.
Matplotlib (интерфейс TkAgg) — популярная библиотека для визуализации данных в Python. Использует Tkinter как один из бэкендов для интерактивных графиков.
Pillow (PIL Fork) — популярная библиотека для обработки изображений, включает компоненты для интеграции с Tkinter.
Pmw (Python Mega Widgets) — библиотека высокоуровневых виджетов, построенная поверх Tkinter.
SymPy (Plotting с Tkinter) — библиотека SymPy для символьных вычислений использует Tkinter в своем модуле построения графиков.
Пример использования:
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
class TkinterDemoApp:
def __init__(self, root):
self.root = root
self.root.title("Демонстрация Tkinter виджетов")
self.root.geometry("800x600")
self.root.resizable(True, True)
# Настройка стилей
self.style = ttk.Style()
self.style.theme_use('clam')
self.create_widgets()
def create_widgets(self):
# Создаем основную структуру
self.main_frame = ttk.Frame(self.root, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# Панель вкладок
self.notebook = ttk.Notebook(self.main_frame)
self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# Вкладка "Основные виджеты"
self.basic_tab = ttk.Frame(self.notebook)
self.notebook.add(self.basic_tab, text="Основные")
self.create_basic_widgets()
# Вкладка "Продвинутые виджеты"
self.advanced_tab = ttk.Frame(self.notebook)
self.notebook.add(self.advanced_tab, text="Продвинутые")
self.create_advanced_widgets()
# Вкладка "Действия"
self.actions_tab = ttk.Frame(self.notebook)
self.notebook.add(self.actions_tab, text="Действия")
self.create_actions_widgets()
def create_basic_widgets(self):
"""Создание основных виджетов"""
frame = ttk.LabelFrame(self.basic_tab, text="Базовые элементы", padding="10")
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# Метка и поле ввода
ttk.Label(frame, text="Имя пользователя:").grid(row=0, column=0, sticky=tk.W, pady=2)
self.username_entry = ttk.Entry(frame)
self.username_entry.grid(row=0, column=1, sticky=tk.EW, padx=5, pady=2)
# Пароль
ttk.Label(frame, text="Пароль:").grid(row=1, column=0, sticky=tk.W, pady=2)
self.password_entry = ttk.Entry(frame, show="*")
self.password_entry.grid(row=1, column=1, sticky=tk.EW, padx=5, pady=2)
# Чекбоксы
self.remember_var = tk.BooleanVar()
ttk.Checkbutton(frame, text="Запомнить меня", variable=self.remember_var).grid(
row=2, column=0, columnspan=2, sticky=tk.W, pady=5)
# Радиокнопки
ttk.Label(frame, text="Выберите вариант:").grid(row=3, column=0, sticky=tk.W, pady=2)
self.radio_var = tk.StringVar(value="option1")
ttk.Radiobutton(frame, text="Вариант 1", value="option1", variable=self.radio_var).grid(
row=4, column=0, sticky=tk.W)
ttk.Radiobutton(frame, text="Вариант 2", value="option2", variable=self.radio_var).grid(
row=4, column=1, sticky=tk.W)
# Выпадающий список
ttk.Label(frame, text="Выберите город:").grid(row=5, column=0, sticky=tk.W, pady=2)
self.city_combo = ttk.Combobox(frame, values=["Москва", "Санкт-Петербург", "Новосибирск", "Екатеринбург"])
self.city_combo.grid(row=5, column=1, sticky=tk.EW, padx=5, pady=2)
self.city_combo.current(0)
# Шкала (Slider)
ttk.Label(frame, text="Уровень:").grid(row=6, column=0, sticky=tk.W, pady=2)
self.scale = ttk.Scale(frame, from_=0, to=100, orient=tk.HORIZONTAL)
self.scale.grid(row=6, column=1, sticky=tk.EW, padx=5, pady=2)
# Настройка веса колонок для правильного растягивания
frame.columnconfigure(1, weight=1)
def create_advanced_widgets(self):
"""Создание продвинутых виджетов"""
frame = ttk.LabelFrame(self.advanced_tab, text="Продвинутые элементы", padding="10")
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# Дерево (Treeview)
self.tree = ttk.Treeview(frame, columns=("name", "age", "city"), show="headings")
self.tree.heading("name", text="Имя")
self.tree.heading("age", text="Возраст")
self.tree.heading("city", text="Город")
# Добавляем данные
self.tree.insert("", tk.END, values=("Иван Иванов", 25, "Москва"))
self.tree.insert("", tk.END, values=("Петр Петров", 30, "Санкт-Петербург"))
self.tree.insert("", tk.END, values=("Анна Сидорова", 22, "Новосибирск"))
self.tree.grid(row=0, column=0, columnspan=2, sticky=tk.NSEW, pady=5)
# Прогресс бар
self.progress = ttk.Progressbar(frame, orient=tk.HORIZONTAL, length=200, mode="determinate")
self.progress.grid(row=1, column=0, columnspan=2, sticky=tk.EW, pady=5)
# Панель вкладок внутри вкладки
sub_notebook = ttk.Notebook(frame)
sub_notebook.grid(row=2, column=0, columnspan=2, sticky=tk.NSEW, pady=5)
tab1 = ttk.Frame(sub_notebook)
tab2 = ttk.Frame(sub_notebook)
sub_notebook.add(tab1, text="Вкладка 1")
sub_notebook.add(tab2, text="Вкладка 2")
ttk.Label(tab1, text="Это содержимое первой вкладки").pack(pady=20)
ttk.Label(tab2, text="Это содержимое второй вкладки").pack(pady=20)
# Настройка веса для правильного растягивания
frame.columnconfigure(0, weight=1)
frame.rowconfigure(0, weight=1)
def create_actions_widgets(self):
"""Создание виджетов для выполнения действий"""
frame = ttk.LabelFrame(self.actions_tab, text="Действия", padding="10")
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# Кнопки
ttk.Button(frame, text="Показать сообщение", command=self.show_message).grid(
row=0, column=0, sticky=tk.EW, padx=5, pady=5)
ttk.Button(frame, text="Запустить прогресс", command=self.start_progress).grid(
row=0, column=1, sticky=tk.EW, padx=5, pady=5)
ttk.Button(frame, text="Открыть файл", command=self.open_file).grid(
row=1, column=0, sticky=tk.EW, padx=5, pady=5)
ttk.Button(frame, text="Сохранить файл", command=self.save_file).grid(
row=1, column=1, sticky=tk.EW, padx=5, pady=5)
# Текстовое поле с прокруткой
self.text = tk.Text(frame, wrap=tk.WORD, height=10)
self.scroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=self.text.yview)
self.text.configure(yscrollcommand=self.scroll.set)
self.text.grid(row=2, column=0, columnspan=2, sticky=tk.NSEW, pady=5)
self.scroll.grid(row=2, column=2, sticky=tk.NS, pady=5)
# Настройка веса для правильного растягивания
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
frame.rowconfigure(2, weight=1)
def show_message(self):
"""Показать диалоговое окно с информацией"""
username = self.username_entry.get()
city = self.city_combo.get()
level = self.scale.get()
message = f"Имя: {username}\nГород: {city}\nУровень: {level:.0f}"
messagebox.showinfo("Информация", message)
def start_progress(self):
"""Запустить анимацию прогресс-бара"""
self.progress["value"] = 0
self.root.after(100, self.update_progress)
def update_progress(self):
"""Обновить прогресс-бар"""
if self.progress["value"] < 100:
self.progress["value"] += 5
self.root.after(100, self.update_progress)
def open_file(self):
"""Открыть диалог выбора файла"""
file_path = filedialog.askopenfilename(
title="Открыть файл",
filetypes=(("Текстовые файлы", "*.txt"), ("Все файлы", "*.*"))
)
if file_path:
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
self.text.delete(1.0, tk.END)
self.text.insert(tk.END, content)
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось открыть файл:\n{str(e)}")
def save_file(self):
"""Открыть диалог сохранения файла"""
file_path = filedialog.asksaveasfilename(
title="Сохранить файл",
defaultextension=".txt",
filetypes=(("Текстовые файлы", "*.txt"), ("Все файлы", "*.*"))
)
if file_path:
try:
with open(file_path, 'w', encoding='utf-8') as file:
file.write(self.text.get(1.0, tk.END))
messagebox.showinfo("Успех", "Файл успешно сохранен")
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить файл:\n{str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = TkinterDemoApp(root)
root.mainloop()
Что получилось:





Qt

Разработка Qt началась в 1991 году в норвежской компании Trolltech, основанной Хаавардом Нордом и Эйриком Чумбсом. Первая стабильная версия фреймворка вышла в 1995 году. В 2008 году Trolltech была приобретена Nokia, позже Qt перешел к Digia, а с 2014 года поддерживается независимой компанией The Qt Company. Фреймворк изначально разрабатывался для кроссплатформенных приложений на C++ и стал стандартом для создания графических интерфейсов.
Количество звезд на GitHub: 2,7k.
Коммиты: свыше 72k.
Последняя стабильная версия: 6.9.0.
Дата последнего релиза: 8 апреля 2025 года.
Дата последнего коммита: 17 апреля 2025 года.
Qt — кроссплатформенный фреймворк с открытым исходным кодом (GPL v3). Его можно использовать бесплатно. Но если вы планируете создавать приложение, не делясь исходным кодом, придется приобрести коммерческую лицензию. Предназначен Qt для создания высокопроизводительных графических интерфейсов. Работает на Windows, macOS, Linux, Android, iOS, встраиваемых системах и даже WebAssembly. Использует C++ и OpenGL для рендеринга, обеспечивая быструю работу и нативный вид приложений. Предлагает обширный набор виджетов, поддержку 2D/3D-графики, анимаций и инструмент Qt Designer для визуального дизайна интерфейсов. Подходит для десктопных программ, мобильных приложений, автомобильных систем и IoT-устройств.
Достоинства:
Кроссплатформенность: один код для множества платформ с нативным стилем интерфейса.
Производительность: благодаря C++ и OpenGL фреймворк справляется с тяжелыми задачами.
Инструменты: Qt Designer и QML упрощают создание сложных и динамичных интерфейсов.
Экосистема: поддержка сетей, баз данных, мультимедиа и 3D-графики в одном пакете.
Недостатки:
Сложность: обилие API и возможностей требует времени на освоение.
Размер сборок: зависимости Qt увеличивают объем приложений.
Настройка: сборка и установка могут быть сложными для специфичных платформ.
В 2025 году Qt остается прекрасным инструментом для разработчиков разных приложений — от корпоративного ПО и медиацентров до встраиваемых систем в автомобилях и IoT. Все хорошо с библиотекой, но она не так проста в изучении.
В каких проектах используется:
VLC Media Player — один из популярнейших медиаплееров, полностью построен на Qt.
Telegram Desktop — официальный десктопный клиент Telegram использует Qt.
KDE Plasma — вся экосистема KDE, включая рабочий стол Plasma и приложения. Построена на Qt.
Autodesk Maya — профессиональное ПО для 3D-моделирования использует Qt для своего интерфейса.
Google Earth использует Qt для десктопной версии приложения.
Blender использует Qt для своего файлового браузера.
Spotify (ранние версии) — десктопный клиент Spotify ранее был построен на Qt.
Wolfram Mathematica — система компьютерной алгебры Mathematica использует Qt.
Scribus — профессиональное приложение для верстки с открытым исходным кодом.
LumaFusion — профессиональный мобильный видеоредактор, недавно портированный на Qt для поддержки новых платформ.
Пример использования:
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QPushButton, QLineEdit,
QComboBox, QCheckBox, QRadioButton, QSlider,
QProgressBar, QSpinBox, QDoubleSpinBox, QTextEdit,
QListWidget, QTabWidget, QMessageBox, QInputDialog)
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QIcon, QPixmap
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt6 Widgets Demo")
self.setGeometry(100, 100, 800, 600)
# Центральный виджет и основной layout
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout()
central_widget.setLayout(main_layout)
# Создаем вкладки для разных групп виджетов
self.tabs = QTabWidget()
main_layout.addWidget(self.tabs)
# Вкладка с базовыми виджетами
self.create_basic_widgets_tab()
# Вкладка с виджетами ввода
self.create_input_widgets_tab()
# Вкладка с индикаторами
self.create_indicator_widgets_tab()
# Вкладка с контейнерами
self.create_container_widgets_tab()
# Статус бар
self.statusBar().showMessage("Готово")
def create_basic_widgets_tab(self):
"""Создаем вкладку с базовыми виджетами"""
tab = QWidget()
layout = QVBoxLayout()
tab.setLayout(layout)
# Метка с изображением
label = QLabel("Демонстрация PyQt6 Widgets")
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
font = label.font()
font.setPointSize(16)
label.setFont(font)
layout.addWidget(label)
# Изображение
image_label = QLabel()
pixmap = QPixmap(":images/python-logo.png") # Используем встроенный ресурс
if not pixmap.isNull():
image_label.setPixmap(pixmap.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio))
image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(image_label)
# Кнопки
btn_layout = QHBoxLayout()
btn1 = QPushButton("Обычная кнопка")
btn1.clicked.connect(self.on_button_click)
btn_layout.addWidget(btn1)
btn2 = QPushButton(QIcon(":images/python-logo.png"), "Кнопка с иконкой")
btn2.clicked.connect(self.on_button_click)
btn_layout.addWidget(btn2)
btn3 = QPushButton("Отключенная кнопка")
btn3.setEnabled(False)
btn_layout.addWidget(btn3)
layout.addLayout(btn_layout)
# Горизонтальная линия
layout.addWidget(QLabel("<hr>"), 1)
# Текстовое поле
self.line_edit = QLineEdit()
self.line_edit.setPlaceholderText("Введите текст здесь...")
self.line_edit.textChanged.connect(self.on_text_changed)
layout.addWidget(self.line_edit)
self.tabs.addTab(tab, "Базовые виджеты")
def create_input_widgets_tab(self):
"""Создаем вкладку с виджетами ввода"""
tab = QWidget()
layout = QVBoxLayout()
tab.setLayout(layout)
# Комбобокс (выпадающий список)
combo_label = QLabel("Выберите вариант:")
layout.addWidget(combo_label)
self.combo_box = QComboBox()
self.combo_box.addItems(["Вариант 1", "Вариант 2", "Вариант 3", "Вариант 4"])
self.combo_box.currentIndexChanged.connect(self.on_combo_changed)
layout.addWidget(self.combo_box)
# Чекбоксы
check_label = QLabel("Выберите опции:")
layout.addWidget(check_label)
self.check1 = QCheckBox("Опция 1")
self.check1.stateChanged.connect(self.on_check_changed)
layout.addWidget(self.check1)
self.check2 = QCheckBox("Опция 2")
self.check2.stateChanged.connect(self.on_check_changed)
layout.addWidget(self.check2)
# Радио кнопки
radio_label = QLabel("Выберите один вариант:")
layout.addWidget(radio_label)
self.radio1 = QRadioButton("Вариант A")
self.radio1.toggled.connect(self.on_radio_toggled)
layout.addWidget(self.radio1)
self.radio2 = QRadioButton("Вариант B")
self.radio2.toggled.connect(self.on_radio_toggled)
layout.addWidget(self.radio2)
self.radio3 = QRadioButton("Вариант C")
self.radio3.toggled.connect(self.on_radio_toggled)
layout.addWidget(self.radio3)
# Слайдер
slider_label = QLabel("Регулировка значения:")
layout.addWidget(slider_label)
self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setMinimum(0)
self.slider.setMaximum(100)
self.slider.setValue(50)
self.slider.valueChanged.connect(self.on_slider_changed)
layout.addWidget(self.slider)
# Спинбоксы
spin_layout = QHBoxLayout()
self.spin_box = QSpinBox()
self.spin_box.setRange(0, 100)
self.spin_box.setValue(25)
self.spin_box.valueChanged.connect(self.on_spin_changed)
spin_layout.addWidget(self.spin_box)
self.double_spin = QDoubleSpinBox()
self.double_spin.setRange(0, 10)
self.double_spin.setSingleStep(0.1)
self.double_spin.setValue(2.5)
self.double_spin.valueChanged.connect(self.on_double_spin_changed)
spin_layout.addWidget(self.double_spin)
layout.addLayout(spin_layout)
self.tabs.addTab(tab, "Виджеты ввода")
def create_indicator_widgets_tab(self):
"""Создаем вкладку с индикаторами"""
tab = QWidget()
layout = QVBoxLayout()
tab.setLayout(layout)
# Прогресс бар
progress_label = QLabel("Прогресс бар:")
layout.addWidget(progress_label)
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
layout.addWidget(self.progress_bar)
# Кнопка для запуска прогресса
self.progress_btn = QPushButton("Запустить прогресс")
self.progress_btn.clicked.connect(self.start_progress)
layout.addWidget(self.progress_btn)
# Таймер для прогресс бара
self.progress_timer = QTimer()
self.progress_timer.timeout.connect(self.update_progress)
self.progress_value = 0
# Метка для отображения значений
self.value_label = QLabel("Значения будут отображаться здесь")
self.value_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.value_label)
self.tabs.addTab(tab, "Индикаторы")
def create_container_widgets_tab(self):
"""Создаем вкладку с контейнерными виджетами"""
tab = QWidget()
layout = QVBoxLayout()
tab.setLayout(layout)
# Текстовый редактор
text_edit_label = QLabel("Текстовый редактор:")
layout.addWidget(text_edit_label)
self.text_edit = QTextEdit()
self.text_edit.setPlaceholderText("Введите текст здесь...")
layout.addWidget(self.text_edit)
# Кнопки для работы с текстом
text_btn_layout = QHBoxLayout()
clear_btn = QPushButton("Очистить")
clear_btn.clicked.connect(self.text_edit.clear)
text_btn_layout.addWidget(clear_btn)
get_text_btn = QPushButton("Показать текст")
get_text_btn.clicked.connect(self.show_text)
text_btn_layout.addWidget(get_text_btn)
layout.addLayout(text_btn_layout)
# Список
list_label = QLabel("Список элементов:")
layout.addWidget(list_label)
self.list_widget = QListWidget()
self.list_widget.addItems(["Элемент 1", "Элемент 2", "Элемент 3"])
self.list_widget.itemClicked.connect(self.on_list_item_clicked)
layout.addWidget(self.list_widget)
# Кнопки для работы со списком
list_btn_layout = QHBoxLayout()
add_item_btn = QPushButton("Добавить")
add_item_btn.clicked.connect(self.add_list_item)
list_btn_layout.addWidget(add_item_btn)
remove_item_btn = QPushButton("Удалить")
remove_item_btn.clicked.connect(self.remove_list_item)
list_btn_layout.addWidget(remove_item_btn)
layout.addLayout(list_btn_layout)
self.tabs.addTab(tab, "Контейнеры")
# Методы обработки событий
def on_button_click(self):
sender = self.sender()
QMessageBox.information(self, "Кнопка нажата", f"Нажата кнопка: {sender.text()}")
def on_text_changed(self, text):
self.statusBar().showMessage(f"Текст изменен: {text}", 2000)
def on_combo_changed(self, index):
self.value_label.setText(f"Выбран комбобокс: {self.combo_box.currentText()} (индекс {index})")
def on_check_changed(self, state):
sender = self.sender()
checked = "включена" if state else "отключена"
self.value_label.setText(f"Опция {sender.text()} {checked}")
def on_radio_toggled(self, checked):
if checked:
sender = self.sender()
self.value_label.setText(f"Выбран радио-вариант: {sender.text()}")
def on_slider_changed(self, value):
self.value_label.setText(f"Значение слайдера: {value}")
def on_spin_changed(self, value):
self.value_label.setText(f"Значение спинбокса: {value}")
def on_double_spin_changed(self, value):
self.value_label.setText(f"Значение double спинбокса: {value:.2f}")
def start_progress(self):
if not self.progress_timer.isActive():
self.progress_value = 0
self.progress_bar.setValue(0)
self.progress_timer.start(100)
self.progress_btn.setText("Остановить прогресс")
else:
self.progress_timer.stop()
self.progress_btn.setText("Запустить прогресс")
def update_progress(self):
self.progress_value += 1
self.progress_bar.setValue(self.progress_value)
if self.progress_value >= 100:
self.progress_timer.stop()
self.progress_btn.setText("Запустить прогресс")
QMessageBox.information(self, "Готово", "Прогресс завершен!")
def show_text(self):
text = self.text_edit.toPlainText()
if text:
QMessageBox.information(self, "Текст", text)
else:
QMessageBox.warning(self, "Ошибка", "Текст не введен!")
def on_list_item_clicked(self, item):
self.value_label.setText(f"Выбран элемент списка: {item.text()}")
def add_list_item(self):
text, ok = QInputDialog.getText(self, "Добавить элемент", "Введите текст элемента:")
if ok and text:
self.list_widget.addItem(text)
def remove_list_item(self):
current_item = self.list_widget.currentItem()
if current_item:
self.list_widget.takeItem(self.list_widget.row(current_item))
if __name__ == "__main__":
app = QApplication(sys.argv)
# Устанавливаем стиль для приложения
app.setStyle("Fusion")
window = MainWindow()
window.show()
sys.exit(app.exec())
Что получилось:






Особенности примера:
Структура приложения:
Использование QMainWindow как основного окна.
Организация кода в методы для создания разных вкладок.
Чистая архитектура с разделением на UI и логику.
Демонстрация виджетов:
Базовые: QLabel, QPushButton, QLineEdit.
Ввод данных: QComboBox, QCheckBox, QRadioButton, QSlider, QSpinBox.
Индикаторы: QProgressBar с QTimer.
Контейнеры: QTextEdit, QListWidget, QTabWidget.
Обработка событий:
Подключение сигналов к слотам.
Примеры обработки кликов, изменений значений и т. д.
Дополнительные возможности:
Использование QMessageBox для диалогов.
Работа с QInputDialog.
Статус-бар.
Kivy

Kivy появилась в феврале 2011 года как продолжение проекта PyMT (Python MultiTouch). Основные разработчики — команда Kivy Organization, включая таких участников, как Матье Вирбел (Mathieu Virbel, известен как @tito). Проект эволюционировал из PyMT, созданного для мультитач-приложений, в более профессиональный и универсальный фреймворк.
Количество звезд на GitHub: 18,2k.
Коммиты: почти 13k.
Последняя стабильная версия: 2.3.1.
Дата последнего релиза: 26 декабря 2024 года.
Дата последнего коммита: 8 апреля 2025 года.
Это кроссплатформенная библиотека Python с открытым исходным кодом, предназначенная для создания приложений с естественным пользовательским интерфейсом (NUI), включая поддержку мультитач. Работает на Windows, macOS, Linux, Android, iOS и даже Raspberry Pi. Kivy использует OpenGL ES 2.0 для рендеринга графики, что обеспечивает высокую производительность и гибкость. Библиотека предлагает большой выбор виджетов и свой язык Kv, с помощью которого удобно описывать интерфейс отдельно от логики. Чаще всего ее используют для мобильных приложений, но она хорошо подходит и для десктопных программ, и для простых игр.
Достоинства:
Кроссплатформенность: один код для всех платформ — от десктопа до мобильных девайсов.
Мультитач: встроенная поддержка сенсорных интерфейсов, идеально для современных устройств.
Открытый код: лицензия MIT позволяет свободное использование и модификацию.
Графика: использование OpenGL дает высокую скорость и возможность создавать визуально насыщенные приложения.
Недостатки:
Не нативный вид: Kivy рисует собственные виджеты, что отличает их от системного стиля платформ (например, iOS или Windows).
Сложность установки: много зависимостей (SDL2, GStreamer и др.), что может затруднить настройку, особенно на Windows.
Крутая кривая обучения: новичкам может быть сложно освоить архитектуру Kivy.
Размер приложений: даже простые APK для Android могут быть больше 10 Мб из-за зависимостей.
В 2025 году библиотека остается мощным инструментом для разработчиков, которым нужна кроссплатформенность и гибкость, особенно в мобильных приложениях с мультитач. Это выбор для средних и сложных проектов, но требует времени на освоение и терпения при сборке.
В каких проектах используется:
Python для Android (Pydroid 3) — популярное мобильное приложение для программирования на Python, используемое миллионами пользователей.
KivyMD. Хотя это технически расширение Kivy, KivyMD — популярная библиотека компонентов в стиле Material Design, используемая во многих приложениях.
Buildozer — инструмент для упаковки приложений Kivy для мобильных платформ, разработанный создателями Kivy.
TouchViz — приложение от NASA для визуализации научных данных и проведения презентаций на больших сенсорных экранах.
Bord от Novation — приложение-компаньон для музыкального оборудования Novation Launchpad.
Plyer — библиотека платформенно-независимого доступа к возможностям различных устройств.
Многие коммерческие приложения не раскрывают свой технический стек публично, поэтому список известных приложений на Kivy ограничен теми, для которых существуют публичные доказательства использования этого фреймворка.
Пример использования:
Для запуска устанавливаем Kivy:
pip install kivy
Базовое приложение Kivy с основными виджетами:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.slider import Slider
from kivy.uix.switch import Switch
from kivy.uix.progressbar import ProgressBar
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
from kivy.core.window import Window
class KivyWidgetsDemo(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = 'vertical'
self.spacing = 15
self.padding = 30
# Устанавливаем фон
with self.canvas.before:
Color(0.95, 0.95, 0.95, 1) # Серый фон
self.rect = Rectangle(size=Window.size, pos=self.pos)
# Привязка к изменению размера окна
Window.bind(size=self._update_rect)
# Добавляем виджеты
self._add_widgets()
def _update_rect(self, instance, value):
self.rect.size = value
def _add_widgets(self):
"""Добавление и настройка виджетов"""
# Метка (Label)
self.label = Label(
text='Демонстрация виджетов Kivy',
font_size='24sp',
color=(0.1, 0.2, 0.6, 1),
bold=True
)
self.add_widget(self.label)
# Поле ввода (TextInput)
self.text_input = TextInput(
hint_text='Введите текст здесь',
size_hint=(1, None),
height=40,
multiline=False
)
self.text_input.bind(text=self.on_text_change)
self.add_widget(self.text_input)
# Кнопка (Button)
self.button = Button(
text='Нажми меня',
size_hint=(0.5, None),
height=50,
pos_hint={'center_x': 0.5},
background_color=(0.4, 0.7, 0.9, 1)
)
self.button.bind(on_press=self.on_button_press)
self.add_widget(self.button)
# Переключатель (Switch)
self.switch = Switch(
active=False,
pos_hint={'center_x': 0.5}
)
self.switch.bind(active=self.on_switch_active)
self.add_widget(self.switch)
# Слайдер (Slider)
self.slider = Slider(
min=0,
max=100,
value=30,
step=1,
size_hint=(1, None),
height=40
)
self.slider.bind(value=self.on_slider_change)
self.add_widget(self.slider)
# Индикатор прогресса (ProgressBar)
self.progress = ProgressBar(
max=100,
value=30,
size_hint=(1, None),
height=30
)
self.add_widget(self.progress)
# Изображение (Image)
self.image = Image(
source='kivy-logo-black-512.png', # Замените на свой файл
size_hint=(1, None),
height=150,
allow_stretch=True
)
self.add_widget(self.image)
# Обновление прогресс-бара по таймеру
Clock.schedule_interval(self.update_progress, 0.1)
def on_text_change(self, instance, value):
self.label.text = f'Вы ввели: {value}'
def on_button_press(self, instance):
self.label.text = 'Кнопка нажата!'
instance.text = 'Спасибо!'
def on_switch_active(self, instance, value):
status = 'включен' if value else 'выключен'
self.label.text = f'Переключатель {status}'
def on_slider_change(self, instance, value):
self.progress.value = value
self.label.text = f'Значение: {int(value)}'
def update_progress(self, dt):
if self.progress.value < 100:
self.progress.value += 1
else:
self.progress.value = 0
class KivyDemoApp(App):
def build(self):
self.title = 'Kivy Widgets Demo'
return KivyWidgetsDemo()
if __name__ == '__main__':
KivyDemoApp().run()
Что получилось:


Особенности примера:
Структура приложения:
Четкое разделение на методы для лучшей читаемости.
Использование классов для организации кода.
Основные виджеты:
Label — для отображения текста.
Button — интерактивная кнопка.
TextInput — поле для ввода текста.
Slider — ползунок для выбора значений.
Switch — переключатель.
ProgressBar — индикатор прогресса.
Image — отображение изображений.
Профессиональные техники:
Обработка событий (bind).
Динамическое обновление через Clock.
Кастомизация внешнего вида.
Адаптация к разным размерам экрана.
Дополнительные возможности:
Работа с canvas для кастомного фона.
Использование pos_hint и size_hint для адаптивности.
Изменение свойств виджетов динамически.
BeeWare Toga

Toga — часть проекта BeeWare, основанного Расселом Китом-Маге (Russell Keith-Magee) и другими участниками сообщества. Первая публичная версия появилась в 2014 году, но активная разработка началась позже, со значительными обновлениями после 2020 года.
Количество звезд на GitHub: 4,7k.
Коммиты: свыше 10k.
Последняя стабильная версия: 0.5.0.
Дата последнего релиза: 14 марта 2025 года.
Дата последнего коммита: 14 апреля 2025 года.
Toga — кроссплатформенный GUI-фреймворк из экосистемы BeeWare, разработанный для создания нативных приложений на Python. Он поддерживает Windows, macOS, Linux (GTK), Android, iOS и даже веб-приложения (на экспериментальном уровне) и консольные интерфейсы через разные бэкенды. Toga использует настоящие системные виджеты, поэтому приложения выглядят и работают как обычные программы на той платформе, где запускаются. Библиотеку легко установить через pip, а ее интерфейс одинаково хорошо работает везде. Главное удобство — можно писать один и тот же код для десктопа и мобильных устройств, а с помощью инструментов вроде Briefcase — еще и собирать готовые аппы.
Достоинства:
Нативность: использует системные виджеты, что делает интерфейс неотличимым от других приложений платформы.
Кроссплатформенность: поддержка множества платформ, включая мобильные, без изменения кода.
Простота установки: не требует компиляции расширений, достаточно стандартного pip install.
Интеграция с BeeWare: работает в связке с Briefcase для создания автономных приложений.
Python-ориентированность: API спроектирован с учетом особенностей языка, таких как генераторы и контекстные менеджеры.
Недостатки:
Небольшой набор виджетов: по сравнению с Qt или Tkinter, Toga предлагает меньше готовых компонентов.
Документация: скудная, что затрудняет обучение.
Производительность: нативность иногда идет в ущерб скорости из-за дополнительных абстракций.
Хороший выбор для разработчиков, создающих кроссплатформенные приложения. Она идеальна для проектов в экосистеме BeeWare, но может уступать по зрелости и функционалу более старым библиотекам. Отличный вариант для экспериментов и мобильной разработки.
Приложение BeeWare Toga с виджетами:
import toga
from toga.style import Pack
from toga.style.pack import CENTER, COLUMN, ROW
class TogaDemoApp(toga.App):
def startup(self):
"""Создание и отображение главного окна с виджетами"""
self.name = "Toga Demo App"
# Создаем главное окно
self.main_window = toga.MainWindow(title=self.name, size=(800, 600))
# Основные виджеты будут организованы в ScrollContainer для поддержки больших форм
main_box = toga.ScrollContainer(style=Pack(direction=COLUMN, margin=10))
# Создаем вкладки
self.tabs = toga.OptionContainer(style=Pack(flex=1))
# Добавляем вкладки
self.tabs.content.append("Основные", self.create_basic_widgets())
self.tabs.content.append("Ввод данных", self.create_input_widgets())
self.tabs.content.append("Списки", self.create_list_widgets())
self.tabs.content.append("Действия", self.create_action_widgets())
main_box.content = self.tabs
# Устанавливаем содержимое главного окна
self.main_window.content = main_box
self.main_window.show()
def create_basic_widgets(self):
"""Создаем вкладку с основными виджетами"""
box = toga.Box(style=Pack(direction=COLUMN, margin=10))
# Метка
label = toga.Label(
"Демонстрация виджетов BeeWare Toga",
style=Pack(margin=(0, 5), font_size=16, text_align=CENTER),
)
box.add(label)
# Разделитель
box.add(toga.Divider(style=Pack(margin_top=10, margin_bottom=10)))
# Кнопки
btn_box = toga.Box(style=Pack(direction=ROW, margin=5))
btn1 = toga.Button(
"Обычная кнопка",
on_press=self.on_button_press,
style=Pack(margin=5, flex=1),
)
btn_box.add(btn1)
btn2 = toga.Button(
"Отключенная кнопка", enabled=False, style=Pack(margin=5, flex=1)
)
btn_box.add(btn2)
box.add(btn_box)
# Изображение (используем встроенную иконку)
try:
image = toga.Image("resources/icon.png")
imageview = toga.ImageView(
image, style=Pack(margin=10, width=100, height=100)
)
box.add(imageview)
except FileNotFoundError:
box.add(toga.Label("Изображение не найдено", style=Pack(color="red")))
# Текстовое поле
self.text_input = toga.TextInput(
placeholder="Введите текст...", style=Pack(margin=5)
)
self.text_input.on_change = self.on_text_change
box.add(self.text_input)
# Многострочный текст
self.multiline_text = toga.MultilineTextInput(
placeholder="Многострочный текст...", style=Pack(margin=5, height=100)
)
box.add(self.multiline_text)
return box
def create_input_widgets(self):
"""Создаем вкладку с виджетами ввода данных"""
box = toga.Box(style=Pack(direction=COLUMN, margin=10))
# Чекбоксы
self.checkbox1 = toga.Switch("Опция 1", on_change=self.on_switch_change)
self.checkbox2 = toga.Switch(
"Опция 2", value=True, on_change=self.on_switch_change
)
box.add(self.checkbox1)
box.add(self.checkbox2)
box.add(toga.Divider(style=Pack(margin_top=10, margin_bottom=10)))
# Слайдер
self.slider = toga.Slider(
min=0,
max=100,
value=50,
on_change=self.on_slider_change,
style=Pack(margin=5),
)
box.add(toga.Label("Регулировка значения:", style=Pack(margin_left=5)))
box.add(self.slider)
self.slider_value_label = toga.Label("50", style=Pack(text_align=CENTER))
box.add(self.slider_value_label)
return box
def create_list_widgets(self):
"""Создаем вкладку с виджетами списков"""
box = toga.Box(style=Pack(direction=COLUMN, margin=10))
# Простой список
self.simple_list = toga.DetailedList(
data=[
{
"icon": toga.Icon("resources/icon.png"),
"title": "Элемент 1",
"subtitle": "",
},
{
"icon": toga.Icon("resources/icon.png"),
"title": "Элемент 2",
"subtitle": "",
},
{
"icon": toga.Icon("resources/icon.png"),
"title": "Элемент 3",
"subtitle": "",
},
],
on_select=self.on_list_select,
style=Pack(margin=5, height=100),
)
box.add(toga.Label("Простой список:", style=Pack(margin_left=5)))
box.add(self.simple_list)
# Таблица (более сложный список)
table_data = [
("Иван", 25, "Москва"),
("Мария", 30, "Санкт-Петербург"),
("Алексей", 28, "Новосибирск"),
]
self.table = toga.Table(
headings=["Имя", "Возраст", "Город"],
data=table_data,
on_select=self.on_table_select,
style=Pack(margin=5, height=150),
)
box.add(toga.Label("Таблица:", style=Pack(margin_left=5)))
box.add(self.table)
# Дерево
tree_data = {
("Россия",): [
("Москва", "Столица"),
("Санкт-Петербург", "Культурная столица"),
],
("США",): [
("Нью-Йорк", "Экономический центр"),
("Лос-Анджелес", "Киноиндустрия"),
],
}
self.tree = toga.Tree(
headings=["Город", "Описание"],
data=tree_data,
on_select=self.on_tree_select,
style=Pack(margin=5, height=150),
)
box.add(toga.Label("Дерево:", style=Pack(margin_left=5)))
box.add(self.tree)
return box
def create_action_widgets(self):
"""Создаем вкладку с виджетами действий"""
box = toga.Box(style=Pack(direction=COLUMN, margin=10))
# Кнопки действий
btn_box = toga.Box(style=Pack(direction=ROW, margin=5))
# Кнопка для показа диалога
dialog_btn = toga.Button(
"Показать диалог", on_press=self.show_dialog, style=Pack(margin=5, flex=1)
)
btn_box.add(dialog_btn)
# Кнопка для выбора файла
file_btn = toga.Button(
"Выбрать файл", on_press=self.select_file, style=Pack(margin=5, flex=1)
)
btn_box.add(file_btn)
box.add(btn_box)
# Индикатор выполнения
self.progress_bar = toga.ProgressBar(
max=100, value=0, running=False, style=Pack(margin=10)
)
box.add(self.progress_bar)
# Кнопки управления прогрессом
progress_btn_box = toga.Box(style=Pack(direction=ROW, margin=5))
start_btn = toga.Button(
"Старт", on_press=self.start_progress, style=Pack(margin=5, flex=1)
)
progress_btn_box.add(start_btn)
stop_btn = toga.Button(
"Стоп", on_press=self.stop_progress, style=Pack(margin=5, flex=1)
)
progress_btn_box.add(stop_btn)
box.add(progress_btn_box)
# WebView (браузер)
self.webview = toga.WebView(style=Pack(margin=5, height=200))
box.add(toga.Label("WebView:", style=Pack(margin_left=5)))
box.add(self.webview)
# Кнопка для загрузки URL
url_box = toga.Box(style=Pack(direction=ROW, margin=5))
self.url_input = toga.TextInput(
placeholder="Введите URL",
value="https://beeware.org",
style=Pack(margin=5, flex=3),
)
url_box.add(self.url_input)
load_btn = toga.Button(
"Загрузить", on_press=self.load_url, style=Pack(margin=5, flex=1)
)
url_box.add(load_btn)
box.add(url_box)
return box
# Обработчики событий
def on_button_press(self, widget):
self.main_window.info_dialog(
"Уведомление", f"Кнопка '{widget.text}' была нажата!"
)
def on_text_change(self, widget):
print(f"Текст изменен: {widget.value}")
def on_switch_change(self, widget):
state = "включена" if widget.value else "выключена"
print(f"Опция '{widget.text}' {state}")
def on_date_change(self, widget):
selected_date = (
widget.value.strftime("%d.%m.%Y") if widget.value else "не выбрана"
)
print(f"Выбрана дата: {selected_date}")
def on_time_change(self, widget):
selected_time = widget.value.strftime("%H:%M") if widget.value else "не выбрано"
print(f"Выбрано время: {selected_time}")
def on_slider_change(self, widget):
self.slider_value_label.text = str(int(widget.value))
print(f"Значение слайдера: {widget.value}")
def on_list_select(self, widget):
if widget.selection:
print(f"Выбран элемент списка: {widget.selection}")
def on_table_select(self, widget):
if widget.selection:
name, age, city = widget.selection
print(f"Выбрана строка: {name}, {age} лет, {city}")
def on_tree_select(self, widget):
if widget.selection:
city, desc = widget.selection
print(f"Выбран узел дерева: {city} - {desc}")
def show_dialog(self, widget):
self.main_window.question_dialog(
"Подтверждение",
"Вы уверены, что хотите выполнить это действие?",
on_result=self.dialog_result,
)
def dialog_result(self, widget, result):
if result:
print("Пользователь подтвердил действие")
else:
print("Пользователь отменил действие")
def select_file(self, widget):
try:
file_path = self.main_window.open_file_dialog(
title="Выберите файл", multiselect=False
)
if file_path:
print(f"Выбран файл: {file_path}")
except ValueError as exception:
print(f"Ошибка выбора файла: {exception}")
def start_progress(self, widget):
self.progress_bar.start()
print("Прогресс запущен")
def stop_progress(self, widget):
self.progress_bar.stop()
print("Прогресс остановлен")
def load_url(self, widget):
url = self.url_input.value.strip()
if url:
if not url.startswith(("http://", "https://")):
url = "https://" + url
self.webview.url = url
print(f"Загружаем URL: {url}")
def main():
return TogaDemoApp(
"BeeWare Toga Demo",
app_id="beeware.toga.demo",
app_name="Example",
icon="resources/icon",
)
if __name__ == "__main__":
app = main()
app.main_loop()
Что получилось:




Особенности примера:
Структура приложения:
Использование toga.App как базового класса.
Организация виджетов по вкладкам с помощью OptionContainer.
Чистая архитектура с разделением на UI и обработчики событий.
Демонстрация виджетов:
Базовые: Label, Button, TextInput, MultilineTextInput, ImageView.
Ввод данных: Switch, DatePicker, TimePicker, Slider.
Списки: ListBox, Table, Tree.
Действия: ProgressBar, WebView.
Диалоги: info_dialog, question_dialog, open_file_dialog.
Обработка событий:
Подключение обработчиков через on_press, on_change, on_select.
Примеры обработки различных событий виджетов.
Особенности BeeWare Toga:
Кроссплатформенность (работает на Windows, macOS, Linux, мобильных платформах).
Нативный внешний вид на каждой платформе.
Простота создания пользовательских интерфейсов.
Для работы этого примера вам потребуется создать папку resources и поместить туда файл иконки icon.png.
В каких проектах используется BeeWare Toga для GUI:
Cricket — это графический инструмент для запуска тестов в Python, разработанный командой BeeWare. Он использует Toga для своего пользовательского интерфейса.
Podium — это презентационный инструмент, работающий на Toga, также разработанный командой BeeWare.
Travel Tips — пример полноценного мобильного приложения, созданного с помощью Toga и опубликованного в качестве примера реального приложения командой BeeWare.
BeeWare Toga — относительно молодой фреймворк по сравнению с PyQt или wxPython, поэтому количество крупных сторонних проектов, использующих его, ограничено. Большинство проектов — часть экосистемы BeeWare или экспериментальные приложения.
WxPython

Python-обертка для кроссплатформенной библиотеки wxWidgets, написанной на C++. wxPython создал Джулиан Смарт (Julian Smart) в 1992 году.
Количество звезд на GitHub: 1,4k.
Коммиты: свыше 5,9k.
Последняя стабильная версия: 4.2.2.
Дата последнего релиза: 10 апреля 2025 года.
Дата последнего коммита: 12 апреля 2025 года.
wxPython дает доступ к возможностям wxWidgets и позволяет создавать нативные интерфейсы для Windows, macOS и Linux. Она использует системные элементы управления, поэтому приложения выглядят привычно на каждой платформе. В библиотеке есть все — от простых кнопок до сложных диалогов и графических областей. Подходит как для небольших утилит, так и для крупных проектов вроде редакторов или IDE. Устанавливается через pip, но важно учитывать поддержку нужной версии Python (начиная с 3.8).
Кстати, репозиторий wxPython на GitHub называется Phoenix (Феникс), потому что это имя было выбрано для новой версии wxPython — полной переработки оригинального проекта.
Достоинства:
Нативный вид: интерфейсы выглядят как часть операционной системы.
Богатый функционал: огромное количество виджетов и инструментов для сложных приложений.
Кроссплатформенность: единый код работает на всех основных ОС.
Сообщество: долгая история и активная поддержка разработчиков;
Гибкость: поддерживает кастомизацию и сложные макеты.
Недостатки:
Сложность: крутая кривая обучения из-за обилия классов и методов.
Размер: приложения могут быть тяжелее из-за зависимостей wxWidgets.
Устаревшая документация: некоторые части не обновлялись годами.
wxPython в 2025 году остается классикой для создания надежных десктопных приложений с нативным интерфейсом. Это отличный выбор для опытных разработчиков, которым нужен полный контроль над GUI, но новичкам может показаться громоздким.
В каких проектах используется:
Dropbox — облачное хранилище данных.
BitTorrent — оригинальный клиент BitTorrent, разработанный Брэмом Коэном. Использовал wxPython для своего интерфейса.
Диск Google — настольный клиент для системы облачного хранения данных Google.
Editra — это кроссплатформенный текстовый редактор с подсветкой синтаксиса для множества языков программирования. Проект был создан Криком Мацциотти (Cody Precord), одним из основных разработчиков wxPython.
Примеры использования:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super(MyFrame, self).__init__(parent, title=title, size=(600, 400))
self.panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
# Создаем различные виджеты
self.create_widgets()
# Настраиваем обработчики событий
self.bind_events()
self.panel.SetSizer(self.sizer)
self.Centre()
self.Show()
def create_widgets(self):
"""Создание и размещение виджетов на панели"""
# Статический текст
self.title = wx.StaticText(self.panel, label="Демонстрация wxPython виджетов")
self.title.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
self.sizer.Add(self.title, 0, wx.ALL | wx.CENTER, 10)
# Поле ввода текста
self.text_ctrl = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE, size=(400, 100))
self.sizer.Add(self.text_ctrl, 0, wx.ALL | wx.EXPAND, 10)
# Кнопки
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.btn_ok = wx.Button(self.panel, label="OK")
self.btn_cancel = wx.Button(self.panel, label="Cancel")
self.btn_clear = wx.Button(self.panel, label="Clear")
button_sizer.Add(self.btn_ok, 0, wx.ALL, 5)
button_sizer.Add(self.btn_cancel, 0, wx.ALL, 5)
button_sizer.Add(self.btn_clear, 0, wx.ALL, 5)
self.sizer.Add(button_sizer, 0, wx.CENTER)
# Чекбокс
self.checkbox = wx.CheckBox(self.panel, label="Включить опцию")
self.sizer.Add(self.checkbox, 0, wx.ALL, 10)
# Радио-кнопки
radio_box = wx.RadioBox(self.panel,
label="Выберите вариант",
choices=["Вариант 1", "Вариант 2", "Вариант 3"],
majorDimension=1,
style=wx.RA_SPECIFY_COLS)
self.sizer.Add(radio_box, 0, wx.ALL | wx.EXPAND, 10)
# Выпадающий список
choices = ["Элемент 1", "Элемент 2", "Элемент 3"]
self.combo = wx.ComboBox(self.panel, choices=choices, style=wx.CB_READONLY)
self.sizer.Add(self.combo, 0, wx.ALL | wx.EXPAND, 10)
# Ползунок
self.slider = wx.Slider(self.panel, minValue=0, maxValue=100, style=wx.SL_HORIZONTAL | wx.SL_LABELS)
self.sizer.Add(self.slider, 0, wx.ALL | wx.EXPAND, 10)
def bind_events(self):
"""Привязка обработчиков событий"""
self.btn_ok.Bind(wx.EVT_BUTTON, self.on_ok)
self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)
self.btn_clear.Bind(wx.EVT_BUTTON, self.on_clear)
self.checkbox.Bind(wx.EVT_CHECKBOX, self.on_check)
self.combo.Bind(wx.EVT_COMBOBOX, self.on_combo)
self.slider.Bind(wx.EVT_SLIDER, self.on_slide)
def on_ok(self, event):
"""Обработчик нажатия кнопки OK"""
wx.MessageBox("Вы нажали OK!", "Информация", wx.OK | wx.ICON_INFORMATION)
def on_cancel(self, event):
"""Обработчик нажатия кнопки Cancel"""
self.Close()
def on_clear(self, event):
"""Обработчик нажатия кнопки Clear"""
self.text_ctrl.Clear()
def on_check(self, event):
"""Обработчик изменения состояния чекбокса"""
state = "включена" if self.checkbox.GetValue() else "выключена"
self.text_ctrl.AppendText(f"Опция {state}\n")
def on_combo(self, event):
"""Обработчик выбора в комбобоксе"""
selection = self.combo.GetStringSelection()
self.text_ctrl.AppendText(f"Выбран: {selection}\n")
def on_slide(self, event):
"""Обработчик перемещения ползунка"""
value = self.slider.GetValue()
self.text_ctrl.AppendText(f"Значение ползунка: {value}\n")
if __name__ == "__main__":
app = wx.App()
frame = MyFrame(None, "wxPython Demo")
app.MainLoop()
Основные виджеты wxPython:
Основные элементы:
wx.Frame — главное окно приложения.
wx.Panel — контейнер для виджетов.
wx.BoxSizer — менеджер компоновки.
Виджеты:
wx.StaticText — текстовая метка.
wx.TextCtrl — поле ввода текста (однострочное/многострочное).
wx.Button — кнопка.
wx.CheckBox — чекбокс.
wx.RadioBox — группа радиокнопок.
wx.ComboBox — выпадающий список.
wx.Slider — ползунок для выбора значения.
Обработка событий:
Привязка обработчиков через метод Bind().
Примеры обработчиков для кнопок, чекбокса, комбобокса и ползунка.
Что получилось:
Для запуска примера требуется установленный wxPython (pip install wxPython). Приложение создает окно с различными элементами управления и демонстрирует их взаимодействие.


Remi

Remi создан Давидом Розой (Davide Rosa, известен как @dddomodossola) и впервые представлен в 2015 году. Проект активно развивается благодаря сообществу open-source.
Количество звезд на GitHub: 3,6k.
Коммиты: свыше 1,4k.
Последняя стабильная версия: 2022.7.27.
Дата последнего релиза: 27 июля 2022 года.
Дата последнего коммита: 9 марта 2025 года.
Remi (Remote Interface) — легковесная библиотека GUI для Python, которая рендерит интерфейс в веб-браузере. Она не требует нативных зависимостей, как Kivy или PyGObject, а использует встроенный веб-сервер для отображения приложения через HTML. Код пишется на Python, а Remi автоматически преобразует его в HTML и JavaScript, что избавляет от необходимости знать веб-технологии. Библиотека весит меньше 100 Кб, что удобно для проектов, где важна компактность — например, на Raspberry Pi. Ее можно использовать как для локальных, так и для удаленных интерфейсов, и она работает на разных платформах.
Достоинства:
Легковесность: минимальный размер и отсутствие сложных зависимостей.
Кроссплатформенность: работает везде, где есть браузер (Windows, Linux, macOS, Android).
Простота: не нужно знать HTML/CSS/JavaScript, только Python.
Удаленный доступ: встроенный веб-сервер позволяет управлять приложением через сеть.
Недостатки:
Ограниченная функциональность: меньше виджетов и возможностей по сравнению с Qt или GTK+.
Не нативный вид: интерфейс в браузере не всегда выглядит как системное приложение.
Производительность: зависит от обозревателя и может быть ниже, чем у нативных библиотек.
Remi идеальна для прототипов, проектов на устройствах вроде Raspberry Pi или ситуаций, где важна простота развертывания. Однако для сложных или визуально насыщенных интерфейсов ее возможностей может не хватить.
Пример кода для библиотеки Remi с различными виджетами:
from remi import gui
from remi import start, App
class RemiWidgetsExample(App):
def __init__(self, *args):
super(RemiWidgetsExample, self).__init__(*args)
def main(self):
# Основной контейнер
container = gui.VBox(width=500, height=500, margin='0px auto')
container.style['overflow'] = 'auto'
container.style['padding'] = '20px'
container.style['font-family'] = 'Arial, sans-serif'
# Заголовок
title = gui.Label('Примеры виджетов Remi', width='100%', height='30px')
title.style['font-size'] = '24px'
title.style['text-align'] = 'center'
title.style['margin-bottom'] = '20px'
container.append(title)
# 1. Кнопка с обработчиком события
self.btn = gui.Button('Нажми меня', width=200, height=100)
self.btn.style['margin'] = '10px auto'
self.btn.onclick.do(self.on_button_pressed)
container.append(self.btn)
# 2. Текстовое поле
self.text_input = gui.TextInput(width=300, height=150)
self.text_input.style['margin'] = '10px auto'
self.text_input.set_text('Введите текст здесь')
container.append(self.text_input)
# 3. Метка для вывода текста
self.output_label = gui.Label('Здесь будет результат', width='90%')
self.output_label.style['margin'] = '10px auto'
self.output_label.style['padding'] = '10px'
self.output_label.style['background-color'] = '#f0f0f0'
container.append(self.output_label)
# 4. Выпадающий список
self.dropdown = gui.DropDown(width=200, height=30)
self.dropdown.style['margin'] = '10px auto'
for item in ['Вариант 1', 'Вариант 2', 'Вариант 3']:
self.dropdown.append(gui.DropDownItem(item))
self.dropdown.onchange.do(self.on_dropdown_change)
container.append(self.dropdown)
# 5. Чекбокс
self.checkbox = gui.CheckBoxLabel('Согласен с условиями', width=200)
self.checkbox.style['margin'] = '10px auto'
self.checkbox.onchange.do(self.on_checkbox_change)
container.append(self.checkbox)
# 6. Слайдер
self.slider = gui.Slider(min=0, max=100, step=1, width=300)
self.slider.style['margin'] = '20px auto'
self.slider_label = gui.Label('Значение: 50', width=300)
self.slider_label.style['margin'] = '0 auto'
self.slider.onchange.do(self.on_slider_change)
container.append(self.slider)
container.append(self.slider_label)
# 7. Изображение
try:
self.image = gui.Image('/res:logo.png', width=100, height=100)
self.image.style['margin'] = '10px auto'
container.append(self.image)
except:
pass
return container
# Обработчики событий
def on_button_pressed(self, widget):
self.output_label.set_text(f'Кнопка нажата! Текст: {self.text_input.get_text()}')
def on_dropdown_change(self, widget, value):
self.output_label.set_text(f'Выбран: {value}')
def on_checkbox_change(self, widget, value):
status = "Да" if value else "Нет"
self.output_label.set_text(f'Согласен: {status}')
def on_slider_change(self, widget, value):
self.slider_label.set_text(f'Значение: {value}')
# Запуск приложения
if __name__ == "__main__":
start(RemiWidgetsExample, address='0.0.0.0', port=0,
multiple_instance=False, enable_file_cache=True,
update_interval=0.1, start_browser=True)
Что получилось:



Особенности примера:
Структура приложения:
Создается класс, наследующий от
App
.В методе main() определяются все виджеты.
Используемые виджеты:
Button
— кнопка с обработчиком события.TextInput
— поле для ввода текста.Label
— метка для вывода информации.DropDown
— выпадающий список.CheckBoxLabel
— чекбокс с текстом.Slider
— ползунок для выбора значения.Image
— отображение изображения.
Особенности:
Все виджеты центрированы с помощью CSS-стилей.
Реализованы обработчики событий для каждого интерактивного элемента.
Используется вертикальное расположение (VBox).
Для запуска сохраните код в файл remi_example.py
и выполните:
python remi_example.py
Приложение откроется в браузере по умолчанию.
Почему remi не так популярна в крупных проектах
По сравнению с более популярными решениями, у remi есть несколько слабых мест:
Меньшая экосистема и сообщество разработчиков.
Ограниченные возможности стилизации и кастомизации.
Меньшая производительность при высоких нагрузках.
Нет такой гибкости, как связка Flask/Django + современный JavaScript фреймворк.
Remi максимально полезна для быстрых прототипов, образовательных проектов и случаев, когда разработчик хочет создать GUI, не погружаясь в веб-разработку.
Что в итоге
Выбирать GUI-библиотеку для Python в 2025 году стоит под конкретные задачи:
Tkinter — простой и встроенный.
Qt обладает целой экосистемой для создания сложных и динамичных интерфейсов.
Kivy подходит для кроссплатформенных приложений, особенно на Android и iOS.
Toga делает нативные интерфейсы на десктопе.
wxPython хорош для серьезных приложений под Windows, macOS и Linux.
PyGObject — для GNOME- и Linux-сред.
Remi — легкое решение для браузерных интерфейсов, особенно на Raspberry Pi.
Конечно, у каждой библиотеки есть свои плюсы и минусы. Лучше попробовать несколько и выбрать то, что подходит именно вам.
А если у вас есть свои фавориты, расскажите о них в комментариях. Будет интересно почитать!