diff --git a/app/license_dialog.py b/app/license_dialog.py new file mode 100644 index 0000000..a555ea9 --- /dev/null +++ b/app/license_dialog.py @@ -0,0 +1,187 @@ +"""Dialogue de saisie de licence — style moderne.""" + +import os +import sys +from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel, + QLineEdit, QPushButton, QApplication) +from PyQt6.QtGui import QPixmap, QFont +from PyQt6.QtCore import Qt + + +class LicenseDialog(QDialog): + """Dialogue de saisie de cle de licence.""" + + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("CopyDev Imposing Tool — Activation") + self.setFixedSize(420, 340) + self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint) + self._key = "" + self._setup_ui() + self._apply_style() + + def _setup_ui(self): + layout = QVBoxLayout(self) + layout.setContentsMargins(32, 24, 32, 24) + layout.setSpacing(0) + + # Logo + logo_label = QLabel() + base = sys._MEIPASS if getattr(sys, 'frozen', False) else os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + logo_path = os.path.join(base, "resources", "logo.png") + if os.path.exists(logo_path): + pixmap = QPixmap(logo_path).scaled(80, 80, + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation) + logo_label.setPixmap(pixmap) + logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(logo_label) + + layout.addSpacing(12) + + title = QLabel("Activation") + title.setAlignment(Qt.AlignmentFlag.AlignCenter) + title.setFont(QFont("Segoe UI", 16, QFont.Weight.Bold)) + layout.addWidget(title) + + layout.addSpacing(4) + + subtitle = QLabel("Entrez votre cle de licence pour activer le logiciel") + subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter) + subtitle.setFont(QFont("Segoe UI", 10)) + subtitle.setStyleSheet("color: #888;") + subtitle.setWordWrap(True) + layout.addWidget(subtitle) + + layout.addSpacing(20) + + # Champ de saisie + self._input = QLineEdit() + self._input.setPlaceholderText("COPYDEV-XXXX-XXXX-XXXX") + self._input.setFont(QFont("Consolas", 13)) + self._input.setAlignment(Qt.AlignmentFlag.AlignCenter) + self._input.setMinimumHeight(40) + self._input.returnPressed.connect(self._on_activate) + layout.addWidget(self._input) + + layout.addSpacing(16) + + # Boutons + btn_layout = QHBoxLayout() + btn_layout.setSpacing(10) + + self._btn_cancel = QPushButton("Annuler") + self._btn_cancel.setMinimumHeight(36) + self._btn_cancel.clicked.connect(self.reject) + btn_layout.addWidget(self._btn_cancel) + + self._btn_activate = QPushButton("Activer") + self._btn_activate.setMinimumHeight(36) + self._btn_activate.setDefault(True) + self._btn_activate.clicked.connect(self._on_activate) + btn_layout.addWidget(self._btn_activate) + + layout.addLayout(btn_layout) + + layout.addStretch() + + # Contact + contact = QLabel("contact@copydev.fr — www.copydev.fr") + contact.setAlignment(Qt.AlignmentFlag.AlignCenter) + contact.setFont(QFont("Segoe UI", 8)) + contact.setStyleSheet("color: #aaa;") + layout.addWidget(contact) + + def _apply_style(self): + self.setStyleSheet(""" + QDialog { background: #f5f5f5; } + QLabel { color: #2d2d2d; } + QLineEdit { + background: #fff; color: #2d2d2d; + border: 2px solid #d0d0d0; border-radius: 8px; + padding: 8px; font-size: 13px; + } + QLineEdit:focus { border-color: #00b4d8; } + QPushButton#cancel { + background: #e8e8e8; color: #555; border: 1px solid #d0d0d0; + border-radius: 8px; font-size: 13px; + } + QPushButton#cancel:hover { background: #ddd; } + """) + self._btn_activate.setStyleSheet(""" + QPushButton { + background: #00b4d8; color: white; border: none; + border-radius: 8px; font-size: 13px; font-weight: bold; + } + QPushButton:hover { background: #0096b7; } + QPushButton:pressed { background: #007a96; } + """) + self._btn_cancel.setStyleSheet(""" + QPushButton { + background: #e8e8e8; color: #555; border: 1px solid #d0d0d0; + border-radius: 8px; font-size: 13px; + } + QPushButton:hover { background: #ddd; } + """) + + def _on_activate(self): + self._key = self._input.text().strip() + if self._key: + self.accept() + + def get_key(self): + return self._key + + +class LicenseMessageDialog(QDialog): + """Dialogue de message stylise (remplace QMessageBox).""" + + def __init__(self, title, message, msg_type="info", parent=None): + super().__init__(parent) + self.setWindowTitle(title) + self.setFixedWidth(400) + self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint) + + layout = QVBoxLayout(self) + layout.setContentsMargins(28, 24, 28, 20) + layout.setSpacing(12) + + # Icone couleur selon le type + colors = {"info": "#00b4d8", "success": "#2ecc71", "error": "#e74c3c", "warning": "#f39c12"} + color = colors.get(msg_type, "#00b4d8") + + bar = QLabel() + bar.setFixedHeight(4) + bar.setStyleSheet(f"background: {color}; border-radius: 2px;") + layout.addWidget(bar) + + title_lbl = QLabel(title) + title_lbl.setFont(QFont("Segoe UI", 14, QFont.Weight.Bold)) + title_lbl.setStyleSheet(f"color: {color};") + layout.addWidget(title_lbl) + + msg_lbl = QLabel(message) + msg_lbl.setFont(QFont("Segoe UI", 11)) + msg_lbl.setWordWrap(True) + msg_lbl.setStyleSheet("color: #444; line-height: 1.4;") + layout.addWidget(msg_lbl) + + layout.addSpacing(8) + + btn = QPushButton("OK") + btn.setMinimumHeight(36) + btn.setStyleSheet(f""" + QPushButton {{ + background: {color}; color: white; border: none; + border-radius: 8px; font-size: 13px; font-weight: bold; + min-width: 100px; + }} + QPushButton:hover {{ background: {color}cc; }} + """) + btn.clicked.connect(self.accept) + btn_layout = QHBoxLayout() + btn_layout.addStretch() + btn_layout.addWidget(btn) + layout.addLayout(btn_layout) + + self.setStyleSheet("QDialog { background: #f5f5f5; }") diff --git a/app/main_window.py b/app/main_window.py index 7291c15..ca124b5 100644 --- a/app/main_window.py +++ b/app/main_window.py @@ -714,16 +714,27 @@ class MainWindow(QMainWindow): ) def on_crop_accepted(): + if not hasattr(self, '_crop_dlg') or not self._crop_dlg: + return + dlg_ref = self._crop_dlg + self._crop_dlg = None + try: + settings = dlg_ref.get_settings() + except Exception: + return + if not settings.get("pages"): + return self._show_progress() - settings = self._crop_dlg.get_settings() output = self._make_temp() try: crop_pages(self._current_file, output, **settings) self._show_result(output) + self._status_label.setText( + f"Recadrage applique — {len(settings['pages'])} page(s)" + ) except Exception as e: self._hide_progress() QMessageBox.critical(self, "Erreur", str(e)) - self._crop_dlg = None self._crop_dlg.accepted.connect(on_crop_accepted) self._crop_dlg.rejected.connect(lambda: setattr(self, '_crop_dlg', None)) diff --git a/app/splash.py b/app/splash.py index 80aa5b1..412fc56 100644 --- a/app/splash.py +++ b/app/splash.py @@ -13,7 +13,12 @@ class SplashScreen(QWidget): self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.WindowStaysOnTopHint) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False) self.setFixedSize(500, 400) - self.setStyleSheet("background-color: #f5f5f5; border: 1px solid #d0d0d0;") + self.setStyleSheet(""" + QWidget { + background-color: #f5f5f5; + border-radius: 16px; + } + """) layout = QVBoxLayout(self) layout.setContentsMargins(40, 30, 40, 30) diff --git a/app/updater.py b/app/updater.py index 6487a45..d0f62d8 100644 --- a/app/updater.py +++ b/app/updater.py @@ -14,7 +14,7 @@ REPO = "jules/copydev-imposing-tool" BRANCH = "master" # Version actuelle (incrementee a chaque release) -VERSION = "2.5.0" +VERSION = "2.5.1" def get_remote_version(): diff --git a/main.py b/main.py index cac0c9c..989c485 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ else: sys.path.insert(0, BUNDLE_DIR) -from PyQt6.QtWidgets import QApplication, QInputDialog, QMessageBox, QLineEdit +from PyQt6.QtWidgets import QApplication, QMessageBox import hashlib import platform @@ -131,57 +131,48 @@ def _check_license(): return True if result is False: - QMessageBox.critical( - None, "Licence invalide", + from app.license_dialog import LicenseMessageDialog + LicenseMessageDialog("Licence invalide", f"Votre licence a ete refusee :\n{msg}\n\n" - "Contactez CopyDev : contact@copydev.fr" - ) + "Contactez CopyDev : contact@copydev.fr", "error").exec() return False # Hors-ligne et cache expire - QMessageBox.critical( - None, "Licence expiree hors-ligne", + from app.license_dialog import LicenseMessageDialog + LicenseMessageDialog("Licence expiree hors-ligne", f"Impossible de verifier votre licence depuis {int(elapsed_hours)}h.\n" f"Le delai hors-ligne autorise est de {timeout_hours}h.\n\n" "Connectez-vous a internet ou contactez CopyDev :\n" - "contact@copydev.fr" - ) + "contact@copydev.fr", "error").exec() return False # Pas de cache — demander la cle de licence - key, ok = QInputDialog.getText( - None, - "Copydev Imposing Tool — Activation", - "Entrez votre cle de licence :", - QLineEdit.EchoMode.Normal, - ) - if not ok or not key.strip(): + from app.license_dialog import LicenseDialog, LicenseMessageDialog + dlg = LicenseDialog() + if dlg.exec() != LicenseDialog.DialogCode.Accepted: + return False + + key = dlg.get_key() + if not key: return False - key = key.strip() result, msg = _check_license_online(key) if result is True: - QMessageBox.information( - None, "Licence activee", - f"Bienvenue, {msg} !\nVotre licence est activee." - ) + LicenseMessageDialog("Licence activee", + f"Bienvenue, {msg} !\nVotre licence est activee.", "success").exec() return True if result is False: - QMessageBox.critical( - None, "Licence invalide", - f"{msg}\n\nContactez CopyDev : contact@copydev.fr" - ) + LicenseMessageDialog("Licence invalide", + f"{msg}\n\nContactez CopyDev : contact@copydev.fr", "error").exec() return False # Hors-ligne a la premiere activation - QMessageBox.critical( - None, "Connexion requise", + LicenseMessageDialog("Connexion requise", "Impossible de contacter le serveur de licences.\n" "Une connexion internet est necessaire pour la premiere activation.\n\n" - "Contactez CopyDev : contact@copydev.fr" - ) + "Contactez CopyDev : contact@copydev.fr", "error").exec() return False diff --git a/version.txt b/version.txt index 437459c..73462a5 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.5.0 +2.5.1