""" Demo application using pyqt6_scaffold. Run via start.sh / start.cmd - environment variables must be set. start.sh: export SQLITE_PATH=demo.db python demo_app.py """ import sys import sqlite3 from PyQt6.QtWidgets import ( QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QTableView, QMessageBox ) from PyQt6.QtCore import Qt from pyqt6_scaffold import ( AbstractDatabase, BaseWindow, Composer, BaseUser, NavigateRequest, NavigationContext, BaseTableModel ) from pyqt6_scaffold.contrib.auth import Role # --- Database ----------------------------------------------------------- class AppDatabase(AbstractDatabase): @property def placeholder(self) -> str: return "?" def _connect(self): import os path = os.getenv("SQLITE_PATH", "demo.db") conn = sqlite3.connect(path) self._bootstrap(conn) return conn def _bootstrap(self, conn): """Create tables and seed data if not exists.""" conn.executescript(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, login TEXT UNIQUE, password TEXT, role TEXT, level INTEGER ); CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY, name TEXT, category TEXT, price REAL, stock INTEGER ); INSERT OR IGNORE INTO users VALUES (1, 'admin', 'admin', 'Administrator', 100); INSERT OR IGNORE INTO users VALUES (2, 'user', 'user', 'User', 25); INSERT OR IGNORE INTO products VALUES (1, 'Notebook', 'Electronics', 79999, 5); INSERT OR IGNORE INTO products VALUES (2, 'Mouse', 'Electronics', 1299, 0); INSERT OR IGNORE INTO products VALUES (3, 'Table', 'Furniture', 12500, 3); INSERT OR IGNORE INTO products VALUES (4, 'Chair', 'Furniture', 8900, 0); INSERT OR IGNORE INTO products VALUES (5, 'Monitor', 'Electronics', 24999, 2); """) conn.commit() def auth(self, login: str, password: str) -> BaseUser | None: with self.execute( f"SELECT id, login, role, level FROM users WHERE login = {self.placeholder} AND password = {self.placeholder}", (login, password) ) as cursor: row = cursor.fetchone() if not row: return None role = Role(name=row[2], level=row[3]) return BaseUser(id=row[0], name=row[1], role=role) def get_products(self) -> list: with self.execute( "SELECT id, name, category, price, stock FROM products ORDER BY id" ) as cursor: return cursor.fetchall() # --- Models ----------------------------------------------------------- class ProductModel(BaseTableModel): headers = ["ID", "Name", "Category", "Price", "In stock"] def row_background(self, data): from PyQt6.QtGui import QColor if data[4] == 0: # if out of stock - blue return QColor("#cce5ff") return None def row_foreground(self, data): return None def row_font(self, data): return None # --- Windows ----------------------------------------------------------- class LoginWindow(BaseWindow): def _define_widgets(self): self._title = QLabel("Entry in system") self._login = QLineEdit() self._password = QLineEdit() self._password.setEchoMode(QLineEdit.EchoMode.Password) self._btn = QPushButton("Enter") self._hint = QLabel("admin/admin or user/user") self._login.setPlaceholderText("Login") self._password.setPlaceholderText("Password") self._hint.setAlignment(Qt.AlignmentFlag.AlignCenter) def _tune_layouts(self): layout = QVBoxLayout() layout.setSpacing(10) layout.setContentsMargins(40, 40, 40, 40) layout.addWidget(self._title) layout.addWidget(self._login) layout.addWidget(self._password) layout.addWidget(self._btn) layout.addWidget(self._hint) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def _connect_slots(self): self._btn.clicked.connect(self._on_login) self._password.returnPressed.connect(self._on_login) def _apply_windows_settings(self): self.setWindowTitle("Authorization") self.setFixedSize(300, 220) def _on_login(self): user = self._db.auth( self._login.text().strip(), self._password.text().strip() ) if user is None: QMessageBox.warning(self, "Error", "Incorrect login or password") return self._composer.navigate_request.emit( NavigateRequest( target="main", context=NavigationContext(data={"user": user}) ) ) class MainWindow(BaseWindow): def _define_widgets(self): user = self._composer.context.data.get("user") name = user.name if user else "" role = user.role.name if user else "" self._info = QLabel(f"User: {name} | Role: {role}") self._table = QTableView() self._model = ProductModel() self._logout = QPushButton("Exit") self._table.setModel(self._model) self._table.horizontalHeader().setStretchLastSection(True) self._table.setSelectionBehavior( QTableView.SelectionBehavior.SelectRows ) self._table.setEditTriggers( QTableView.EditTrigger.NoEditTriggers ) rows = self._db.get_products() self._model.refresh(rows) def _tune_layouts(self): top = QHBoxLayout() top.addWidget(self._info) top.addStretch() top.addWidget(self._logout) layout = QVBoxLayout() layout.setContentsMargins(16, 16, 16, 16) layout.setSpacing(8) layout.addLayout(top) layout.addWidget(self._table) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def _connect_slots(self): self._logout.clicked.connect(self._on_logout) def _apply_windows_settings(self): self.setWindowTitle("Product catalog") self.resize(700, 400) def _on_logout(self): self._composer.navigate_request.emit( NavigateRequest( target="login", context=NavigationContext() ) ) # --- Entry point ----------------------------------------------------------- def main(): app = QApplication(sys.argv) db = AppDatabase() db.connect() composer = Composer(app=app, db=db) composer.register("login", LoginWindow) composer.register("main", MainWindow) sys.exit(composer.run(start="login")) if __name__ == "__main__": main()