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