Введение
PyQt и PySide — это мощные библиотеки для создания кроссплатформенных графических интерфейсов на Python с использованием фреймворка Qt. Они предоставляют доступ к богатому набору инструментов Qt: виджетам, системам событий, стилям, анимациям, работе с базами данных и даже QML для создания современных интерфейсов.
Главное отличие между PyQt и PySide заключается в лицензировании: PyQt требует GPL или коммерческую лицензию, а PySide (официальная реализация от Qt Company) распространяется под более либеральной LGPL, что делает его предпочтительным для коммерческих проектов.
В этом подробном руководстве мы рассмотрим все аспекты работы с этими библиотеками: от установки и создания простых интерфейсов до сложных проектов с базами данных, стилизацией и сборкой готовых приложений.
Установка и настройка
Установка PyQt5 и PyQt6
# PyQt5 (стабильная версия)
pip install pyqt5 pyqt5-tools
# PyQt6 (современная версия)
pip install pyqt6 pyqt6-tools
Установка PySide2 и PySide6
# PySide2 (для Qt 5)
pip install pyside2
# PySide6 (рекомендуемая версия)
pip install pyside6
Проверка установки
# Для PyQt5
from PyQt5.QtWidgets import QApplication
print("PyQt5 установлен успешно")
# Для PySide6
from PySide6.QtWidgets import QApplication
print("PySide6 установлен успешно")
Основы создания приложений
Простое окно с кнопкой
PyQt5 версия
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
import sys
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Мое первое приложение PyQt5')
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
button = QPushButton('Нажми меня')
button.clicked.connect(self.on_click)
layout.addWidget(button)
self.setLayout(layout)
def on_click(self):
print("Кнопка нажата!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
PySide6 версия
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
import sys
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Мое первое приложение PySide6')
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
button = QPushButton('Нажми меня')
button.clicked.connect(self.on_click)
layout.addWidget(button)
self.setLayout(layout)
def on_click(self):
print("Кнопка нажата!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Архитектура Qt приложений
Основные компоненты
QApplication — это центральный объект любого Qt приложения. Он управляет циклом событий, обрабатывает системные события и координирует работу всех виджетов.
QWidget — базовый класс для всех элементов пользовательского интерфейса. Может содержать другие виджеты и служить контейнером.
QMainWindow — специализированный виджет для создания главного окна приложения с поддержкой меню, панелей инструментов, статусной строки и центрального виджета.
QDialog — класс для создания диалоговых окон, включая модальные и немодальные диалоги.
Жизненный цикл приложения
- Создание экземпляра QApplication
- Создание и настройка виджетов
- Отображение главного окна
- Запуск цикла событий через exec() или exec_()
- Завершение приложения
Система сигналов и слотов
Основные принципы
Сигналы и слоты — это механизм Qt для обработки событий и межобъектного взаимодействия. Сигнал генерируется при возникновении события, а слот — это функция, которая его обрабатывает.
Подключение сигналов
# Простое подключение
button.clicked.connect(self.on_button_clicked)
# Подключение с параметрами
button.clicked.connect(lambda: self.process_data("параметр"))
# Подключение к встроенному слоту
button.clicked.connect(self.close)
# Отключение сигнала
button.clicked.disconnect()
Создание пользовательских сигналов
from PyQt5.QtCore import pyqtSignal, QObject
class DataProcessor(QObject):
# Определение пользовательского сигнала
data_processed = pyqtSignal(str)
progress_updated = pyqtSignal(int)
def process_data(self):
# Эмитирование сигнала с данными
self.progress_updated.emit(50)
self.data_processed.emit("Обработка завершена")
Виджеты и компоненты интерфейса
Основные виджеты ввода
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
class InputDemo(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
# Текстовые поля
self.line_edit = QLineEdit("Однострочный ввод")
self.text_edit = QTextEdit("Многострочный ввод")
# Числовые поля
self.spin_box = QSpinBox()
self.spin_box.setRange(0, 100)
# Выбор даты и времени
self.date_edit = QDateEdit()
self.time_edit = QTimeEdit()
# Чекбоксы и радиокнопки
self.checkbox = QCheckBox("Включить опцию")
self.radio1 = QRadioButton("Вариант 1")
self.radio2 = QRadioButton("Вариант 2")
# Выпадающий список
self.combo_box = QComboBox()
self.combo_box.addItems(["Элемент 1", "Элемент 2", "Элемент 3"])
# Ползунки
self.slider = QSlider(Qt.Horizontal)
self.slider.setRange(0, 100)
# Добавление всех виджетов в layout
for widget in [self.line_edit, self.text_edit, self.spin_box,
self.date_edit, self.time_edit, self.checkbox,
self.radio1, self.radio2, self.combo_box, self.slider]:
layout.addWidget(widget)
self.setLayout(layout)
Отображение данных
class DisplayDemo(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
# Метки
self.label = QLabel("Простая метка")
self.rich_label = QLabel("<b>Жирный</b> <i>курсив</i> <u>подчеркнутый</u>")
# Прогресс-бар
self.progress = QProgressBar()
self.progress.setValue(75)
# Таблица
self.table = QTableWidget(3, 2)
self.table.setHorizontalHeaderLabels(["Колонка 1", "Колонка 2"])
# Список
self.list_widget = QListWidget()
self.list_widget.addItems(["Элемент 1", "Элемент 2", "Элемент 3"])
# Древовидный список
self.tree = QTreeWidget()
self.tree.setHeaderLabels(["Название", "Значение"])
for widget in [self.label, self.rich_label, self.progress,
self.table, self.list_widget, self.tree]:
layout.addWidget(widget)
self.setLayout(layout)
Полная таблица методов и функций
| Компонент | Метод/Свойство | Описание | Пример использования |
|---|---|---|---|
| QWidget | setWindowTitle() | Установка заголовка окна | widget.setWindowTitle("Мое окно") |
| setGeometry() | Размер и позиция окна | widget.setGeometry(100, 100, 800, 600) |
|
| show() | Отображение виджета | widget.show() |
|
| hide() | Скрытие виджета | widget.hide() |
|
| close() | Закрытие виджета | widget.close() |
|
| setEnabled() | Включение/отключение | widget.setEnabled(False) |
|
| setStyleSheet() | Применение CSS стилей | widget.setStyleSheet("color: red;") |
|
| QPushButton | clicked | Сигнал нажатия | button.clicked.connect(func) |
| setText() | Установка текста | button.setText("Новый текст") |
|
| setIcon() | Установка иконки | button.setIcon(QIcon("icon.png")) |
|
| setCheckable() | Кнопка-переключатель | button.setCheckable(True) |
|
| QLineEdit | textChanged | Сигнал изменения текста | edit.textChanged.connect(func) |
| text() | Получение текста | text = edit.text() |
|
| setText() | Установка текста | edit.setText("Новый текст") |
|
| setPlaceholderText() | Текст-подсказка | edit.setPlaceholderText("Введите...") |
|
| setValidator() | Установка валидатора | edit.setValidator(QIntValidator()) |
|
| QLabel | setText() | Установка текста | label.setText("Текст") |
| setPixmap() | Установка изображения | label.setPixmap(QPixmap("image.png")) |
|
| setAlignment() | Выравнивание | label.setAlignment(Qt.AlignCenter) |
|
| QComboBox | addItem() | Добавление элемента | combo.addItem("Элемент") |
| addItems() | Добавление списка | combo.addItems(["1", "2", "3"]) |
|
| currentText() | Текущий выбранный текст | text = combo.currentText() |
|
| currentIndexChanged | Сигнал смены индекса | combo.currentIndexChanged.connect(func) |
|
| QTableWidget | setRowCount() | Количество строк | table.setRowCount(10) |
| setColumnCount() | Количество столбцов | table.setColumnCount(5) |
|
| setItem() | Установка элемента | table.setItem(0, 0, QTableWidgetItem("Текст")) |
|
| setHorizontalHeaderLabels() | Заголовки столбцов | table.setHorizontalHeaderLabels(["Кол1", "Кол2"]) |
|
| QListWidget | addItem() | Добавление элемента | list.addItem("Элемент") |
| addItems() | Добавление списка | list.addItems(["1", "2", "3"]) |
|
| currentRow() | Текущая строка | row = list.currentRow() |
|
| itemClicked | Сигнал клика по элементу | list.itemClicked.connect(func) |
|
| QApplication | exec_() / exec() | Запуск цикла событий | app.exec_() |
| quit() | Завершение приложения | app.quit() |
|
| processEvents() | Обработка событий | app.processEvents() |
Компоновка интерфейса (Layouts)
Основные типы компоновщиков
# Вертикальная компоновка
v_layout = QVBoxLayout()
v_layout.addWidget(widget1)
v_layout.addWidget(widget2)
# Горизонтальная компоновка
h_layout = QHBoxLayout()
h_layout.addWidget(widget1)
h_layout.addWidget(widget2)
# Сетка
grid_layout = QGridLayout()
grid_layout.addWidget(widget1, 0, 0) # строка 0, столбец 0
grid_layout.addWidget(widget2, 0, 1) # строка 0, столбец 1
grid_layout.addWidget(widget3, 1, 0, 1, 2) # строка 1, столбцы 0-1
# Форма
form_layout = QFormLayout()
form_layout.addRow("Имя:", QLineEdit())
form_layout.addRow("Email:", QLineEdit())
Вложенные компоновки
class ComplexLayout(QWidget):
def __init__(self):
super().__init__()
# Главная вертикальная компоновка
main_layout = QVBoxLayout()
# Верхняя горизонтальная панель
top_layout = QHBoxLayout()
top_layout.addWidget(QLabel("Заголовок"))
top_layout.addWidget(QPushButton("Настройки"))
# Средняя часть с сеткой
grid_layout = QGridLayout()
grid_layout.addWidget(QLabel("Поле 1:"), 0, 0)
grid_layout.addWidget(QLineEdit(), 0, 1)
grid_layout.addWidget(QLabel("Поле 2:"), 1, 0)
grid_layout.addWidget(QLineEdit(), 1, 1)
# Нижняя панель кнопок
button_layout = QHBoxLayout()
button_layout.addWidget(QPushButton("ОК"))
button_layout.addWidget(QPushButton("Отмена"))
# Сборка всех компоновок
main_layout.addLayout(top_layout)
main_layout.addLayout(grid_layout)
main_layout.addLayout(button_layout)
self.setLayout(main_layout)
Стилизация интерфейса
Qt Style Sheets (CSS-подобные стили)
# Стилизация отдельного виджета
button.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
font-size: 16px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3e8e41;
}
""")
# Глобальные стили для всего приложения
app.setStyleSheet("""
QMainWindow {
background-color: #f0f0f0;
}
QLabel {
font-family: Arial;
font-size: 14px;
color: #333;
}
QLineEdit {
border: 2px solid #ddd;
border-radius: 4px;
padding: 8px;
font-size: 14px;
}
QLineEdit:focus {
border-color: #4CAF50;
}
""")
Темы оформления
class ThemeManager:
@staticmethod
def apply_dark_theme(app):
dark_stylesheet = """
QMainWindow {
background-color: #2b2b2b;
color: #ffffff;
}
QWidget {
background-color: #2b2b2b;
color: #ffffff;
}
QPushButton {
background-color: #404040;
border: 1px solid #555555;
padding: 8px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #505050;
}
QLineEdit {
background-color: #404040;
border: 1px solid #555555;
padding: 5px;
border-radius: 3px;
}
"""
app.setStyleSheet(dark_stylesheet)
@staticmethod
def apply_light_theme(app):
app.setStyleSheet("") # Возврат к стандартной теме
Работа с Qt Designer
Создание интерфейса в Designer
- Запустите Qt Designer через команду
designer(для PyQt) или через Qt Creator - Создайте новый виджет или главное окно
- Перетащите нужные компоненты из панели виджетов
- Настройте свойства и компоновку
- Сохраните файл с расширением .ui
Конвертация .ui файлов в Python
# Для PyQt5
pyuic5 interface.ui -o interface.py
# Для PyQt6
pyuic6 interface.ui -o interface.py
# Для PySide6
pyside6-uic interface.ui -o interface.py
Использование .ui файлов в коде
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# Загрузка интерфейса из .ui файла
uic.loadUi('interface.ui', self)
# Подключение сигналов
self.pushButton.clicked.connect(self.on_button_clicked)
def on_button_clicked(self):
print("Кнопка нажата!")
Работа с QML
Основы QML
QML (Qt Modeling Language) — это декларативный язык для создания современных пользовательских интерфейсов с поддержкой анимаций и эффектов.
Простой QML файл (main.qml)
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 400
height: 300
title: "QML приложение"
Rectangle {
anchors.fill: parent
color: "#f0f0f0"
Column {
anchors.centerIn: parent
spacing: 20
Text {
text: "Привет из QML!"
font.pixelSize: 24
color: "#333"
}
Button {
text: "Нажми меня"
onClicked: console.log("Кнопка нажата!")
}
Rectangle {
width: 200
height: 50
color: "lightblue"
border.color: "blue"
radius: 10
Text {
anchors.centerIn: parent
text: "Красивый блок"
}
}
}
}
}
Интеграция QML с Python
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal, Slot
class Backend(QObject):
# Сигнал для отправки данных в QML
dataChanged = Signal(str)
@Slot(str)
def process_data(self, data):
"""Слот для обработки данных из QML"""
result = f"Обработано: {data}"
self.dataChanged.emit(result)
return result
app = QGuiApplication([])
engine = QQmlApplicationEngine()
# Регистрация Python объекта в QML
backend = Backend()
engine.rootContext().setContextProperty("backend", backend)
# Загрузка QML файла
engine.load("main.qml")
app.exec()
Обработка событий и продвинутые техники
Переопределение событий
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QKeyEvent, QCloseEvent
class CustomWidget(QWidget):
def keyPressEvent(self, event: QKeyEvent):
"""Обработка нажатий клавиш"""
if event.key() == Qt.Key_Escape:
self.close()
elif event.key() == Qt.Key_F11:
if self.isFullScreen():
self.showNormal()
else:
self.showFullScreen()
else:
super().keyPressEvent(event)
def closeEvent(self, event: QCloseEvent):
"""Обработка закрытия окна"""
reply = QMessageBox.question(self, 'Подтверждение',
'Вы уверены, что хотите выйти?',
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
def mousePressEvent(self, event):
"""Обработка кликов мыши"""
if event.button() == Qt.LeftButton:
print(f"Левый клик в позиции: {event.pos()}")
elif event.button() == Qt.RightButton:
print(f"Правый клик в позиции: {event.pos()}")
Работа с потоками (Threading)
from PyQt5.QtCore import QThread, pyqtSignal
import time
class WorkerThread(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(str)
def run(self):
"""Выполнение долгой задачи в отдельном потоке"""
for i in range(101):
time.sleep(0.1) # Имитация работы
self.progress.emit(i)
self.finished.emit("Задача выполнена!")
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.worker = WorkerThread()
self.worker.progress.connect(self.update_progress)
self.worker.finished.connect(self.task_finished)
self.progress_bar = QProgressBar()
self.setCentralWidget(self.progress_bar)
# Запуск потока
self.worker.start()
def update_progress(self, value):
self.progress_bar.setValue(value)
def task_finished(self, message):
QMessageBox.information(self, "Готово", message)
Работа с базами данных
Подключение к SQLite
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
from PyQt5.QtWidgets import QTableView
class DatabaseManager:
def __init__(self):
self.db = QSqlDatabase.addDatabase('QSQLITE')
self.db.setDatabaseName('app_database.db')
if not self.db.open():
print("Ошибка подключения к базе данных")
def create_tables(self):
query = QSqlQuery()
query.exec_("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
def add_user(self, name, email):
query = QSqlQuery()
query.prepare("INSERT INTO users (name, email) VALUES (?, ?)")
query.addBindValue(name)
query.addBindValue(email)
return query.exec_()
class DatabaseView(QWidget):
def __init__(self):
super().__init__()
self.db_manager = DatabaseManager()
self.db_manager.create_tables()
layout = QVBoxLayout()
# Модель для отображения данных
self.model = QSqlTableModel()
self.model.setTable('users')
self.model.select()
# Представление таблицы
self.table_view = QTableView()
self.table_view.setModel(self.model)
layout.addWidget(self.table_view)
self.setLayout(layout)
Сборка приложения в исполняемый файл
Подготовка к сборке
# main.py
import sys
import os
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon
def resource_path(relative_path):
"""Получить абсолютный путь к ресурсу"""
try:
# PyInstaller создает временную папку и сохраняет путь в _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
class MainApp(QApplication):
def __init__(self, argv):
super().__init__(argv)
# Установка иконки приложения
self.setWindowIcon(QIcon(resource_path('icons/app_icon.ico')))
# Создание главного окна
self.main_window = MainWindow()
self.main_window.show()
if __name__ == '__main__':
app = MainApp(sys.argv)
sys.exit(app.exec_())
Сборка с PyInstaller
# Простая сборка
pyinstaller --onefile main.py
# Сборка с иконкой и дополнительными файлами
pyinstaller --onefile --windowed --icon=app_icon.ico --add-data "icons;icons" --add-data "data;data" main.py
# Создание spec файла для сложных проектов
pyinstaller --onefile --windowed main.py
# Затем отредактировать main.spec и пересобрать:
pyinstaller main.spec
Пример spec файла
# main.spec
a = Analysis(['main.py'],
pathex=[],
binaries=[],
datas=[('icons', 'icons'), ('data', 'data')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='MyApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
icon='app_icon.ico')
Подробное сравнение PyQt и PySide
| Параметр | PyQt5/PyQt6 | PySide2/PySide6 |
|---|---|---|
| Лицензия | GPL v3 / Коммерческая | LGPL v3 |
| Разработчик | Riverbank Computing | Qt Company (официально) |
| Стабильность | Очень стабильная | Стабильная |
| Документация | Хорошая | Отличная (официальная) |
| Производительность | Высокая | Высокая |
| Сигналы и слоты | pyqtSignal, pyqtSlot | Signal, Slot |
| Установка дополнительных инструментов | pyqt5-tools | Включены в основной пакет |
| Поддержка Python 2 | PyQt5 - да | PySide2 - ограниченная |
| Коммерческое использование | Требует лицензии | Бесплатно при соблюдении LGPL |
| Размер пакета | Меньше | Больше |
| Обновления | Регулярные | Синхронизированы с Qt |
Практические проекты
Менеджер задач
class TaskManager(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Менеджер задач")
self.setGeometry(100, 100, 800, 600)
# Центральный виджет
central_widget = QWidget()
self.setCentralWidget(central_widget)
# Компоновка
layout = QVBoxLayout()
# Поле ввода новой задачи
input_layout = QHBoxLayout()
self.task_input = QLineEdit()
self.task_input.setPlaceholderText("Введите новую задачу...")
self.add_button = QPushButton("Добавить")
self.add_button.clicked.connect(self.add_task)
input_layout.addWidget(self.task_input)
input_layout.addWidget(self.add_button)
# Список задач
self.task_list = QListWidget()
self.task_list.itemDoubleClicked.connect(self.toggle_task)
# Кнопки управления
button_layout = QHBoxLayout()
self.delete_button = QPushButton("Удалить")
self.delete_button.clicked.connect(self.delete_task)
self.clear_button = QPushButton("Очистить все")
self.clear_button.clicked.connect(self.clear_all)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.clear_button)
# Сборка интерфейса
layout.addLayout(input_layout)
layout.addWidget(self.task_list)
layout.addLayout(button_layout)
central_widget.setLayout(layout)
def add_task(self):
task_text = self.task_input.text().strip()
if task_text:
self.task_list.addItem(task_text)
self.task_input.clear()
def delete_task(self):
current_row = self.task_list.currentRow()
if current_row >= 0:
self.task_list.takeItem(current_row)
def clear_all(self):
self.task_list.clear()
def toggle_task(self, item):
font = item.font()
font.setStrikeOut(not font.strikeOut())
item.setFont(font)
Калькулятор с историей
class Calculator(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Умный калькулятор")
self.setFixedSize(400, 600)
# История вычислений
self.history = []
self.init_ui()
def init_ui(self):
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
# Дисплей
self.display = QLineEdit()
self.display.setReadOnly(True)
self.display.setAlignment(Qt.AlignRight)
self.display.setStyleSheet("font-size: 20px; padding: 10px;")
# История
self.history_list = QListWidget()
self.history_list.setMaximumHeight(150)
self.history_list.itemClicked.connect(self.load_from_history)
# Кнопки
buttons_layout = QGridLayout()
buttons = [
('C', 0, 0), ('±', 0, 1), ('%', 0, 2), ('÷', 0, 3),
('7', 1, 0), ('8', 1, 1), ('9', 1, 2), ('×', 1, 3),
('4', 2, 0), ('5', 2, 1), ('6', 2, 2), ('-', 2, 3),
('1', 3, 0), ('2', 3, 1), ('3', 3, 2), ('+', 3, 3),
('0', 4, 0, 1, 2), ('.', 4, 2), ('=', 4, 3)
]
for button_data in buttons:
text = button_data[0]
row = button_data[1]
col = button_data[2]
row_span = button_data[3] if len(button_data) > 3 else 1
col_span = button_data[4] if len(button_data) > 4 else 1
button = QPushButton(text)
button.clicked.connect(lambda checked, t=text: self.button_clicked(t))
button.setMinimumHeight(50)
buttons_layout.addWidget(button, row, col, row_span, col_span)
# Сборка интерфейса
layout.addWidget(QLabel("История:"))
layout.addWidget(self.history_list)
layout.addWidget(self.display)
layout.addLayout(buttons_layout)
central_widget.setLayout(layout)
self.current_input = ""
self.display.setText("0")
def button_clicked(self, text):
if text.isdigit() or text == '.':
if self.current_input == "0":
self.current_input = text
else:
self.current_input += text
elif text in ['+', '-', '×', '÷']:
self.current_input += f" {text} "
elif text == '=':
try:
# Замена символов для Python
expression = self.current_input.replace('×', '*').replace('÷', '/')
result = eval(expression)
# Добавление в историю
history_item = f"{self.current_input} = {result}"
self.history.append(history_item)
self.history_list.addItem(history_item)
self.current_input = str(result)
except:
self.current_input = "Ошибка"
elif text == 'C':
self.current_input = "0"
self.display.setText(self.current_input)
def load_from_history(self, item):
# Загрузка результата из истории
text = item.text()
result = text.split(' = ')[-1]
self.current_input = result
self.display.setText(result)
Часто задаваемые вопросы
Какую версию выбрать: PyQt или PySide?
Для коммерческих проектов рекомендуется PySide6 из-за более либеральной лицензии LGPL. PyQt требует покупки коммерческой лицензии или открытия исходного кода под GPL.
Как обновиться с PyQt5 на PyQt6?
Основные изменения:
exec_()заменено наexec()- Некоторые модули перенесены (например, QtWebKit удален)
- Изменения в системе событий
Можно ли использовать PyQt/PySide для мобильных приложений?
Да, но ограниченно. Qt поддерживает Android и iOS, но требует специальной настройки и может быть не оптимальным для мобильной разработки.
Как оптимизировать производительность Qt приложений?
- Используйте модели данных вместо добавления элементов по одному
- Минимизируйте количество обновлений интерфейса
- Используйте потоки для долгих операций
- Применяйте ленивую загрузку для больших данных
Как создать системный трей для приложения?
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
from PyQt5.QtGui import QIcon
class SystemTrayApp:
def __init__(self):
self.tray_icon = QSystemTrayIcon()
self.tray_icon.setIcon(QIcon("icon.png"))
# Контекстное меню
menu = QMenu()
menu.addAction("Показать", self.show_window)
menu.addAction("Выход", self.quit_app)
self.tray_icon.setContextMenu(menu)
self.tray_icon.show()
Заключение
PyQt и PySide представляют собой мощные и зрелые инструменты для создания профессиональных графических интерфейсов на Python. Они обеспечивают полный доступ к возможностям фреймворка Qt, включая современные интерфейсы с QML, работу с базами данных, сетевые возможности и кроссплатформенную совместимость.
Выбор между PyQt и PySide в первую очередь зависит от лицензионных требований вашего проекта. PySide6 с лицензией LGPL является более предпочтительным для коммерческих проектов, в то время как PyQt может потребовать приобретения коммерческой лицензии.
Обе библиотеки активно развиваются и поддерживаются, имеют обширную документацию и большое сообщество разработчиков. Они подходят как для создания простых утилит, так и для разработки сложных enterprise-приложений с богатым функционалом.
Настоящее и будущее развития ИИ: классической математики уже недостаточно
Эксперты предупредили о рисках фейковой благотворительности с помощью ИИ
В России разработали универсального ИИ-агента для роботов и индустриальных процессов