Config admin complète + IMAP polling
Config admin : - Serveur upload : IP d'écoute, port, URL publique (QR code) - Email IMAP : activer/désactiver, serveur, port, user, password, dossier, intervalle de polling - Checkbox pour activer IMAP Service IMAP : - Thread daemon, polling configurable (défaut 30s) - Connexion IMAP SSL, récupère mails UNSEEN - Extrait les PJ photos (jpg/png/etc.) - Sauvegarde dans data/photos/, notifie l'app via signal Qt - Bouton Email passe au vert + Continuer quand photos reçues main.py : - Lit la config pour IP/port du serveur upload - Démarre IMAP polling si activé dans la config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,14 @@
|
|||||||
"Bash(cp -r /home/jules/Documents/Projet/photobooth-new/frontend .)",
|
"Bash(cp -r /home/jules/Documents/Projet/photobooth-new/frontend .)",
|
||||||
"Bash(cp -r /home/jules/Documents/Projet/photobooth-new/data .)",
|
"Bash(cp -r /home/jules/Documents/Projet/photobooth-new/data .)",
|
||||||
"Bash(mkdir -p scripts)",
|
"Bash(mkdir -p scripts)",
|
||||||
"Bash(curl:*)"
|
"Bash(curl:*)",
|
||||||
|
"Bash(grep -r \"proxmox\\\\|lxc\\\\|pve\\\\|mxc\\\\|container\" /home/jules/Documents/Projet/*/memoire.md)",
|
||||||
|
"Bash(grep -r \"LXC\\\\|lxc\\\\|proxmox\\\\|pve\" /home/jules/Documents/Projet/*/memoire.md)",
|
||||||
|
"Bash(grep -r \"proxmox\\\\|pve\\\\|8006\" /home/jules/Documents/Projet/*/memoire.md)",
|
||||||
|
"Bash(ssh:*)",
|
||||||
|
"Bash(grep -r \"npm\\\\|nginx\\\\|proxy\\\\|reverse\" /home/jules/Documents/Projet/*/memoire.md)",
|
||||||
|
"Bash(ssh -i ~/.ssh/id_ed25519 root@192.168.111.4 \"pct exec 112 -- bash -c ''''cat /etc/nginx-proxy-manager/.env 2>/dev/null; ls /opt/npm/ 2>/dev/null; ls /app/ 2>/dev/null; docker ps 2>/dev/null | grep nginx''''\")",
|
||||||
|
"Bash(ssh -i ~/.ssh/id_ed25519 root@192.168.111.4 \"pct exec 112 -- bash -c ''''a2dissite photobooth && systemctl reload apache2'''' 2>/dev/null\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/main.py
24
src/main.py
@@ -5,24 +5,44 @@ import sys
|
|||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
from PyQt6.QtCore import Qt
|
from PyQt6.QtCore import Qt
|
||||||
from ui.main_window import MainWindow
|
from ui.main_window import MainWindow
|
||||||
|
from ui.widgets.config_dialog import load_config
|
||||||
from services.upload_server import UploadServer
|
from services.upload_server import UploadServer
|
||||||
|
from services.imap_watcher import ImapWatcher
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
app.setApplicationName("Photostation")
|
app.setApplicationName("Photostation")
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
window.setWindowFlags(Qt.WindowType.FramelessWindowHint)
|
window.setWindowFlags(Qt.WindowType.FramelessWindowHint)
|
||||||
|
|
||||||
# Serveur upload (WiFi / QR Code)
|
# Serveur upload (WiFi / QR Code)
|
||||||
port = 8080 if "--no-root" in sys.argv else 80
|
port = config.get("server_port", 8080)
|
||||||
upload_server = UploadServer(port=port)
|
host = config.get("server_ip", "0.0.0.0")
|
||||||
|
upload_server = UploadServer(host=host, port=port)
|
||||||
upload_server.signals.photos_received.connect(
|
upload_server.signals.photos_received.connect(
|
||||||
window.home_screen.notify_qr_photos
|
window.home_screen.notify_qr_photos
|
||||||
)
|
)
|
||||||
upload_server.start()
|
upload_server.start()
|
||||||
|
|
||||||
|
# IMAP polling (Email)
|
||||||
|
imap_watcher = ImapWatcher()
|
||||||
|
if config.get("imap_enabled"):
|
||||||
|
imap_watcher.signals.photos_received.connect(
|
||||||
|
window.home_screen.notify_email_photos
|
||||||
|
)
|
||||||
|
imap_watcher.start(
|
||||||
|
server=config.get("imap_server", ""),
|
||||||
|
port=config.get("imap_port", 993),
|
||||||
|
user=config.get("imap_user", ""),
|
||||||
|
password=config.get("imap_password", ""),
|
||||||
|
folder=config.get("imap_folder", "INBOX"),
|
||||||
|
interval=config.get("imap_interval", 30),
|
||||||
|
)
|
||||||
|
|
||||||
if "--kiosk" in sys.argv:
|
if "--kiosk" in sys.argv:
|
||||||
screen = app.primaryScreen().geometry()
|
screen = app.primaryScreen().geometry()
|
||||||
window.setGeometry(screen)
|
window.setGeometry(screen)
|
||||||
|
|||||||
119
src/services/imap_watcher.py
Normal file
119
src/services/imap_watcher.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
"""Service IMAP polling — consulte une boîte mail et récupère les pièces jointes photos."""
|
||||||
|
|
||||||
|
import imaplib
|
||||||
|
import email
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from email.header import decode_header
|
||||||
|
from PyQt6.QtCore import QObject, pyqtSignal
|
||||||
|
|
||||||
|
UPLOAD_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "data", "photos")
|
||||||
|
ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "bmp", "tiff", "tif", "webp", "heic"}
|
||||||
|
|
||||||
|
|
||||||
|
class ImapSignals(QObject):
|
||||||
|
"""Signaux Qt pour communiquer du thread IMAP vers le thread principal."""
|
||||||
|
photos_received = pyqtSignal(list)
|
||||||
|
error = pyqtSignal(str)
|
||||||
|
|
||||||
|
|
||||||
|
class ImapWatcher:
|
||||||
|
"""Consulte une boîte IMAP en boucle et récupère les photos en PJ."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.signals = ImapSignals()
|
||||||
|
self._thread = None
|
||||||
|
self._running = False
|
||||||
|
self._seen_uids = set()
|
||||||
|
|
||||||
|
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
def start(self, server, port, user, password, folder="INBOX", interval=30):
|
||||||
|
"""Démarre le polling IMAP dans un thread daemon."""
|
||||||
|
if not server or not user:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._running = True
|
||||||
|
self._config = {
|
||||||
|
"server": server,
|
||||||
|
"port": port,
|
||||||
|
"user": user,
|
||||||
|
"password": password,
|
||||||
|
"folder": folder,
|
||||||
|
"interval": interval,
|
||||||
|
}
|
||||||
|
self._thread = threading.Thread(target=self._poll_loop, daemon=True)
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._running = False
|
||||||
|
|
||||||
|
def _poll_loop(self):
|
||||||
|
"""Boucle de polling."""
|
||||||
|
while self._running:
|
||||||
|
try:
|
||||||
|
self._check_mail()
|
||||||
|
except Exception as e:
|
||||||
|
self.signals.error.emit(str(e))
|
||||||
|
time.sleep(self._config["interval"])
|
||||||
|
|
||||||
|
def _check_mail(self):
|
||||||
|
"""Connexion IMAP, récupère les nouveaux mails avec PJ photos."""
|
||||||
|
cfg = self._config
|
||||||
|
mail = imaplib.IMAP4_SSL(cfg["server"], cfg["port"])
|
||||||
|
mail.login(cfg["user"], cfg["password"])
|
||||||
|
mail.select(cfg["folder"])
|
||||||
|
|
||||||
|
# Chercher les mails non lus
|
||||||
|
status, data = mail.search(None, "UNSEEN")
|
||||||
|
if status != "OK" or not data[0]:
|
||||||
|
mail.logout()
|
||||||
|
return
|
||||||
|
|
||||||
|
saved_photos = []
|
||||||
|
|
||||||
|
for uid in data[0].split():
|
||||||
|
if uid in self._seen_uids:
|
||||||
|
continue
|
||||||
|
self._seen_uids.add(uid)
|
||||||
|
|
||||||
|
status, msg_data = mail.fetch(uid, "(RFC822)")
|
||||||
|
if status != "OK":
|
||||||
|
continue
|
||||||
|
|
||||||
|
msg = email.message_from_bytes(msg_data[0][1])
|
||||||
|
|
||||||
|
for part in msg.walk():
|
||||||
|
content_type = part.get_content_type()
|
||||||
|
filename = part.get_filename()
|
||||||
|
|
||||||
|
if filename:
|
||||||
|
# Décoder le nom de fichier
|
||||||
|
decoded_parts = decode_header(filename)
|
||||||
|
filename = ""
|
||||||
|
for content, encoding in decoded_parts:
|
||||||
|
if isinstance(content, bytes):
|
||||||
|
filename += content.decode(encoding or "utf-8", errors="replace")
|
||||||
|
else:
|
||||||
|
filename += content
|
||||||
|
|
||||||
|
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
|
||||||
|
if ext not in ALLOWED_EXTENSIONS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sauvegarder la pièce jointe
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if not payload:
|
||||||
|
continue
|
||||||
|
|
||||||
|
safe_name = f"{int(time.time() * 1000)}_{filename}"
|
||||||
|
path = os.path.join(UPLOAD_DIR, safe_name)
|
||||||
|
with open(path, "wb") as f:
|
||||||
|
f.write(payload)
|
||||||
|
saved_photos.append(path)
|
||||||
|
|
||||||
|
mail.logout()
|
||||||
|
|
||||||
|
if saved_photos:
|
||||||
|
self.signals.photos_received.emit(saved_photos)
|
||||||
@@ -275,7 +275,8 @@ class UploadSignals(QObject):
|
|||||||
class UploadServer:
|
class UploadServer:
|
||||||
"""Serveur Flask d'upload, tourne dans un thread séparé."""
|
"""Serveur Flask d'upload, tourne dans un thread séparé."""
|
||||||
|
|
||||||
def __init__(self, port=80):
|
def __init__(self, host="0.0.0.0", port=80):
|
||||||
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.signals = UploadSignals()
|
self.signals = UploadSignals()
|
||||||
self.app = Flask(__name__)
|
self.app = Flask(__name__)
|
||||||
@@ -315,7 +316,7 @@ class UploadServer:
|
|||||||
"""Démarre le serveur dans un thread daemon."""
|
"""Démarre le serveur dans un thread daemon."""
|
||||||
self._thread = threading.Thread(
|
self._thread = threading.Thread(
|
||||||
target=lambda: self.app.run(
|
target=lambda: self.app.run(
|
||||||
host="0.0.0.0", port=self.port,
|
host=self.host, port=self.port,
|
||||||
debug=False, use_reloader=False
|
debug=False, use_reloader=False
|
||||||
),
|
),
|
||||||
daemon=True
|
daemon=True
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import json
|
|||||||
import os
|
import os
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QDialog, QVBoxLayout, QHBoxLayout, QGridLayout,
|
QDialog, QVBoxLayout, QHBoxLayout, QGridLayout,
|
||||||
QLineEdit, QPushButton, QLabel, QComboBox, QScrollArea, QWidget
|
QLineEdit, QPushButton, QLabel, QComboBox, QScrollArea, QWidget,
|
||||||
|
QSpinBox, QCheckBox
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import Qt
|
from PyQt6.QtCore import Qt
|
||||||
from PyQt6.QtGui import QFont
|
from PyQt6.QtGui import QFont
|
||||||
@@ -14,13 +15,25 @@ CONFIG_PATH = os.path.join(
|
|||||||
)
|
)
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
|
# WiFi hotspot
|
||||||
"wifi_ap_name": "Photostation",
|
"wifi_ap_name": "Photostation",
|
||||||
"wifi_ap_password": "photo1234",
|
"wifi_ap_password": "photo1234",
|
||||||
|
# Serveur upload
|
||||||
|
"server_ip": "0.0.0.0",
|
||||||
|
"server_port": 8080,
|
||||||
|
"upload_url": "http://192.168.4.1:8080/upload",
|
||||||
|
# Email IMAP
|
||||||
"email_address": "photo@photostation.local",
|
"email_address": "photo@photostation.local",
|
||||||
"upload_url": "http://192.168.4.1/upload",
|
"imap_enabled": False,
|
||||||
|
"imap_server": "",
|
||||||
|
"imap_port": 993,
|
||||||
|
"imap_user": "",
|
||||||
|
"imap_password": "",
|
||||||
|
"imap_folder": "INBOX",
|
||||||
|
"imap_interval": 30,
|
||||||
|
# Impression
|
||||||
"printer_name": "",
|
"printer_name": "",
|
||||||
"print_format": "10x15 cm",
|
"print_format": "10x15 cm",
|
||||||
"kiosk_mode": False,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -52,27 +65,60 @@ class ConfigDialog(QDialog):
|
|||||||
|
|
||||||
def showEvent(self, event):
|
def showEvent(self, event):
|
||||||
super().showEvent(event)
|
super().showEvent(event)
|
||||||
# Plein écran pour masquer l'interface derrière
|
|
||||||
if self.parent():
|
if self.parent():
|
||||||
self.setGeometry(self.parent().rect())
|
self.setGeometry(self.parent().rect())
|
||||||
self.setStyleSheet("ConfigDialog { background-color: #f0f0f0; }")
|
self.setStyleSheet("ConfigDialog { background-color: #f0f0f0; }")
|
||||||
|
|
||||||
def _fs(self, pct):
|
def _section_title(self, text):
|
||||||
"""Font size en % de la hauteur du dialogue."""
|
lbl = QLabel(text)
|
||||||
return max(8, int(self.height() * pct / 100)) if self.height() > 0 else 14
|
lbl.setFont(QFont("", 13, QFont.Weight.Bold))
|
||||||
|
lbl.setStyleSheet("color: #2196F3; padding-top: 5px;")
|
||||||
|
return lbl
|
||||||
|
|
||||||
|
def _label(self, text):
|
||||||
|
lbl = QLabel(text)
|
||||||
|
lbl.setFont(QFont("", 12))
|
||||||
|
lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
||||||
|
return lbl
|
||||||
|
|
||||||
|
def _input(self, value, placeholder=""):
|
||||||
|
inp = QLineEdit(str(value))
|
||||||
|
inp.setFont(QFont("", 12))
|
||||||
|
inp.setMinimumHeight(35)
|
||||||
|
inp.setStyleSheet("""
|
||||||
|
QLineEdit {
|
||||||
|
border: 2px solid #ccc; border-radius: 6px;
|
||||||
|
padding: 5px 10px; background: white;
|
||||||
|
}
|
||||||
|
QLineEdit:focus { border-color: #2196F3; }
|
||||||
|
""")
|
||||||
|
if placeholder:
|
||||||
|
inp.setPlaceholderText(placeholder)
|
||||||
|
return inp
|
||||||
|
|
||||||
|
def _password_input(self, value):
|
||||||
|
inp = self._input(value)
|
||||||
|
inp.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
|
return inp
|
||||||
|
|
||||||
|
def _spinbox(self, value, min_val, max_val):
|
||||||
|
sb = QSpinBox()
|
||||||
|
sb.setFont(QFont("", 12))
|
||||||
|
sb.setMinimumHeight(35)
|
||||||
|
sb.setRange(min_val, max_val)
|
||||||
|
sb.setValue(value)
|
||||||
|
return sb
|
||||||
|
|
||||||
def _setup_ui(self):
|
def _setup_ui(self):
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(20, 10, 20, 10)
|
layout.setContentsMargins(20, 10, 20, 10)
|
||||||
layout.setSpacing(8)
|
layout.setSpacing(8)
|
||||||
|
|
||||||
# Titre
|
|
||||||
title = QLabel("Configuration Photostation")
|
title = QLabel("Configuration Photostation")
|
||||||
title.setFont(QFont("", 18, QFont.Weight.Bold))
|
title.setFont(QFont("", 18, QFont.Weight.Bold))
|
||||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
layout.addWidget(title)
|
layout.addWidget(title)
|
||||||
|
|
||||||
# Scroll area pour le contenu
|
|
||||||
scroll = QScrollArea()
|
scroll = QScrollArea()
|
||||||
scroll.setWidgetResizable(True)
|
scroll.setWidgetResizable(True)
|
||||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||||
@@ -87,10 +133,7 @@ class ConfigDialog(QDialog):
|
|||||||
row = 0
|
row = 0
|
||||||
|
|
||||||
# ── WiFi ──
|
# ── WiFi ──
|
||||||
wifi_title = QLabel("WiFi (Hotspot)")
|
grid.addWidget(self._section_title("WiFi (Hotspot)"), row, 0, 1, 2)
|
||||||
wifi_title.setFont(QFont("", 13, QFont.Weight.Bold))
|
|
||||||
wifi_title.setStyleSheet("color: #2196F3; padding-top: 5px;")
|
|
||||||
grid.addWidget(wifi_title, row, 0, 1, 2)
|
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
grid.addWidget(self._label("Nom du réseau :"), row, 0)
|
grid.addWidget(self._label("Nom du réseau :"), row, 0)
|
||||||
@@ -103,35 +146,73 @@ class ConfigDialog(QDialog):
|
|||||||
grid.addWidget(self.wifi_pass_input, row, 1)
|
grid.addWidget(self.wifi_pass_input, row, 1)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
# ── QR Code ──
|
# ── Serveur upload ──
|
||||||
qr_title = QLabel("QR Code")
|
grid.addWidget(self._section_title("Serveur upload (QR Code / WiFi)"), row, 0, 1, 2)
|
||||||
qr_title.setFont(QFont("", 13, QFont.Weight.Bold))
|
|
||||||
qr_title.setStyleSheet("color: #2196F3; padding-top: 5px;")
|
|
||||||
grid.addWidget(qr_title, row, 0, 1, 2)
|
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
grid.addWidget(self._label("URL d'upload :"), row, 0)
|
grid.addWidget(self._label("IP d'écoute :"), row, 0)
|
||||||
self.upload_url_input = self._input(self.config["upload_url"])
|
self.server_ip_input = self._input(self.config["server_ip"], "0.0.0.0 = toutes les interfaces")
|
||||||
|
grid.addWidget(self.server_ip_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Port :"), row, 0)
|
||||||
|
self.server_port_input = self._spinbox(self.config["server_port"], 80, 65535)
|
||||||
|
grid.addWidget(self.server_port_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("URL publique (QR) :"), row, 0)
|
||||||
|
self.upload_url_input = self._input(self.config["upload_url"], "https://photo.exemple.fr/upload")
|
||||||
grid.addWidget(self.upload_url_input, row, 1)
|
grid.addWidget(self.upload_url_input, row, 1)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
# ── Email ──
|
# ── Email IMAP ──
|
||||||
email_title = QLabel("Email")
|
grid.addWidget(self._section_title("Email (réception IMAP)"), row, 0, 1, 2)
|
||||||
email_title.setFont(QFont("", 13, QFont.Weight.Bold))
|
|
||||||
email_title.setStyleSheet("color: #2196F3; padding-top: 5px;")
|
|
||||||
grid.addWidget(email_title, row, 0, 1, 2)
|
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
grid.addWidget(self._label("Adresse email :"), row, 0)
|
grid.addWidget(self._label("Activer :"), row, 0)
|
||||||
|
self.imap_enabled_cb = QCheckBox("Consulter la boîte mail")
|
||||||
|
self.imap_enabled_cb.setFont(QFont("", 12))
|
||||||
|
self.imap_enabled_cb.setChecked(self.config["imap_enabled"])
|
||||||
|
grid.addWidget(self.imap_enabled_cb, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Adresse affichée :"), row, 0)
|
||||||
self.email_input = self._input(self.config["email_address"])
|
self.email_input = self._input(self.config["email_address"])
|
||||||
grid.addWidget(self.email_input, row, 1)
|
grid.addWidget(self.email_input, row, 1)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Serveur IMAP :"), row, 0)
|
||||||
|
self.imap_server_input = self._input(self.config["imap_server"], "imap.gmail.com")
|
||||||
|
grid.addWidget(self.imap_server_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Port IMAP :"), row, 0)
|
||||||
|
self.imap_port_input = self._spinbox(self.config["imap_port"], 1, 65535)
|
||||||
|
grid.addWidget(self.imap_port_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Utilisateur :"), row, 0)
|
||||||
|
self.imap_user_input = self._input(self.config["imap_user"])
|
||||||
|
grid.addWidget(self.imap_user_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Mot de passe :"), row, 0)
|
||||||
|
self.imap_pass_input = self._password_input(self.config["imap_password"])
|
||||||
|
grid.addWidget(self.imap_pass_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Dossier :"), row, 0)
|
||||||
|
self.imap_folder_input = self._input(self.config["imap_folder"], "INBOX")
|
||||||
|
grid.addWidget(self.imap_folder_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
grid.addWidget(self._label("Intervalle (sec) :"), row, 0)
|
||||||
|
self.imap_interval_input = self._spinbox(self.config["imap_interval"], 10, 300)
|
||||||
|
grid.addWidget(self.imap_interval_input, row, 1)
|
||||||
|
row += 1
|
||||||
|
|
||||||
# ── Impression ──
|
# ── Impression ──
|
||||||
print_title = QLabel("Impression")
|
grid.addWidget(self._section_title("Impression"), row, 0, 1, 2)
|
||||||
print_title.setFont(QFont("", 13, QFont.Weight.Bold))
|
|
||||||
print_title.setStyleSheet("color: #2196F3; padding-top: 5px;")
|
|
||||||
grid.addWidget(print_title, row, 0, 1, 2)
|
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
grid.addWidget(self._label("Imprimante :"), row, 0)
|
grid.addWidget(self._label("Imprimante :"), row, 0)
|
||||||
@@ -151,7 +232,7 @@ class ConfigDialog(QDialog):
|
|||||||
scroll.setWidget(content)
|
scroll.setWidget(content)
|
||||||
layout.addWidget(scroll, 1)
|
layout.addWidget(scroll, 1)
|
||||||
|
|
||||||
# Boutons en bas
|
# Boutons
|
||||||
buttons = QHBoxLayout()
|
buttons = QHBoxLayout()
|
||||||
buttons.setSpacing(15)
|
buttons.setSpacing(15)
|
||||||
|
|
||||||
@@ -177,36 +258,20 @@ class ConfigDialog(QDialog):
|
|||||||
|
|
||||||
layout.addLayout(buttons)
|
layout.addLayout(buttons)
|
||||||
|
|
||||||
def _label(self, text):
|
|
||||||
lbl = QLabel(text)
|
|
||||||
lbl.setFont(QFont("", 12))
|
|
||||||
lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
|
||||||
return lbl
|
|
||||||
|
|
||||||
def _input(self, value, placeholder=""):
|
|
||||||
inp = QLineEdit(value)
|
|
||||||
inp.setFont(QFont("", 12))
|
|
||||||
inp.setMinimumHeight(35)
|
|
||||||
inp.setStyleSheet("""
|
|
||||||
QLineEdit {
|
|
||||||
border: 2px solid #ccc;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 5px 10px;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
QLineEdit:focus {
|
|
||||||
border-color: #2196F3;
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
if placeholder:
|
|
||||||
inp.setPlaceholderText(placeholder)
|
|
||||||
return inp
|
|
||||||
|
|
||||||
def _save(self):
|
def _save(self):
|
||||||
self.config["wifi_ap_name"] = self.wifi_name_input.text()
|
self.config["wifi_ap_name"] = self.wifi_name_input.text()
|
||||||
self.config["wifi_ap_password"] = self.wifi_pass_input.text()
|
self.config["wifi_ap_password"] = self.wifi_pass_input.text()
|
||||||
|
self.config["server_ip"] = self.server_ip_input.text()
|
||||||
|
self.config["server_port"] = self.server_port_input.value()
|
||||||
self.config["upload_url"] = self.upload_url_input.text()
|
self.config["upload_url"] = self.upload_url_input.text()
|
||||||
self.config["email_address"] = self.email_input.text()
|
self.config["email_address"] = self.email_input.text()
|
||||||
|
self.config["imap_enabled"] = self.imap_enabled_cb.isChecked()
|
||||||
|
self.config["imap_server"] = self.imap_server_input.text()
|
||||||
|
self.config["imap_port"] = self.imap_port_input.value()
|
||||||
|
self.config["imap_user"] = self.imap_user_input.text()
|
||||||
|
self.config["imap_password"] = self.imap_pass_input.text()
|
||||||
|
self.config["imap_folder"] = self.imap_folder_input.text()
|
||||||
|
self.config["imap_interval"] = self.imap_interval_input.value()
|
||||||
self.config["printer_name"] = self.printer_input.text()
|
self.config["printer_name"] = self.printer_input.text()
|
||||||
self.config["print_format"] = self.format_combo.currentText()
|
self.config["print_format"] = self.format_combo.currentText()
|
||||||
save_config(self.config)
|
save_config(self.config)
|
||||||
|
|||||||
Reference in New Issue
Block a user