Code With Python
Photo
## 🔹 Multi-Window Applications
### 1. Creating Secondary Windows
### 2. Window Communication
---
## 🔹 Model-View Architecture
### 1. QListView with StringListModel
### 2. Custom Table Model
---
## 🔹 Practical Example: Text Editor
---
## 🔹 Best Practices
1. Separate UI code from business logic
2. Use models for complex data views
3. Optimize performance for large datasets
4. Localize strings for internationalization
5. Document signals and public methods
---
### 📌 What's Next?
In Part 3, we'll cover:
➡️ Dialogs & Message Boxes
➡️ File System Operations
➡️ Drag & Drop
➡️ Threading with QThread
#PyQt5 #GUIPython #ProfessionalDevelopment 🚀
Practice Exercise:
1. Create a contacts app with tree view and detail form
2. Build a styled calculator with custom buttons
3. Implement a multi-window image viewer with thumbnails
### 1. Creating Secondary Windows
class SettingsWindow(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Settings")
layout = QVBoxLayout()
layout.addWidget(QLabel("Application Settings"))
self.setLayout(layout)
# In main window:
def show_settings(self):
settings = SettingsWindow()
settings.exec_() # Modal dialog
# OR settings.show() for non-modal
### 2. Window Communication
# Main window with signal
class MainWindow(QMainWindow):
settings_changed = pyqtSignal(dict)
def open_settings(self):
dialog = SettingsDialog(self) # Pass parent
if dialog.exec_():
self.settings_changed.emit(dialog.get_settings())
# Settings dialog
class SettingsDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# ... setup UI ...
def get_settings(self):
return {"theme": self.theme_combo.currentText()}
---
## 🔹 Model-View Architecture
### 1. QListView with StringListModel
model = QStringListModel()
model.setStringList(["Item 1", "Item 2", "Item 3"])
list_view = QListView()
list_view.setModel(model)
# Add items
model.insertRow(model.rowCount())
model.setData(model.index(model.rowCount()-1), "New Item")
### 2. Custom Table Model
class CustomTableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def rowCount(self, parent=None):
return len(self._data)
def columnCount(self, parent=None):
return len(self._data[0]) if self._data else 0
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return str(self._data[index.row()][index.column()])
return None
# Usage
data = [[1, "Alice"], [2, "Bob"], [3, "Charlie"]]
model = CustomTableModel(data)
table = QTableView()
table.setModel(model)
---
## 🔹 Practical Example: Text Editor
class TextEditor(QMainWindow):
def __init__(self):
super().__init__()
self.setup_ui()
self.setup_menu()
def setup_ui(self):
self.text_edit = QTextEdit()
self.setCentralWidget(self.text_edit)
# Status bar
self.statusBar().showMessage("Ready")
# Toolbar
toolbar = self.addToolBar("Tools")
save_act = QAction(QIcon("save.png"), "Save", self)
save_act.triggered.connect(self.save_file)
toolbar.addAction(save_act)
def setup_menu(self):
menubar = self.menuBar()
# File menu
file_menu = menubar.addMenu("File")
open_act = QAction("Open", self)
open_act.triggered.connect(self.open_file)
file_menu.addAction(open_act)
# Edit menu
edit_menu = menubar.addMenu("Edit")
edit_menu.addAction("Copy", self.text_edit.copy)
edit_menu.addAction("Paste", self.text_edit.paste)
def open_file(self):
path, _ = QFileDialog.getOpenFileName()
if path:
with open(path, 'r') as f:
self.text_edit.setText(f.read())
def save_file(self):
path, _ = QFileDialog.getSaveFileName()
if path:
with open(path, 'w') as f:
f.write(self.text_edit.toPlainText())
---
## 🔹 Best Practices
1. Separate UI code from business logic
2. Use models for complex data views
3. Optimize performance for large datasets
4. Localize strings for internationalization
5. Document signals and public methods
---
### 📌 What's Next?
In Part 3, we'll cover:
➡️ Dialogs & Message Boxes
➡️ File System Operations
➡️ Drag & Drop
➡️ Threading with QThread
#PyQt5 #GUIPython #ProfessionalDevelopment 🚀
Practice Exercise:
1. Create a contacts app with tree view and detail form
2. Build a styled calculator with custom buttons
3. Implement a multi-window image viewer with thumbnails
Code With Python
Photo
# 📚 PyQt5 Tutorial - Part 3/6: Dialogs, Files & Threading
#PyQt5 #Python #Threading #FileDialogs #DragAndDrop
Welcome to Part 3 of our PyQt5 series! This comprehensive lesson dives into professional dialog handling, file operations, drag-and-drop functionality, and threading - essential for building production-grade applications.
---
## 🔹 Professional Dialog Handling
### 1. Standard Dialogs
PyQt5 provides built-in dialogs for common tasks:
### 2. Custom Dialog Classes
Create reusable dialog windows:
---
## 🔹 File System Operations
### 1. File and Directory Handling
### 2. Monitoring File Changes
---
## 🔹 Drag and Drop
### 1. Enabling Drag-and-Drop
#PyQt5 #Python #Threading #FileDialogs #DragAndDrop
Welcome to Part 3 of our PyQt5 series! This comprehensive lesson dives into professional dialog handling, file operations, drag-and-drop functionality, and threading - essential for building production-grade applications.
---
## 🔹 Professional Dialog Handling
### 1. Standard Dialogs
PyQt5 provides built-in dialogs for common tasks:
from PyQt5.QtWidgets import (QFileDialog, QColorDialog,
QFontDialog, QInputDialog, QMessageBox)
# File Dialog
file_path, _ = QFileDialog.getOpenFileName(
self, "Open File", "", "Text Files (*.txt);;All Files (*)")
# Color Dialog
color = QColorDialog.getColor()
# Font Dialog
font, ok = QFontDialog.getFont()
# Input Dialog
text, ok = QInputDialog.getText(self, "Input", "Enter your name:")
# Message Box
reply = QMessageBox.question(
self, "Message", "Are you sure?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
### 2. Custom Dialog Classes
Create reusable dialog windows:
class LoginDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Login")
self.username = QLineEdit()
self.password = QLineEdit()
self.password.setEchoMode(QLineEdit.Password)
buttons = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout = QFormLayout()
layout.addRow("Username:", self.username)
layout.addRow("Password:", self.password)
layout.addRow(buttons)
self.setLayout(layout)
def get_credentials(self):
return (self.username.text(), self.password.text())
# Usage
dialog = LoginDialog()
if dialog.exec_():
username, password = dialog.get_credentials()
---
## 🔹 File System Operations
### 1. File and Directory Handling
from PyQt5.QtCore import QDir, QFile, QFileInfo
# Check file existence
file_info = QFileInfo("path/to/file")
if file_info.exists():
print("File size:", file_info.size())
# Directory operations
directory = QDir()
directory.mkdir("new_folder")
print("Current path:", directory.currentPath())
# File reading/writing
file = QFile("data.txt")
if file.open(QIODevice.ReadOnly | QIODevice.Text):
stream = QTextStream(file)
content = stream.readAll()
file.close()
### 2. Monitoring File Changes
from PyQt5.QtCore import QFileSystemWatcher
class FileMonitor(QObject):
def __init__(self):
super().__init__()
self.watcher = QFileSystemWatcher()
self.watcher.fileChanged.connect(self.on_file_changed)
def add_file(self, path):
self.watcher.addPath(path)
def on_file_changed(self, path):
print(f"File changed: {path}")
monitor = FileMonitor()
monitor.add_file("important_file.txt")
---
## 🔹 Drag and Drop
### 1. Enabling Drag-and-Drop
class DropArea(QLabel):
def __init__(self):
super().__init__("Drop files here")
self.setAcceptDrops(True)
self.setAlignment(Qt.AlignCenter)
self.setStyleSheet("border: 2px dashed #aaa;")
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
def dropEvent(self, event):
for url in event.mimeData().urls():
file_path = url.toLocalFile()
print("Dropped file:", file_path)
❤1
Code With Python
Photo
def show_error(self, message):
self.thread.quit()
QMessageBox.critical(self, "Error", message)
class FileWorker(QObject):
progress = pyqtSignal(int)
finished = pyqtSignal()
error = pyqtSignal(str)
def __init__(self):
super().__init__()
self.files = []
def set_files(self, files):
self.files = files
def process(self):
try:
total = len(self.files)
for i, file in enumerate(self.files):
# Simulate processing
time.sleep(0.5)
# Check for cancellation
if QThread.currentThread().isInterruptionRequested():
break
# Update progress
self.progress.emit(int((i + 1) / total * 100))
self.finished.emit()
except Exception as e:
self.error.emit(str(e))
---
## 🔹 Best Practices
1. Always clean up threads - Use
finished signals2. Never update UI from worker threads - Use signals
3. Validate file operations - Check permissions/existence
4. Handle drag-and-drop properly - Check MIME types
5. Make dialogs modal/non-modal appropriately -
exec_() vs show()---
### 📌 What's Next?
In Part 4, we'll cover:
➡️ Database Integration (SQLite, PostgreSQL)
➡️ Data Visualization (Charts, Graphs)
➡️ Model-View-Controller Pattern
➡️ Advanced Widget Customization
#PyQt5 #ProfessionalDevelopment #PythonGUI 🚀
Practice Exercise:
1. Build a thumbnail generator with progress reporting
2. Create a JSON config editor with file monitoring
3. Implement a thread-safe logging system for background tasks
❤4
Code With Python
Photo
# 📚 PyQt5 Tutorial - Part 4/6: Database Integration & Data Visualization
#PyQt5 #SQL #DataViz #MVC #ProfessionalDevelopment
Welcome to Part 4 of our PyQt5 series! This comprehensive lesson covers professional database integration, data visualization, and architectural patterns for building robust desktop applications.
---
## 🔹 Database Integration with PyQt5
### 1. SQLite Connection & Setup
### 2. SQL Model-View Integration
### 3. PostgreSQL Connection
---
#PyQt5 #SQL #DataViz #MVC #ProfessionalDevelopment
Welcome to Part 4 of our PyQt5 series! This comprehensive lesson covers professional database integration, data visualization, and architectural patterns for building robust desktop applications.
---
## 🔹 Database Integration with PyQt5
### 1. SQLite Connection & Setup
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def setup_database():
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("app_data.db")
if not db.open():
QMessageBox.critical(None, "Database Error", db.lastError().text())
return False
# Create tables if they don't exist
query = QSqlQuery()
query.exec_("""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
phone TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
return True
### 2. SQL Model-View Integration
from PyQt5.QtSql import QSqlTableModel
class ContactManager(QWidget):
def __init__(self):
super().__init__()
self.model = QSqlTableModel()
self.model.setTable("contacts")
self.model.select()
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout()
# Table View
self.table = QTableView()
self.table.setModel(self.model)
self.table.setSelectionBehavior(QTableView.SelectRows)
self.table.setEditTriggers(QTableView.DoubleClicked)
# Buttons
add_btn = QPushButton("Add Contact")
add_btn.clicked.connect(self.add_contact)
del_btn = QPushButton("Delete Selected")
del_btn.clicked.connect(self.delete_contact)
# Layout
btn_layout = QHBoxLayout()
btn_layout.addWidget(add_btn)
btn_layout.addWidget(del_btn)
layout.addWidget(self.table)
layout.addLayout(btn_layout)
self.setLayout(layout)
def add_contact(self):
name, ok = QInputDialog.getText(self, "Add Contact", "Name:")
if ok and name:
record = self.model.record()
record.setValue("name", name)
self.model.insertRecord(-1, record)
self.model.submitAll()
def delete_contact(self):
selected = self.table.selectionModel().selectedRows()
for index in sorted(selected, reverse=True):
self.model.removeRow(index.row())
self.model.submitAll()
### 3. PostgreSQL Connection
def connect_postgresql():
db = QSqlDatabase.addDatabase("QPSQL")
db.setHostName("localhost")
db.setDatabaseName("myapp")
db.setUserName("postgres")
db.setPassword("password")
db.setPort(5432)
if not db.open():
error = db.lastError()
QMessageBox.critical(None, "Database Error",
f"Code: {error.number()}\n{error.text()}")
return False
return True
---
Code With Python
Photo
## 🔹 Advanced Widget Customization
### 1. Custom Delegate for Table Views
### 2. Custom Widget with QPainter
---
## 🔹 Best Practices
1. Separate database logic from UI code
2. Use transactions for batch database operations
3. Optimize chart performance with limited data points
4. Follow MVC pattern for complex applications
5. Document custom widgets thoroughly
---
### 📌 What's Next?
In Part 5, we'll cover:
➡️ Networking & Web APIs
➡️ Multimedia Applications
➡️ Internationalization
➡️ Deployment & Packaging
#PyQt5 #Database #DataVisualization 🚀
Practice Exercise:
1. Build a sales dashboard with database-backed charts
2. Create a custom weather widget with API data
3. Implement an MVC-based inventory management system
### 1. Custom Delegate for Table Views
class ProgressBarDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
progress = index.data(Qt.DisplayRole)
# Draw background
painter.save()
painter.setPen(Qt.NoPen)
painter.setBrush(QColor("#e0e0e0"))
painter.drawRect(option.rect)
# Draw progress
if progress > 0:
width = option.rect.width() * progress / 100
progress_rect = QRectF(option.rect)
progress_rect.setWidth(width)
gradient = QLinearGradient(progress_rect.topLeft(), progress_rect.topRight())
gradient.setColorAt(0, QColor("#4CAF50"))
gradient.setColorAt(1, QColor("#2E7D32"))
painter.setBrush(QBrush(gradient))
painter.drawRect(progress_rect)
# Draw text
painter.setPen(QColor("#333"))
painter.drawText(option.rect, Qt.AlignCenter, f"{progress}%")
painter.restore()
# Usage
table = QTableView()
table.setItemDelegateForColumn(2, ProgressBarDelegate())
### 2. Custom Widget with QPainter
class GaugeWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.value = 0
self.min_value = 0
self.max_value = 100
def set_value(self, value):
self.value = max(self.min_value, min(value, self.max_value))
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# Draw background
rect = self.rect().adjusted(5, 5, -5, -5)
painter.setPen(QPen(QColor("#333"), 2)
painter.setBrush(QColor("#f5f5f5"))
painter.drawEllipse(rect)
# Draw gauge
angle = 180 * (self.value - self.min_value) / (self.max_value - self.min_value)
span_angle = -angle * 16 # 1/16th of a degree
pen = QPen(QColor("#FF5722"), 10)
pen.setCapStyle(Qt.RoundCap)
painter.setPen(pen)
painter.drawArc(rect, 180 * 16, span_angle)
# Draw text
font = painter.font()
font.setPointSize(20)
painter.setFont(font)
painter.drawText(rect, Qt.AlignCenter, f"{self.value}%")
---
## 🔹 Best Practices
1. Separate database logic from UI code
2. Use transactions for batch database operations
3. Optimize chart performance with limited data points
4. Follow MVC pattern for complex applications
5. Document custom widgets thoroughly
---
### 📌 What's Next?
In Part 5, we'll cover:
➡️ Networking & Web APIs
➡️ Multimedia Applications
➡️ Internationalization
➡️ Deployment & Packaging
#PyQt5 #Database #DataVisualization 🚀
Practice Exercise:
1. Build a sales dashboard with database-backed charts
2. Create a custom weather widget with API data
3. Implement an MVC-based inventory management system
❤2👍1
Code With Python
Photo
# 📚 PyQt5 Tutorial - Part 5/6: Networking, Multimedia & Internationalization
#PyQt5 #Networking #Multimedia #i18n #Deployment
Welcome to Part 5 of our PyQt5 series! This comprehensive lesson covers professional networking, multimedia handling, internationalization, and deployment strategies for production applications.
---
## 🔹 Networking with PyQt5
### 1. HTTP Requests with QNetworkAccessManager
### 2. WebSocket Communication
### 3. TCP Socket Server
---
#PyQt5 #Networking #Multimedia #i18n #Deployment
Welcome to Part 5 of our PyQt5 series! This comprehensive lesson covers professional networking, multimedia handling, internationalization, and deployment strategies for production applications.
---
## 🔹 Networking with PyQt5
### 1. HTTP Requests with QNetworkAccessManager
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtCore import QUrl
class ApiClient(QObject):
response_received = pyqtSignal(str)
error_occurred = pyqtSignal(str)
def __init__(self):
super().__init__()
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.handle_response)
def fetch_data(self, url):
request = QNetworkRequest(QUrl(url))
self.manager.get(request)
def handle_response(self, reply):
if reply.error():
self.error_occurred.emit(reply.errorString())
else:
data = reply.readAll().data().decode('utf-8')
self.response_received.emit(data)
reply.deleteLater()
# Usage
client = ApiClient()
client.response_received.connect(lambda data: print("Received:", data))
client.error_occurred.connect(lambda err: print("Error:", err))
client.fetch_data("https://api.example.com/data")
### 2. WebSocket Communication
from PyQt5.QtWebSockets import QWebSocket
class WebSocketClient(QObject):
message_received = pyqtSignal(str)
connected = pyqtSignal()
disconnected = pyqtSignal()
def __init__(self):
super().__init__()
self.socket = QWebSocket()
self.socket.textMessageReceived.connect(self.message_received)
self.socket.connected.connect(self.connected)
self.socket.disconnected.connect(self.disconnected)
def connect_to_server(self, url):
self.socket.open(QUrl(url))
def send_message(self, message):
self.socket.sendTextMessage(message)
# Usage
ws_client = WebSocketClient()
ws_client.connect_to_server("ws://echo.websocket.org")
ws_client.iss.onessage_received.connect(print)
### 3. TCP Socket Server
from PyQt5.QtNetwork import QTcpServer, QTcpSocket
class TcpServer(QObject):
new_connection = pyqtSignal(QTcpSocket)
def __init__(self):
super().__init__()
self.server = QTcpServer()
self.server.newConnection.connect(self.handle_new_connection)
def start(self, port=12345):
if not self.server.listen(QHostAddress.Any, port):
print("Server error:", self.server.errorString())
return False
print(f"Server started on port {port}")
return True
def handle_new_connection(self):
client = self.server.nextPendingConnection()
client.readyRead.connect(lambda: self.read_data(client))
client.disconnected.connect(client.deleteLater)
self.new_connection.emit(client)
def read_data(self, client):
data = client.readAll().data().decode('utf-8')
print("Received:", data)
client.write(f"Echo: {data}".encode())
---
❤2
Code With Python
Photo
## 🔹 Practical Example: Weather App
---
## 🔹 Best Practices
1. Handle network errors gracefully - Always check
2. Clean up resources - Call
3. Cache API responses - Reduce unnecessary network calls
4. Use QThread for intensive operations - Keep UI responsive
5. Test translations thoroughly - Verify all UI elements adapt
---
### 📌 What's Next?
In Final Part 6, we'll cover:
➡️ Advanced Architecture Patterns
➡️ Plugin Systems
➡️ Performance Optimization
➡️ Cross-Platform Considerations
#PyQt5 #Networking #Multimedia #Deployment 🚀
Practice Exercise:
1. Build a podcast player with download capability
2. Create a multilingual chat application
3. Implement an auto-updating stock ticker
class WeatherApp(QMainWindow):
def __init__(self):
super().__init__()
self.api_key = "YOUR_API_KEY"
self.setup_ui()
self.setup_network()
def setup_ui(self):
self.setWindowTitle(self.tr("Weather App"))
# Location Input
self.location_input = QLineEdit()
search_btn = QPushButton(self.tr("Search"))
search_btn.clicked.connect(self.fetch_weather)
# Weather Display
self.weather_icon = QLabel()
self.weather_icon.setAlignment(Qt.AlignCenter)
self.temp_label = QLabel()
self.temp_label.setAlignment(Qt.AlignCenter)
font = self.temp_label.font()
font.setPointSize(48)
self.temp_label.setFont(font)
self.details_label = QLabel()
self.details_label.setAlignment(Qt.AlignCenter)
# Layout
input_layout = QHBoxLayout()
input_layout.addWidget(self.location_input)
input_layout.addWidget(search_btn)
main_layout = QVBoxLayout()
main_layout.addLayout(input_layout)
main_layout.addWidget(self.weather_icon)
main_layout.addWidget(self.temp_label)
main_layout.addWidget(self.details_label)
container = QWidget()
container.setLayout(main_layout)
self.setCentralWidget(container)
def setup_network(self):
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.handle_weather_response)
self.icon_manager = QNetworkAccessManager()
self.icon_manager.finished.connect(self.handle_icon_response)
def fetch_weather(self):
location = self.location_input.text()
if not location:
return
url = f"https://api.openweathermap.org/data/2.5/weather?q={location}&appid={self.api_key}&units=metric"
self.manager.get(QNetworkRequest(QUrl(url)))
def handle_weather_response(self, reply):
if reply.error():
QMessageBox.warning(self, self.tr("Error"),
self.tr("Failed to fetch weather data"))
return
data = json.loads(reply.readAll().data().decode('utf-8'))
# Update UI
temp = data['main']['temp']
self.temp_label.setText(f"{temp}°C")
description = data['weather'][0]['description'].capitalize()
humidity = data['main']['humidity']
wind = data['wind']['speed']
self.details_label.setText(
self.tr(f"{description}\nHumidity: {humidity}%\nWind: {wind} m/s"))
# Fetch weather icon
icon_code = data['weather'][0]['icon']
icon_url = f"https://openweathermap.org/img/wn/{icon_code}@2x.png"
self.icon_manager.get(QNetworkRequest(QUrl(icon_url)))
def handle_icon_response(self, reply):
if not reply.error():
pixmap = QPixmap()
pixmap.loadFromData(reply.readAll())
self.weather_icon.setPixmap(pixmap.scaledToWidth(100))
---
## 🔹 Best Practices
1. Handle network errors gracefully - Always check
reply.error()2. Clean up resources - Call
deleteLater() on network objects3. Cache API responses - Reduce unnecessary network calls
4. Use QThread for intensive operations - Keep UI responsive
5. Test translations thoroughly - Verify all UI elements adapt
---
### 📌 What's Next?
In Final Part 6, we'll cover:
➡️ Advanced Architecture Patterns
➡️ Plugin Systems
➡️ Performance Optimization
➡️ Cross-Platform Considerations
#PyQt5 #Networking #Multimedia #Deployment 🚀
Practice Exercise:
1. Build a podcast player with download capability
2. Create a multilingual chat application
3. Implement an auto-updating stock ticker
❤2
Code With Python
Photo
# 📚 PyQt5 Tutorial - Part 6/6: Advanced Architecture & Optimization
#PyQt5 #AdvancedPatterns #PluginSystem #Performance #CrossPlatform
Welcome to the final installment of our PyQt5 series! This comprehensive lesson explores professional architectural patterns, plugin systems, performance optimization, and cross-platform development strategies.
---
## 🔹 Advanced Architectural Patterns
### 1. Model-View-ViewModel (MVVM)
### 2. Dependency Injection Container
### 3. Event Bus Pattern
---
## 🔹 Plugin System Implementation
### 1. Plugin Architecture
#PyQt5 #AdvancedPatterns #PluginSystem #Performance #CrossPlatform
Welcome to the final installment of our PyQt5 series! This comprehensive lesson explores professional architectural patterns, plugin systems, performance optimization, and cross-platform development strategies.
---
## 🔹 Advanced Architectural Patterns
### 1. Model-View-ViewModel (MVVM)
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
class ViewModel(QObject):
data_changed = pyqtSignal()
def __init__(self):
super().__init__()
self._username = ""
self._password = ""
@pyqtProperty(str, notify=data_changed)
def username(self):
return self._username
@username.setter
def username(self, value):
if self._username != value:
self._username = value
self.data_changed.emit()
@pyqtProperty(str, notify=data_changed)
def password(self):
return self._password
@password.setter
def password(self, value):
if self._password != value:
self._password = value
self.data_changed.emit()
# View binds to ViewModel properties
view_model = ViewModel()
username_edit = QLineEdit()
username_edit.textChanged.connect(view_model.setUsername)
view_model.data_changed.connect(
lambda: username_edit.setText(view_model.username))
### 2. Dependency Injection Container
class Container:
_instance = None
def __init__(self):
self._services = {}
def register(self, name, service):
self._services[name] = service
def resolve(self, name):
return self._services.get(name)
@classmethod
def instance(cls):
if cls._instance is None:
cls._instance = Container()
return cls._instance
# Usage
container = Container.instance()
container.register('database', DatabaseService())
container.register('api', ApiClient())
# In other classes
db_service = container.resolve('database')
### 3. Event Bus Pattern
from PyQt5.QtCore import QObject, pyqtSignal
class EventBus(QObject):
app_started = pyqtSignal()
user_logged_in = pyqtSignal(str) # username
data_loaded = pyqtSignal(dict)
_instance = None
@classmethod
def instance(cls):
if cls._instance is None:
cls._instance = EventBus()
return cls._instance
# Publisher
EventBus.instance().user_logged_in.emit("john_doe")
# Subscriber
EventBus.instance().user_logged_in.connect(
lambda username: print(f"User {username} logged in"))
---
## 🔹 Plugin System Implementation
### 1. Plugin Architecture
# Plugin Interface
class IPlugin:
def initialize(self, app):
raise NotImplementedError
def name(self):
raise NotImplementedError
# Main Application
class Application:
def __init__(self):
self.plugins = []
def load_plugins(self, plugin_dir):
for filename in os.listdir(plugin_dir):
if filename.endswith('.py'):
module_name = filename[:-3]
spec = importlib.util.spec_from_file_location(
module_name, os.path.join(plugin_dir, filename))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for item in dir(module):
obj = getattr(module, item)
if (isinstance(obj, type) and
issubclass(obj, IPlugin) and
obj != IPlugin):
plugin = obj()
plugin.initialize(self)
self.plugins.append(plugin)
# Example Plugin
class WeatherPlugin(IPlugin):
def initialize(self, app):
self.app = app
print(f"WeatherPlugin initialized for {app}")
def name(self):
return "Weather Plugin"
❤2
#PyQt5 #SQLite #DesktopApp #WarehouseManagement #ERP #Python
Lesson: Advanced Warehouse ERP with PyQt5, SQLite, and Reporting
This tutorial covers building a complete desktop Enterprise Resource Planning (ERP) application for warehouse management. It features persistent storage using SQLite, inventory control, sales and purchase invoice management, a production module, and the ability to export reports to CSV.
---
First, we create a dedicated file to handle all database interactions. This separation of concerns is crucial for a scalable application. Create a file named
---
Now, create the main application file,
Lesson: Advanced Warehouse ERP with PyQt5, SQLite, and Reporting
This tutorial covers building a complete desktop Enterprise Resource Planning (ERP) application for warehouse management. It features persistent storage using SQLite, inventory control, sales and purchase invoice management, a production module, and the ability to export reports to CSV.
---
#Step 1: Database Setup (database.py)First, we create a dedicated file to handle all database interactions. This separation of concerns is crucial for a scalable application. Create a file named
database.py.import sqlite3
import csv
DB_NAME = 'warehouse.db'
def connect():
return sqlite3.connect(DB_NAME)
def setup_database():
conn = connect()
cursor = conn.cursor()
# Inventory Table: Stores raw materials and finished goods
cursor.execute('''
CREATE TABLE IF NOT EXISTS inventory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
quantity INTEGER NOT NULL,
price REAL NOT NULL
)
''')
# Invoices Table: Tracks both sales and purchases
cursor.execute('''
CREATE TABLE IF NOT EXISTS invoices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL, -- 'SALE' or 'PURCHASE'
party_name TEXT, -- Customer or Supplier Name
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Invoice Items Table: Links items from inventory to an invoice
cursor.execute('''
CREATE TABLE IF NOT EXISTS invoice_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
invoice_id INTEGER,
item_id INTEGER,
quantity INTEGER NOT NULL,
price_per_unit REAL NOT NULL,
FOREIGN KEY (invoice_id) REFERENCES invoices (id),
FOREIGN KEY (item_id) REFERENCES inventory (id)
)
''')
conn.commit()
conn.close()
def get_inventory():
conn = connect()
cursor = conn.cursor()
cursor.execute("SELECT id, name, quantity, price FROM inventory ORDER BY name")
items = cursor.fetchall()
conn.close()
return items
def add_inventory_item(name, quantity, price):
conn = connect()
cursor = conn.cursor()
try:
cursor.execute("INSERT INTO inventory (name, quantity, price) VALUES (?, ?, ?)", (name, quantity, price))
conn.commit()
except sqlite3.IntegrityError:
# Item with this name already exists
return False
finally:
conn.close()
return True
def update_item_quantity(item_id, change_in_quantity):
conn = connect()
cursor = conn.cursor()
cursor.execute("UPDATE inventory SET quantity = quantity + ? WHERE id = ?", (change_in_quantity, item_id))
conn.commit()
conn.close()
def find_item_by_name(name):
conn = connect()
cursor = conn.cursor()
cursor.execute("SELECT * FROM inventory WHERE name = ?", (name,))
item = cursor.fetchone()
conn.close()
return item
# Add more functions here for invoices, etc. as we build the app.
# Hashtags: #SQLite #DatabaseDesign #DataPersistence #Python
---
#Step 2: Main Application Shell and Inventory TabNow, create the main application file,
main.py. We'll build the window, tabs, and fully implement the Inventory tab, which will read from and write to our SQLite database.❤1
import sys
import csv
from PyQt5.QtWidgets import *
import database as db # Import our database module
class WarehouseApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Warehouse ERP System")
self.setGeometry(100, 100, 1000, 700)
db.setup_database() # Ensure tables are created
self.tabs = QTabWidget()
self.setCentralWidget(self.tabs)
# Create tabs
self.inventory_tab = QWidget()
self.purchase_tab = QWidget() # Incoming
self.sales_tab = QWidget() # Outgoing
self.production_tab = QWidget()
self.reports_tab = QWidget()
self.tabs.addTab(self.inventory_tab, "Inventory")
# Add other tabs later...
self.setup_inventory_ui()
self.load_inventory_data()
def setup_inventory_ui(self):
layout = QVBoxLayout()
# Table view
self.inventory_table = QTableWidget()
self.inventory_table.setColumnCount(4)
self.inventory_table.setHorizontalHeaderLabels(['ID', 'Name', 'Quantity', 'Price'])
self.inventory_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
layout.addWidget(self.inventory_table)
# Form for adding new items
form = QFormLayout()
self.item_name = QLineEdit()
self.item_qty = QSpinBox()
self.item_qty.setRange(0, 99999)
self.item_price = QLineEdit()
form.addRow("Name:", self.item_name)
form.addRow("Quantity:", self.item_qty)
form.addRow("Price:", self.item_price)
add_btn = QPushButton("Add New Item")
add_btn.clicked.connect(self.add_item)
layout.addLayout(form)
layout.addWidget(add_btn)
self.inventory_tab.setLayout(layout)
def load_inventory_data(self):
items = db.get_inventory()
self.inventory_table.setRowCount(len(items))
for row_num, row_data in enumerate(items):
for col_num, data in enumerate(row_data):
self.inventory_table.setItem(row_num, col_num, QTableWidgetItem(str(data)))
def add_item(self):
name = self.item_name.text()
qty = self.item_qty.value()
price = float(self.item_price.text())
if not name:
QMessageBox.warning(self, "Input Error", "Item name cannot be empty.")
return
if db.add_inventory_item(name, qty, price):
self.load_inventory_data() # Refresh table
self.item_name.clear()
self.item_qty.setValue(0)
self.item_price.clear()
else:
QMessageBox.warning(self, "DB Error", f"Item '{name}' already exists.")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = WarehouseApp()
window.show()
sys.exit(app.exec_())
# Hashtags: #PyQt5 #GUI #CRUD #InventoryManagement
---
#Step 3: Purchase (Incoming) and Sales (Outgoing) TabsWe'll manage invoices. A purchase increases stock, and a sale decreases it. We need to add functions to
database.py first, then build the UI.Add to
database.py:❤1