alert_manager and improvements
This commit is contained in:
parent
508ba8461a
commit
dd43bf5abf
@ -59,7 +59,7 @@ exe = EXE(
|
|||||||
upx=True,
|
upx=True,
|
||||||
upx_exclude=[],
|
upx_exclude=[],
|
||||||
runtime_tmpdir=None,
|
runtime_tmpdir=None,
|
||||||
console=True,
|
console=False,
|
||||||
disable_windowed_traceback=False,
|
disable_windowed_traceback=False,
|
||||||
argv_emulation=False,
|
argv_emulation=False,
|
||||||
target_arch=None,
|
target_arch=None,
|
||||||
|
20
app/core/alert_manager.py
Normal file
20
app/core/alert_manager.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from PyQt6.QtWidgets import QMessageBox
|
||||||
|
|
||||||
|
class AlertManager:
|
||||||
|
|
||||||
|
def __init__(self, language_manager, theme_manager) -> None:
|
||||||
|
self.language_manager = language_manager
|
||||||
|
self.theme_manager = theme_manager
|
||||||
|
|
||||||
|
def show_success(self, success_key: str, parent=None) -> None:
|
||||||
|
success_title = self.language_manager.get_text("success")
|
||||||
|
success_text = self.language_manager.get_text(success_key)
|
||||||
|
|
||||||
|
QMessageBox.information(parent, success_title, success_text)
|
||||||
|
|
||||||
|
def show_error(self, error_key: str, parent=None) -> None:
|
||||||
|
|
||||||
|
error_title = self.language_manager.get_text("error")
|
||||||
|
error_text = self.language_manager.get_text(error_key)
|
||||||
|
|
||||||
|
QMessageBox.critical(parent, error_title, error_text)
|
@ -2,6 +2,7 @@ from json import JSONDecodeError, load
|
|||||||
from os import listdir, path
|
from os import listdir, path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
import app.utils.paths as paths
|
import app.utils.paths as paths
|
||||||
|
from app.core.settings_manager import SettingsManager
|
||||||
|
|
||||||
class LanguageManager:
|
class LanguageManager:
|
||||||
"""
|
"""
|
||||||
@ -9,9 +10,9 @@ class LanguageManager:
|
|||||||
Charge les fichiers JSON de langue dans data/lang/, permet
|
Charge les fichiers JSON de langue dans data/lang/, permet
|
||||||
de changer la langue courante, et récupérer des textes traduits.
|
de changer la langue courante, et récupérer des textes traduits.
|
||||||
"""
|
"""
|
||||||
def __init__(self, settings_manager) -> None:
|
def __init__(self, settings_manager: SettingsManager) -> None:
|
||||||
self.translations: Dict[str, Dict[str, str]] = {}
|
self.translations: Dict[str, Dict[str, str]] = {}
|
||||||
self.settings_manager = settings_manager
|
self.settings_manager: SettingsManager = settings_manager
|
||||||
|
|
||||||
self.load_all_translations()
|
self.load_all_translations()
|
||||||
|
|
||||||
@ -19,12 +20,12 @@ class LanguageManager:
|
|||||||
"""
|
"""
|
||||||
Charge tous les fichiers JSON dans data/lang/ comme dictionnaires.
|
Charge tous les fichiers JSON dans data/lang/ comme dictionnaires.
|
||||||
"""
|
"""
|
||||||
lang_dir = paths.get_lang_path()
|
lang_dir: str = paths.get_lang_path()
|
||||||
|
|
||||||
for filename in listdir(lang_dir):
|
for filename in listdir(lang_dir):
|
||||||
if filename.endswith(".json"):
|
if filename.endswith(".json"):
|
||||||
lang_code = filename[:-5] # strip .json
|
lang_code: str = filename[:-5] # strip .json
|
||||||
file_path = path.join(lang_dir, filename)
|
file_path: str = path.join(lang_dir, filename)
|
||||||
try:
|
try:
|
||||||
with open(file_path, "r", encoding="utf-8") as f:
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
self.translations[lang_code] = load(f)
|
self.translations[lang_code] = load(f)
|
||||||
|
@ -2,31 +2,40 @@ from app.core.observer_manager import ObserverManager, NotificationType
|
|||||||
from app.core.language_manager import LanguageManager
|
from app.core.language_manager import LanguageManager
|
||||||
from app.core.theme_manager import ThemeManager
|
from app.core.theme_manager import ThemeManager
|
||||||
from app.core.settings_manager import SettingsManager
|
from app.core.settings_manager import SettingsManager
|
||||||
|
from app.core.alert_manager import AlertManager
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
class MainManager:
|
class MainManager:
|
||||||
_instance = None
|
_instance: Optional['MainManager'] = None
|
||||||
def __init__(self):
|
|
||||||
|
def __init__(self) -> None:
|
||||||
if MainManager._instance is not None:
|
if MainManager._instance is not None:
|
||||||
raise Exception("This class is a singleton!")
|
raise Exception("This class is a singleton!")
|
||||||
else:
|
else:
|
||||||
MainManager._instance = self
|
MainManager._instance = self
|
||||||
self.observer_manager = ObserverManager()
|
self.observer_manager: ObserverManager = ObserverManager()
|
||||||
self.theme_manager = ThemeManager()
|
self.theme_manager: ThemeManager = ThemeManager()
|
||||||
self.settings_manager = SettingsManager(self.observer_manager,self.theme_manager)
|
self.settings_manager: SettingsManager = SettingsManager(self.observer_manager, self.theme_manager)
|
||||||
self.language_manager = LanguageManager(self.settings_manager)
|
self.language_manager: LanguageManager = LanguageManager(self.settings_manager)
|
||||||
|
self.alert_manager: AlertManager = AlertManager(self.language_manager, self.theme_manager)
|
||||||
|
|
||||||
def get_observer_manager(self):
|
|
||||||
return self.observer_manager
|
|
||||||
def get_theme_manager(self):
|
|
||||||
return self.theme_manager
|
|
||||||
def get_settings_manager(self):
|
|
||||||
return self.settings_manager
|
|
||||||
def get_language_manager(self):
|
|
||||||
return self.language_manager
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_instance(cls):
|
def get_instance(cls) -> 'MainManager':
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = cls()
|
cls._instance = cls()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
|
def get_observer_manager(self) -> ObserverManager:
|
||||||
|
return self.observer_manager
|
||||||
|
|
||||||
|
def get_theme_manager(self) -> ThemeManager:
|
||||||
|
return self.theme_manager
|
||||||
|
|
||||||
|
def get_settings_manager(self) -> SettingsManager:
|
||||||
|
return self.settings_manager
|
||||||
|
|
||||||
|
def get_language_manager(self) -> LanguageManager:
|
||||||
|
return self.language_manager
|
||||||
|
|
||||||
|
def get_alert_manager(self) -> AlertManager:
|
||||||
|
return self.alert_manager
|
@ -1,65 +1,210 @@
|
|||||||
from PyQt6.QtCore import QSettings
|
from PyQt6.QtCore import QSettings
|
||||||
from app.core.observer_manager import NotificationType
|
from app.core.observer_manager import ObserverManager, NotificationType
|
||||||
|
from app.core.theme_manager import ThemeManager
|
||||||
from os import path
|
from os import path
|
||||||
import app.utils.paths as paths
|
import app.utils.paths as paths
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
from typing import Dict, Any, Union
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logger: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SettingsManager:
|
class SettingsManager:
|
||||||
"""
|
"""
|
||||||
Gestion des paramètres utilisateurs avec sauvegarde persistante via QSettings.
|
Gestion des paramètres utilisateurs avec sauvegarde persistante via QSettings.
|
||||||
Notifie les changements via ObserverManager.
|
Notifie les changements via ObserverManager.
|
||||||
"""
|
"""
|
||||||
def __init__(self, observer_manager, theme_manager):
|
def __init__(self, observer_manager: ObserverManager, theme_manager: ThemeManager) -> None:
|
||||||
self.observer_manager = observer_manager
|
self.observer_manager: ObserverManager = observer_manager
|
||||||
self.theme_manager = theme_manager
|
self.theme_manager: ThemeManager = theme_manager
|
||||||
|
|
||||||
# Load default settings from JSON file
|
# Hardcoded fallback settings in case files are missing
|
||||||
defaults_path = path.join(paths.get_data_dir(), "others", "defaults_settings.json")
|
self.fallback_settings: Dict[str, Any] = {
|
||||||
with open(defaults_path, 'r', encoding='utf-8') as f:
|
"theme": "dark",
|
||||||
self.default_settings = json.load(f)
|
"lang": "en",
|
||||||
|
"window_size": {"width": 1000, "height": 600},
|
||||||
with open(paths.resource_path("config.json"), 'r', encoding='utf-8') as f:
|
"maximized": False
|
||||||
self.config = json.load(f)
|
}
|
||||||
|
|
||||||
|
self.fallback_config: Dict[str, Any] = {
|
||||||
|
"app_name": "Application",
|
||||||
|
"python_version": "3.11.7",
|
||||||
|
"app_os": "Windows",
|
||||||
|
"app_version": "1.0.0",
|
||||||
|
"architecture": "x64",
|
||||||
|
"icon_path": "data/assets/icon.ico",
|
||||||
|
"main_script": "main.py",
|
||||||
|
"git_repo": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load default settings
|
||||||
|
self.default_settings: Dict[str, Any] = self._load_default_settings()
|
||||||
|
# Load config
|
||||||
|
self.config: Dict[str, Any] = self._load_config()
|
||||||
|
|
||||||
|
# Initialize QSettings with error handling
|
||||||
|
self.settings: QSettings = self._initialize_qsettings()
|
||||||
|
|
||||||
self.settings = QSettings(path.join(paths.get_user_data_dir(self.get_config("app_name")), self.get_config("app_name") + ".ini"), QSettings.Format.IniFormat)
|
# Set initial theme
|
||||||
|
try:
|
||||||
|
self.theme_manager.set_theme(self.get_theme())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting initial theme: {e}")
|
||||||
|
self.theme_manager.set_theme(self.fallback_settings["theme"])
|
||||||
|
|
||||||
self.theme_manager.set_theme(self.get_theme())
|
def _load_default_settings(self) -> Dict[str, Any]:
|
||||||
|
"""Load default settings with error handling"""
|
||||||
|
try:
|
||||||
|
defaults_path = path.join(paths.get_data_dir(), "others", "defaults_settings.json")
|
||||||
|
if path.exists(defaults_path):
|
||||||
|
with open(defaults_path, 'r', encoding='utf-8') as f:
|
||||||
|
settings = json.load(f)
|
||||||
|
# Validate required keys
|
||||||
|
for key in self.fallback_settings.keys():
|
||||||
|
if key not in settings:
|
||||||
|
logger.warning(f"Missing key '{key}' in defaults_settings.json, using fallback")
|
||||||
|
settings[key] = self.fallback_settings[key]
|
||||||
|
return settings
|
||||||
|
else:
|
||||||
|
logger.warning(f"defaults_settings.json not found, using fallback settings")
|
||||||
|
return self.fallback_settings.copy()
|
||||||
|
except (json.JSONDecodeError, FileNotFoundError, KeyError, UnicodeDecodeError) as e:
|
||||||
|
logger.error(f"Error loading default settings: {e}")
|
||||||
|
return self.fallback_settings.copy()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error loading default settings: {e}")
|
||||||
|
return self.fallback_settings.copy()
|
||||||
|
|
||||||
|
def _load_config(self) -> Dict[str, Any]:
|
||||||
|
"""Load config.json with error handling"""
|
||||||
|
try:
|
||||||
|
config_path = paths.resource_path("config.json")
|
||||||
|
if path.exists(config_path):
|
||||||
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
# Validate required keys
|
||||||
|
for key in self.fallback_config.keys():
|
||||||
|
if key not in config:
|
||||||
|
logger.warning(f"Missing key '{key}' in config.json, using fallback")
|
||||||
|
config[key] = self.fallback_config[key]
|
||||||
|
return config
|
||||||
|
else:
|
||||||
|
logger.warning("config.json not found, using fallback config")
|
||||||
|
return self.fallback_config.copy()
|
||||||
|
except (json.JSONDecodeError, FileNotFoundError, KeyError, UnicodeDecodeError) as e:
|
||||||
|
logger.error(f"Error loading config: {e}")
|
||||||
|
return self.fallback_config.copy()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error loading config: {e}")
|
||||||
|
return self.fallback_config.copy()
|
||||||
|
|
||||||
|
def _initialize_qsettings(self) -> QSettings:
|
||||||
|
"""Initialize QSettings with error handling"""
|
||||||
|
try:
|
||||||
|
app_name = self.get_config("app_name")
|
||||||
|
user_data_dir = paths.get_user_data_dir(app_name)
|
||||||
|
settings_path = path.join(user_data_dir, f"{app_name}.ini")
|
||||||
|
return QSettings(settings_path, QSettings.Format.IniFormat)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error initializing QSettings: {e}")
|
||||||
|
# Fallback to memory-only settings
|
||||||
|
return QSettings()
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
def get_config(self, key: str):
|
def get_config(self, key: str) -> Any:
|
||||||
return self.config.get(key, None)
|
"""Get configuration value by key"""
|
||||||
|
try:
|
||||||
|
return self.config.get(key, self.fallback_config.get(key))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting config key '{key}': {e}")
|
||||||
|
return self.fallback_config.get(key)
|
||||||
|
|
||||||
# Theme
|
# Theme
|
||||||
def get_theme(self) -> str:
|
def get_theme(self) -> str:
|
||||||
return self.settings.value("theme", self.default_settings.get("theme"))
|
"""Get the current theme"""
|
||||||
|
try:
|
||||||
|
return self.settings.value("theme", self.default_settings.get("theme"))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting theme: {e}")
|
||||||
|
return self.fallback_settings["theme"]
|
||||||
|
|
||||||
def set_theme(self, mode: str) -> None:
|
def set_theme(self, mode: str) -> None:
|
||||||
if mode != self.get_theme():
|
"""Set the application theme"""
|
||||||
self.settings.setValue("theme", mode)
|
try:
|
||||||
self.theme_manager.set_theme(mode)
|
if mode != self.get_theme():
|
||||||
self.observer_manager.notify(NotificationType.THEME)
|
self.settings.setValue("theme", mode)
|
||||||
|
self.theme_manager.set_theme(mode)
|
||||||
|
self.observer_manager.notify(NotificationType.THEME)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting theme: {e}")
|
||||||
|
|
||||||
# Language
|
# Language
|
||||||
def get_language(self) -> str:
|
def get_language(self) -> str:
|
||||||
return self.settings.value("lang", self.default_settings.get("lang"))
|
"""Get the current language"""
|
||||||
|
try:
|
||||||
|
return self.settings.value("lang", self.default_settings.get("lang"))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting language: {e}")
|
||||||
|
return self.fallback_settings["lang"]
|
||||||
|
|
||||||
def set_language(self, lang_code: str) -> None:
|
def set_language(self, lang_code: str) -> None:
|
||||||
if lang_code != self.get_language():
|
"""Set the application language"""
|
||||||
self.settings.setValue("lang", lang_code)
|
try:
|
||||||
self.observer_manager.notify(NotificationType.LANGUAGE)
|
if lang_code != self.get_language():
|
||||||
|
self.settings.setValue("lang", lang_code)
|
||||||
|
self.observer_manager.notify(NotificationType.LANGUAGE)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting language: {e}")
|
||||||
|
|
||||||
# Window size and maximized
|
# Window size and maximized
|
||||||
def get_window_size(self) -> dict:
|
def get_window_size(self) -> Dict[str, int]:
|
||||||
return self.settings.value("window_size", self.default_settings.get("window_size"))
|
"""Get the window size"""
|
||||||
|
try:
|
||||||
|
size = self.settings.value("window_size", self.default_settings.get("window_size"))
|
||||||
|
# Validate window size values
|
||||||
|
if isinstance(size, dict) and "width" in size and "height" in size:
|
||||||
|
width = int(size["width"]) if size["width"] > 0 else self.fallback_settings["window_size"]["width"]
|
||||||
|
height = int(size["height"]) if size["height"] > 0 else self.fallback_settings["window_size"]["height"]
|
||||||
|
return {"width": width, "height": height}
|
||||||
|
else:
|
||||||
|
return self.fallback_settings["window_size"].copy()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting window size: {e}")
|
||||||
|
return self.fallback_settings["window_size"].copy()
|
||||||
|
|
||||||
def set_window_size(self, width: int, height: int) -> None:
|
def set_window_size(self, width: int, height: int) -> None:
|
||||||
current_size = self.get_window_size()
|
"""Set the window size"""
|
||||||
if current_size["width"] != width or current_size["height"] != height:
|
try:
|
||||||
new_size = {"width": width, "height": height}
|
# Validate input values
|
||||||
self.settings.setValue("window_size", new_size)
|
if width <= 0 or height <= 0:
|
||||||
|
logger.warning(f"Invalid window size: {width}x{height}")
|
||||||
|
return
|
||||||
|
|
||||||
|
current_size = self.get_window_size()
|
||||||
|
if current_size["width"] != width or current_size["height"] != height:
|
||||||
|
new_size = {"width": width, "height": height}
|
||||||
|
self.settings.setValue("window_size", new_size)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting window size: {e}")
|
||||||
|
|
||||||
def get_maximized(self) -> bool:
|
def get_maximized(self) -> bool:
|
||||||
return self.settings.value("maximized", self.default_settings.get("maximized")) == "true"
|
"""Check if the window is maximized"""
|
||||||
|
try:
|
||||||
|
value = self.settings.value("maximized", self.default_settings.get("maximized"))
|
||||||
|
if isinstance(value, str):
|
||||||
|
return value.lower() == "true"
|
||||||
|
elif isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return self.fallback_settings["maximized"]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting maximized state: {e}")
|
||||||
|
return self.fallback_settings["maximized"]
|
||||||
|
|
||||||
def set_maximized(self, maximized: bool):
|
def set_maximized(self, maximized: bool) -> None:
|
||||||
self.settings.setValue("maximized", maximized)
|
"""Set the window maximized state"""
|
||||||
|
try:
|
||||||
|
self.settings.setValue("maximized", maximized)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting maximized state: {e}")
|
@ -1,32 +1,35 @@
|
|||||||
import app.utils.paths as paths
|
import app.utils.paths as paths
|
||||||
import os, json
|
import os, json
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
||||||
class Theme:
|
class Theme:
|
||||||
def __init__(self, name: str, colors: dict):
|
def __init__(self, name: str, colors: Dict[str, str]) -> None:
|
||||||
self.name = name
|
self.name: str = name
|
||||||
self.colors = colors
|
self.colors: Dict[str, str] = colors
|
||||||
|
|
||||||
def get_color(self, element: str) -> str:
|
def get_color(self, element: str) -> str:
|
||||||
return self.colors.get(element, "#FFFFFF")
|
return self.colors.get(element, "#FFFFFF")
|
||||||
|
|
||||||
class ThemeManager:
|
class ThemeManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
theme_path = os.path.join(paths.get_data_dir(), "themes")
|
theme_path: str = os.path.join(paths.get_data_dir(), "themes")
|
||||||
self.themes = []
|
self.themes: List[Theme] = []
|
||||||
for theme_file in os.listdir(theme_path):
|
for theme_file in os.listdir(theme_path):
|
||||||
if theme_file.endswith(".json"):
|
if theme_file.endswith(".json"):
|
||||||
with open(os.path.join(theme_path, theme_file), 'r', encoding='utf-8') as f:
|
with open(os.path.join(theme_path, theme_file), 'r', encoding='utf-8') as f:
|
||||||
theme_data = json.load(f)
|
theme_data: Dict[str, Any] = json.load(f)
|
||||||
theme = Theme(theme_data["theme_name"], theme_data["colors"])
|
theme: Theme = Theme(theme_data["theme_name"], theme_data["colors"])
|
||||||
self.themes.append(theme)
|
self.themes.append(theme)
|
||||||
self.current_theme = self.themes[0]
|
self.current_theme: Theme = self.themes[0]
|
||||||
|
|
||||||
def set_theme(self, theme: str) -> None:
|
def set_theme(self, theme: str) -> None:
|
||||||
if theme != self.current_theme.name:
|
if theme != self.current_theme.name:
|
||||||
self.current_theme = next((t for t in self.themes if t.name == theme), self.current_theme)
|
found_theme: Optional[Theme] = next((t for t in self.themes if t.name == theme), None)
|
||||||
|
if found_theme:
|
||||||
|
self.current_theme = found_theme
|
||||||
|
|
||||||
def get_theme(self) -> str:
|
def get_theme(self) -> Theme:
|
||||||
return self.current_theme
|
return self.current_theme
|
||||||
|
|
||||||
def get_sheet(self) -> str:
|
def get_sheet(self) -> str:
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
from PyQt6.QtWidgets import QApplication, QMainWindow
|
from PyQt6.QtWidgets import QApplication, QMainWindow
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon, QResizeEvent, QCloseEvent
|
||||||
from PyQt6.QtCore import QSize
|
from PyQt6.QtCore import QSize, QEvent
|
||||||
from app.core.main_manager import MainManager, NotificationType
|
from app.core.main_manager import MainManager, NotificationType
|
||||||
from app.ui.widgets.tabs_widget import TabsWidget, MenuDirection, ButtonPosition, BorderSide
|
from app.ui.widgets.tabs_widget import TabsWidget, MenuDirection, ButtonPosition, BorderSide
|
||||||
from app.ui.windows.settings_window import SettingsWindow
|
from app.ui.windows.settings_window import SettingsWindow
|
||||||
from app.ui.windows.suggestion_window import SuggestionWindow
|
from app.ui.windows.suggestion_window import SuggestionWindow
|
||||||
import app.utils.paths as paths
|
import app.utils.paths as paths
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.main_manager = MainManager.get_instance()
|
self.main_manager: MainManager = MainManager.get_instance()
|
||||||
|
|
||||||
self.language_manager = self.main_manager.get_language_manager()
|
self.language_manager = self.main_manager.get_language_manager()
|
||||||
self.theme_manager = self.main_manager.get_theme_manager()
|
self.theme_manager = self.main_manager.get_theme_manager()
|
||||||
@ -19,23 +20,26 @@ class MainWindow(QMainWindow):
|
|||||||
self.observer_manager = self.main_manager.get_observer_manager()
|
self.observer_manager = self.main_manager.get_observer_manager()
|
||||||
self.observer_manager.subscribe(NotificationType.THEME, self.update_theme)
|
self.observer_manager.subscribe(NotificationType.THEME, self.update_theme)
|
||||||
|
|
||||||
self.is_maximizing = False
|
self.is_maximizing: bool = False
|
||||||
|
self.current_size: QSize
|
||||||
|
self.previous_size: QSize
|
||||||
|
|
||||||
app = QApplication.instance()
|
# UI elements
|
||||||
size = app.primaryScreen().size()
|
self.side_menu: TabsWidget
|
||||||
|
self.settings_window: SettingsWindow
|
||||||
|
self.suggestion_window: SuggestionWindow
|
||||||
|
|
||||||
|
app: Optional[QApplication] = QApplication.instance()
|
||||||
|
size: QSize = app.primaryScreen().size()
|
||||||
self.settings_manager.minScreenSize = min(size.height(),size.width())
|
self.settings_manager.minScreenSize = min(size.height(),size.width())
|
||||||
|
|
||||||
self.setMinimumSize(600, 400)
|
self.setMinimumSize(600, 400)
|
||||||
self.setWindowTitle(self.settings_manager.get_config("app_name"))
|
|
||||||
self.setStyleSheet(self.theme_manager.get_sheet())
|
|
||||||
self.setWindowIcon(QIcon(paths.get_asset_path("icon")))
|
|
||||||
|
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
self.apply_saved_window_state()
|
self.apply_saved_window_state()
|
||||||
|
|
||||||
def apply_saved_window_state(self):
|
def apply_saved_window_state(self) -> None:
|
||||||
"""Apply saved window size and maximized state"""
|
"""Apply saved window size and maximized state"""
|
||||||
window_size = self.settings_manager.get_window_size()
|
window_size: dict = self.settings_manager.get_window_size()
|
||||||
self.current_size = QSize(window_size["width"], window_size["height"])
|
self.current_size = QSize(window_size["width"], window_size["height"])
|
||||||
self.previous_size = QSize(window_size["width"], window_size["height"])
|
self.previous_size = QSize(window_size["width"], window_size["height"])
|
||||||
self.resize(self.current_size)
|
self.resize(self.current_size)
|
||||||
@ -43,7 +47,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.is_maximizing = True
|
self.is_maximizing = True
|
||||||
self.showMaximized()
|
self.showMaximized()
|
||||||
|
|
||||||
def changeEvent(self, event):
|
def changeEvent(self, event: QEvent) -> None:
|
||||||
"""Handle window state changes"""
|
"""Handle window state changes"""
|
||||||
super().changeEvent(event)
|
super().changeEvent(event)
|
||||||
if event.type() == event.Type.WindowStateChange:
|
if event.type() == event.Type.WindowStateChange:
|
||||||
@ -56,13 +60,13 @@ class MainWindow(QMainWindow):
|
|||||||
self.current_size = self.previous_size
|
self.current_size = self.previous_size
|
||||||
self.settings_manager.set_maximized(self.isMaximized())
|
self.settings_manager.set_maximized(self.isMaximized())
|
||||||
|
|
||||||
def resizeEvent(self, a0):
|
def resizeEvent(self, a0: QResizeEvent) -> None:
|
||||||
# Ne pas sauvegarder la taille si on est en train de maximiser
|
# Ne pas sauvegarder la taille si on est en train de maximiser
|
||||||
if not self.isMaximized() and not self.is_maximizing:
|
if not self.isMaximized() and not self.is_maximizing:
|
||||||
self.previous_size = self.current_size
|
self.previous_size = self.current_size
|
||||||
self.current_size = self.size()
|
self.current_size = self.size()
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event: QCloseEvent) -> None:
|
||||||
"""Handle application close event"""
|
"""Handle application close event"""
|
||||||
super().closeEvent(event)
|
super().closeEvent(event)
|
||||||
# si la difference de taille est plus grande que 10 pixels, enregistrer previoussize
|
# si la difference de taille est plus grande que 10 pixels, enregistrer previoussize
|
||||||
@ -73,7 +77,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.current_size.height()
|
self.current_size.height()
|
||||||
)
|
)
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self) -> None:
|
||||||
self.side_menu = TabsWidget(self,MenuDirection.HORIZONTAL,70,None,10,1,BorderSide.TOP)
|
self.side_menu = TabsWidget(self,MenuDirection.HORIZONTAL,70,None,10,1,BorderSide.TOP)
|
||||||
|
|
||||||
self.settings_window = SettingsWindow(self)
|
self.settings_window = SettingsWindow(self)
|
||||||
@ -84,5 +88,5 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
self.setCentralWidget(self.side_menu)
|
self.setCentralWidget(self.side_menu)
|
||||||
|
|
||||||
def update_theme(self):
|
def update_theme(self) -> None:
|
||||||
self.setStyleSheet(self.theme_manager.get_sheet())
|
self.setStyleSheet(self.theme_manager.get_sheet())
|
@ -1,21 +1,30 @@
|
|||||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QLabel, QHBoxLayout
|
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QLabel, QHBoxLayout
|
||||||
from PyQt6.QtCore import Qt
|
from PyQt6.QtCore import Qt
|
||||||
from app.core.main_manager import MainManager, NotificationType
|
from app.core.main_manager import MainManager, NotificationType
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
class SettingsWindow(QWidget):
|
class SettingsWindow(QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent: Optional[QWidget] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.main_manager = MainManager.get_instance()
|
self.main_manager: MainManager = MainManager.get_instance()
|
||||||
self.language_manager = self.main_manager.get_language_manager()
|
self.language_manager = self.main_manager.get_language_manager()
|
||||||
self.settings_manager = self.main_manager.get_settings_manager()
|
self.settings_manager = self.main_manager.get_settings_manager()
|
||||||
|
|
||||||
self.observer_manager = self.main_manager.get_observer_manager()
|
self.observer_manager = self.main_manager.get_observer_manager()
|
||||||
self.observer_manager.subscribe(NotificationType.LANGUAGE, self.update_language)
|
self.observer_manager.subscribe(NotificationType.LANGUAGE, self.update_language)
|
||||||
|
|
||||||
|
# Type hints for UI elements
|
||||||
|
self.language_layout: QHBoxLayout
|
||||||
|
self.languageLabel: QLabel
|
||||||
|
self.languageCombo: QComboBox
|
||||||
|
self.theme_layout: QHBoxLayout
|
||||||
|
self.themeLabel: QLabel
|
||||||
|
self.themeCombo: QComboBox
|
||||||
|
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self) -> None:
|
||||||
layout = QVBoxLayout(self)
|
layout: QVBoxLayout = QVBoxLayout(self)
|
||||||
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||||
layout.setSpacing(20)
|
layout.setSpacing(20)
|
||||||
layout.setContentsMargins(20, 20, 20, 20)
|
layout.setContentsMargins(20, 20, 20, 20)
|
||||||
@ -32,7 +41,6 @@ class SettingsWindow(QWidget):
|
|||||||
layout.addLayout(self.language_layout)
|
layout.addLayout(self.language_layout)
|
||||||
|
|
||||||
# Paramètres de thème
|
# Paramètres de thème
|
||||||
|
|
||||||
self.theme_layout = QHBoxLayout()
|
self.theme_layout = QHBoxLayout()
|
||||||
|
|
||||||
self.themeLabel = QLabel(self.language_manager.get_text("theme"), self)
|
self.themeLabel = QLabel(self.language_manager.get_text("theme"), self)
|
||||||
@ -46,8 +54,8 @@ class SettingsWindow(QWidget):
|
|||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
def createLanguageSelector(self):
|
def createLanguageSelector(self) -> QComboBox:
|
||||||
combo = QComboBox()
|
combo: QComboBox = QComboBox()
|
||||||
# Ajouter toutes les langues disponibles
|
# Ajouter toutes les langues disponibles
|
||||||
for langCode, langData in self.language_manager.translations.items():
|
for langCode, langData in self.language_manager.translations.items():
|
||||||
combo.addItem(langData["lang_name"], langCode)
|
combo.addItem(langData["lang_name"], langCode)
|
||||||
@ -59,8 +67,8 @@ class SettingsWindow(QWidget):
|
|||||||
|
|
||||||
return combo
|
return combo
|
||||||
|
|
||||||
def createThemeSelector(self):
|
def createThemeSelector(self) -> QComboBox:
|
||||||
combo = QComboBox()
|
combo: QComboBox = QComboBox()
|
||||||
|
|
||||||
# Ajouter les options de thème
|
# Ajouter les options de thème
|
||||||
combo.addItem(self.language_manager.get_text("light_theme"), "light")
|
combo.addItem(self.language_manager.get_text("light_theme"), "light")
|
||||||
@ -73,14 +81,14 @@ class SettingsWindow(QWidget):
|
|||||||
|
|
||||||
return combo
|
return combo
|
||||||
|
|
||||||
def change_language(self, index):
|
def change_language(self, index: int) -> None:
|
||||||
self.settings_manager.set_language(self.languageCombo.itemData(index))
|
self.settings_manager.set_language(self.languageCombo.itemData(index))
|
||||||
|
|
||||||
def change_theme(self, index):
|
def change_theme(self, index: int) -> None:
|
||||||
theme = self.themeCombo.itemData(index)
|
theme: str = self.themeCombo.itemData(index)
|
||||||
self.settings_manager.set_theme(theme)
|
self.settings_manager.set_theme(theme)
|
||||||
|
|
||||||
def update_language(self):
|
def update_language(self) -> None:
|
||||||
self.languageLabel.setText(self.language_manager.get_text("language"))
|
self.languageLabel.setText(self.language_manager.get_text("language"))
|
||||||
self.themeLabel.setText(self.language_manager.get_text("theme"))
|
self.themeLabel.setText(self.language_manager.get_text("theme"))
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QPushButton, QLabel, QMessageBox, QHBoxLayout
|
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QPushButton, QLabel, QMessageBox, QHBoxLayout
|
||||||
from PyQt6.QtCore import Qt, QThread, pyqtSignal
|
from PyQt6.QtCore import Qt, QThread, pyqtSignal
|
||||||
import time
|
import smtplib, os
|
||||||
import smtplib
|
|
||||||
import os
|
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from app.core.main_manager import MainManager, NotificationType
|
from app.core.main_manager import MainManager, NotificationType
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
# Load environment variables from .env file
|
# Load environment variables from .env file
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
@ -15,25 +14,25 @@ class EmailSender(QThread):
|
|||||||
success = pyqtSignal()
|
success = pyqtSignal()
|
||||||
error = pyqtSignal(str)
|
error = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, subject, message):
|
def __init__(self, subject: str, message: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.subject = subject
|
self.subject: str = subject
|
||||||
self.message = message
|
self.message: str = message
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
try:
|
try:
|
||||||
# Get email configuration from environment variables
|
# Get email configuration from environment variables
|
||||||
email = os.getenv('EMAIL_ADDRESS')
|
email: Optional[str] = os.getenv('EMAIL_ADDRESS')
|
||||||
password = os.getenv('EMAIL_PASSWORD')
|
password: Optional[str] = os.getenv('EMAIL_PASSWORD')
|
||||||
smtp_server = os.getenv('EMAIL_SMTP_SERVER', 'smtp.gmail.com')
|
smtp_server: str = os.getenv('EMAIL_SMTP_SERVER', 'smtp.gmail.com')
|
||||||
smtp_port = int(os.getenv('EMAIL_SMTP_PORT', '587'))
|
smtp_port: int = int(os.getenv('EMAIL_SMTP_PORT', '587'))
|
||||||
|
|
||||||
if not email or not password:
|
if not email or not password:
|
||||||
self.error.emit("password")
|
self.error.emit("password")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create message
|
# Create message
|
||||||
msg = MIMEMultipart()
|
msg: MIMEMultipart = MIMEMultipart()
|
||||||
msg['From'] = email
|
msg['From'] = email
|
||||||
msg['To'] = email
|
msg['To'] = email
|
||||||
msg['Subject'] = self.subject
|
msg['Subject'] = self.subject
|
||||||
@ -42,64 +41,69 @@ class EmailSender(QThread):
|
|||||||
msg.attach(MIMEText(self.message, 'plain'))
|
msg.attach(MIMEText(self.message, 'plain'))
|
||||||
|
|
||||||
# Create SMTP session
|
# Create SMTP session
|
||||||
server = smtplib.SMTP(smtp_server, smtp_port)
|
server: smtplib.SMTP = smtplib.SMTP(smtp_server, smtp_port)
|
||||||
server.starttls() # Enable TLS encryption
|
server.starttls() # Enable TLS encryption
|
||||||
# Login with app password
|
# Login with app password
|
||||||
server.login(email, password)
|
server.login(email, password)
|
||||||
# Send email
|
# Send email
|
||||||
text = msg.as_string()
|
text: str = msg.as_string()
|
||||||
server.sendmail(email, email, text)
|
server.sendmail(email, email, text)
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
||||||
self.success.emit()
|
self.success.emit()
|
||||||
|
except smtplib.SMTPAuthenticationError:
|
||||||
|
self.error.emit("email_credentials_error")
|
||||||
except Exception:
|
except Exception:
|
||||||
self.error.emit("")
|
self.error.emit("suggestion_send_error")
|
||||||
|
|
||||||
class SuggestionWindow(QWidget):
|
class SuggestionWindow(QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent: Optional[QWidget] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.main_manager = MainManager.get_instance()
|
self.main_manager: MainManager = MainManager.get_instance()
|
||||||
self.language_manager = self.main_manager.get_language_manager()
|
self.language_manager = self.main_manager.get_language_manager()
|
||||||
self.settings_manager = self.main_manager.get_settings_manager()
|
self.settings_manager = self.main_manager.get_settings_manager()
|
||||||
|
self.alert_manager = self.main_manager.get_alert_manager()
|
||||||
|
|
||||||
self.observer_manager = self.main_manager.get_observer_manager()
|
self.observer_manager = self.main_manager.get_observer_manager()
|
||||||
self.observer_manager.subscribe(NotificationType.LANGUAGE, self.update_language)
|
self.observer_manager.subscribe(NotificationType.LANGUAGE, self.update_language)
|
||||||
|
|
||||||
|
self.email_sender: Optional[EmailSender] = None
|
||||||
|
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self) -> None:
|
||||||
layout = QVBoxLayout(self)
|
layout: QVBoxLayout = QVBoxLayout(self)
|
||||||
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||||
layout.setSpacing(20)
|
layout.setSpacing(20)
|
||||||
layout.setContentsMargins(20, 20, 20, 20)
|
layout.setContentsMargins(20, 20, 20, 20)
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
self.title_label = QLabel(self.language_manager.get_text("suggestion_text"), self)
|
self.title_label: QLabel = QLabel(self.language_manager.get_text("suggestion_text"), self)
|
||||||
self.title_label.setStyleSheet("font-size: 18px; font-weight: bold; margin-bottom: 10px;")
|
self.title_label.setStyleSheet("font-size: 18px; font-weight: bold; margin-bottom: 10px;")
|
||||||
layout.addWidget(self.title_label)
|
layout.addWidget(self.title_label)
|
||||||
|
|
||||||
# Text area for suggestion
|
# Text area for suggestion
|
||||||
self.text_edit = QTextEdit(self)
|
self.text_edit: QTextEdit = QTextEdit(self)
|
||||||
self.text_edit.setPlaceholderText(self.language_manager.get_text("suggestion_placeholder"))
|
self.text_edit.setPlaceholderText(self.language_manager.get_text("suggestion_placeholder"))
|
||||||
self.text_edit.setMinimumHeight(200)
|
self.text_edit.setMinimumHeight(200)
|
||||||
layout.addWidget(self.text_edit)
|
layout.addWidget(self.text_edit)
|
||||||
|
|
||||||
# Button layout
|
# Button layout
|
||||||
button_layout = QHBoxLayout()
|
button_layout: QHBoxLayout = QHBoxLayout()
|
||||||
button_layout.addStretch()
|
button_layout.addStretch()
|
||||||
|
|
||||||
# Send button
|
# Send button
|
||||||
self.send_button = QPushButton(self.language_manager.get_text("send_suggestion"), self)
|
self.send_button: QPushButton = QPushButton(self.language_manager.get_text("send_suggestion"), self)
|
||||||
self.send_button.clicked.connect(self.send_suggestion)
|
self.send_button.clicked.connect(self.send_suggestion)
|
||||||
button_layout.addWidget(self.send_button)
|
button_layout.addWidget(self.send_button)
|
||||||
|
|
||||||
layout.addLayout(button_layout)
|
layout.addLayout(button_layout)
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
def send_suggestion(self):
|
def send_suggestion(self) -> None:
|
||||||
message = self.text_edit.toPlainText().strip()
|
message: str = self.text_edit.toPlainText().strip()
|
||||||
|
|
||||||
if not message:
|
if len(message)<15:
|
||||||
|
self.alert_manager.show_error("suggestion_too_short")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Disable send button during sending
|
# Disable send button during sending
|
||||||
@ -107,7 +111,7 @@ class SuggestionWindow(QWidget):
|
|||||||
self.send_button.setText(self.language_manager.get_text("sending"))
|
self.send_button.setText(self.language_manager.get_text("sending"))
|
||||||
|
|
||||||
# Create subject with app name
|
# Create subject with app name
|
||||||
subject = f"Suggestion for {self.settings_manager.get_config('app_name')}"
|
subject: str = f"Suggestion for {self.settings_manager.get_config('app_name')}"
|
||||||
|
|
||||||
# Create and start email sender thread
|
# Create and start email sender thread
|
||||||
self.email_sender = EmailSender(subject, message)
|
self.email_sender = EmailSender(subject, message)
|
||||||
@ -115,27 +119,18 @@ class SuggestionWindow(QWidget):
|
|||||||
self.email_sender.error.connect(self.on_email_error)
|
self.email_sender.error.connect(self.on_email_error)
|
||||||
self.email_sender.start()
|
self.email_sender.start()
|
||||||
|
|
||||||
def on_email_sent(self):
|
def on_email_sent(self) -> None:
|
||||||
self.send_button.setEnabled(True)
|
self.send_button.setEnabled(True)
|
||||||
self.send_button.setText(self.language_manager.get_text("send_suggestion"))
|
self.send_button.setText(self.language_manager.get_text("send_suggestion"))
|
||||||
|
self.alert_manager.show_success("suggestion_sent_success")
|
||||||
QMessageBox.information(self,
|
|
||||||
self.language_manager.get_text("success"),
|
|
||||||
self.language_manager.get_text("suggestion_sent_success"))
|
|
||||||
self.text_edit.clear()
|
self.text_edit.clear()
|
||||||
|
|
||||||
def on_email_error(self, error):
|
def on_email_error(self, error: str) -> None:
|
||||||
self.send_button.setEnabled(True)
|
self.send_button.setEnabled(True)
|
||||||
self.send_button.setText(self.language_manager.get_text("send_suggestion"))
|
self.send_button.setText(self.language_manager.get_text("send_suggestion"))
|
||||||
if error == "password":
|
self.alert_manager.show_error(error)
|
||||||
message = self.language_manager.get_text("email_credentials_error")
|
|
||||||
else:
|
|
||||||
message = self.language_manager.get_text("suggestion_send_error")
|
|
||||||
QMessageBox.critical(self,
|
|
||||||
self.language_manager.get_text("error"),
|
|
||||||
message)
|
|
||||||
|
|
||||||
def update_language(self):
|
def update_language(self) -> None:
|
||||||
self.title_label.setText(self.language_manager.get_text("suggestion_text"))
|
self.title_label.setText(self.language_manager.get_text("suggestion_text"))
|
||||||
self.text_edit.setPlaceholderText(self.language_manager.get_text("suggestion_placeholder"))
|
self.text_edit.setPlaceholderText(self.language_manager.get_text("suggestion_placeholder"))
|
||||||
self.send_button.setText(self.language_manager.get_text("send_suggestion"))
|
self.send_button.setText(self.language_manager.get_text("send_suggestion"))
|
||||||
|
@ -2,46 +2,47 @@ from os import path, getenv, mkdir
|
|||||||
import sys
|
import sys
|
||||||
from platform import system
|
from platform import system
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
def resource_path(relative_path: str) -> Path:
|
def resource_path(relative_path: str) -> str:
|
||||||
"""
|
"""
|
||||||
Get absolute path to resource, works for dev and for PyInstaller bundle.
|
Get absolute path to resource, works for dev and for PyInstaller bundle.
|
||||||
|
|
||||||
PyInstaller stores bundled files in _MEIPASS folder.
|
PyInstaller stores bundled files in _MEIPASS folder.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
base_path = Path(sys._MEIPASS) # PyInstaller temp folder
|
base_path: Path = Path(sys._MEIPASS) # PyInstaller temp folder
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
base_path = Path(__file__).parent.parent.parent # Dev environment: source/ folder
|
base_path: Path = Path(__file__).parent.parent.parent # Dev environment: source/ folder
|
||||||
|
|
||||||
return path.join(base_path, relative_path)
|
return path.join(base_path, relative_path)
|
||||||
|
|
||||||
def get_data_dir() -> Path:
|
def get_data_dir() -> str:
|
||||||
return resource_path("data")
|
return resource_path("data")
|
||||||
|
|
||||||
def get_lang_path() -> Path:
|
def get_lang_path() -> str:
|
||||||
return path.join(get_data_dir(), "lang")
|
return path.join(get_data_dir(), "lang")
|
||||||
|
|
||||||
def get_asset_path(asset: str) -> Path:
|
def get_asset_path(asset: str) -> str:
|
||||||
return path.join(get_data_dir(), "assets", f"{asset}.png")
|
return path.join(get_data_dir(), "assets", f"{asset}.png")
|
||||||
|
|
||||||
def get_asset_svg_path(asset: str) -> Path:
|
def get_asset_svg_path(asset: str) -> str:
|
||||||
return path.join(get_data_dir(), "assets", f"{asset}.svg")
|
return path.join(get_data_dir(), "assets", f"{asset}.svg")
|
||||||
|
|
||||||
def get_user_data_dir(app_name: str) -> Path:
|
def get_user_data_dir(app_name: str) -> str:
|
||||||
home = Path.home()
|
home: Path = Path.home()
|
||||||
os = system()
|
os: str = system()
|
||||||
|
|
||||||
if os == "Windows":
|
if os == "Windows":
|
||||||
appdata = getenv('APPDATA')
|
appdata: Optional[str] = getenv('APPDATA')
|
||||||
if appdata:
|
if appdata:
|
||||||
user_data_path = path.join(Path(appdata), app_name)
|
user_data_path: str = path.join(Path(appdata), app_name)
|
||||||
else:
|
else:
|
||||||
user_data_path = path.join(home, "AppData", "Roaming", app_name)
|
user_data_path: str = path.join(home, "AppData", "Roaming", app_name)
|
||||||
elif os == "Darwin":
|
elif os == "Darwin":
|
||||||
user_data_path = path.join(home, "Library", "Application Support", app_name)
|
user_data_path: str = path.join(home, "Library", "Application Support", app_name)
|
||||||
else:
|
else:
|
||||||
user_data_path = path.join(home, ".local", "share", app_name)
|
user_data_path: str = path.join(home, ".local", "share", app_name)
|
||||||
|
|
||||||
if not path.exists(user_data_path):
|
if not path.exists(user_data_path):
|
||||||
mkdir(user_data_path)
|
mkdir(user_data_path)
|
||||||
|
@ -15,5 +15,6 @@
|
|||||||
"error": "Error",
|
"error": "Error",
|
||||||
"suggestion_sent_success": "Your message has been sent successfully!",
|
"suggestion_sent_success": "Your message has been sent successfully!",
|
||||||
"suggestion_send_error": "Error sending message. Try again later.",
|
"suggestion_send_error": "Error sending message. Try again later.",
|
||||||
"email_credentials_error": "Email credentials not configured. Please set your email and password in the .env file."
|
"email_credentials_error": "Email credentials not or bad configured. Please set your email and password in the .env file.",
|
||||||
|
"suggestion_too_short": "The message must be at least 15 characters long."
|
||||||
}
|
}
|
@ -15,5 +15,6 @@
|
|||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
"suggestion_sent_success": "Votre message a été envoyé avec succès !",
|
"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.",
|
"suggestion_send_error": "Erreur lors de l'envoi du message. Essayez à nouveau plus tard.",
|
||||||
"email_credentials_error": "Identifiants de messagerie non configurés. Veuillez définir votre email et mot de passe dans le fichier .env."
|
"email_credentials_error": "Identifiants de messagerie non ou mal configurés. Veuillez définir votre email et mot de passe dans le fichier .env.",
|
||||||
|
"suggestion_too_short": "Le message doit contenir au moins 15 caractères."
|
||||||
}
|
}
|
@ -2,5 +2,5 @@
|
|||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"lang": "fr",
|
"lang": "fr",
|
||||||
"window_size": {"width": 1000, "height": 600},
|
"window_size": {"width": 1000, "height": 600},
|
||||||
"maximize": true
|
"maximized": true
|
||||||
}
|
}
|
15
main.py
15
main.py
@ -1,11 +1,20 @@
|
|||||||
import sys
|
import sys
|
||||||
|
import app.utils.paths as paths
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
from PyQt6.QtGui import QIcon
|
||||||
from app.ui.main_window import MainWindow
|
from app.ui.main_window import MainWindow
|
||||||
|
from app.core.main_manager import MainManager
|
||||||
|
def main() -> int:
|
||||||
|
|
||||||
def main():
|
main_manager: MainManager = MainManager.get_instance()
|
||||||
app = QApplication(sys.argv)
|
theme_manager = main_manager.get_theme_manager()
|
||||||
|
settings_manager = main_manager.get_settings_manager()
|
||||||
|
|
||||||
window = MainWindow()
|
app: QApplication = QApplication(sys.argv)
|
||||||
|
app.setStyleSheet(theme_manager.get_sheet())
|
||||||
|
app.setApplicationName(settings_manager.get_config("app_name"))
|
||||||
|
app.setWindowIcon(QIcon(paths.get_asset_path("icon")))
|
||||||
|
window: MainWindow = MainWindow()
|
||||||
window.show()
|
window.show()
|
||||||
return app.exec()
|
return app.exec()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user