Interface responsive — tailles proportionnelles à la fenêtre
- Nouveau module ui/style.py : scale(), scaled_font(), styles dynamiques - Toutes les tailles (polices, boutons, marges, padding, border-radius) calculées en % de la hauteur de la fenêtre - S'adapte automatiquement à 800x480, 1024x768, 1920x1080, etc. - Recalcul au resize de la fenêtre Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,69 +9,11 @@ from PyQt6.QtGui import QFont, QPixmap, QImage, QMouseEvent
|
|||||||
|
|
||||||
from services.usb_watcher import UsbWatcher
|
from services.usb_watcher import UsbWatcher
|
||||||
from ui.widgets.config_dialog import load_config, ConfigDialog
|
from ui.widgets.config_dialog import load_config, ConfigDialog
|
||||||
|
from ui.style import (
|
||||||
BTN_STYLE = """
|
scale, scaled_font, Sizes,
|
||||||
QPushButton {{
|
blue_btn, green_btn, back_btn_style,
|
||||||
background-color: {bg};
|
highlight_box, error_box, success_box
|
||||||
color: white;
|
)
|
||||||
border-radius: 15px;
|
|
||||||
padding: 20px;
|
|
||||||
}}
|
|
||||||
QPushButton:pressed {{
|
|
||||||
background-color: {pressed};
|
|
||||||
}}
|
|
||||||
"""
|
|
||||||
BTN_BLUE = BTN_STYLE.format(bg="#2196F3", pressed="#1976D2")
|
|
||||||
BTN_GREEN = BTN_STYLE.format(bg="#4CAF50", pressed="#388E3C")
|
|
||||||
|
|
||||||
BACK_BTN_STYLE = """
|
|
||||||
QPushButton {
|
|
||||||
background-color: #757575;
|
|
||||||
color: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 12px 30px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
QPushButton:pressed { background-color: #616161; }
|
|
||||||
"""
|
|
||||||
|
|
||||||
HIGHLIGHT_STYLE = """
|
|
||||||
QLabel {
|
|
||||||
background-color: #E3F2FD;
|
|
||||||
border: 2px solid #2196F3;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
color: #1565C0;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
ERROR_STYLE = """
|
|
||||||
QLabel {
|
|
||||||
background-color: #FFEBEE;
|
|
||||||
border: 2px solid #F44336;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
color: #C62828;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
SUCCESS_STYLE = """
|
|
||||||
QLabel {
|
|
||||||
background-color: #E8F5E9;
|
|
||||||
border: 2px solid #4CAF50;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
color: #2E7D32;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
INFO_STYLE = """
|
|
||||||
QLabel {
|
|
||||||
color: #333;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class LongPressLabel(QLabel):
|
class LongPressLabel(QLabel):
|
||||||
@@ -80,7 +22,6 @@ class LongPressLabel(QLabel):
|
|||||||
def __init__(self, text, callback, parent=None):
|
def __init__(self, text, callback, parent=None):
|
||||||
super().__init__(text, parent)
|
super().__init__(text, parent)
|
||||||
self._callback = callback
|
self._callback = callback
|
||||||
self._press_timer = QElapsedTimer()
|
|
||||||
self._long_press_timer = QTimer(self)
|
self._long_press_timer = QTimer(self)
|
||||||
self._long_press_timer.setSingleShot(True)
|
self._long_press_timer.setSingleShot(True)
|
||||||
self._long_press_timer.timeout.connect(self._on_long_press)
|
self._long_press_timer.timeout.connect(self._on_long_press)
|
||||||
@@ -109,12 +50,11 @@ class HomeScreen(QWidget):
|
|||||||
|
|
||||||
def _setup_ui(self):
|
def _setup_ui(self):
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(20, 15, 20, 15)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
layout.setSpacing(10)
|
layout.setSpacing(0)
|
||||||
|
|
||||||
# Titre (appui long 5s = config)
|
# Titre (appui long 5s = config)
|
||||||
self.title = LongPressLabel("Photostation", self._open_config)
|
self.title = LongPressLabel("Photostation", self._open_config)
|
||||||
self.title.setFont(QFont("", 42, QFont.Weight.Bold))
|
|
||||||
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
layout.addWidget(self.title)
|
layout.addWidget(self.title)
|
||||||
|
|
||||||
@@ -129,16 +69,58 @@ class HomeScreen(QWidget):
|
|||||||
self.stack.addWidget(self._build_qr_page()) # 3
|
self.stack.addWidget(self._build_qr_page()) # 3
|
||||||
self.stack.addWidget(self._build_email_page()) # 4
|
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))
|
||||||
|
|
||||||
|
# Boutons de mode
|
||||||
|
btn_h = scale(self, Sizes.BTN_MODE_H)
|
||||||
|
for btn in self.mode_buttons.values():
|
||||||
|
btn.setMinimumHeight(btn_h)
|
||||||
|
btn.setStyleSheet(blue_btn(self))
|
||||||
|
btn.setFont(scaled_font(self, Sizes.BUTTON_LARGE))
|
||||||
|
|
||||||
|
# USB bouton vert si clé détectée
|
||||||
|
if self._usb_photos:
|
||||||
|
self.mode_buttons["USB"].setStyleSheet(green_btn(self))
|
||||||
|
|
||||||
|
# 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):
|
def _make_back_button(self):
|
||||||
btn = QPushButton("< Retour")
|
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)
|
btn.clicked.connect(self._show_modes)
|
||||||
|
self._back_buttons.append(btn)
|
||||||
return 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):
|
def _make_page_header(self):
|
||||||
"""Header commun : gros bouton Retour en haut à gauche."""
|
|
||||||
header = QHBoxLayout()
|
header = QHBoxLayout()
|
||||||
header.addWidget(self._make_back_button())
|
header.addWidget(self._make_back_button())
|
||||||
header.addStretch()
|
header.addStretch()
|
||||||
@@ -147,18 +129,17 @@ class HomeScreen(QWidget):
|
|||||||
# ─── Page 0 : Sélection des modes ───
|
# ─── Page 0 : Sélection des modes ───
|
||||||
|
|
||||||
def _build_mode_selection(self):
|
def _build_mode_selection(self):
|
||||||
|
self._back_buttons = []
|
||||||
|
|
||||||
page = QWidget()
|
page = QWidget()
|
||||||
layout = QVBoxLayout(page)
|
layout = QVBoxLayout(page)
|
||||||
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
layout.setSpacing(30)
|
|
||||||
|
|
||||||
subtitle = QLabel("Comment souhaitez-vous envoyer vos photos ?")
|
self.mode_subtitle = QLabel("Comment souhaitez-vous envoyer vos photos ?")
|
||||||
subtitle.setFont(QFont("", 18))
|
self.mode_subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.mode_subtitle)
|
||||||
layout.addWidget(subtitle)
|
|
||||||
|
|
||||||
buttons_layout = QHBoxLayout()
|
buttons_layout = QHBoxLayout()
|
||||||
buttons_layout.setSpacing(20)
|
|
||||||
|
|
||||||
modes = [
|
modes = [
|
||||||
("USB", "Clé USB", self._show_usb),
|
("USB", "Clé USB", self._show_usb),
|
||||||
@@ -170,21 +151,16 @@ class HomeScreen(QWidget):
|
|||||||
self.mode_buttons = {}
|
self.mode_buttons = {}
|
||||||
for label, desc, callback in modes:
|
for label, desc, callback in modes:
|
||||||
btn = QPushButton(f"{label}\n\n{desc}")
|
btn = QPushButton(f"{label}\n\n{desc}")
|
||||||
btn.setFont(QFont("", 16))
|
|
||||||
btn.setMinimumSize(200, 150)
|
|
||||||
btn.setStyleSheet(BTN_BLUE)
|
|
||||||
btn.clicked.connect(callback)
|
btn.clicked.connect(callback)
|
||||||
buttons_layout.addWidget(btn)
|
buttons_layout.addWidget(btn)
|
||||||
self.mode_buttons[label] = btn
|
self.mode_buttons[label] = btn
|
||||||
|
|
||||||
layout.addLayout(buttons_layout)
|
layout.addLayout(buttons_layout)
|
||||||
|
|
||||||
# Hint config
|
self.config_hint = QLabel("Appui long sur le titre pour accéder à la configuration")
|
||||||
hint = QLabel("Appui long sur le titre pour accéder à la configuration")
|
self.config_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
hint.setFont(QFont("", 10))
|
self.config_hint.setStyleSheet("color: #bbb;")
|
||||||
hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.config_hint)
|
||||||
hint.setStyleSheet("color: #bbb;")
|
|
||||||
layout.addWidget(hint)
|
|
||||||
|
|
||||||
return page
|
return page
|
||||||
|
|
||||||
@@ -193,28 +169,19 @@ class HomeScreen(QWidget):
|
|||||||
def _build_usb_page(self):
|
def _build_usb_page(self):
|
||||||
page = QWidget()
|
page = QWidget()
|
||||||
layout = QVBoxLayout(page)
|
layout = QVBoxLayout(page)
|
||||||
layout.setSpacing(20)
|
|
||||||
|
|
||||||
layout.addLayout(self._make_page_header())
|
layout.addLayout(self._make_page_header())
|
||||||
|
|
||||||
instruction = QLabel("Insérez votre clé USB dans le port\nde la borne")
|
self.usb_instruction = QLabel("Insérez votre clé USB\ndans le port de la borne")
|
||||||
instruction.setFont(QFont("", 24))
|
self.usb_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.usb_instruction)
|
||||||
layout.addWidget(instruction)
|
|
||||||
|
|
||||||
layout.addStretch()
|
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 = 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.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.usb_status_label.setMinimumHeight(80)
|
|
||||||
self.usb_status_label.setStyleSheet(ERROR_STYLE)
|
|
||||||
layout.addWidget(self.usb_status_label)
|
layout.addWidget(self.usb_status_label)
|
||||||
|
|
||||||
self.usb_continue_btn = QPushButton("Continuer")
|
self.usb_continue_btn = QPushButton("Continuer")
|
||||||
self.usb_continue_btn.setFont(QFont("", 18, QFont.Weight.Bold))
|
|
||||||
self.usb_continue_btn.setMinimumSize(250, 60)
|
|
||||||
self.usb_continue_btn.setStyleSheet(BTN_GREEN)
|
|
||||||
self.usb_continue_btn.clicked.connect(self._import_usb)
|
self.usb_continue_btn.clicked.connect(self._import_usb)
|
||||||
self.usb_continue_btn.hide()
|
self.usb_continue_btn.hide()
|
||||||
layout.addWidget(self.usb_continue_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.usb_continue_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
@@ -222,127 +189,138 @@ class HomeScreen(QWidget):
|
|||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
return page
|
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 ───
|
# ─── Page 2 : WiFi ───
|
||||||
|
|
||||||
def _build_wifi_page(self):
|
def _build_wifi_page(self):
|
||||||
page = QWidget()
|
page = QWidget()
|
||||||
layout = QVBoxLayout(page)
|
layout = QVBoxLayout(page)
|
||||||
layout.setSpacing(20)
|
|
||||||
|
|
||||||
layout.addLayout(self._make_page_header())
|
layout.addLayout(self._make_page_header())
|
||||||
|
|
||||||
instruction = QLabel("Connectez-vous au réseau WiFi\nde la borne depuis votre téléphone")
|
self.wifi_instruction = QLabel("Connectez-vous au réseau WiFi\nde la borne depuis votre téléphone")
|
||||||
instruction.setFont(QFont("", 24))
|
self.wifi_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.wifi_instruction)
|
||||||
layout.addWidget(instruction)
|
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
self.wifi_name_label = QLabel(f"Nom du réseau : {self.config['wifi_ap_name']}")
|
self.wifi_name_label = QLabel()
|
||||||
self.wifi_name_label.setFont(QFont("", 22, QFont.Weight.Bold))
|
|
||||||
self.wifi_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.wifi_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.wifi_name_label.setStyleSheet(HIGHLIGHT_STYLE)
|
|
||||||
layout.addWidget(self.wifi_name_label)
|
layout.addWidget(self.wifi_name_label)
|
||||||
|
|
||||||
self.wifi_pass_label = QLabel(f"Mot de passe : {self.config['wifi_ap_password']}")
|
self.wifi_pass_label = QLabel()
|
||||||
self.wifi_pass_label.setFont(QFont("", 22, QFont.Weight.Bold))
|
|
||||||
self.wifi_pass_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.wifi_pass_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.wifi_pass_label.setStyleSheet(HIGHLIGHT_STYLE)
|
|
||||||
layout.addWidget(self.wifi_pass_label)
|
layout.addWidget(self.wifi_pass_label)
|
||||||
|
|
||||||
step2 = QLabel("Une fois connecté, une page s'ouvrira\nautomatiquement pour envoyer vos photos")
|
self.wifi_hint = QLabel("Une fois connecté, une page s'ouvrira\nautomatiquement pour envoyer vos photos")
|
||||||
step2.setFont(QFont("", 16))
|
self.wifi_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
step2.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.wifi_hint.setStyleSheet("color: #666;")
|
||||||
step2.setStyleSheet("color: #666;")
|
layout.addWidget(self.wifi_hint)
|
||||||
layout.addWidget(step2)
|
|
||||||
|
|
||||||
self.wifi_status_label = QLabel("En attente de connexion...")
|
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.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.wifi_status_label.setStyleSheet(INFO_STYLE)
|
|
||||||
layout.addWidget(self.wifi_status_label)
|
layout.addWidget(self.wifi_status_label)
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
return page
|
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 ───
|
# ─── Page 3 : QR Code ───
|
||||||
|
|
||||||
def _build_qr_page(self):
|
def _build_qr_page(self):
|
||||||
page = QWidget()
|
page = QWidget()
|
||||||
layout = QVBoxLayout(page)
|
layout = QVBoxLayout(page)
|
||||||
layout.setSpacing(15)
|
|
||||||
|
|
||||||
layout.addLayout(self._make_page_header())
|
layout.addLayout(self._make_page_header())
|
||||||
|
|
||||||
instruction = QLabel("Scannez ce QR code avec votre téléphone\npour envoyer vos photos")
|
self.qr_instruction = QLabel("Scannez ce QR code avec votre téléphone\npour envoyer vos photos")
|
||||||
instruction.setFont(QFont("", 24))
|
self.qr_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.qr_instruction)
|
||||||
layout.addWidget(instruction)
|
|
||||||
|
|
||||||
self.qr_image_label = QLabel()
|
self.qr_image_label = QLabel()
|
||||||
self.qr_image_label.setFixedSize(250, 250)
|
|
||||||
self.qr_image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.qr_image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.qr_image_label.setStyleSheet("background-color: white; border: 2px solid #ccc; border-radius: 10px;")
|
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)
|
layout.addWidget(self.qr_image_label, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
self.qr_url_label = QLabel("")
|
self.qr_url_label = QLabel("")
|
||||||
self.qr_url_label.setFont(QFont("", 14))
|
|
||||||
self.qr_url_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.qr_url_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.qr_url_label.setStyleSheet("color: #666;")
|
self.qr_url_label.setStyleSheet("color: #666;")
|
||||||
layout.addWidget(self.qr_url_label)
|
layout.addWidget(self.qr_url_label)
|
||||||
|
|
||||||
hint = QLabel("Envoyez vos photos depuis la page web\nqui s'ouvrira sur votre téléphone")
|
self.qr_hint = QLabel("Envoyez vos photos depuis la page web\nqui s'ouvrira sur votre téléphone")
|
||||||
hint.setFont(QFont("", 16))
|
self.qr_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.qr_hint.setStyleSheet("color: #666;")
|
||||||
hint.setStyleSheet("color: #666;")
|
layout.addWidget(self.qr_hint)
|
||||||
layout.addWidget(hint)
|
|
||||||
|
|
||||||
self.qr_status_label = QLabel("En attente de photos...")
|
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.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.qr_status_label.setStyleSheet(INFO_STYLE)
|
|
||||||
layout.addWidget(self.qr_status_label)
|
layout.addWidget(self.qr_status_label)
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
return page
|
return page
|
||||||
|
|
||||||
|
def _style_qr_page(self):
|
||||||
|
self.qr_instruction.setFont(scaled_font(self, Sizes.HEADING))
|
||||||
|
qr_size = scale(self, 35) # QR = 35% de la hauteur
|
||||||
|
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))
|
||||||
|
|
||||||
# ─── Page 4 : Email ───
|
# ─── Page 4 : Email ───
|
||||||
|
|
||||||
def _build_email_page(self):
|
def _build_email_page(self):
|
||||||
page = QWidget()
|
page = QWidget()
|
||||||
layout = QVBoxLayout(page)
|
layout = QVBoxLayout(page)
|
||||||
layout.setSpacing(20)
|
|
||||||
|
|
||||||
layout.addLayout(self._make_page_header())
|
layout.addLayout(self._make_page_header())
|
||||||
|
|
||||||
instruction = QLabel("Envoyez vos photos par email\nà l'adresse suivante :")
|
self.email_instruction = QLabel("Envoyez vos photos par email\nà l'adresse suivante :")
|
||||||
instruction.setFont(QFont("", 24))
|
self.email_instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
instruction.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
layout.addWidget(self.email_instruction)
|
||||||
layout.addWidget(instruction)
|
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
self.email_address_label = QLabel(self.config["email_address"])
|
self.email_address_label = QLabel()
|
||||||
self.email_address_label.setFont(QFont("", 28, QFont.Weight.Bold))
|
|
||||||
self.email_address_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
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)
|
layout.addWidget(self.email_address_label)
|
||||||
|
|
||||||
hint = QLabel("Joignez vos photos en pièce jointe\net envoyez depuis votre téléphone")
|
self.email_hint = QLabel("Joignez vos photos en pièce jointe\net envoyez depuis votre téléphone")
|
||||||
hint.setFont(QFont("", 16))
|
self.email_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.email_hint.setStyleSheet("color: #666;")
|
||||||
hint.setStyleSheet("color: #666;")
|
layout.addWidget(self.email_hint)
|
||||||
layout.addWidget(hint)
|
|
||||||
|
|
||||||
self.email_status_label = QLabel("En attente de 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.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.email_status_label.setStyleSheet(INFO_STYLE)
|
|
||||||
layout.addWidget(self.email_status_label)
|
layout.addWidget(self.email_status_label)
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
return page
|
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._refresh_config_labels()
|
||||||
|
|
||||||
# ─── Navigation ───
|
# ─── Navigation ───
|
||||||
|
|
||||||
def _show_modes(self):
|
def _show_modes(self):
|
||||||
@@ -373,7 +351,6 @@ class HomeScreen(QWidget):
|
|||||||
self._refresh_config_labels()
|
self._refresh_config_labels()
|
||||||
|
|
||||||
def _refresh_config_labels(self):
|
def _refresh_config_labels(self):
|
||||||
"""Met à jour les labels avec la config actuelle."""
|
|
||||||
self.config = load_config()
|
self.config = load_config()
|
||||||
self.wifi_name_label.setText(f"Nom du réseau : {self.config['wifi_ap_name']}")
|
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.wifi_pass_label.setText(f"Mot de passe : {self.config['wifi_ap_password']}")
|
||||||
@@ -390,14 +367,14 @@ class HomeScreen(QWidget):
|
|||||||
def _on_usb_inserted(self, mount_path, photos):
|
def _on_usb_inserted(self, mount_path, photos):
|
||||||
self._usb_mount = mount_path
|
self._usb_mount = mount_path
|
||||||
self._usb_photos = photos
|
self._usb_photos = photos
|
||||||
self.mode_buttons["USB"].setStyleSheet(BTN_GREEN)
|
self.mode_buttons["USB"].setStyleSheet(green_btn(self))
|
||||||
self._update_usb_status()
|
self._update_usb_status()
|
||||||
|
|
||||||
def _on_usb_removed(self, mount_path):
|
def _on_usb_removed(self, mount_path):
|
||||||
if mount_path == self._usb_mount:
|
if mount_path == self._usb_mount:
|
||||||
self._usb_mount = None
|
self._usb_mount = None
|
||||||
self._usb_photos = []
|
self._usb_photos = []
|
||||||
self.mode_buttons["USB"].setStyleSheet(BTN_BLUE)
|
self.mode_buttons["USB"].setStyleSheet(blue_btn(self))
|
||||||
self._update_usb_status()
|
self._update_usb_status()
|
||||||
|
|
||||||
def _update_usb_status(self):
|
def _update_usb_status(self):
|
||||||
@@ -406,11 +383,11 @@ class HomeScreen(QWidget):
|
|||||||
self.usb_status_label.setText(
|
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 ''}"
|
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_STYLE)
|
self.usb_status_label.setStyleSheet(success_box(self))
|
||||||
self.usb_continue_btn.show()
|
self.usb_continue_btn.show()
|
||||||
else:
|
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.setText("Clé USB non détectée\nInsérez votre clé USB puis patientez quelques secondes")
|
||||||
self.usb_status_label.setStyleSheet(ERROR_STYLE)
|
self.usb_status_label.setStyleSheet(error_box(self))
|
||||||
self.usb_continue_btn.hide()
|
self.usb_continue_btn.hide()
|
||||||
|
|
||||||
def _import_usb(self):
|
def _import_usb(self):
|
||||||
@@ -438,8 +415,10 @@ class HomeScreen(QWidget):
|
|||||||
qimage.loadFromData(buffer.read())
|
qimage.loadFromData(buffer.read())
|
||||||
pixmap = QPixmap.fromImage(qimage)
|
pixmap = QPixmap.fromImage(qimage)
|
||||||
|
|
||||||
|
qr_size = scale(self, 35)
|
||||||
self.qr_image_label.setPixmap(
|
self.qr_image_label.setPixmap(
|
||||||
pixmap.scaled(230, 230, Qt.AspectRatioMode.KeepAspectRatio,
|
pixmap.scaled(qr_size - 20, qr_size - 20,
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
Qt.TransformationMode.SmoothTransformation)
|
Qt.TransformationMode.SmoothTransformation)
|
||||||
)
|
)
|
||||||
self.qr_url_label.setText(upload_url)
|
self.qr_url_label.setText(upload_url)
|
||||||
|
|||||||
128
src/ui/style.py
Normal file
128
src/ui/style.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
"""Styles et tailles responsive — tout est relatif à la taille de la fenêtre."""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import QWidget
|
||||||
|
from PyQt6.QtGui import QFont
|
||||||
|
|
||||||
|
|
||||||
|
def scale(widget: QWidget, base_pct: float) -> int:
|
||||||
|
"""Retourne une valeur en pixels proportionnelle à la hauteur de la fenêtre.
|
||||||
|
base_pct = pourcentage de la hauteur (ex: 5.0 = 5% de la hauteur)."""
|
||||||
|
window = widget.window()
|
||||||
|
h = window.height() if window else 768
|
||||||
|
return max(1, int(h * base_pct / 100))
|
||||||
|
|
||||||
|
|
||||||
|
def scaled_font(widget: QWidget, pct: float, bold: bool = False) -> QFont:
|
||||||
|
"""Crée une QFont dont la taille est proportionnelle à la fenêtre."""
|
||||||
|
size = scale(widget, pct)
|
||||||
|
weight = QFont.Weight.Bold if bold else QFont.Weight.Normal
|
||||||
|
return QFont("", size, weight)
|
||||||
|
|
||||||
|
|
||||||
|
# Pourcentages de référence (basés sur hauteur fenêtre)
|
||||||
|
class Sizes:
|
||||||
|
TITLE = 7.0 # titre principal
|
||||||
|
SUBTITLE = 3.0 # sous-titre
|
||||||
|
HEADING = 4.0 # titres de page
|
||||||
|
BODY = 2.8 # texte courant
|
||||||
|
BODY_LARGE = 3.5 # texte mis en évidence
|
||||||
|
BUTTON_TEXT = 2.5 # texte des boutons
|
||||||
|
BUTTON_LARGE = 3.0 # gros boutons
|
||||||
|
HINT = 1.8 # texte discret
|
||||||
|
SPACING = 2.5 # espacement standard
|
||||||
|
MARGIN = 2.5 # marges
|
||||||
|
BTN_HEIGHT = 8.0 # hauteur bouton standard
|
||||||
|
BTN_WIDTH = 22.0 # largeur bouton mode (% largeur)
|
||||||
|
BTN_MODE_H = 20.0 # hauteur bouton mode
|
||||||
|
RADIUS = 2.0 # border-radius
|
||||||
|
PADDING = 2.5 # padding boutons
|
||||||
|
|
||||||
|
|
||||||
|
def btn_style(widget: QWidget, bg: str, pressed: str) -> str:
|
||||||
|
r = scale(widget, Sizes.RADIUS)
|
||||||
|
p = scale(widget, Sizes.PADDING)
|
||||||
|
fs = scale(widget, Sizes.BUTTON_TEXT)
|
||||||
|
return f"""
|
||||||
|
QPushButton {{
|
||||||
|
background-color: {bg};
|
||||||
|
color: white;
|
||||||
|
border-radius: {r}px;
|
||||||
|
padding: {p}px;
|
||||||
|
font-size: {fs}px;
|
||||||
|
}}
|
||||||
|
QPushButton:pressed {{
|
||||||
|
background-color: {pressed};
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def blue_btn(widget: QWidget) -> str:
|
||||||
|
return btn_style(widget, "#2196F3", "#1976D2")
|
||||||
|
|
||||||
|
|
||||||
|
def green_btn(widget: QWidget) -> str:
|
||||||
|
return btn_style(widget, "#4CAF50", "#388E3C")
|
||||||
|
|
||||||
|
|
||||||
|
def back_btn_style(widget: QWidget) -> str:
|
||||||
|
r = scale(widget, Sizes.RADIUS)
|
||||||
|
p = scale(widget, Sizes.PADDING)
|
||||||
|
fs = scale(widget, Sizes.BUTTON_TEXT)
|
||||||
|
return f"""
|
||||||
|
QPushButton {{
|
||||||
|
background-color: #757575;
|
||||||
|
color: white;
|
||||||
|
border-radius: {r}px;
|
||||||
|
padding: {p}px {int(p*1.5)}px;
|
||||||
|
font-size: {fs}px;
|
||||||
|
}}
|
||||||
|
QPushButton:pressed {{ background-color: #616161; }}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def highlight_box(widget: QWidget) -> str:
|
||||||
|
r = scale(widget, Sizes.RADIUS)
|
||||||
|
p = scale(widget, Sizes.PADDING)
|
||||||
|
fs = scale(widget, Sizes.BODY_LARGE)
|
||||||
|
return f"""
|
||||||
|
QLabel {{
|
||||||
|
background-color: #E3F2FD;
|
||||||
|
border: 2px solid #2196F3;
|
||||||
|
border-radius: {r}px;
|
||||||
|
padding: {p}px;
|
||||||
|
color: #1565C0;
|
||||||
|
font-size: {fs}px;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def error_box(widget: QWidget) -> str:
|
||||||
|
r = scale(widget, Sizes.RADIUS)
|
||||||
|
p = scale(widget, Sizes.PADDING)
|
||||||
|
fs = scale(widget, Sizes.BODY)
|
||||||
|
return f"""
|
||||||
|
QLabel {{
|
||||||
|
background-color: #FFEBEE;
|
||||||
|
border: 2px solid #F44336;
|
||||||
|
border-radius: {r}px;
|
||||||
|
padding: {p}px;
|
||||||
|
color: #C62828;
|
||||||
|
font-size: {fs}px;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def success_box(widget: QWidget) -> str:
|
||||||
|
r = scale(widget, Sizes.RADIUS)
|
||||||
|
p = scale(widget, Sizes.PADDING)
|
||||||
|
fs = scale(widget, Sizes.BODY)
|
||||||
|
return f"""
|
||||||
|
QLabel {{
|
||||||
|
background-color: #E8F5E9;
|
||||||
|
border: 2px solid #4CAF50;
|
||||||
|
border-radius: {r}px;
|
||||||
|
padding: {p}px;
|
||||||
|
color: #2E7D32;
|
||||||
|
font-size: {fs}px;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user