← Назад к курсу

Создание оконных интерфейсов в Python

В этом уроке мы рассмотрим три самые популярные библиотеки для создания графических интерфейсов (GUI) в Python: Tkinter, PyQt5 и Kivy.

1. Tkinter - стандартная библиотека Python

Tkinter входит в стандартную библиотеку Python и является наиболее простым способом создания GUI.

Базовый пример Tkinter

import tkinter as tk
from tkinter import ttk, messagebox

def on_button_click():
    name = entry.get()
    if name:
        messagebox.showinfo("Приветствие", f"Привет, {name}!")
        label_result.config(text=f"Введено: {name}")
    else:
        messagebox.showwarning("Ошибка", "Введите имя!")

# Создание главного окна
root = tk.Tk()
root.title("Tkinter Пример")
root.geometry("400x300")

# Создание элементов интерфейса
label = ttk.Label(root, text="Введите ваше имя:")
label.pack(pady=10)

entry = ttk.Entry(root, width=30)
entry.pack(pady=5)

button = ttk.Button(root, text="Поздороваться", command=on_button_click)
button.pack(pady=10)

label_result = ttk.Label(root, text="")
label_result.pack(pady=10)

# Запуск главного цикла
root.mainloop()

Расширенный пример с меню и вкладками

import tkinter as tk
from tkinter import ttk, messagebox, filedialog

class TkinterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Расширенное приложение Tkinter")
        self.root.geometry("600x400")
        
        self.create_menu()
        self.create_widgets()
    
    def create_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Файл", menu=file_menu)
        file_menu.add_command(label="Открыть", command=self.open_file)
        file_menu.add_command(label="Сохранить", command=self.save_file)
        file_menu.add_separator()
        file_menu.add_command(label="Выход", command=self.root.quit)
        
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Помощь", menu=help_menu)
        help_menu.add_command(label="О программе", command=self.show_about)
    
    def create_widgets(self):
        # Создание вкладок
        tab_control = ttk.Notebook(self.root)
        
        # Вкладка 1: Форма
        tab1 = ttk.Frame(tab_control)
        tab_control.add(tab1, text="Форма")
        self.create_form_tab(tab1)
        
        # Вкладка 2: Таблица
        tab2 = ttk.Frame(tab_control)
        tab_control.add(tab2, text="Таблица")
        self.create_table_tab(tab2)
        
        tab_control.pack(expand=1, fill="both")
    
    def create_form_tab(self, parent):
        frame = ttk.LabelFrame(parent, text="Регистрация", padding=10)
        frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Поля формы
        ttk.Label(frame, text="Имя:").grid(row=0, column=0, sticky="w", pady=5)
        self.name_entry = ttk.Entry(frame, width=30)
        self.name_entry.grid(row=0, column=1, pady=5, padx=5)
        
        ttk.Label(frame, text="Email:").grid(row=1, column=0, sticky="w", pady=5)
        self.email_entry = ttk.Entry(frame, width=30)
        self.email_entry.grid(row=1, column=1, pady=5, padx=5)
        
        ttk.Label(frame, text="Возраст:").grid(row=2, column=0, sticky="w", pady=5)
        self.age_spinbox = ttk.Spinbox(frame, from_=0, to=120, width=28)
        self.age_spinbox.grid(row=2, column=1, pady=5, padx=5)
        
        # Кнопки
        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=3, column=0, columnspan=2, pady=10)
        
        ttk.Button(btn_frame, text="Сохранить", 
                  command=self.save_form).pack(side="left", padx=5)
        ttk.Button(btn_frame, text="Очистить", 
                  command=self.clear_form).pack(side="left", padx=5)
    
    def create_table_tab(self, parent):
        # Таблица с данными
        columns = ("Имя", "Email", "Возраст")
        self.tree = ttk.Treeview(parent, columns=columns, show="headings", height=10)
        
        for col in columns:
            self.tree.heading(col, text=col)
            self.tree.column(col, width=150)
        
        # Добавление прокрутки
        scrollbar = ttk.Scrollbar(parent, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)
        
        self.tree.pack(side="left", fill="both", expand=True, padx=10, pady=10)
        scrollbar.pack(side="right", fill="y")
        
        # Добавление тестовых данных
        for i in range(1, 11):
            self.tree.insert("", "end", values=(f"Пользователь {i}", 
                                               f"user{i}@example.com", 
                                               20 + i))
    
    def save_form(self):
        name = self.name_entry.get()
        email = self.email_entry.get()
        age = self.age_spinbox.get()
        
        if name and email and age:
            self.tree.insert("", "end", values=(name, email, age))
            messagebox.showinfo("Успех", "Данные сохранены!")
            self.clear_form()
        else:
            messagebox.showwarning("Ошибка", "Заполните все поля!")
    
    def clear_form(self):
        self.name_entry.delete(0, tk.END)
        self.email_entry.delete(0, tk.END)
        self.age_spinbox.delete(0, tk.END)
        self.age_spinbox.insert(0, "0")
    
    def open_file(self):
        file_path = filedialog.askopenfilename(
            title="Выберите файл",
            filetypes=[("Текстовые файлы", "*.txt"), ("Все файлы", "*.*")]
        )
        if file_path:
            messagebox.showinfo("Файл", f"Выбран файл: {file_path}")
    
    def save_file(self):
        file_path = filedialog.asksaveasfilename(
            title="Сохранить файл",
            defaultextension=".txt",
            filetypes=[("Текстовые файлы", "*.txt"), ("Все файлы", "*.*")]
        )
        if file_path:
            messagebox.showinfo("Сохранение", f"Файл сохранен как: {file_path}")
    
    def show_about(self):
        messagebox.showinfo("О программе", 
                           "Пример приложения на Tkinter\nВерсия 1.0")

