231 lines
No EOL
6.9 KiB
Python
231 lines
No EOL
6.9 KiB
Python
"""
|
|
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() |