Files
NexusLauncher/main.py
2025-11-23 20:41:50 +08:00

385 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
NexusLauncher - 主程序(简化版)
一个现代化的应用启动器
"""
import customtkinter as ctk
from config import ConfigManager
from config.constants import (
BG_COLOR_DARK,
BG_COLOR_BUTTON,
BG_COLOR_BUTTON_HOVER,
BORDER_COLOR,
SEGMENTED_BUTTON_SELECTED_COLOR,
SEGMENTED_BUTTON_SELECTED_HOVER_COLOR,
SEGMENTED_BUTTON_UNSELECTED_COLOR,
SEGMENTED_BUTTON_UNSELECTED_HOVER_COLOR,
DROPDOWN_FG_COLOR,
DROPDOWN_HOVER_COLOR,
TEXT_COLOR_PRIMARY,
COLOR_TRANSPARENT
)
from ui import SettingsWindow, get_icons_dir, IconManager
from ui.task import TaskPanel
from ui.project import ProjectPanel
from ui.utilities import WindowManager, UIHelpers
class NexusLauncher(ctk.CTk):
"""主应用程序类(简化版)"""
def __init__(self):
super().__init__()
# 调试模式控制
self.debug_mode = False
# 创建启动画面
self.splash = None
try:
from ui import SplashScreen
self.splash = SplashScreen(self)
except Exception as e:
self._log(f"Failed to create splash screen: {e}", "WARNING")
# 初始化管理器
self.config_manager = ConfigManager()
self.window_manager = WindowManager(self, self.config_manager)
# 设置Windows AppUserModelID
self.window_manager.setup_window_appid()
# 图标管理
self.icons_dir = get_icons_dir()
self.icon_size = self.config_manager.get_icon_size()
self.icon_manager = IconManager(self.icons_dir, self.icon_size)
# 窗口配置
self._setup_window()
# 创建界面
self._create_widgets()
# 绑定事件
self._bind_events()
# 初始化UI
self._initialize_ui()
# 设置托盘和窗口关闭事件
self.protocol("WM_DELETE_WINDOW", self.window_manager.hide_window)
self.window_manager.setup_tray_icon()
def _log(self, message: str, level: str = "INFO"):
"""统一的日志方法
Args:
message: 日志消息
level: 日志级别 (DEBUG, INFO, WARNING, ERROR)
"""
# DEBUG级别的日志只在调试模式下输出
if level == "DEBUG" and not self.debug_mode:
return
prefix = f"[{level}]"
full_message = f"{prefix} {message}"
print(full_message)
def _setup_window(self):
"""设置窗口属性"""
self.title("NexusLauncher")
width, height = self.config_manager.get_window_size()
self.minsize(200, 200)
# 定位窗口到右下角
self.window_manager.position_window_bottom_right(width, height)
# 设置图标和主题
self.window_manager.set_window_icon()
ctk.set_appearance_mode("dark")
self.configure(fg_color=BG_COLOR_DARK)
def _create_widgets(self):
"""创建界面组件"""
# 配置网格布局
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
# 创建各个部分
self._create_header()
self._create_project_area()
def _create_header(self):
"""创建顶部菜单栏"""
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_columnconfigure(0, weight=1)
# 标题
ctk.CTkLabel(
self.menu_frame,
text="NexusLauncher",
font=ctk.CTkFont(size=16, weight="bold")
).grid(row=0, column=0, padx=20, pady=8, sticky="w")
# 设置按钮
ctk.CTkButton(
self.menu_frame,
text="⚙ 设置",
command=self._open_settings,
width=90,
height=30,
font=ctk.CTkFont(size=12)
).grid(row=0, column=1, padx=20, pady=8, sticky="e")
def _create_project_area(self):
"""创建项目区域"""
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_columnconfigure(0, weight=1)
self.project_frame.grid_rowconfigure(1, weight=1)
self._create_project_selector()
self._create_tabview()
def _create_project_selector(self):
"""创建项目选择下拉框"""
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_columnconfigure(1, weight=1)
ctk.CTkLabel(
project_select_frame,
text="当前项目:",
font=ctk.CTkFont(size=13, weight="bold")
).grid(row=0, column=0, padx=(0, 10), sticky="w")
self.project_combo = ctk.CTkComboBox(
project_select_frame,
command=self._on_project_changed,
height=32,
font=ctk.CTkFont(size=12),
dropdown_font=ctk.CTkFont(size=12),
button_color=BG_COLOR_BUTTON,
button_hover_color=BG_COLOR_BUTTON_HOVER,
border_color=BORDER_COLOR,
border_width=1,
state="readonly",
corner_radius=8,
dropdown_fg_color=DROPDOWN_FG_COLOR,
dropdown_hover_color=DROPDOWN_HOVER_COLOR,
dropdown_text_color=TEXT_COLOR_PRIMARY,
justify="left"
)
self.project_combo.grid(row=0, column=1, sticky="ew")
def _create_tabview(self):
"""创建标签页"""
self.tabview = ctk.CTkTabview(
self.project_frame,
height=40,
corner_radius=10,
segmented_button_fg_color=SEGMENTED_BUTTON_UNSELECTED_COLOR,
segmented_button_selected_color=SEGMENTED_BUTTON_SELECTED_COLOR,
segmented_button_selected_hover_color=SEGMENTED_BUTTON_SELECTED_HOVER_COLOR,
segmented_button_unselected_color=SEGMENTED_BUTTON_UNSELECTED_COLOR,
segmented_button_unselected_hover_color=SEGMENTED_BUTTON_UNSELECTED_HOVER_COLOR,
anchor="w"
)
self.tabview.grid(row=1, column=0, sticky="nsew", padx=5, pady=(0, 5))
self._create_project_tab()
self._create_task_tab()
self.tabview.configure(command=self._on_tab_changed)
self.tabview.set("Project")
def _create_project_tab(self):
"""创建Project标签页"""
self.project_tab = self.tabview.add("Project")
self.project_tab.grid_columnconfigure(0, weight=1)
self.project_tab.grid_rowconfigure(0, weight=1)
# 使用ProjectPanel
self.project_panel = ProjectPanel(
self.project_tab,
self.config_manager,
self.icon_manager,
log_callback=self.window_manager.log_with_timestamp,
fg_color=COLOR_TRANSPARENT
)
self.project_panel.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
# 设置图标大小
self.project_panel.set_icon_size(self.icon_size)
# 初始化项目背景颜色
self._update_project_background()
def _create_task_tab(self):
"""创建Task标签页"""
self.task_tab = self.tabview.add("Task")
self.task_tab.grid_columnconfigure(0, weight=1)
self.task_tab.grid_rowconfigure(0, weight=1)
# 使用TaskPanel
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._update_task_colors()
def _bind_events(self):
"""绑定事件"""
self.project_combo.bind("<Button-1>", self._on_combo_click, add="+")
self.bind_all("<Control-MouseWheel>", self._on_zoom, add="+")
self.bind("<Configure>", self._on_window_resize)
def _initialize_ui(self):
"""初始化UI"""
self.after(100, lambda: UIHelpers.adjust_tab_button_width(self.tabview))
self.after(150, self._configure_tab_style)
self.after(200, lambda: UIHelpers.fix_dropdown_width(self.project_combo))
self.after(250, self._update_tab_appearance) # 延迟更新标签页外观确保UI完全初始化
self._update_project_list()
# 初始化Project标签页内容
self.after(300, self.project_panel.refresh)
# 关闭启动画面
if self.splash:
self.after(350, self._close_splash)
def _close_splash(self):
"""关闭启动画面"""
if self.splash:
try:
self.splash.close()
self.splash = None
except:
pass
def _configure_tab_style(self):
"""配置标签页样式"""
UIHelpers.configure_tab_transparency(self.project_tab, self.task_tab)
def _on_combo_click(self, event):
"""下拉框点击事件"""
self.after(1, lambda: UIHelpers.fix_dropdown_width(self.project_combo))
def _on_tab_changed(self):
"""标签页切换事件"""
current_tab = self.tabview.get()
self._log(f"Switched to tab: {current_tab}", "DEBUG")
if current_tab == "Task":
self.task_panel.refresh()
elif current_tab == "Project":
self.project_panel.refresh()
def _on_zoom(self, event):
"""处理缩放事件"""
# 检查事件是否来自主窗口
widget = event.widget
if hasattr(widget, 'winfo_toplevel'):
if widget.winfo_toplevel() != self:
return
# 委托给project_panel处理
return self.project_panel.handle_zoom(event)
def _on_window_resize(self, event):
"""窗口大小改变事件"""
if event.widget == self:
if hasattr(self, '_resize_timer'):
self.after_cancel(self._resize_timer)
self._resize_timer = self.after(150, self._on_resize_complete)
def _on_resize_complete(self):
"""窗口调整完成后的处理"""
try:
self.project_panel.on_window_resize()
UIHelpers.adjust_tab_button_width(self.tabview)
except Exception as e:
self._log(f"Window adjustment handling failed: {e}", "WARNING")
def _update_project_list(self):
"""更新项目列表"""
projects = self.config_manager.get_projects()
if projects:
self.project_combo.configure(values=projects)
current_project = self.config_manager.get_current_project()
if current_project in projects:
self.project_combo.set(current_project)
else:
self.project_combo.set(projects[0])
self.config_manager.set_current_project(projects[0])
else:
self.project_combo.configure(values=["无项目"])
self.project_combo.set("无项目")
def _on_project_changed(self, choice):
"""项目切换事件"""
self.config_manager.set_current_project(choice)
self._update_tab_appearance()
self.project_panel.refresh()
self.task_panel.refresh()
def _update_tab_appearance(self):
"""更新标签页外观"""
current_project = self.config_manager.get_current_project()
if not current_project:
return
# 更新背景颜色
self._update_project_background()
self._update_task_colors()
# 更新标签页图标(包含高度设置)
self.project_panel.update_tab_icon(self.tabview, current_project)
def _update_project_background(self):
"""更新project panel背景颜色"""
current_project = self.config_manager.get_current_project()
if current_project:
project_color = self.config_manager.get_project_color(current_project)
if project_color:
self.project_panel.configure(fg_color=project_color)
if hasattr(self.project_panel, 'update_background_color'):
self.project_panel.update_background_color(project_color)
def _update_task_colors(self):
"""更新task panel颜色"""
current_project = self.config_manager.get_current_project()
if current_project:
project_color = self.config_manager.get_project_color(current_project)
if project_color and hasattr(self.task_panel, 'update_colors'):
self.task_panel.update_colors(project_color)
def _open_settings(self):
"""打开设置窗口"""
self.window_manager.log_with_timestamp("🔧 打开设置窗口")
settings_window = SettingsWindow(self, self.config_manager, self._on_settings_updated)
def _on_settings_updated(self):
"""设置更新后的回调"""
self.window_manager.log_with_timestamp("🔄 设置已更新,重新加载应用")
self._update_project_list()
self._update_tab_appearance()
self.project_panel.refresh()
def log(self, message: str):
"""日志记录委托给window_manager"""
self.window_manager.log(message)
def main():
"""主函数"""
app = NexusLauncher()
app.mainloop()
if __name__ == "__main__":
main()