diff --git a/src/ui/screens/home_screen.py b/src/ui/screens/home_screen.py index f9840ef..1cf173b 100644 --- a/src/ui/screens/home_screen.py +++ b/src/ui/screens/home_screen.py @@ -2,16 +2,13 @@ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, - QStackedWidget, QSizePolicy + QStackedWidget ) -from PyQt6.QtCore import Qt, QTimer -from PyQt6.QtGui import QFont, QPixmap, QImage +from PyQt6.QtCore import Qt, QTimer, QElapsedTimer +from PyQt6.QtGui import QFont, QPixmap, QImage, QMouseEvent from services.usb_watcher import UsbWatcher - -WIFI_AP_NAME = "Photostation" -WIFI_AP_PASSWORD = "photo1234" -EMAIL_ADDRESS = "photo@photostation.local" +from ui.widgets.config_dialog import load_config, ConfigDialog BTN_STYLE = """ QPushButton {{ @@ -31,19 +28,13 @@ BACK_BTN_STYLE = """ QPushButton { background-color: #757575; color: white; - border-radius: 10px; - padding: 10px 30px; + border-radius: 12px; + padding: 12px 30px; + font-size: 16px; } QPushButton:pressed { background-color: #616161; } """ -INFO_STYLE = """ - QLabel { - color: #333; - padding: 10px; - } -""" - HIGHLIGHT_STYLE = """ QLabel { background-color: #E3F2FD; @@ -75,6 +66,36 @@ SUCCESS_STYLE = """ } """ +INFO_STYLE = """ + QLabel { + color: #333; + padding: 10px; + } +""" + + +class LongPressLabel(QLabel): + """QLabel qui détecte l'appui long (5 sec) pour ouvrir la config.""" + + def __init__(self, text, callback, parent=None): + super().__init__(text, parent) + self._callback = callback + self._press_timer = QElapsedTimer() + self._long_press_timer = QTimer(self) + self._long_press_timer.setSingleShot(True) + self._long_press_timer.timeout.connect(self._on_long_press) + + def mousePressEvent(self, event: QMouseEvent): + self._long_press_timer.start(5000) + super().mousePressEvent(event) + + def mouseReleaseEvent(self, event: QMouseEvent): + self._long_press_timer.stop() + super().mouseReleaseEvent(event) + + def _on_long_press(self): + self._callback() + class HomeScreen(QWidget): def __init__(self, main_window): @@ -82,34 +103,46 @@ class HomeScreen(QWidget): self.main_window = main_window self._usb_photos = [] self._usb_mount = None + self.config = load_config() self._setup_ui() self._setup_usb_watcher() def _setup_ui(self): layout = QVBoxLayout(self) - layout.setContentsMargins(20, 20, 20, 20) - layout.setSpacing(20) + layout.setContentsMargins(20, 15, 20, 15) + layout.setSpacing(10) - # Titre (toujours visible) - title = QLabel("Photostation") - title.setFont(QFont("", 42, QFont.Weight.Bold)) - title.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.addWidget(title) + # Titre (appui long 5s = config) + self.title = LongPressLabel("Photostation", self._open_config) + self.title.setFont(QFont("", 42, QFont.Weight.Bold)) + self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(self.title) - # Zone switchable : choix des modes OU instructions d'un mode + # Zone switchable self.stack = QStackedWidget() layout.addWidget(self.stack, 1) - # Page 0 : Choix des modes - self.stack.addWidget(self._build_mode_selection()) - # Page 1 : Instructions USB - self.stack.addWidget(self._build_usb_instructions()) - # Page 2 : Instructions WiFi - self.stack.addWidget(self._build_wifi_instructions()) - # Page 3 : Instructions QR Code - self.stack.addWidget(self._build_qr_instructions()) - # Page 4 : Instructions Email - self.stack.addWidget(self._build_email_instructions()) + # Pages + self.stack.addWidget(self._build_mode_selection()) # 0 + self.stack.addWidget(self._build_usb_page()) # 1 + self.stack.addWidget(self._build_wifi_page()) # 2 + self.stack.addWidget(self._build_qr_page()) # 3 + self.stack.addWidget(self._build_email_page()) # 4 + + def _make_back_button(self): + btn = QPushButton("< Retour") + btn.setFont(QFont("", 16, QFont.Weight.Bold)) + btn.setMinimumSize(150, 55) + btn.setStyleSheet(BACK_BTN_STYLE) + btn.clicked.connect(self._show_modes) + return btn + + def _make_page_header(self): + """Header commun : gros bouton Retour en haut à gauche.""" + header = QHBoxLayout() + header.addWidget(self._make_back_button()) + header.addStretch() + return header # ─── Page 0 : Sélection des modes ─── @@ -129,7 +162,7 @@ class HomeScreen(QWidget): modes = [ ("USB", "Clé USB", self._show_usb), - ("WiFi", "Depuis téléphone", self._show_wifi), + ("WiFi", "Depuis\ntéléphone", self._show_wifi), ("QR Code", "Scanner pour\nenvoyer", self._show_qr), ("Email", "Recevoir par\nmail", self._show_email), ] @@ -145,31 +178,39 @@ class HomeScreen(QWidget): self.mode_buttons[label] = btn layout.addLayout(buttons_layout) + + # Hint config + hint = QLabel("Appui long sur le titre pour accéder à la configuration") + hint.setFont(QFont("", 10)) + hint.setAlignment(Qt.AlignmentFlag.AlignCenter) + hint.setStyleSheet("color: #bbb;") + layout.addWidget(hint) + return page # ─── Page 1 : USB ─── - def _build_usb_instructions(self): + def _build_usb_page(self): page = QWidget() layout = QVBoxLayout(page) - layout.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.setSpacing(25) + layout.setSpacing(20) + + layout.addLayout(self._make_page_header()) - # Instruction principale instruction = QLabel("Insérez votre clé USB dans le port\nde la borne") instruction.setFont(QFont("", 24)) instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(instruction) - # Zone de status (change dynamiquement) - self.usb_status_label = QLabel("En attente de la clé USB...") + layout.addStretch() + + self.usb_status_label = QLabel("Clé USB non détectée\nInsérez votre clé USB puis patientez quelques secondes") self.usb_status_label.setFont(QFont("", 18)) self.usb_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.usb_status_label.setMinimumHeight(70) - self.usb_status_label.setStyleSheet(HIGHLIGHT_STYLE) + self.usb_status_label.setMinimumHeight(80) + self.usb_status_label.setStyleSheet(ERROR_STYLE) layout.addWidget(self.usb_status_label) - # Bouton continuer (masqué tant que pas de clé) self.usb_continue_btn = QPushButton("Continuer") self.usb_continue_btn.setFont(QFont("", 18, QFont.Weight.Bold)) self.usb_continue_btn.setMinimumSize(250, 60) @@ -178,46 +219,36 @@ class HomeScreen(QWidget): self.usb_continue_btn.hide() layout.addWidget(self.usb_continue_btn, alignment=Qt.AlignmentFlag.AlignCenter) - # Bouton retour - back = QPushButton("Retour") - back.setFont(QFont("", 14)) - back.setStyleSheet(BACK_BTN_STYLE) - back.clicked.connect(self._show_modes) - layout.addWidget(back, alignment=Qt.AlignmentFlag.AlignCenter) - + layout.addStretch() return page # ─── Page 2 : WiFi ─── - def _build_wifi_instructions(self): + def _build_wifi_page(self): page = QWidget() layout = QVBoxLayout(page) - layout.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.setSpacing(25) + layout.setSpacing(20) + + layout.addLayout(self._make_page_header()) instruction = QLabel("Connectez-vous au réseau WiFi\nde la borne depuis votre téléphone") instruction.setFont(QFont("", 24)) instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(instruction) - # Infos réseau - info_box = QWidget() - info_layout = QVBoxLayout(info_box) - info_layout.setSpacing(10) + layout.addStretch() - net_name = QLabel(f"Nom du réseau : {WIFI_AP_NAME}") - net_name.setFont(QFont("", 22, QFont.Weight.Bold)) - net_name.setAlignment(Qt.AlignmentFlag.AlignCenter) - net_name.setStyleSheet(HIGHLIGHT_STYLE) - info_layout.addWidget(net_name) + self.wifi_name_label = QLabel(f"Nom du réseau : {self.config['wifi_ap_name']}") + self.wifi_name_label.setFont(QFont("", 22, QFont.Weight.Bold)) + self.wifi_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.wifi_name_label.setStyleSheet(HIGHLIGHT_STYLE) + layout.addWidget(self.wifi_name_label) - net_pass = QLabel(f"Mot de passe : {WIFI_AP_PASSWORD}") - net_pass.setFont(QFont("", 22, QFont.Weight.Bold)) - net_pass.setAlignment(Qt.AlignmentFlag.AlignCenter) - net_pass.setStyleSheet(HIGHLIGHT_STYLE) - info_layout.addWidget(net_pass) - - layout.addWidget(info_box) + self.wifi_pass_label = QLabel(f"Mot de passe : {self.config['wifi_ap_password']}") + self.wifi_pass_label.setFont(QFont("", 22, QFont.Weight.Bold)) + self.wifi_pass_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.wifi_pass_label.setStyleSheet(HIGHLIGHT_STYLE) + layout.addWidget(self.wifi_pass_label) step2 = QLabel("Une fois connecté, une page s'ouvrira\nautomatiquement pour envoyer vos photos") step2.setFont(QFont("", 16)) @@ -225,42 +256,35 @@ class HomeScreen(QWidget): step2.setStyleSheet("color: #666;") layout.addWidget(step2) - # Status connexions self.wifi_status_label = QLabel("En attente de connexion...") self.wifi_status_label.setFont(QFont("", 16)) self.wifi_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.wifi_status_label.setStyleSheet(INFO_STYLE) layout.addWidget(self.wifi_status_label) - back = QPushButton("Retour") - back.setFont(QFont("", 14)) - back.setStyleSheet(BACK_BTN_STYLE) - back.clicked.connect(self._show_modes) - layout.addWidget(back, alignment=Qt.AlignmentFlag.AlignCenter) - + layout.addStretch() return page # ─── Page 3 : QR Code ─── - def _build_qr_instructions(self): + def _build_qr_page(self): page = QWidget() layout = QVBoxLayout(page) - layout.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.setSpacing(20) + layout.setSpacing(15) + + layout.addLayout(self._make_page_header()) instruction = QLabel("Scannez ce QR code avec votre téléphone\npour envoyer vos photos") instruction.setFont(QFont("", 24)) instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(instruction) - # QR Code affiché self.qr_image_label = QLabel() self.qr_image_label.setFixedSize(250, 250) self.qr_image_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.qr_image_label.setStyleSheet("background-color: white; border: 2px solid #ccc; border-radius: 10px;") layout.addWidget(self.qr_image_label, alignment=Qt.AlignmentFlag.AlignCenter) - # URL affichée en texte aussi self.qr_url_label = QLabel("") self.qr_url_label.setFont(QFont("", 14)) self.qr_url_label.setAlignment(Qt.AlignmentFlag.AlignCenter) @@ -273,40 +297,35 @@ class HomeScreen(QWidget): hint.setStyleSheet("color: #666;") layout.addWidget(hint) - # Status réception self.qr_status_label = QLabel("En attente de photos...") self.qr_status_label.setFont(QFont("", 16)) self.qr_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.qr_status_label.setStyleSheet(INFO_STYLE) layout.addWidget(self.qr_status_label) - back = QPushButton("Retour") - back.setFont(QFont("", 14)) - back.setStyleSheet(BACK_BTN_STYLE) - back.clicked.connect(self._show_modes) - layout.addWidget(back, alignment=Qt.AlignmentFlag.AlignCenter) - + layout.addStretch() return page # ─── Page 4 : Email ─── - def _build_email_instructions(self): + def _build_email_page(self): page = QWidget() layout = QVBoxLayout(page) - layout.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.setSpacing(25) + layout.setSpacing(20) + + layout.addLayout(self._make_page_header()) instruction = QLabel("Envoyez vos photos par email\nà l'adresse suivante :") instruction.setFont(QFont("", 24)) instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(instruction) - # Adresse email mise en évidence - self.email_address_label = QLabel(EMAIL_ADDRESS) + layout.addStretch() + + self.email_address_label = QLabel(self.config["email_address"]) self.email_address_label.setFont(QFont("", 28, QFont.Weight.Bold)) self.email_address_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.email_address_label.setStyleSheet(HIGHLIGHT_STYLE) - self.email_address_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) layout.addWidget(self.email_address_label) hint = QLabel("Joignez vos photos en pièce jointe\net envoyez depuis votre téléphone") @@ -315,22 +334,16 @@ class HomeScreen(QWidget): hint.setStyleSheet("color: #666;") layout.addWidget(hint) - # Status réception self.email_status_label = QLabel("En attente de réception...") self.email_status_label.setFont(QFont("", 16)) self.email_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.email_status_label.setStyleSheet(INFO_STYLE) layout.addWidget(self.email_status_label) - back = QPushButton("Retour") - back.setFont(QFont("", 14)) - back.setStyleSheet(BACK_BTN_STYLE) - back.clicked.connect(self._show_modes) - layout.addWidget(back, alignment=Qt.AlignmentFlag.AlignCenter) - + layout.addStretch() return page - # ─── Navigation entre pages ─── + # ─── Navigation ─── def _show_modes(self): self.stack.setCurrentIndex(0) @@ -340,15 +353,32 @@ class HomeScreen(QWidget): self.stack.setCurrentIndex(1) def _show_wifi(self): + self._refresh_config_labels() self.stack.setCurrentIndex(2) def _show_qr(self): + self._refresh_config_labels() self._generate_qr() self.stack.setCurrentIndex(3) def _show_email(self): + self._refresh_config_labels() self.stack.setCurrentIndex(4) + # ─── Config ─── + + def _open_config(self): + if ConfigDialog.open_config(self): + self.config = load_config() + self._refresh_config_labels() + + def _refresh_config_labels(self): + """Met à jour les labels avec la config actuelle.""" + self.config = load_config() + self.wifi_name_label.setText(f"Nom du réseau : {self.config['wifi_ap_name']}") + self.wifi_pass_label.setText(f"Mot de passe : {self.config['wifi_ap_password']}") + self.email_address_label.setText(self.config["email_address"]) + # ─── USB ─── def _setup_usb_watcher(self): @@ -395,13 +425,12 @@ class HomeScreen(QWidget): import qrcode import io - upload_url = f"http://192.168.4.1/upload" + upload_url = self.config["upload_url"] qr = qrcode.QRCode(version=1, box_size=8, border=2) qr.add_data(upload_url) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") - # PIL Image -> QPixmap buffer = io.BytesIO() img.save(buffer, format="PNG") buffer.seek(0) diff --git a/src/ui/widgets/config_dialog.py b/src/ui/widgets/config_dialog.py new file mode 100644 index 0000000..d9320a7 --- /dev/null +++ b/src/ui/widgets/config_dialog.py @@ -0,0 +1,148 @@ +"""Dialogue de configuration admin — accessible par appui long sur le titre.""" + +import json +import os +from PyQt6.QtWidgets import ( + QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, + QLineEdit, QPushButton, QLabel, QComboBox, QGroupBox +) +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QFont + +CONFIG_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "..", "config", "settings.json" +) + +DEFAULT_CONFIG = { + "wifi_ap_name": "Photostation", + "wifi_ap_password": "photo1234", + "email_address": "photo@photostation.local", + "upload_url": "http://192.168.4.1/upload", + "printer_name": "", + "print_format": "10x15 cm", + "kiosk_mode": False, +} + + +def load_config(): + try: + with open(CONFIG_PATH, "r") as f: + saved = json.load(f) + config = dict(DEFAULT_CONFIG) + config.update(saved) + return config + except (FileNotFoundError, json.JSONDecodeError): + return dict(DEFAULT_CONFIG) + + +def save_config(config): + os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) + with open(CONFIG_PATH, "w") as f: + json.dump(config, f, indent=2, ensure_ascii=False) + + +class ConfigDialog(QDialog): + """Panneau de configuration admin.""" + + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Configuration — Admin") + self.setMinimumSize(500, 450) + self.config = load_config() + self._setup_ui() + + def _setup_ui(self): + layout = QVBoxLayout(self) + layout.setSpacing(15) + + # Titre + title = QLabel("Configuration Photostation") + title.setFont(QFont("", 20, QFont.Weight.Bold)) + title.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(title) + + # WiFi + wifi_group = QGroupBox("WiFi (Hotspot)") + wifi_form = QFormLayout(wifi_group) + self.wifi_name_input = QLineEdit(self.config["wifi_ap_name"]) + self.wifi_name_input.setFont(QFont("", 14)) + self.wifi_name_input.setMinimumHeight(40) + wifi_form.addRow("Nom du réseau :", self.wifi_name_input) + self.wifi_pass_input = QLineEdit(self.config["wifi_ap_password"]) + self.wifi_pass_input.setFont(QFont("", 14)) + self.wifi_pass_input.setMinimumHeight(40) + wifi_form.addRow("Mot de passe :", self.wifi_pass_input) + layout.addWidget(wifi_group) + + # QR Code + qr_group = QGroupBox("QR Code") + qr_form = QFormLayout(qr_group) + self.upload_url_input = QLineEdit(self.config["upload_url"]) + self.upload_url_input.setFont(QFont("", 14)) + self.upload_url_input.setMinimumHeight(40) + qr_form.addRow("URL d'upload :", self.upload_url_input) + layout.addWidget(qr_group) + + # Email + email_group = QGroupBox("Email") + email_form = QFormLayout(email_group) + self.email_input = QLineEdit(self.config["email_address"]) + self.email_input.setFont(QFont("", 14)) + self.email_input.setMinimumHeight(40) + email_form.addRow("Adresse email :", self.email_input) + layout.addWidget(email_group) + + # Imprimante + printer_group = QGroupBox("Impression") + printer_form = QFormLayout(printer_group) + self.printer_input = QLineEdit(self.config["printer_name"]) + self.printer_input.setFont(QFont("", 14)) + self.printer_input.setMinimumHeight(40) + self.printer_input.setPlaceholderText("Nom CUPS (vide = par défaut)") + printer_form.addRow("Imprimante :", self.printer_input) + self.format_combo = QComboBox() + self.format_combo.setFont(QFont("", 14)) + self.format_combo.setMinimumHeight(40) + self.format_combo.addItems(["10x15 cm", "13x18 cm", "15x20 cm"]) + self.format_combo.setCurrentText(self.config["print_format"]) + printer_form.addRow("Format par défaut :", self.format_combo) + layout.addWidget(printer_group) + + # Boutons + buttons = QHBoxLayout() + cancel_btn = QPushButton("Annuler") + cancel_btn.setFont(QFont("", 14)) + cancel_btn.setMinimumHeight(50) + cancel_btn.setStyleSheet(""" + QPushButton { background-color: #757575; color: white; border-radius: 10px; padding: 10px 30px; } + QPushButton:pressed { background-color: #616161; } + """) + cancel_btn.clicked.connect(self.reject) + buttons.addWidget(cancel_btn) + + save_btn = QPushButton("Enregistrer") + save_btn.setFont(QFont("", 14, QFont.Weight.Bold)) + save_btn.setMinimumHeight(50) + save_btn.setStyleSheet(""" + QPushButton { background-color: #4CAF50; color: white; border-radius: 10px; padding: 10px 30px; } + QPushButton:pressed { background-color: #388E3C; } + """) + save_btn.clicked.connect(self._save) + buttons.addWidget(save_btn) + + layout.addLayout(buttons) + + def _save(self): + self.config["wifi_ap_name"] = self.wifi_name_input.text() + self.config["wifi_ap_password"] = self.wifi_pass_input.text() + self.config["upload_url"] = self.upload_url_input.text() + self.config["email_address"] = self.email_input.text() + self.config["printer_name"] = self.printer_input.text() + self.config["print_format"] = self.format_combo.currentText() + save_config(self.config) + self.accept() + + @staticmethod + def open_config(parent=None): + dialog = ConfigDialog(parent) + return dialog.exec() == QDialog.DialogCode.Accepted