if __name__ == "__main__":
    root = tk.Tk()
    app = TkinterApp(root)
    root.mainloop()

2. PyQt5 - профессиональный фреймворк для GUI

PyQt5 - мощная библиотека для создания профессиональных приложений с современным интерфейсом.

Установка PyQt5:

pip install PyQt5

Базовый пример PyQt5

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                            QHBoxLayout, QLabel, QLineEdit, QPushButton, 
                            QMessageBox, QListWidget)
from PyQt5.QtCore import Qt

class PyQt5App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
    
    def initUI(self):
        # Настройка главного окна
        self.setWindowTitle("PyQt5 Пример")
        self.setGeometry(100, 100, 500, 400)
        
        # Центральный виджет
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # Основной layout
        main_layout = QVBoxLayout()
        central_widget.setLayout(main_layout)
        
        # Создание виджетов
        self.label = QLabel("Добавьте элементы в список:")
        main_layout.addWidget(self.label)
        
        # Поле ввода и кнопка в горизонтальном layout
        input_layout = QHBoxLayout()
        self.input_field = QLineEdit()
        self.input_field.setPlaceholderText("Введите текст...")
        input_layout.addWidget(self.input_field)
        
        self.add_button = QPushButton("Добавить")
        self.add_button.clicked.connect(self.add_item)
        input_layout.addWidget(self.add_button)
        
        main_layout.addLayout(input_layout)
        
        # Список элементов
        self.list_widget = QListWidget()
        main_layout.addWidget(self.list_widget)
        
        # Кнопки управления
        button_layout = QHBoxLayout()
        
        self.clear_button = QPushButton("Очистить список")
        self.clear_button.clicked.connect(self.clear_list)
        button_layout.addWidget(self.clear_button)
        
        self.info_button = QPushButton("Информация")
        self.info_button.clicked.connect(self.show_info)
        button_layout.addWidget(self.info_button)
        
        main_layout.addLayout(button_layout)
        
        # Статус бар
        self.statusBar().showMessage("Готово")
    
    def add_item(self):
        text = self.input_field.text().strip()
        if text:
            self.list_widget.addItem(text)
            self.input_field.clear()
            self.statusBar().showMessage(f"Добавлен элемент: {text}")
        else:
            QMessageBox.warning(self, "Предупреждение", "Введите текст!")
    
    def clear_list(self):
        self.list_widget.clear()
        self.statusBar().showMessage("Список очищен")
    
    def show_info(self):
        count = self.list_widget.count()
        QMessageBox.information(self, "Информация", 
                               f"Количество элементов в списке: {count}")

def main():
    app = QApplication(sys.argv)
    window = PyQt5App()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Расширенный пример PyQt5 с вкладками и таблицей

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                            QHBoxLayout, QTabWidget, QTableWidget, 
                            QTableWidgetItem, QLabel, QLineEdit, QPushButton,
                            QComboBox, QDateEdit, QMessageBox, QHeaderView)
from PyQt5.QtCore import QDate, Qt
from PyQt5.QtGui import QFont

class EmployeeManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.employees = []
    
    def initUI(self):
        self.setWindowTitle("Менеджер сотрудников")
        self.setGeometry(100, 100, 800, 600)
        
        # Центральный виджет с вкладками
        self.tab_widget = QTabWidget()
        self.setCentralWidget(self.tab_widget)
        
        # Создание вкладок
        self.create_add_employee_tab()
        self.create_view_employees_tab()
        
        # Статус бар
        self.statusBar().showMessage("Добро пожаловать в менеджер сотрудников")
    
    def create_add_employee_tab(self):
        tab = QWidget()
        self.tab_widget.addTab(tab, "Добавить сотрудника")
        
        layout = QVBoxLayout()
        tab.setLayout(layout)
        
        # Заголовок
        title = QLabel("Добавление нового сотрудника")
        title_font = QFont()
        title_font.setPointSize(14)
        title_font.setBold(True)
        title.setFont(title_font)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        # Форма
        form_layout = QVBoxLayout()
        
        # Имя
        name_layout = QHBoxLayout()
        name_label = QLabel("Имя:")
        name_label.setFixedWidth(100)
        self.name_input = QLineEdit()
        self.name_input.setPlaceholderText("Введите имя")
        name_layout.addWidget(name_label)
        name_layout.addWidget(self.name_input)
        form_layout.addLayout(name_layout)
        
        # Должность
        position_layout = QHBoxLayout()
        position_label = QLabel("Должность:")
        position_label.setFixedWidth(100)
        self.position_combo = QComboBox()
        self.position_combo.addItems(["Разработчик", "Дизайнер", "Менеджер", 
                                      "Тестировщик", "Аналитик"])
        position_layout.addWidget(position_label)
        position_layout.addWidget(self.position_combo)
        form_layout.addLayout(position_layout)
        
        # Зарплата
        salary_layout = QHBoxLayout()
        salary_label = QLabel("Зарплата:")
        salary_label.setFixedWidth(100)
        self.salary_input = QLineEdit()
        self.salary_input.setPlaceholderText("Введите зарплату")
        salary_layout.addWidget(salary_label)
        salary_layout.addWidget(self.salary_input)
        form_layout.addLayout(salary_layout)
        
        # Дата приема
        date_layout = QHBoxLayout()
        date_label = QLabel("Дата приема:")
        date_label.setFixedWidth(100)
        self.date_input = QDateEdit()
        self.date_input.setDate(QDate.currentDate())
        self.date_input.setCalendarPopup(True)
        date_layout.addWidget(date_label)
        date_layout.addWidget(self.date_input)
        form_layout.addLayout(date_layout)
        
        layout.addLayout(form_layout)
        
        # Кнопки
        button_layout = QHBoxLayout()
        
        self.add_button = QPushButton("Добавить сотрудника")
        self.add_button.clicked.connect(self.add_employee)
        self.add_button.setStyleSheet("background-color: #4CAF50; color: white;")
        
        self.clear_button = QPushButton("Очистить форму")
        self.clear_button.clicked.connect(self.clear_form)
        self.clear_button.setStyleSheet("background-color: #f44336; color: white;")
        
        button_layout.addWidget(self.add_button)
        button_layout.addWidget(self.clear_button)
        layout.addLayout(button_layout)
    
    def create_view_employees_tab(self):
        tab = QWidget()
        self.tab_widget.addTab(tab, "Просмотр сотрудников")
        
        layout = QVBoxLayout()
        tab.setLayout(layout)
        
        # Таблица сотрудников
        self.table = QTableWidget()
        self.table.setColumnCount(5)
        self.table.setHorizontalHeaderLabels(["ID", "Имя", "Должность", 
                                             "Зарплата", "Дата приема"])
        
        # Настройка таблицы
        header = self.table.horizontalHeader()
        header.setSectionResizeMode(1, QHeaderView.Stretch)
        
        layout.addWidget(self.table)
        
        # Кнопки для таблицы
        button_layout = QHBoxLayout()
        
        self.refresh_button = QPushButton("Обновить таблицу")
        self.refresh_button.clicked.connect(self.refresh_table)
        
        self.delete_button = QPushButton("Удалить выбранного")
        self.delete_button.clicked.connect(self.delete_employee)
        self.delete_button.setStyleSheet("background-color: #ff9800; color: white;")
        
        button_layout.addWidget(self.refresh_button)
        button_layout.addWidget(self.delete_button)
        layout.addLayout(button_layout)
    
    def add_employee(self):
        name = self.name_input.text().strip()
        position = self.position_combo.currentText()
        salary = self.salary_input.text().strip()
        date = self.date_input.date().toString("dd.MM.yyyy")
        
        if not name:
            QMessageBox.warning(self, "Ошибка", "Введите имя сотрудника!")
            return
        
        if not salary.isdigit():
            QMessageBox.warning(self, "Ошибка", "Зарплата должна быть числом!")
            return
        
        # Добавление сотрудника
        employee_id = len(self.employees) + 1
        employee = {
            'id': employee_id,
            'name': name,
            'position': position,
            'salary': int(salary),
            'date': date
        }
        self.employees.append(employee)
        
        QMessageBox.information(self, "Успех", 
                               f"Сотрудник {name} добавлен успешно!")
        self.clear_form()
        self.refresh_table()
        self.statusBar().showMessage(f"Добавлен сотрудник: {name}")
    
    def clear_form(self):
        self.name_input.clear()
        self.position_combo.setCurrentIndex(0)
        self.salary_input.clear()
        self.date_input.setDate(QDate.currentDate())
    
    def refresh_table(self):
        self.table.setRowCount(len(self.employees))
        
        for row, employee in enumerate(self.employees):
            self.table.setItem(row, 0, QTableWidgetItem(str(employee['id'])))
            self.table.setItem(row, 1, QTableWidgetItem(employee['name']))
            self.table.setItem(row, 2, QTableWidgetItem(employee['position']))
            self.table.setItem(row, 3, QTableWidgetItem(str(employee['salary'])))
            self.table.setItem(row, 4, QTableWidgetItem(employee['date']))
        
        self.statusBar().showMessage(f"Загружено {len(self.employees)} сотрудников")
    
    def delete_employee(self):
        selected_row = self.table.currentRow()
        if selected_row >= 0:
            employee_name = self.employees[selected_row]['name']
            reply = QMessageBox.question(self, "Подтверждение", 
                                        f"Удалить сотрудника {employee_name}?",
                                        QMessageBox.Yes | QMessageBox.No)
            
            if reply == QMessageBox.Yes:
                del self.employees[selected_row]
                self.refresh_table()
                self.statusBar().showMessage(f"Удален сотрудник: {employee_name}")
        else:
            QMessageBox.warning(self, "Ошибка", "Выберите сотрудника для удаления!")

