Introduction
PyQt and PySide are powerful libraries for creating cross‑platform graphical user interfaces in Python using the Qt framework. They provide access to Qt’s rich toolkit: widgets, event systems, styles, animations, database integration, and even QML for building modern interfaces.
The main difference between PyQt and PySide lies in licensing: PyQt requires a GPL or commercial license, while PySide (the official implementation from the Qt Company) is released under the more permissive LGPL, making it preferable for commercial projects.
In this comprehensive guide we will cover every aspect of working with these libraries: from installation and building simple interfaces to complex projects with databases, styling, and packaging ready‑to‑run applications.
Installation and Setup
Installing PyQt5 and PyQt6
# PyQt5 (stable version)
pip install pyqt5 pyqt5-tools
# PyQt6 (modern version)
pip install pyqt6 pyqt6-tools
Installing PySide2 and PySide6
# PySide2 (for Qt 5)
pip install pyside2
# PySide6 (recommended version)
pip install pyside6
Verifying the Installation
# For PyQt5
from PyQt5.QtWidgets import QApplication
print("PyQt5 installed successfully")
# For PySide6
from PySide6.QtWidgets import QApplication
print("PySide6 installed successfully")
Basics of Application Development
Simple Window with a Button
PyQt5 version
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
import sys
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('My First PyQt5 Application')
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
button = QPushButton('Press Me')
button.clicked.connect(self.on_click)
layout.addWidget(button)
self.setLayout(layout)
def on_click(self):
print("Button pressed!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
PySide6 version
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
import sys
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('My First PySide6 Application')
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
button = QPushButton('Press Me')
button.clicked.connect(self.on_click)
layout.addWidget(button)
self.setLayout(layout)
def on_click(self):
print("Button pressed!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Qt Application Architecture
Main Components
QApplication – the central object of any Qt application. It manages the event loop, processes system events, and coordinates all widgets.
QWidget – the base class for all UI elements. It can contain other widgets and act as a container.
QMainWindow – a specialized widget for creating the main application window with support for menus, toolbars, status bars, and a central widget.
QDialog – class for creating dialog windows, including modal and modeless dialogs.
Application Lifecycle
- Create a QApplication instance
- Instantiate and configure widgets
- Show the main window
- Start the event loop with
exec()orexec_() - Terminate the application
Signals and Slots System
Core Principles
Signals and slots are Qt’s mechanism for handling events and inter‑object communication. A signal is emitted when an event occurs, and a slot is a function that handles it.
Connecting Signals
# Simple connection
button.clicked.connect(self.on_button_clicked)
# Connection with parameters
button.clicked.connect(lambda: self.process_data("parameter"))
# Connecting to a built‑in slot
button.clicked.connect(self.close)
# Disconnecting a signal
button.clicked.disconnect()
Creating Custom Signals
from PyQt5.QtCore import pyqtSignal, QObject
class DataProcessor(QObject):
# Define custom signals
data_processed = pyqtSignal(str)
progress_updated = pyqtSignal(int)
def process_data(self):
# Emit signals with data
self.progress_updated.emit(50)
self.data_processed.emit("Processing complete")
Widgets and UI Components
Fundamental Input Widgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
class InputDemo(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
# Text fields
self.line_edit = QLineEdit("Single‑line input")
self.text_edit = QTextEdit("Multi‑line input")
# Numeric fields
self.spin_box = QSpinBox()
self.spin_box.setRange(0, 100)
# Date and time selectors
self.date_edit = QDateEdit()
self.time_edit = QTimeEdit()
# Checkboxes and radio buttons
self.checkbox = QCheckBox("Enable option")
self.radio1 = QRadioButton("Option 1")
self.radio2 = QRadioButton("Option 2")
# Combo box
self.combo_box = QComboBox()
self.combo_box.addItems(["Item 1", "Item 2", "Item 3"])
# Slider
self.slider = QSlider(Qt.Horizontal)
self.slider.setRange(0, 100)
# Add all widgets to the 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)
Data Display Widgets
class DisplayDemo(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
# Labels
self.label = QLabel("Simple label")
self.rich_label = QLabel("<b>Bold</b> <i>Italic</i> <u>Underlined</u>")
# Progress bar
self.progress = QProgressBar()
self.progress.setValue(75)
# Table
self.table = QTableWidget(3, 2)
self.table.setHorizontalHeaderLabels(["Column 1", "Column 2"])
# List
self.list_widget = QListWidget()
self.list_widget.addItems(["Item 1", "Item 2", "Item 3"])
# Tree view
self.tree = QTreeWidget()
self.tree.setHeaderLabels(["Name", "Value"])
for widget in [self.label, self.rich_label, self.progress,
self.table, self.list_widget, self.tree]:
layout.addWidget(widget)
self.setLayout(layout)
Complete Methods and Functions Table
| Component | Method / Property | Description | Example Usage |
|---|---|---|---|
| QWidget | setWindowTitle() | Sets the window title | widget.setWindowTitle("My Window") |
| setGeometry() | Window size and position | widget.setGeometry(100, 100, 800, 600) |
|
| show() | Displays the widget | widget.show() |
|
| hide() | Hides the widget | widget.hide() |
|
| close() | Closes the widget | widget.close() |
|
| setEnabled() | Enables / disables the widget | widget.setEnabled(False) |
|
| setStyleSheet() | Applies CSS‑like styles | widget.setStyleSheet("color: red;") |
|
| QPushButton | clicked | Click signal | button.clicked.connect(func) |
| setText() | Sets button text | button.setText("New Text") |
|
| setIcon() | Sets an icon | button.setIcon(QIcon("icon.png")) |
|
| setCheckable() | Makes the button checkable | button.setCheckable(True) |
|
| QLineEdit | textChanged | Signal emitted when text changes | edit.textChanged.connect(func) |
| text() | Returns the current text | text = edit.text() |
|
| setText() | Sets the text | edit.setText("New Text") |
|
| setPlaceholderText() | Placeholder text | edit.setPlaceholderText("Enter...") |
|
| setValidator() | Assigns a validator | edit.setValidator(QIntValidator()) |
|
| QLabel | setText() | Sets label text | label.setText("Text") |
| setPixmap() | Sets an image | label.setPixmap(QPixmap("image.png")) |
|
| setAlignment() | Aligns the content | label.setAlignment(Qt.AlignCenter) |
|
| QComboBox | addItem() | Adds a single item | combo.addItem("Item") |
| addItems() | Adds multiple items | combo.addItems(["1", "2", "3"]) |
|
| currentText() | Returns the selected text | text = combo.currentText() |
|
| currentIndexChanged | Signal emitted when the index changes | combo.currentIndexChanged.connect(func) |
|
| QTableWidget | setRowCount() | Sets number of rows | table.setRowCount(10) |
| setColumnCount() | Sets number of columns | table.setColumnCount(5) |
|
| setItem() | Places an item in a cell | table.setItem(0, 0, QTableWidgetItem("Text")) |
|
| setHorizontalHeaderLabels() | Sets column headers | table.setHorizontalHeaderLabels(["Col1", "Col2"]) |
|
| QListWidget | addItem() | Adds a single list item | list.addItem("Item") |
| addItems() | Adds multiple list items | list.addItems(["1", "2", "3"]) |
|
| currentRow() | Returns the currently selected row | row = list.currentRow() |
|
| itemClicked | Signal emitted when an item is clicked | list.itemClicked.connect(func) |
|
| QApplication | exec_() / exec() | Starts the event loop | app.exec_() |
| quit() | Exits the application | app.quit() |
|
| processEvents() | Processes pending events | app.processEvents() |
Interface Layouts
Primary Layout Types
# Vertical layout
v_layout = QVBoxLayout()
v_layout.addWidget(widget1)
v_layout.addWidget(widget2)
# Horizontal layout
h_layout = QHBoxLayout()
h_layout.addWidget(widget1)
h_layout.addWidget(widget2)
# Grid layout
grid_layout = QGridLayout()
grid_layout.addWidget(widget1, 0, 0) # row 0, column 0
grid_layout.addWidget(widget2, 0, 1) # row 0, column 1
grid_layout.addWidget(widget3, 1, 0, 1, 2) # row 1, spanning columns 0‑1
# Form layout
form_layout = QFormLayout()
form_layout.addRow("Name:", QLineEdit())
form_layout.addRow("Email:", QLineEdit())
Nested Layouts
class ComplexLayout(QWidget):
def __init__(self):
super().__init__()
# Main vertical layout
main_layout = QVBoxLayout()
# Top horizontal bar
top_layout = QHBoxLayout()
top_layout.addWidget(QLabel("Header"))
top_layout.addWidget(QPushButton("Settings"))
# Central grid area
grid_layout = QGridLayout()
grid_layout.addWidget(QLabel("Field 1:"), 0, 0)
grid_layout.addWidget(QLineEdit(), 0, 1)
grid_layout.addWidget(QLabel("Field 2:"), 1, 0)
grid_layout.addWidget(QLineEdit(), 1, 1)
# Bottom button panel
button_layout = QHBoxLayout()
button_layout.addWidget(QPushButton("OK"))
button_layout.addWidget(QPushButton("Cancel"))
# Assemble all layouts
main_layout.addLayout(top_layout)
main_layout.addLayout(grid_layout)
main_layout.addLayout(button_layout)
self.setLayout(main_layout)
Interface Styling
Qt Style Sheets (CSS‑like)
# Styling an individual widget
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;
}
""")
# Global application stylesheet
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;
}
""")
Themes
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("") # Revert to the default theme
Working with Qt Designer
Creating an Interface in Designer
- Launch Qt Designer via the
designercommand (for PyQt) or through Qt Creator. - Create a new widget or main window.
- Drag the required components from the widget palette.
- Configure properties and layout.
- Save the file with a
.uiextension.
Converting .ui Files to Python
# For PyQt5
pyuic5 interface.ui -o interface.py
# For PyQt6
pyuic6 interface.ui -o interface.py
# For PySide6
pyside6-uic interface.ui -o interface.py
Using .ui Files in Code
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# Load the UI from the .ui file
uic.loadUi('interface.ui', self)
# Connect signals
self.pushButton.clicked.connect(self.on_button_clicked)
def on_button_clicked(self):
print("Button pressed!")
Working with QML
QML Basics
QML (Qt Modeling Language) is a declarative language for building modern user interfaces with animation and effect support.
Simple QML File (main.qml)
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 400
height: 300
title: "QML Application"
Rectangle {
anchors.fill: parent
color: "#f0f0f0"
Column {
anchors.centerIn: parent
spacing: 20
Text {
text: "Hello from QML!"
font.pixelSize: 24
color: "#333"
}
Button {
text: "Press Me"
onClicked: console.log("Button pressed!")
}
Rectangle {
width: 200
height: 50
color: "lightblue"
border.color: "blue"
radius: 10
Text {
anchors.centerIn: parent
text: "Nice block"
}
}
}
}
}
Integrating QML with Python
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal, Slot
class Backend(QObject):
# Signal to send data to QML
dataChanged = Signal(str)
@Slot(str)
def process_data(self, data):
"""Slot for processing data from QML"""
result = f"Processed: {data}"
self.dataChanged.emit(result)
return result
app = QGuiApplication([])
engine = QQmlApplicationEngine()
# Register the Python object in QML
backend = Backend()
engine.rootContext().setContextProperty("backend", backend)
# Load the QML file
engine.load("main.qml")
app.exec()
Event Handling and Advanced Techniques
Overriding Events
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QKeyEvent, QCloseEvent
class CustomWidget(QWidget):
def keyPressEvent(self, event: QKeyEvent):
"""Handle key presses"""
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):
"""Handle window close"""
reply = QMessageBox.question(self, 'Confirmation',
'Are you sure you want to exit?',
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
def mousePressEvent(self, event):
"""Handle mouse clicks"""
if event.button() == Qt.LeftButton:
print(f"Left click at position: {event.pos()}")
elif event.button() == Qt.RightButton:
print(f"Right click at position: {event.pos()}")
Working with Threads
from PyQt5.QtCore import QThread, pyqtSignal
import time
class WorkerThread(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(str)
def run(self):
"""Run a long‑running task in a separate thread"""
for i in range(101):
time.sleep(0.1) # Simulate work
self.progress.emit(i)
self.finished.emit("Task completed!")
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)
# Start the thread
self.worker.start()
def update_progress(self, value):
self.progress_bar.setValue(value)
def task_finished(self, message):
QMessageBox.information(self, "Done", message)
Database Interaction
Connecting to 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("Failed to connect to the database")
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()
# Model for displaying data
self.model = QSqlTableModel()
self.model.setTable('users')
self.model.select()
# Table view
self.table_view = QTableView()
self.table_view.setModel(self.model)
layout.addWidget(self.table_view)
self.setLayout(layout)
Packaging the Application into an Executable
Preparation for Packaging
# main.py
import sys
import os
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon
def resource_path(relative_path):
"""Get absolute path to resource, works for PyInstaller"""
try:
# PyInstaller creates a temporary folder and stores path in _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)
# Set application icon
self.setWindowIcon(QIcon(resource_path('icons/app_icon.ico')))
# Create main window
self.main_window = MainWindow()
self.main_window.show()
if __name__ == '__main__':
app = MainApp(sys.argv)
sys.exit(app.exec_())
Building with PyInstaller
# Simple build
pyinstaller --onefile main.py
# Build with icon and additional data files
pyinstaller --onefile --windowed --icon=app_icon.ico --add-data "icons;icons" --add-data "data;data" main.py
# Create a spec file for complex projects
pyinstaller --onefile --windowed main.py
# Then edit main.spec and rebuild:
pyinstaller main.spec
Example spec File
# 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')
Detailed Comparison of PyQt and PySide
| Parameter | PyQt5 / PyQt6 | PySide2 / PySide6 |
|---|---|---|
| License | GPL v3 / Commercial | LGPL v3 |
| Developer | Riverbank Computing | Qt Company (official) |
| Stability | Very stable | Stable |
| Documentation | Good | Excellent (official) |
| Performance | High | High |
| Signals & Slots | pyqtSignal, pyqtSlot | Signal, Slot |
| Additional Tooling | pyqt5-tools | Included in the core package |
| Python 2 Support | PyQt5 – yes | PySide2 – limited |
| Commercial Use | Requires a license | Free under LGPL compliance |
| Package Size | Smaller | Larger |
| Updates | Regular | Synchronized with Qt releases |
Practical Projects
Task Manager
class TaskManager(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Task Manager")
self.setGeometry(100, 100, 800, 600)
# Central widget
central_widget = QWidget()
self.setCentralWidget(central_widget)
# Layout
layout = QVBoxLayout()
# New task input
input_layout = QHBoxLayout()
self.task_input = QLineEdit()
self.task_input.setPlaceholderText("Enter a new task...")
self.add_button = QPushButton("Add")
self.add_button.clicked.connect(self.add_task)
input_layout.addWidget(self.task_input)
input_layout.addWidget(self.add_button)
# Task list
self.task_list = QListWidget()
self.task_list.itemDoubleClicked.connect(self.toggle_task)
# Control buttons
button_layout = QHBoxLayout()
self.delete_button = QPushButton("Delete")
self.delete_button.clicked.connect(self.delete_task)
self.clear_button = QPushButton("Clear All")
self.clear_button.clicked.connect(self.clear_all)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.clear_button)
# Assemble UI
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)
Calculator with History
class Calculator(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Smart Calculator")
self.setFixedSize(400, 600)
# History list
self.history = []
self.init_ui()
def init_ui(self):
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
# Display
self.display = QLineEdit()
self.display.setReadOnly(True)
self.display.setAlignment(Qt.AlignRight)
self.display.setStyleSheet("font-size: 20px; padding: 10px;")
# History view
self.history_list = QListWidget()
self.history_list.setMaximumHeight(150)
self.history_list.itemClicked.connect(self.load_from_history)
# Buttons grid
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 btn in buttons:
text = btn[0]
row = btn[1]
col = btn[2]
row_span = btn[3] if len(btn) > 3 else 1
col_span = btn[4] if len(btn) > 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)
# Build UI
layout.addWidget(QLabel("History:"))
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:
# Replace symbols for Python evaluation
expression = self.current_input.replace('×', '*').replace('÷', '/')
result = eval(expression)
# Add to history
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 = "Error"
elif text == 'C':
self.current_input = "0"
self.display.setText(self.current_input)
def load_from_history(self, item):
# Load result from history
text = item.text()
result = text.split(' = ')[-1]
self.current_input = result
self.display.setText(result)
Frequently Asked Questions
Which version should I choose: PyQt or PySide?
For commercial projects PySide6 is recommended because of its more permissive LGPL license. PyQt requires purchasing a commercial license or releasing the source under GPL.
How do I upgrade from PyQt5 to PyQt6?
Key changes:
exec_()has been replaced byexec()- Some modules have been moved (e.g., QtWebKit removed)
- Adjustments in the event system
Can PyQt/PySide be used for mobile applications?
Yes, but with limitations. Qt supports Android and iOS, yet it requires special configuration and may not be optimal for native mobile development.
How can I optimise the performance of Qt applications?
- Use data models instead of adding items one by one
- Minimize the number of UI updates
- Run long‑running tasks in separate threads
- Employ lazy loading for large datasets
How do I create a system tray icon for my application?
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"))
# Context menu
menu = QMenu()
menu.addAction("Show", self.show_window)
menu.addAction("Exit", self.quit_app)
self.tray_icon.setContextMenu(menu)
self.tray_icon.show()
Conclusion
PyQt and PySide are powerful, mature tools for building professional graphical interfaces in Python. They provide full access to the Qt framework, including modern QML‑based UIs, database integration, networking capabilities, and cross‑platform compatibility.
The choice between PyQt and PySide primarily depends on the licensing requirements of your project. PySide6 with an LGPL license is generally preferred for commercial applications, whereas PyQt may require a commercial license.
Both libraries are actively developed and supported, boast extensive documentation, and have large developer communities. They are suitable for simple utilities as well as complex enterprise‑level applications with rich functionality.
The Future of AI in Mathematics and Everyday Life: How Intelligent Agents Are Already Changing the Game
Experts warned about the risks of fake charity with AI
In Russia, universal AI-agent for robots and industrial processes was developed