更新 main.py

This commit is contained in:
2026-01-21 12:43:28 +08:00
parent b660984e23
commit 4c94ecc329

185
main.py
View File

@@ -1,9 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
NexusLauncher - 主程序(简化版) NexusLauncher - Main Program
一个现代化的应用启动器 A modern application launcher
""" """
import sys
import ctypes
import customtkinter as ctk import customtkinter as ctk
from config import ConfigManager from config import ConfigManager
from config.constants import ( from config.constants import (
@@ -27,15 +29,15 @@ from ui.utilities import WindowManager, UIHelpers
class NexusLauncher(ctk.CTk): class NexusLauncher(ctk.CTk):
"""主应用程序类(简化版)""" """Main application class"""
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# 调试模式控制 # Debug mode control
self.debug_mode = False self.debug_mode = False
# 创建启动画面 # Create a splash screen
self.splash = None self.splash = None
try: try:
from ui import SplashScreen from ui import SplashScreen
@@ -43,42 +45,42 @@ class NexusLauncher(ctk.CTk):
except Exception as e: except Exception as e:
self._log(f"Failed to create splash screen: {e}", "WARNING") self._log(f"Failed to create splash screen: {e}", "WARNING")
# 初始化管理器 # Initialization Manager
self.config_manager = ConfigManager() self.config_manager = ConfigManager()
self.window_manager = WindowManager(self, self.config_manager) self.window_manager = WindowManager(self, self.config_manager)
# 设置Windows AppUserModelID # Set Windows AppUserModelID
self.window_manager.setup_window_appid() self.window_manager.setup_window_appid()
# 图标管理 # Icon Management
self.icons_dir = get_icons_dir() self.icons_dir = get_icons_dir()
self.icon_size = self.config_manager.get_icon_size() self.icon_size = self.config_manager.get_icon_size()
self.icon_manager = IconManager(self.icons_dir, self.icon_size) self.icon_manager = IconManager(self.icons_dir, self.icon_size)
# 窗口配置 # Window Configuration
self._setup_window() self._setup_window()
# 创建界面 # Create Interface
self._create_widgets() self._create_widgets()
# 绑定事件 # Binding events
self._bind_events() self._bind_events()
# 初始化UI # Initialize UI
self._initialize_ui() self._initialize_ui()
# 设置托盘和窗口关闭事件 # Set tray and window close events
self.protocol("WM_DELETE_WINDOW", self.window_manager.hide_window) self.protocol("WM_DELETE_WINDOW", self.window_manager.hide_window)
self.window_manager.setup_tray_icon() self.window_manager.setup_tray_icon()
def _log(self, message: str, level: str = "INFO"): def _log(self, message: str, level: str = "INFO"):
"""统一的日志方法 """Unified logging method
Args: Args:
message: 日志消息 message: Log messages
level: 日志级别 (DEBUG, INFO, WARNING, ERROR) level: Log levels (DEBUG, INFO, WARNING, ERROR)
""" """
# DEBUG级别的日志只在调试模式下输出 # DEBUG level logs are only output in debug mode.
if level == "DEBUG" and not self.debug_mode: if level == "DEBUG" and not self.debug_mode:
return return
@@ -87,46 +89,46 @@ class NexusLauncher(ctk.CTk):
print(full_message) print(full_message)
def _setup_window(self): def _setup_window(self):
"""设置窗口属性""" """Set window properties"""
self.title("NexusLauncher") self.title("NexusLauncher")
width, height = self.config_manager.get_window_size() width, height = self.config_manager.get_window_size()
self.minsize(200, 200) self.minsize(200, 200)
# 定位窗口到右下角 # Position the window to the bottom right corner
self.window_manager.position_window_bottom_right(width, height) self.window_manager.position_window_bottom_right(width, height)
# 设置图标和主题 # Set icon and theme
self.window_manager.set_window_icon() self.window_manager.set_window_icon()
ctk.set_appearance_mode("dark") ctk.set_appearance_mode("dark")
self.configure(fg_color=BG_COLOR_DARK) self.configure(fg_color=BG_COLOR_DARK)
def _create_widgets(self): def _create_widgets(self):
"""创建界面组件""" """Creating UI Components"""
# 配置网格布局 # Configuring Grid Layout
self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1) self.grid_rowconfigure(1, weight=1)
# 创建各个部分 # Create each part
self._create_header() self._create_header()
self._create_project_area() self._create_project_area()
def _create_header(self): def _create_header(self):
"""创建顶部菜单栏""" """Create a top menu bar"""
self.menu_frame = ctk.CTkFrame(self, height=40, corner_radius=0) self.menu_frame = ctk.CTkFrame(self, height=40, corner_radius=0)
self.menu_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0) self.menu_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0)
self.menu_frame.grid_columnconfigure(0, weight=1) self.menu_frame.grid_columnconfigure(0, weight=1)
# 标题 # Title
ctk.CTkLabel( ctk.CTkLabel(
self.menu_frame, self.menu_frame,
text="NexusLauncher", text="NexusLauncher",
font=ctk.CTkFont(size=16, weight="bold") font=ctk.CTkFont(size=16, weight="bold")
).grid(row=0, column=0, padx=20, pady=8, sticky="w") ).grid(row=0, column=0, padx=20, pady=8, sticky="w")
# 设置按钮 # Set button
ctk.CTkButton( ctk.CTkButton(
self.menu_frame, self.menu_frame,
text="设置", text="Setting",
command=self._open_settings, command=self._open_settings,
width=90, width=90,
height=30, height=30,
@@ -134,7 +136,7 @@ class NexusLauncher(ctk.CTk):
).grid(row=0, column=1, padx=20, pady=8, sticky="e") ).grid(row=0, column=1, padx=20, pady=8, sticky="e")
def _create_project_area(self): def _create_project_area(self):
"""创建项目区域""" """Create project area"""
self.project_frame = ctk.CTkFrame(self, corner_radius=0, fg_color=COLOR_TRANSPARENT) self.project_frame = ctk.CTkFrame(self, corner_radius=0, fg_color=COLOR_TRANSPARENT)
self.project_frame.grid(row=1, column=0, sticky="nsew", padx=0, pady=0) self.project_frame.grid(row=1, column=0, sticky="nsew", padx=0, pady=0)
self.project_frame.grid_columnconfigure(0, weight=1) self.project_frame.grid_columnconfigure(0, weight=1)
@@ -144,14 +146,14 @@ class NexusLauncher(ctk.CTk):
self._create_tabview() self._create_tabview()
def _create_project_selector(self): def _create_project_selector(self):
"""创建项目选择下拉框""" """Project creation selection dropdown"""
project_select_frame = ctk.CTkFrame(self.project_frame, fg_color=COLOR_TRANSPARENT) project_select_frame = ctk.CTkFrame(self.project_frame, fg_color=COLOR_TRANSPARENT)
project_select_frame.grid(row=0, column=0, sticky="ew", padx=20, pady=(3, 3)) project_select_frame.grid(row=0, column=0, sticky="ew", padx=20, pady=(3, 3))
project_select_frame.grid_columnconfigure(1, weight=1) project_select_frame.grid_columnconfigure(1, weight=1)
ctk.CTkLabel( ctk.CTkLabel(
project_select_frame, project_select_frame,
text="当前项目:", text="Current Project:",
font=ctk.CTkFont(size=13, weight="bold") font=ctk.CTkFont(size=13, weight="bold")
).grid(row=0, column=0, padx=(0, 10), sticky="w") ).grid(row=0, column=0, padx=(0, 10), sticky="w")
@@ -175,7 +177,7 @@ class NexusLauncher(ctk.CTk):
self.project_combo.grid(row=0, column=1, sticky="ew") self.project_combo.grid(row=0, column=1, sticky="ew")
def _create_tabview(self): def _create_tabview(self):
"""创建标签页""" """Create a tab"""
self.tabview = ctk.CTkTabview( self.tabview = ctk.CTkTabview(
self.project_frame, self.project_frame,
height=40, height=40,
@@ -196,12 +198,12 @@ class NexusLauncher(ctk.CTk):
self.tabview.set("Project") self.tabview.set("Project")
def _create_project_tab(self): def _create_project_tab(self):
"""创建Project标签页""" """Create Project tab"""
self.project_tab = self.tabview.add("Project") self.project_tab = self.tabview.add("Project")
self.project_tab.grid_columnconfigure(0, weight=1) self.project_tab.grid_columnconfigure(0, weight=1)
self.project_tab.grid_rowconfigure(0, weight=1) self.project_tab.grid_rowconfigure(0, weight=1)
# 使用ProjectPanel # Use ProjectPanel
self.project_panel = ProjectPanel( self.project_panel = ProjectPanel(
self.project_tab, self.project_tab,
self.config_manager, self.config_manager,
@@ -211,48 +213,48 @@ class NexusLauncher(ctk.CTk):
) )
self.project_panel.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) self.project_panel.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
# 设置图标大小 # Set icon size
self.project_panel.set_icon_size(self.icon_size) self.project_panel.set_icon_size(self.icon_size)
# 初始化项目背景颜色 # Initialize project background color
self._update_project_background() self._update_project_background()
def _create_task_tab(self): def _create_task_tab(self):
"""创建Task标签页""" """Create Task tab"""
self.task_tab = self.tabview.add("Task") self.task_tab = self.tabview.add("Task")
self.task_tab.grid_columnconfigure(0, weight=1) self.task_tab.grid_columnconfigure(0, weight=1)
self.task_tab.grid_rowconfigure(0, weight=1) self.task_tab.grid_rowconfigure(0, weight=1)
# 使用TaskPanel # Using TaskPanel
self.task_panel = TaskPanel(self.task_tab, self.config_manager, fg_color=COLOR_TRANSPARENT) self.task_panel = TaskPanel(self.task_tab, self.config_manager, fg_color=COLOR_TRANSPARENT)
self.task_panel.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) self.task_panel.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
# 初始化项目颜色 # Initialize project colors
self._update_task_colors() self._update_task_colors()
def _bind_events(self): def _bind_events(self):
"""绑定事件""" """Binding events"""
self.project_combo.bind("<Button-1>", self._on_combo_click, add="+") self.project_combo.bind("<Button-1>", self._on_combo_click, add="+")
self.bind_all("<Control-MouseWheel>", self._on_zoom, add="+") self.bind_all("<Control-MouseWheel>", self._on_zoom, add="+")
self.bind("<Configure>", self._on_window_resize) self.bind("<Configure>", self._on_window_resize)
def _initialize_ui(self): def _initialize_ui(self):
"""初始化UI""" """Initialize UI"""
self.after(100, lambda: UIHelpers.adjust_tab_button_width(self.tabview)) self.after(100, lambda: UIHelpers.adjust_tab_button_width(self.tabview))
self.after(150, self._configure_tab_style) self.after(150, self._configure_tab_style)
self.after(200, lambda: UIHelpers.fix_dropdown_width(self.project_combo)) self.after(200, lambda: UIHelpers.fix_dropdown_width(self.project_combo))
self.after(250, self._update_tab_appearance) # 延迟更新标签页外观确保UI完全初始化 self.after(250, self._update_tab_appearance) # Delay updating the tab appearance to ensure the UI is fully initialized.
self._update_project_list() self._update_project_list()
# 初始化Project标签页内容 # Initialize Project tab content
self.after(300, self.project_panel.refresh) self.after(300, self.project_panel.refresh)
# 关闭启动画面 # Close the startup screen
if self.splash: if self.splash:
self.after(350, self._close_splash) self.after(350, self._close_splash)
def _close_splash(self): def _close_splash(self):
"""关闭启动画面""" """Close the startup screen"""
if self.splash: if self.splash:
try: try:
self.splash.close() self.splash.close()
@@ -261,15 +263,15 @@ class NexusLauncher(ctk.CTk):
pass pass
def _configure_tab_style(self): def _configure_tab_style(self):
"""配置标签页样式""" """Configure tab styles"""
UIHelpers.configure_tab_transparency(self.project_tab, self.task_tab) UIHelpers.configure_tab_transparency(self.project_tab, self.task_tab)
def _on_combo_click(self, event): def _on_combo_click(self, event):
"""下拉框点击事件""" """Drop-down click event"""
self.after(1, lambda: UIHelpers.fix_dropdown_width(self.project_combo)) self.after(1, lambda: UIHelpers.fix_dropdown_width(self.project_combo))
def _on_tab_changed(self): def _on_tab_changed(self):
"""标签页切换事件""" """Tab switching event"""
current_tab = self.tabview.get() current_tab = self.tabview.get()
self._log(f"Switched to tab: {current_tab}", "DEBUG") self._log(f"Switched to tab: {current_tab}", "DEBUG")
@@ -279,25 +281,25 @@ class NexusLauncher(ctk.CTk):
self.project_panel.refresh() self.project_panel.refresh()
def _on_zoom(self, event): def _on_zoom(self, event):
"""处理缩放事件""" """Handling zoom events"""
# 检查事件是否来自主窗口 # Check if the event originated from the main window.
widget = event.widget widget = event.widget
if hasattr(widget, 'winfo_toplevel'): if hasattr(widget, 'winfo_toplevel'):
if widget.winfo_toplevel() != self: if widget.winfo_toplevel() != self:
return return
# 委托给project_panel处理 # Delegate to project_panel
return self.project_panel.handle_zoom(event) return self.project_panel.handle_zoom(event)
def _on_window_resize(self, event): def _on_window_resize(self, event):
"""窗口大小改变事件""" """Window resize event"""
if event.widget == self: if event.widget == self:
if hasattr(self, '_resize_timer'): if hasattr(self, '_resize_timer'):
self.after_cancel(self._resize_timer) self.after_cancel(self._resize_timer)
self._resize_timer = self.after(150, self._on_resize_complete) self._resize_timer = self.after(150, self._on_resize_complete)
def _on_resize_complete(self): def _on_resize_complete(self):
"""窗口调整完成后的处理""" """Processing after window adjustment"""
try: try:
self.project_panel.on_window_resize() self.project_panel.on_window_resize()
UIHelpers.adjust_tab_button_width(self.tabview) UIHelpers.adjust_tab_button_width(self.tabview)
@@ -305,7 +307,7 @@ class NexusLauncher(ctk.CTk):
self._log(f"Window adjustment handling failed: {e}", "WARNING") self._log(f"Window adjustment handling failed: {e}", "WARNING")
def _update_project_list(self): def _update_project_list(self):
"""更新项目列表""" """Update project list"""
projects = self.config_manager.get_projects() projects = self.config_manager.get_projects()
if projects: if projects:
self.project_combo.configure(values=projects) self.project_combo.configure(values=projects)
@@ -316,31 +318,31 @@ class NexusLauncher(ctk.CTk):
self.project_combo.set(projects[0]) self.project_combo.set(projects[0])
self.config_manager.set_current_project(projects[0]) self.config_manager.set_current_project(projects[0])
else: else:
self.project_combo.configure(values=["无项目"]) self.project_combo.configure(values=["No project"])
self.project_combo.set("无项目") self.project_combo.set("No project")
def _on_project_changed(self, choice): def _on_project_changed(self, choice):
"""项目切换事件""" """Project Switching Event"""
self.config_manager.set_current_project(choice) self.config_manager.set_current_project(choice)
self._update_tab_appearance() self._update_tab_appearance()
self.project_panel.refresh() self.project_panel.refresh()
self.task_panel.refresh() self.task_panel.refresh()
def _update_tab_appearance(self): def _update_tab_appearance(self):
"""更新标签页外观""" """Update tab appearance"""
current_project = self.config_manager.get_current_project() current_project = self.config_manager.get_current_project()
if not current_project: if not current_project:
return return
# 更新背景颜色 # Update background color
self._update_project_background() self._update_project_background()
self._update_task_colors() self._update_task_colors()
# 更新标签页图标(包含高度设置) # Update tab icons (including height settings).
self.project_panel.update_tab_icon(self.tabview, current_project) self.project_panel.update_tab_icon(self.tabview, current_project)
def _update_project_background(self): def _update_project_background(self):
"""更新project panel背景颜色""" """Update project panel background color"""
current_project = self.config_manager.get_current_project() current_project = self.config_manager.get_current_project()
if current_project: if current_project:
project_color = self.config_manager.get_project_color(current_project) project_color = self.config_manager.get_project_color(current_project)
@@ -350,7 +352,7 @@ class NexusLauncher(ctk.CTk):
self.project_panel.update_background_color(project_color) self.project_panel.update_background_color(project_color)
def _update_task_colors(self): def _update_task_colors(self):
"""更新task panel颜色""" """Update task panel colors"""
current_project = self.config_manager.get_current_project() current_project = self.config_manager.get_current_project()
if current_project: if current_project:
project_color = self.config_manager.get_project_color(current_project) project_color = self.config_manager.get_project_color(current_project)
@@ -358,27 +360,76 @@ class NexusLauncher(ctk.CTk):
self.task_panel.update_colors(project_color) self.task_panel.update_colors(project_color)
def _open_settings(self): def _open_settings(self):
"""打开设置窗口""" """Open settings window"""
self.window_manager.log_with_timestamp("🔧 打开设置窗口") self.window_manager.log_with_timestamp("🔧 Open settings window")
settings_window = SettingsWindow(self, self.config_manager, self._on_settings_updated) settings_window = SettingsWindow(self, self.config_manager, self._on_settings_updated)
def _on_settings_updated(self): def _on_settings_updated(self):
"""设置更新后的回调""" """Set the callback after the update"""
self.window_manager.log_with_timestamp("🔄 设置已更新,重新加载应用") self.window_manager.log_with_timestamp("🔄 Settings have been updated. Please reload the application.")
self._update_project_list() self._update_project_list()
self._update_tab_appearance() self._update_tab_appearance()
self.project_panel.refresh() self.project_panel.refresh()
def log(self, message: str): def log(self, message: str):
"""日志记录(委托给window_manager""" """Log recording (delegated to window_manager)"""
self.window_manager.log(message) self.window_manager.log(message)
def main(): def main():
"""主函数""" """Main function"""
# Create a mutex lock to ensure that only one instance runs.
mutex_name = "Global\\NexusLauncher_SingleInstance_Mutex"
try:
# Try to create a mutex lock
kernel32 = ctypes.windll.kernel32
mutex = kernel32.CreateMutexW(None, False, mutex_name)
last_error = kernel32.GetLastError()
# ERROR_ALREADY_EXISTS = 183
if last_error == 183:
print("[INFO] NexusLauncher is already running and attempting to display the main window....")
# Try to find and activate an existing window.
try:
user32 = ctypes.windll.user32
# First try finding the window.
hwnd = user32.FindWindowW(None, "NexusLauncher")
if hwnd:
# Show the window (whether it's hidden or minimized).
# SW_SHOW = 5, SW_RESTORE = 9
user32.ShowWindow(hwnd, 9) # First restore
user32.ShowWindow(hwnd, 5) # Re-display
# Bring the window to the foreground and activate it.
user32.SetForegroundWindow(hwnd)
user32.SetActiveWindow(hwnd)
print("[INFO] The existing NexusLauncher window is now displayed.")
else:
print("[WARNING] NexusLauncher window handle not found")
print("[INFO] The program may run in the system tray; please open it from the tray menu.")
except Exception as e:
print(f"[WARNING] Unable to activate an existing window: {e}")
print("[INFO] Please open NexusLauncher from the system tray.")
sys.exit(0)
# Launch application
app = NexusLauncher() app = NexusLauncher()
app.mainloop() app.mainloop()
# Release the mutex
if mutex:
kernel32.CloseHandle(mutex)
except Exception as e:
print(f"[ERROR] Startup failed: {e}")
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
main() main()