V2.5.1 — Dialogue licence custom + fix recadrage + style

- Dialogue de licence custom (remplace QInputDialog moche)
  - Logo CopyDev, champ stylise, bouton Activer cyan
  - Messages erreur/succes en dialogues custom colores
- Fix recadrage : reference au dialogue preservee avant destruction
- Splash screen sans bordure carree
- Style coherent gris/cyan sur tous les dialogues

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jules
2026-03-21 14:49:29 +01:00
parent d7a538aa8c
commit e94158aae8
6 changed files with 228 additions and 34 deletions

187
app/license_dialog.py Normal file
View File

@@ -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; }")

View File

@@ -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))

View File

@@ -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)

View File

@@ -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():

49
main.py
View File

@@ -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

View File

@@ -1 +1 @@
2.5.0
2.5.1