diff --git a/BUILD.spec b/BUILD.spec index 079d3d4..602f831 100644 --- a/BUILD.spec +++ b/BUILD.spec @@ -3,7 +3,7 @@ import json from pathlib import Path # Load config.json -config_path = Path("config.json") +config_path = Path("../config.json") with config_path.open("r", encoding="utf-8") as f: config = json.load(f) @@ -24,7 +24,8 @@ icon = getenv("ICON_PATH", "") # Data files to bundle datas = [ ("data/assets/*", "data/assets/"), - ("data/", "data/") + ("data/", "data/"), + ("config.json", ".") ] binaries = [] diff --git a/app/core/settings_manager.py b/app/core/settings_manager.py index df58b4b..3c72d1f 100644 --- a/app/core/settings_manager.py +++ b/app/core/settings_manager.py @@ -18,8 +18,7 @@ class SettingsManager: with open(defaults_path, 'r', encoding='utf-8') as f: self.default_settings = json.load(f) - config_path = path.join(paths.get_current_dir(), "config.json") - with open(config_path, 'r', encoding='utf-8') as f: + with open(paths.resource_path("config.json"), 'r', encoding='utf-8') as f: self.config = json.load(f) self.settings = QSettings(path.join(paths.get_user_data_dir(self.get_config("app_name")), self.get_config("app_name") + ".ini"), QSettings.Format.IniFormat) @@ -49,7 +48,7 @@ class SettingsManager: self.settings.setValue("lang", lang_code) self.observer_manager.notify(NotificationType.LANGUAGE) - # Window size and fullscreen + # Window size and maximized def get_window_size(self) -> dict: return self.settings.value("window_size", self.default_settings.get("window_size")) @@ -59,18 +58,8 @@ class SettingsManager: new_size = {"width": width, "height": height} self.settings.setValue("window_size", new_size) - def get_fullscreen(self) -> bool: - return self.settings.value("fullscreen", self.default_settings.get("fullscreen")) + def get_maximized(self) -> bool: + return self.settings.value("maximized", self.default_settings.get("maximized")) == "true" - def set_fullscreen(self, fullscreen: bool, width: int, height: int): - self.settings.setValue("fullscreen", fullscreen) - self.settings.setValue("fullscreen_size", {"width": width, "height": height}) - - def get_fullscreen_size(self) -> dict: - return self.settings.value("fullscreen_size", self.default_settings.get("fullscreen_size")) - - def set_fullscreen_size(self, width: int, height: int) -> None: - current_size = self.get_fullscreen_size() - if current_size["width"] != width or current_size["height"] != height: - new_size = {"width": width, "height": height} - self.settings.setValue("fullscreen_size", new_size) \ No newline at end of file + def set_maximized(self, maximized: bool): + self.settings.setValue("maximized", maximized) \ No newline at end of file diff --git a/app/ui/main_window.py b/app/ui/main_window.py index 3e0f9c6..cc454fd 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -1,9 +1,11 @@ from PyQt6.QtWidgets import QMainWindow from PyQt6.QtGui import QIcon from PyQt6.QtWidgets import QApplication +from PyQt6.QtCore import QSize from app.core.main_manager import MainManager, NotificationType from app.ui.widgets.tabs_widget import TabsWidget, MenuDirection, ButtonPosition, BorderSide from app.ui.windows.settings_window import SettingsWindow +from app.ui.windows.suggestion_window import SuggestionWindow import app.utils.paths as paths class MainWindow(QMainWindow): @@ -11,6 +13,7 @@ class MainWindow(QMainWindow): super().__init__() self.main_manager = MainManager() + self.language_manager = self.main_manager.get_language_manager() self.theme_manager = self.main_manager.get_theme_manager() self.settings_manager = self.main_manager.get_settings_manager() @@ -19,24 +22,62 @@ class MainWindow(QMainWindow): # Configurer la fenêtre avant de créer les widgets app = QApplication.instance() - self.settings_manager.dpi = app.primaryScreen().devicePixelRatio() size = app.primaryScreen().size() self.settings_manager.minScreenSize = min(size.height(),size.width()) - configWidth = int(self.settings_manager.get_fullscreen_size()["width"] if self.settings_manager.get_fullscreen() else self.settings_manager.get_window_size()["width"] /self.settings_manager.dpi) - configHeight = int(self.settings_manager.get_fullscreen_size()["height"] if self.settings_manager.get_fullscreen() else self.settings_manager.get_window_size()["height"] /self.settings_manager.dpi) + self.setMinimumSize(600, 400) - self.resize(configWidth, configHeight) self.setWindowTitle(self.settings_manager.get_config("app_name")) self.setStyleSheet(self.theme_manager.get_sheet()) self.setWindowIcon(QIcon(paths.get_asset_path("icon"))) + + # Apply saved window state + self.apply_saved_window_state() + self.setup_ui() + def apply_saved_window_state(self): + """Apply saved window size and maximized state""" + window_size = self.settings_manager.get_window_size() + self.current_size = QSize(window_size["width"], window_size["height"]) + self.previous_size = self.current_size + self.resize(self.current_size) + if self.settings_manager.get_maximized(): + self.showMaximized() + + if hasattr(self, 'current_size'): + print(self.current_size) + + def changeEvent(self, event): + """Handle window state changes""" + super().changeEvent(event) + if event.type() == event.Type.WindowStateChange: + # Si on quitte le mode maximisé, sauvegarder la taille actuelle + if self.isMaximized(): + self.current_size = self.previous_size + self.settings_manager.set_maximized(self.isMaximized()) + if hasattr(self, 'current_size'): + print(self.current_size) + + def resizeEvent(self, a0): + self.previous_size = self.current_size + self.current_size = self.size() + self.settings_manager.set_window_size( + self.current_size.width(), + self.current_size.height() + ) + + def closeEvent(self, event): + """Handle application close event""" + super().closeEvent(event) def setup_ui(self): self.side_menu = TabsWidget(self,MenuDirection.HORIZONTAL,70,None,10,1,BorderSide.TOP) self.settings_window = SettingsWindow(self) self.side_menu.add_widget(self.settings_window,"",paths.get_asset_svg_path("settings"), position=ButtonPosition.CENTER) - + + self.suggestion_window = SuggestionWindow(self) + self.side_menu.add_widget(self.suggestion_window,"",paths.get_asset_svg_path("suggestion"), position=ButtonPosition.CENTER) + self.setCentralWidget(self.side_menu) def update_theme(self): diff --git a/app/ui/windows/settings_window.py b/app/ui/windows/settings_window.py index 97d20bb..b2bad14 100644 --- a/app/ui/windows/settings_window.py +++ b/app/ui/windows/settings_window.py @@ -1,4 +1,4 @@ -from PyQt6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QLabel, QHBoxLayout, QSizePolicy +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QLabel, QHBoxLayout from PyQt6.QtCore import Qt from app.core.main_manager import MainManager, NotificationType diff --git a/app/ui/windows/suggestion_window.py b/app/ui/windows/suggestion_window.py new file mode 100644 index 0000000..69c12c0 --- /dev/null +++ b/app/ui/windows/suggestion_window.py @@ -0,0 +1,146 @@ +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QPushButton, QLabel, QMessageBox, QHBoxLayout +from PyQt6.QtCore import Qt, QThread, pyqtSignal +import time +import smtplib +import os +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from app.core.main_manager import MainManager, NotificationType + +class EmailSender(QThread): + success = pyqtSignal() + error = pyqtSignal(str) + + def __init__(self, subject, message): + super().__init__() + self.subject = subject + self.message = message + + def run(self): + try: + # Get email configuration from environment variables + password = 'xprqegxuqixgljyi' + email = 'louismazindev@gmail.com' + smtp_server = 'smtp.gmail.com' + smtp_port = 587 + + if not password: + # Fallback to simulation if no credentials are configured + self._simulate_email_sending() + return + + # Create message + msg = MIMEMultipart() + msg['From'] = email + msg['To'] = email + msg['Subject'] = self.subject + + # Add body to email + msg.attach(MIMEText(self.message, 'plain')) + + # Create SMTP session + server = smtplib.SMTP(smtp_server, smtp_port) + server.starttls() # Enable TLS encryption + # Login with app password + server.login(email, password) + # Send email + text = msg.as_string() + server.sendmail(email, email, text) + server.quit() + + self.success.emit() + except Exception as e: + self.error.emit() + + def _simulate_email_sending(self): + """Fallback simulation when no email credentials are configured""" + time.sleep(2) # Simulate network delay + + print("=== EMAIL SIMULATION (No credentials configured) ===") + print(f"Subject: {self.subject}") + print(f"Message: {self.message}") + print("=" * 55) + + self.success.emit() + +class SuggestionWindow(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.main_manager = MainManager.get_instance() + self.language_manager = self.main_manager.get_language_manager() + self.settings_manager = self.main_manager.get_settings_manager() + + self.observer_manager = self.main_manager.get_observer_manager() + self.observer_manager.subscribe(NotificationType.LANGUAGE, self.update_language) + + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + layout.setAlignment(Qt.AlignmentFlag.AlignTop) + layout.setSpacing(20) + layout.setContentsMargins(20, 20, 20, 20) + + # Title + self.title_label = QLabel(self.language_manager.get_text("suggestion_text"), self) + self.title_label.setStyleSheet("font-size: 18px; font-weight: bold; margin-bottom: 10px;") + layout.addWidget(self.title_label) + + # Text area for suggestion + self.text_edit = QTextEdit(self) + self.text_edit.setPlaceholderText(self.language_manager.get_text("suggestion_placeholder")) + self.text_edit.setMinimumHeight(200) + layout.addWidget(self.text_edit) + + # Button layout + button_layout = QHBoxLayout() + button_layout.addStretch() + + # Send button + self.send_button = QPushButton(self.language_manager.get_text("send_suggestion"), self) + self.send_button.clicked.connect(self.send_suggestion) + button_layout.addWidget(self.send_button) + + layout.addLayout(button_layout) + layout.addStretch() + + def send_suggestion(self): + message = self.text_edit.toPlainText().strip() + + if not message: + return + + # Disable send button during sending + self.send_button.setEnabled(False) + self.send_button.setText(self.language_manager.get_text("sending")) + + # Create subject with app name + subject = f"Suggestion for {self.settings_manager.get_config('app_name')}" + + # Create and start email sender thread + self.email_sender = EmailSender(subject, message) + self.email_sender.success.connect(self.on_email_sent) + self.email_sender.error.connect(self.on_email_error) + self.email_sender.start() + + def on_email_sent(self): + self.send_button.setEnabled(True) + self.send_button.setText(self.language_manager.get_text("send_suggestion")) + + QMessageBox.information(self, + self.language_manager.get_text("success"), + self.language_manager.get_text("suggestion_sent_success")) + self.text_edit.clear() + + def on_email_error(self): + self.send_button.setEnabled(True) + self.send_button.setText(self.language_manager.get_text("send_suggestion")) + + QMessageBox.critical(self, + self.language_manager.get_text("error"), + f"{self.language_manager.get_text('suggestion_send_error')}") + + def update_language(self): + self.title_label.setText(self.language_manager.get_text("suggestion_text")) + self.text_edit.setPlaceholderText(self.language_manager.get_text("suggestion_placeholder")) + self.send_button.setText(self.language_manager.get_text("send_suggestion")) diff --git a/app/utils/paths.py b/app/utils/paths.py index 3dad0ef..d5f777b 100644 --- a/app/utils/paths.py +++ b/app/utils/paths.py @@ -46,14 +46,3 @@ def get_user_data_dir(app_name: str) -> Path: if not path.exists(user_data_path): mkdir(user_data_path) return user_data_path - -def get_current_dir() -> Path: - """ - Return the directory where the exe or main script is located. - """ - if getattr(sys, 'frozen', False): - # If the application is frozen (PyInstaller) - return str(Path(sys.executable).parent) - else: - # If running in a normal Python environment - return str(Path(__file__).parent.parent.parent) \ No newline at end of file diff --git a/data/assets/settings.svg b/data/assets/settings.svg index 9dab5b7..3bfe869 100644 --- a/data/assets/settings.svg +++ b/data/assets/settings.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/data/assets/suggestion.svg b/data/assets/suggestion.svg new file mode 100644 index 0000000..6634fc4 --- /dev/null +++ b/data/assets/suggestion.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/data/lang/en.json b/data/lang/en.json index d0ec107..48dce8e 100644 --- a/data/lang/en.json +++ b/data/lang/en.json @@ -6,5 +6,13 @@ "settings": "Settings", "theme": "Theme :", "dark_theme": "Dark Theme", - "light_theme": "Light Theme" + "light_theme": "Light Theme", + "suggestion_text": "Do you have a question or an idea to improve this application? Send me a message!", + "suggestion_placeholder": "Type your message here...", + "send_suggestion": "Send", + "sending": "Sending...", + "success": "Success", + "error": "Error", + "suggestion_sent_success": "Your message has been sent successfully!", + "suggestion_send_error": "Error sending message. Try again later." } \ No newline at end of file diff --git a/data/lang/fr.json b/data/lang/fr.json index fd8150a..7897f6c 100644 --- a/data/lang/fr.json +++ b/data/lang/fr.json @@ -6,5 +6,13 @@ "settings": "Paramètres", "theme": "Thème :", "dark_theme": "Thème Sombre", - "light_theme": "Thème Clair" + "light_theme": "Thème Clair", + "suggestion_text": "Vous avez une question ou une idée pour améliorer cette application ? Envoyez-moi un message !", + "suggestion_placeholder": "Tapez votre message ici...", + "send_suggestion": "Envoyer", + "sending": "Envoi...", + "success": "Succès", + "error": "Erreur", + "suggestion_sent_success": "Votre message a été envoyé avec succès !", + "suggestion_send_error": "Erreur lors de l'envoi du message. Essayez à nouveau plus tard." } \ No newline at end of file diff --git a/data/others/defaults_settings.json b/data/others/defaults_settings.json index 64a915d..639890e 100644 --- a/data/others/defaults_settings.json +++ b/data/others/defaults_settings.json @@ -2,6 +2,5 @@ "theme": "dark", "lang": "fr", "window_size": {"width": 1000, "height": 600}, - "fullscreen": true, - "fullscreen_size": {"width": 1000, "height": 600} + "maximize": true } \ No newline at end of file