def main():
    app = QApplication(sys.argv)
    window = EmployeeManager()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

3. Kivy - кроссплатформенная библиотека для мобильных и десктопных приложений

Kivy позволяет создавать приложения для Windows, macOS, Linux, Android и iOS.

Установка 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.popup import Popup
from kivy.core.window import Window

Window.clearcolor = (0.9, 0.9, 0.9, 1)  # Светло-серый фон

class KivyApp(App):
    def build(self):
        # Основной layout
        self.layout = BoxLayout(orientation='vertical', spacing=10, padding=20)
        
        # Заголовок
        title = Label(
            text='Приложение Kivy',
            font_size=24,
            color=(0.2, 0.2, 0.2, 1)
        )
        self.layout.add_widget(title)
        
        # Поле ввода
        self.input_field = TextInput(
            hint_text='Введите сообщение...',
            multiline=False,
            size_hint_y=None,
            height=40
        )
        self.layout.add_widget(self.input_field)
        
        # Кнопка
        self.button = Button(
            text='Показать сообщение',
            size_hint_y=None,
            height=50,
            background_color=(0.2, 0.6, 0.8, 1)
        )
        self.button.bind(on_press=self.show_message)
        self.layout.add_widget(self.button)
        
        # Метка для результата
        self.result_label = Label(
            text='',
            font_size=18,
            color=(0.3, 0.3, 0.3, 1)
        )
        self.layout.add_widget(self.result_label)
        
        # Кнопка очистки
        clear_button = Button(
            text='Очистить',
            size_hint_y=None,
            height=40,
            background_color=(0.8, 0.3, 0.3, 1)
        )
        clear_button.bind(on_press=self.clear_text)
        self.layout.add_widget(clear_button)
        
        return self.layout
    
    def show_message(self, instance):
        message = self.input_field.text.strip()
        if message:
            self.result_label.text = f'Вы ввели: {message}'
            
            # Показ всплывающего окна
            popup_content = BoxLayout(orientation='vertical', spacing=10)
            popup_content.add_widget(Label(text=f'Сообщение:\n{message}'))
            
            close_button = Button(text='Закрыть', size_hint_y=None, height=40)
            popup = Popup(
                title='Ваше сообщение',
                content=popup_content,
                size_hint=(0.8, 0.4)
            )
            close_button.bind(on_press=popup.dismiss)
            popup_content.add_widget(close_button)
            popup.open()
        else:
            self.result_label.text = 'Пожалуйста, введите сообщение!'
    
    def clear_text(self, instance):
        self.input_field.text = ''
        self.result_label.text = ''

if __name__ == '__main__':
    KivyApp().run()

Сравнение библиотек

Критерий Tkinter PyQt5 Kivy
Сложность Низкая Высокая Средняя
Внешний вид Базовый Профессиональный Современный
Производительность Хорошая Отличная Хорошая
Кроссплатформенность Да Да Да (включая мобильные)
Документация Хорошая Отличная Хорошая
Поддержка Отличная Хорошая Хорошая
Лицензия Python GPL/Commercial MIT

Рекомендации по выбору:

  1. Tkinter - лучший выбор для начинающих, простых приложений и быстрого прототипирования.
  2. PyQt5 - идеален для профессиональных десктопных приложений с сложным интерфейсом.
  3. Kivy - оптимален для кроссплатформенных приложений, включая мобильные.

Заключение

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