"""Écran d'accueil — choix du mode d'import avec instructions.""" from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QStackedWidget ) from PyQt6.QtCore import Qt, QTimer, QElapsedTimer from PyQt6.QtGui import QFont, QPixmap, QImage, QMouseEvent, QIcon from services.usb_watcher import UsbWatcher from ui.widgets.config_dialog import load_config, ConfigDialog from ui.style import ( scale, scaled_font, Sizes, blue_btn, green_btn, back_btn_style, highlight_box, error_box, success_box ) 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._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): super().__init__() 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(0, 0, 0, 0) layout.setSpacing(0) # Titre (appui long 5s = config) self.title = LongPressLabel("Photostation", self._open_config) self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.title) # Zone switchable self.stack = QStackedWidget() layout.addWidget(self.stack, 1) # 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 showEvent(self, event): """Applique les styles responsive une fois la fenêtre dimensionnée.""" super().showEvent(event) QTimer.singleShot(0, self._apply_responsive_styles) def resizeEvent(self, event): super().resizeEvent(event) self._apply_responsive_styles() def _apply_responsive_styles(self): """Recalcule toutes les tailles proportionnellement à la fenêtre.""" m = scale(self, Sizes.MARGIN) sp = scale(self, Sizes.SPACING) self.layout().setContentsMargins(m, m, m, m) self.layout().setSpacing(sp) # Titre self.title.setFont(scaled_font(self, Sizes.TITLE, bold=True)) # Sous-titre self.mode_subtitle.setFont(scaled_font(self, Sizes.SUBTITLE)) # Boutons de mode — remplissent l'espace btn_h = scale(self, Sizes.BTN_MODE_H) btn_sp = scale(self, 1.5) # Trouver le layout des boutons et mettre le spacing for i in range(self.stack.widget(0).layout().count()): item = self.stack.widget(0).layout().itemAt(i) if item and item.layout() and isinstance(item.layout(), QHBoxLayout): item.layout().setSpacing(btn_sp) break for btn in self.mode_buttons.values(): btn.setMinimumHeight(btn_h) btn.setStyleSheet(blue_btn(self)) btn.setFont(scaled_font(self, Sizes.BUTTON_LARGE)) # Boutons verts si photos détectées/reçues if self._usb_photos: self.mode_buttons["USB"].setStyleSheet(green_btn(self)) if self._qr_received_photos: self.mode_buttons["QR Code"].setStyleSheet(green_btn(self)) if self._email_received_photos: self.mode_buttons["Email"].setStyleSheet(green_btn(self)) # Rafraîchir l'icône QR self._set_qr_button_icon() # Tous les labels et boutons des sous-pages self._style_usb_page() self._style_wifi_page() self._style_qr_page() self._style_email_page() # Hint config self.config_hint.setFont(scaled_font(self, Sizes.HINT)) def _make_back_button(self): btn = QPushButton("< Retour") btn.clicked.connect(self._show_modes) self._back_buttons.append(btn) return btn def _style_back_buttons(self): for btn in self._back_buttons: btn.setFont(scaled_font(self, Sizes.BUTTON_TEXT, bold=True)) btn.setMinimumHeight(scale(self, Sizes.BTN_HEIGHT)) btn.setStyleSheet(back_btn_style(self)) def _make_page_header(self): header = QHBoxLayout() header.addWidget(self._make_back_button()) header.addStretch() return header # ─── Page 0 : Sélection des modes ─── def _build_mode_selection(self): self._back_buttons = [] page = QWidget() layout = QVBoxLayout(page) layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.mode_subtitle = QLabel("Comment souhaitez-vous envoyer vos photos ?") self.mode_subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.mode_subtitle) layout.addStretch() buttons_layout = QHBoxLayout() buttons_layout.setSpacing(0) # spacing géré dans _apply_responsive_styles modes = [ ("USB", "Clé USB", self._show_usb), ("WiFi", "Depuis\ntéléphone", self._show_wifi), ("QR Code", "Scanner pour\nenvoyer", self._show_qr), ("Email", "Recevoir par\nmail", self._show_email), ] self.mode_buttons = {} for label, desc, callback in modes: btn = QPushButton(f"{label}\n\n{desc}") btn.setSizePolicy( btn.sizePolicy().horizontalPolicy(), btn.sizePolicy().verticalPolicy() ) btn.clicked.connect(callback) buttons_layout.addWidget(btn, 1) self.mode_buttons[label] = btn layout.addLayout(buttons_layout) layout.addStretch() # Compteurs de photos reçues (QR et Email) self._qr_received_photos = [] self._email_received_photos = [] # Générer le mini QR sur le bouton QTimer.singleShot(500, self._set_qr_button_icon) self.config_hint = QLabel("Appui long sur le titre pour accéder à la configuration") self.config_hint.setAlignment(Qt.AlignmentFlag.AlignCenter) self.config_hint.setStyleSheet("color: #bbb;") layout.addWidget(self.config_hint) return page # ─── Page 1 : USB ─── def _build_usb_page(self): page = QWidget() layout = QVBoxLayout(page) layout.addLayout(self._make_page_header()) self.usb_instruction = QLabel("Insérez votre clé USB\ndans le port de la borne") self.usb_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.usb_instruction) 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.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.usb_status_label) self.usb_continue_btn = QPushButton("Continuer") self.usb_continue_btn.clicked.connect(self._import_usb) self.usb_continue_btn.hide() layout.addWidget(self.usb_continue_btn, alignment=Qt.AlignmentFlag.AlignCenter) layout.addStretch() return page def _style_usb_page(self): self.usb_instruction.setFont(scaled_font(self, Sizes.HEADING)) self.usb_status_label.setFont(scaled_font(self, Sizes.BODY)) self._update_usb_status() self.usb_continue_btn.setFont(scaled_font(self, Sizes.BUTTON_LARGE, bold=True)) self.usb_continue_btn.setMinimumHeight(scale(self, Sizes.BTN_HEIGHT)) self.usb_continue_btn.setMinimumWidth(scale(self, 30)) self.usb_continue_btn.setStyleSheet(green_btn(self)) self._style_back_buttons() # ─── Page 2 : WiFi ─── def _build_wifi_page(self): page = QWidget() layout = QVBoxLayout(page) layout.addLayout(self._make_page_header()) self.wifi_instruction = QLabel("Connectez-vous au réseau WiFi\nde la borne depuis votre téléphone") self.wifi_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.wifi_instruction) layout.addStretch() self.wifi_name_label = QLabel() self.wifi_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.wifi_name_label) self.wifi_pass_label = QLabel() self.wifi_pass_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.wifi_pass_label) self.wifi_hint = QLabel("Une fois connecté, une page s'ouvrira\nautomatiquement pour envoyer vos photos") self.wifi_hint.setAlignment(Qt.AlignmentFlag.AlignCenter) self.wifi_hint.setStyleSheet("color: #666;") layout.addWidget(self.wifi_hint) self.wifi_status_label = QLabel("En attente de connexion...") self.wifi_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.wifi_status_label) layout.addStretch() return page def _style_wifi_page(self): self.wifi_instruction.setFont(scaled_font(self, Sizes.HEADING)) self.wifi_name_label.setFont(scaled_font(self, Sizes.BODY_LARGE, bold=True)) self.wifi_name_label.setStyleSheet(highlight_box(self)) self.wifi_pass_label.setFont(scaled_font(self, Sizes.BODY_LARGE, bold=True)) self.wifi_pass_label.setStyleSheet(highlight_box(self)) self.wifi_hint.setFont(scaled_font(self, Sizes.BODY)) self.wifi_status_label.setFont(scaled_font(self, Sizes.BODY)) self._refresh_config_labels() # ─── Page 3 : QR Code ─── def _build_qr_page(self): page = QWidget() layout = QVBoxLayout(page) layout.addLayout(self._make_page_header()) self.qr_instruction = QLabel("Scannez ce QR code avec votre téléphone\npour envoyer vos photos") self.qr_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.qr_instruction) self.qr_image_label = QLabel() 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) self.qr_url_label = QLabel("") self.qr_url_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.qr_url_label.setStyleSheet("color: #666;") layout.addWidget(self.qr_url_label) self.qr_hint = QLabel("Envoyez vos photos depuis la page web\nqui s'ouvrira sur votre téléphone") self.qr_hint.setAlignment(Qt.AlignmentFlag.AlignCenter) self.qr_hint.setStyleSheet("color: #666;") layout.addWidget(self.qr_hint) self.qr_status_label = QLabel("En attente de photos...") self.qr_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.qr_status_label) self.qr_continue_btn = QPushButton("Continuer") self.qr_continue_btn.clicked.connect(self._import_qr) self.qr_continue_btn.hide() layout.addWidget(self.qr_continue_btn, alignment=Qt.AlignmentFlag.AlignCenter) layout.addStretch() return page def _style_qr_page(self): self.qr_instruction.setFont(scaled_font(self, Sizes.HEADING)) qr_size = scale(self, 35) self.qr_image_label.setFixedSize(qr_size, qr_size) self.qr_url_label.setFont(scaled_font(self, Sizes.BODY)) self.qr_hint.setFont(scaled_font(self, Sizes.BODY)) self.qr_status_label.setFont(scaled_font(self, Sizes.BODY)) self.qr_continue_btn.setFont(scaled_font(self, Sizes.BUTTON_LARGE, bold=True)) self.qr_continue_btn.setMinimumHeight(scale(self, Sizes.BTN_HEIGHT)) self.qr_continue_btn.setMinimumWidth(scale(self, 30)) self.qr_continue_btn.setStyleSheet(green_btn(self)) if self._qr_received_photos: self.qr_continue_btn.show() else: self.qr_continue_btn.hide() # ─── Page 4 : Email ─── def _build_email_page(self): page = QWidget() layout = QVBoxLayout(page) layout.addLayout(self._make_page_header()) self.email_instruction = QLabel("Envoyez vos photos par email\nà l'adresse suivante :") self.email_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.email_instruction) layout.addStretch() self.email_address_label = QLabel() self.email_address_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.email_address_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) layout.addWidget(self.email_address_label) self.email_hint = QLabel("Joignez vos photos en pièce jointe\net envoyez depuis votre téléphone") self.email_hint.setAlignment(Qt.AlignmentFlag.AlignCenter) self.email_hint.setStyleSheet("color: #666;") layout.addWidget(self.email_hint) self.email_status_label = QLabel("En attente de réception...") self.email_status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.email_status_label) self.email_continue_btn = QPushButton("Continuer") self.email_continue_btn.clicked.connect(self._import_email) self.email_continue_btn.hide() layout.addWidget(self.email_continue_btn, alignment=Qt.AlignmentFlag.AlignCenter) layout.addStretch() return page def _style_email_page(self): self.email_instruction.setFont(scaled_font(self, Sizes.HEADING)) self.email_address_label.setFont(scaled_font(self, 4.5, bold=True)) self.email_address_label.setStyleSheet(highlight_box(self)) self.email_hint.setFont(scaled_font(self, Sizes.BODY)) self.email_status_label.setFont(scaled_font(self, Sizes.BODY)) self.email_continue_btn.setFont(scaled_font(self, Sizes.BUTTON_LARGE, bold=True)) self.email_continue_btn.setMinimumHeight(scale(self, Sizes.BTN_HEIGHT)) self.email_continue_btn.setMinimumWidth(scale(self, 30)) self.email_continue_btn.setStyleSheet(green_btn(self)) if self._email_received_photos: self.email_continue_btn.show() else: self.email_continue_btn.hide() self._refresh_config_labels() # ─── Mini QR sur le bouton ─── def _set_qr_button_icon(self): """Génère un mini QR code et l'affiche sur le bouton QR Code.""" try: import qrcode import io self.config = load_config() upload_url = self.config["upload_url"] qr = qrcode.QRCode(version=1, box_size=4, border=1) qr.add_data(upload_url) qr.make(fit=True) img = qr.make_image(fill_color="white", back_color="transparent") buffer = io.BytesIO() img.save(buffer, format="PNG") buffer.seek(0) qimage = QImage() qimage.loadFromData(buffer.read()) btn = self.mode_buttons.get("QR Code") if btn: qr_size = max(40, btn.height() // 3) if btn.height() > 0 else 60 pixmap = QPixmap.fromImage(qimage).scaled( qr_size, qr_size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) btn.setIcon(QIcon(pixmap)) from PyQt6.QtCore import QSize btn.setIconSize(QSize(qr_size, qr_size)) except ImportError: pass # ─── Notifications photos reçues (QR / Email) ─── def notify_qr_photos(self, photo_paths): """Appelé par le service upload quand des photos arrivent via QR/WiFi.""" self._qr_received_photos.extend(photo_paths) count = len(self._qr_received_photos) self.mode_buttons["QR Code"].setStyleSheet(green_btn(self)) self.qr_status_label.setText( f"{count} photo{'s' if count > 1 else ''} reçue{'s' if count > 1 else ''}" ) self.qr_status_label.setStyleSheet(success_box(self)) self.qr_continue_btn.show() def notify_email_photos(self, photo_paths): """Appelé par le service email quand des photos arrivent.""" self._email_received_photos.extend(photo_paths) count = len(self._email_received_photos) self.mode_buttons["Email"].setStyleSheet(green_btn(self)) self.email_status_label.setText( f"{count} photo{'s' if count > 1 else ''} reçue{'s' if count > 1 else ''}" ) self.email_status_label.setStyleSheet(success_box(self)) self.email_continue_btn.show() def _import_qr(self): if self._qr_received_photos: self.main_window.import_screen.set_photos(self._qr_received_photos) self.main_window.show_import() def _import_email(self): if self._email_received_photos: self.main_window.import_screen.set_photos(self._email_received_photos) self.main_window.show_import() def get_qr_photos(self): return list(self._qr_received_photos) def get_email_photos(self): return list(self._email_received_photos) # ─── Navigation ─── def _show_modes(self): self.stack.setCurrentIndex(0) def _show_usb(self): self._update_usb_status() 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): 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): self.usb_watcher = UsbWatcher(self) self.usb_watcher.usb_inserted.connect(self._on_usb_inserted) self.usb_watcher.usb_removed.connect(self._on_usb_removed) self.usb_watcher.start() def _on_usb_inserted(self, mount_path, photos): self._usb_mount = mount_path self._usb_photos = photos self.mode_buttons["USB"].setStyleSheet(green_btn(self)) self._update_usb_status() def _on_usb_removed(self, mount_path): if mount_path == self._usb_mount: self._usb_mount = None self._usb_photos = [] self.mode_buttons["USB"].setStyleSheet(blue_btn(self)) self._update_usb_status() def _update_usb_status(self): if self._usb_photos: count = len(self._usb_photos) self.usb_status_label.setText( f"Clé USB détectée — {count} photo{'s' if count != 1 else ''} trouvée{'s' if count != 1 else ''}" ) self.usb_status_label.setStyleSheet(success_box(self)) self.usb_continue_btn.show() else: self.usb_status_label.setText("Clé USB non détectée\nInsérez votre clé USB puis patientez quelques secondes") self.usb_status_label.setStyleSheet(error_box(self)) self.usb_continue_btn.hide() def _import_usb(self): if self._usb_photos: self.main_window.import_screen.set_photos(self._usb_photos) self.main_window.show_import() # ─── QR Code ─── def _generate_qr(self): try: import qrcode import io 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") buffer = io.BytesIO() img.save(buffer, format="PNG") buffer.seek(0) qimage = QImage() qimage.loadFromData(buffer.read()) pixmap = QPixmap.fromImage(qimage) qr_size = scale(self, 35) self.qr_image_label.setPixmap( pixmap.scaled(qr_size - 20, qr_size - 20, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) ) self.qr_url_label.setText(upload_url) except ImportError: self.qr_image_label.setText("Module qrcode\nnon installé") self.qr_url_label.setText("")