初始化项目以及GUI简单实现
This commit is contained in:
8
src/ui/__init__.py
Normal file
8
src/ui/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
LuoLuoTool UI模块
|
||||
使用CustomTkinter构建现代化界面
|
||||
"""
|
||||
|
||||
from .app import LuoLuoApp
|
||||
|
||||
__all__ = ["LuoLuoApp"]
|
||||
229
src/ui/app.py
Normal file
229
src/ui/app.py
Normal file
@@ -0,0 +1,229 @@
|
||||
"""
|
||||
LuoLuoTool 主应用类
|
||||
窗口标题: LuoLuo
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
from pathlib import Path
|
||||
from src.utils.logger import logger
|
||||
|
||||
from .pages.run_page import RunPage
|
||||
from .pages.daily_config_page import DailyConfigPage
|
||||
from .pages.misc_config_page import MiscConfigPage
|
||||
from .pages.pending_config_page import PendingConfigPage
|
||||
from .pages.about_page import AboutPage
|
||||
|
||||
|
||||
class LuoLuoApp:
|
||||
"""LuoLuoTool主应用"""
|
||||
|
||||
# 窗口配置
|
||||
WINDOW_TITLE = "LuoLuo"
|
||||
WINDOW_WIDTH = 900
|
||||
WINDOW_HEIGHT = 650
|
||||
|
||||
# 侧边栏配置
|
||||
SIDEBAR_WIDTH = 140
|
||||
|
||||
# 主题配置
|
||||
THEME_COLOR = ("#3B8ED0", "#1F6AA5") # 浅色, 深色
|
||||
|
||||
def __init__(self):
|
||||
# 设置主题
|
||||
ctk.set_appearance_mode("System")
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
# 创建主窗口
|
||||
self.root = ctk.CTk()
|
||||
self.root.title(self.WINDOW_TITLE)
|
||||
self.root.geometry(f"{self.WINDOW_WIDTH}x{self.WINDOW_HEIGHT}")
|
||||
self.root.minsize(800, 550)
|
||||
|
||||
# 当前页面
|
||||
self.current_page = None
|
||||
self.pages = {}
|
||||
|
||||
# 初始化UI
|
||||
self._setup_ui()
|
||||
|
||||
# 设置窗口图标(在UI初始化后设置)
|
||||
self._set_window_icon()
|
||||
|
||||
logger.info("LuoLuoApp初始化完成")
|
||||
|
||||
def _set_window_icon(self):
|
||||
"""设置窗口图标(仅左上角)"""
|
||||
assets_dir = Path(__file__).parent.parent.parent / "assets" / "images"
|
||||
ico_path = assets_dir / "luoluoTool.ico"
|
||||
png_path = assets_dir / "luoluoTool.png"
|
||||
|
||||
try:
|
||||
# 优先使用 ICO 文件(Windows 原生支持)
|
||||
if ico_path.exists():
|
||||
self.root.iconbitmap(str(ico_path))
|
||||
logger.info(f"已设置窗口图标(ICO): {ico_path}")
|
||||
elif png_path.exists():
|
||||
# 备用:使用 PNG
|
||||
from PIL import Image, ImageTk
|
||||
icon_image = Image.open(png_path)
|
||||
if icon_image.mode != 'RGBA':
|
||||
icon_image = icon_image.convert('RGBA')
|
||||
icon_image = icon_image.resize((32, 32), Image.Resampling.LANCZOS)
|
||||
self._icon_photo = ImageTk.PhotoImage(icon_image)
|
||||
self.root.iconphoto(True, self._icon_photo)
|
||||
logger.info(f"已设置窗口图标(PNG): {png_path}")
|
||||
else:
|
||||
logger.warning("未找到图标文件")
|
||||
except Exception as e:
|
||||
logger.warning(f"加载图标失败: {e}")
|
||||
|
||||
def _setup_ui(self):
|
||||
"""初始化界面"""
|
||||
# 配置网格布局
|
||||
self.root.grid_columnconfigure(1, weight=1)
|
||||
self.root.grid_rowconfigure(0, weight=1)
|
||||
|
||||
# 创建侧边栏
|
||||
self._create_sidebar()
|
||||
|
||||
# 创建主内容区域
|
||||
self._create_main_content()
|
||||
|
||||
# 创建底部状态栏
|
||||
self._create_statusbar()
|
||||
|
||||
# 初始化页面
|
||||
self._init_pages()
|
||||
|
||||
# 默认显示运行页面
|
||||
self.show_page("run")
|
||||
|
||||
def _create_sidebar(self):
|
||||
"""创建侧边栏"""
|
||||
self.sidebar = ctk.CTkFrame(
|
||||
self.root,
|
||||
width=self.SIDEBAR_WIDTH,
|
||||
corner_radius=0
|
||||
)
|
||||
self.sidebar.grid(row=0, column=0, rowspan=2, sticky="nsew")
|
||||
self.sidebar.grid_rowconfigure(6, weight=1)
|
||||
|
||||
# Logo/标题
|
||||
self.logo_label = ctk.CTkLabel(
|
||||
self.sidebar,
|
||||
text="🎮 LuoLuo",
|
||||
font=ctk.CTkFont(size=18, weight="bold")
|
||||
)
|
||||
self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 10))
|
||||
|
||||
# 侧边栏按钮
|
||||
self.nav_buttons = {}
|
||||
nav_items = [
|
||||
("run", "▶ 运行", 1),
|
||||
("daily", "📋 日常", 2),
|
||||
("misc", "🔧 杂项", 3),
|
||||
("pending", "⏳ 待定", 4),
|
||||
("about", "ℹ 关于", 5),
|
||||
]
|
||||
|
||||
for page_id, text, row in nav_items:
|
||||
btn = ctk.CTkButton(
|
||||
self.sidebar,
|
||||
text=text,
|
||||
anchor="w",
|
||||
fg_color="transparent",
|
||||
text_color=("gray10", "gray90"),
|
||||
hover_color=("gray70", "gray30"),
|
||||
height=40,
|
||||
command=lambda p=page_id: self.show_page(p)
|
||||
)
|
||||
btn.grid(row=row, column=0, padx=10, pady=5, sticky="ew")
|
||||
self.nav_buttons[page_id] = btn
|
||||
|
||||
# 主题切换
|
||||
self.theme_switch = ctk.CTkSwitch(
|
||||
self.sidebar,
|
||||
text="🌙 深色",
|
||||
command=self._toggle_theme
|
||||
)
|
||||
self.theme_switch.grid(row=7, column=0, padx=20, pady=20, sticky="s")
|
||||
|
||||
def _create_main_content(self):
|
||||
"""创建主内容区域"""
|
||||
self.main_frame = ctk.CTkFrame(self.root, corner_radius=0, fg_color="transparent")
|
||||
self.main_frame.grid(row=0, column=1, sticky="nsew", padx=10, pady=10)
|
||||
self.main_frame.grid_columnconfigure(0, weight=1)
|
||||
self.main_frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
def _create_statusbar(self):
|
||||
"""创建状态栏"""
|
||||
self.statusbar = ctk.CTkFrame(self.root, height=30, corner_radius=0)
|
||||
self.statusbar.grid(row=1, column=1, sticky="ew", padx=10, pady=(0, 5))
|
||||
|
||||
self.status_label = ctk.CTkLabel(
|
||||
self.statusbar,
|
||||
text="就绪",
|
||||
font=ctk.CTkFont(size=12)
|
||||
)
|
||||
self.status_label.pack(side="left", padx=10)
|
||||
|
||||
self.version_label = ctk.CTkLabel(
|
||||
self.statusbar,
|
||||
text="v0.1",
|
||||
font=ctk.CTkFont(size=12),
|
||||
text_color="gray"
|
||||
)
|
||||
self.version_label.pack(side="right", padx=10)
|
||||
|
||||
def _init_pages(self):
|
||||
"""初始化所有页面 - 预加载提升切换速度"""
|
||||
self.pages["run"] = RunPage(self.main_frame, self)
|
||||
self.pages["daily"] = DailyConfigPage(self.main_frame, self)
|
||||
self.pages["misc"] = MiscConfigPage(self.main_frame, self)
|
||||
self.pages["pending"] = PendingConfigPage(self.main_frame, self)
|
||||
self.pages["about"] = AboutPage(self.main_frame, self)
|
||||
|
||||
# 预构建所有页面,避免懒加载导致的卡顿
|
||||
for page_id, page in self.pages.items():
|
||||
if page.frame is None:
|
||||
page.frame = page.build()
|
||||
|
||||
def show_page(self, page_id: str):
|
||||
"""显示指定页面 - 优化切换性能"""
|
||||
if self.current_page == page_id:
|
||||
return
|
||||
|
||||
# 使用 after 延迟更新UI,避免阻塞
|
||||
self.root.after(0, lambda: self._do_show_page(page_id))
|
||||
|
||||
def _do_show_page(self, page_id: str):
|
||||
"""实际执行页面切换"""
|
||||
# 隐藏当前页面
|
||||
if self.current_page:
|
||||
self.pages[self.current_page].hide()
|
||||
self.nav_buttons[self.current_page].configure(fg_color="transparent")
|
||||
|
||||
# 显示新页面
|
||||
self.current_page = page_id
|
||||
self.pages[page_id].show()
|
||||
self.nav_buttons[page_id].configure(fg_color=self.THEME_COLOR)
|
||||
|
||||
logger.debug(f"切换到页面: {page_id}")
|
||||
|
||||
def _toggle_theme(self):
|
||||
"""切换主题"""
|
||||
if self.theme_switch.get():
|
||||
ctk.set_appearance_mode("Dark")
|
||||
self.theme_switch.configure(text="☀ 浅色")
|
||||
else:
|
||||
ctk.set_appearance_mode("Light")
|
||||
self.theme_switch.configure(text="🌙 深色")
|
||||
|
||||
def set_status(self, text: str):
|
||||
"""设置状态栏文本"""
|
||||
self.status_label.configure(text=text)
|
||||
|
||||
def run(self):
|
||||
"""运行应用"""
|
||||
logger.info("启动LuoLuo应用")
|
||||
self.root.mainloop()
|
||||
19
src/ui/pages/__init__.py
Normal file
19
src/ui/pages/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
UI页面模块
|
||||
"""
|
||||
|
||||
from .base_page import BasePage
|
||||
from .run_page import RunPage
|
||||
from .daily_config_page import DailyConfigPage
|
||||
from .misc_config_page import MiscConfigPage
|
||||
from .pending_config_page import PendingConfigPage
|
||||
from .about_page import AboutPage
|
||||
|
||||
__all__ = [
|
||||
"BasePage",
|
||||
"RunPage",
|
||||
"DailyConfigPage",
|
||||
"MiscConfigPage",
|
||||
"PendingConfigPage",
|
||||
"AboutPage",
|
||||
]
|
||||
154
src/ui/pages/about_page.py
Normal file
154
src/ui/pages/about_page.py
Normal file
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
关于页面
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
|
||||
from .base_page import BasePage
|
||||
|
||||
|
||||
class AboutPage(BasePage):
|
||||
"""关于页面"""
|
||||
|
||||
# 项目信息
|
||||
VERSION = "0.1"
|
||||
AUTHOR = "moweishan"
|
||||
BLOG_URL = "https://bk.moweishan.top/"
|
||||
DESCRIPTION = "专为《桃源深处有人家》打造的\n日常任务挂机工具"
|
||||
|
||||
def build(self) -> ctk.CTkFrame:
|
||||
"""构建关于页面"""
|
||||
frame = ctk.CTkFrame(self.parent, fg_color="transparent")
|
||||
frame.grid_columnconfigure(0, weight=1)
|
||||
frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
# 居中容器
|
||||
center_frame = ctk.CTkFrame(frame, fg_color="transparent")
|
||||
center_frame.grid(row=0, column=0, pady=50)
|
||||
|
||||
# 加载并显示图标
|
||||
self._load_icon(center_frame)
|
||||
|
||||
# 应用名称
|
||||
name_label = ctk.CTkLabel(
|
||||
center_frame,
|
||||
text="LuoLuoTool",
|
||||
font=ctk.CTkFont(size=28, weight="bold")
|
||||
)
|
||||
name_label.pack()
|
||||
|
||||
# 版本号
|
||||
version_label = ctk.CTkLabel(
|
||||
center_frame,
|
||||
text=f"版本 {self.VERSION}",
|
||||
font=ctk.CTkFont(size=14),
|
||||
text_color="gray"
|
||||
)
|
||||
version_label.pack(pady=(5, 20))
|
||||
|
||||
# 分隔线
|
||||
separator = ctk.CTkFrame(center_frame, height=2, width=200)
|
||||
separator.pack(pady=10)
|
||||
|
||||
# 描述
|
||||
desc_label = ctk.CTkLabel(
|
||||
center_frame,
|
||||
text=self.DESCRIPTION,
|
||||
font=ctk.CTkFont(size=13),
|
||||
justify="center"
|
||||
)
|
||||
desc_label.pack(pady=20)
|
||||
|
||||
# 分隔线
|
||||
separator2 = ctk.CTkFrame(center_frame, height=2, width=200)
|
||||
separator2.pack(pady=10)
|
||||
|
||||
# 作者信息
|
||||
author_frame = ctk.CTkFrame(center_frame, fg_color="transparent")
|
||||
author_frame.pack(pady=20)
|
||||
|
||||
author_label = ctk.CTkLabel(
|
||||
author_frame,
|
||||
text=f"作者: {self.AUTHOR}",
|
||||
font=ctk.CTkFont(size=13)
|
||||
)
|
||||
author_label.pack()
|
||||
|
||||
# 博客链接
|
||||
blog_btn = ctk.CTkButton(
|
||||
author_frame,
|
||||
text="🌐 访问博客",
|
||||
command=self._open_blog
|
||||
)
|
||||
blog_btn.pack(pady=10)
|
||||
|
||||
# 按钮区域
|
||||
btn_frame = ctk.CTkFrame(center_frame, fg_color="transparent")
|
||||
btn_frame.pack(pady=20)
|
||||
|
||||
check_update_btn = ctk.CTkButton(
|
||||
btn_frame,
|
||||
text="🔍 检查更新",
|
||||
command=self._check_update
|
||||
)
|
||||
check_update_btn.pack(side="left", padx=5)
|
||||
|
||||
return frame
|
||||
|
||||
def _load_icon(self, parent):
|
||||
"""加载并显示图标"""
|
||||
assets_dir = Path(__file__).parent.parent.parent.parent / "assets" / "images"
|
||||
|
||||
# 优先使用 PNG 格式在关于页面显示
|
||||
png_path = assets_dir / "luoluoTool.png"
|
||||
ico_path = assets_dir / "luoluoTool.ico"
|
||||
|
||||
try:
|
||||
if png_path.exists():
|
||||
# 使用 PNG 图标
|
||||
icon_image = Image.open(png_path)
|
||||
# 调整大小为 100x100
|
||||
icon_image = icon_image.resize((100, 100), Image.Resampling.LANCZOS)
|
||||
icon_ctk = ctk.CTkImage(light_image=icon_image, dark_image=icon_image, size=(100, 100))
|
||||
|
||||
icon_label = ctk.CTkLabel(parent, image=icon_ctk, text="")
|
||||
icon_label.pack(pady=(0, 20))
|
||||
# 保持引用防止GC
|
||||
self._about_icon = icon_ctk
|
||||
elif ico_path.exists():
|
||||
# 如果没有PNG,尝试ICO
|
||||
icon_image = Image.open(ico_path)
|
||||
icon_image = icon_image.resize((100, 100), Image.Resampling.LANCZOS)
|
||||
icon_ctk = ctk.CTkImage(light_image=icon_image, dark_image=icon_image, size=(100, 100))
|
||||
|
||||
icon_label = ctk.CTkLabel(parent, image=icon_ctk, text="")
|
||||
icon_label.pack(pady=(0, 20))
|
||||
self._about_icon = icon_ctk
|
||||
else:
|
||||
# 使用默认emoji
|
||||
icon_label = ctk.CTkLabel(
|
||||
parent,
|
||||
text="🎮",
|
||||
font=ctk.CTkFont(size=64)
|
||||
)
|
||||
icon_label.pack(pady=(0, 20))
|
||||
except Exception as e:
|
||||
# 出错时使用默认emoji
|
||||
icon_label = ctk.CTkLabel(
|
||||
parent,
|
||||
text="🎮",
|
||||
font=ctk.CTkFont(size=64)
|
||||
)
|
||||
icon_label.pack(pady=(0, 20))
|
||||
|
||||
def _open_blog(self):
|
||||
"""打开作者博客"""
|
||||
webbrowser.open(self.BLOG_URL)
|
||||
|
||||
def _check_update(self):
|
||||
"""检查更新"""
|
||||
# TODO: 实现检查更新功能
|
||||
self.app.set_status("当前已是最新版本")
|
||||
40
src/ui/pages/base_page.py
Normal file
40
src/ui/pages/base_page.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
页面基类
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class BasePage:
|
||||
"""页面基类"""
|
||||
|
||||
def __init__(self, parent: ctk.CTkFrame, app: "LuoLuoApp"):
|
||||
self.parent = parent
|
||||
self.app = app
|
||||
self.frame: Optional[ctk.CTkFrame] = None
|
||||
|
||||
def build(self) -> ctk.CTkFrame:
|
||||
"""构建页面,子类重写"""
|
||||
raise NotImplementedError
|
||||
|
||||
def show(self):
|
||||
"""显示页面"""
|
||||
if self.frame is None:
|
||||
self.frame = self.build()
|
||||
self.frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.on_show()
|
||||
|
||||
def hide(self):
|
||||
"""隐藏页面"""
|
||||
if self.frame:
|
||||
self.frame.grid_forget()
|
||||
self.on_hide()
|
||||
|
||||
def on_show(self):
|
||||
"""页面显示时的回调,子类可重写"""
|
||||
pass
|
||||
|
||||
def on_hide(self):
|
||||
"""页面隐藏时的回调,子类可重写"""
|
||||
pass
|
||||
99
src/ui/pages/daily_config_page.py
Normal file
99
src/ui/pages/daily_config_page.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
日常挂机配置页面
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from .base_page import BasePage
|
||||
from ...core.tasks.daily_tasks import DailyTaskRunner
|
||||
|
||||
|
||||
class DailyConfigPage(BasePage):
|
||||
"""日常挂机配置页面"""
|
||||
|
||||
def __init__(self, parent: ctk.CTkFrame, app: "LuoLuoApp"):
|
||||
super().__init__(parent, app)
|
||||
self.task_runner = DailyTaskRunner()
|
||||
self.switches = {}
|
||||
|
||||
def build(self) -> ctk.CTkFrame:
|
||||
"""构建页面"""
|
||||
frame = ctk.CTkFrame(self.parent, fg_color="transparent")
|
||||
frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 标题
|
||||
title = ctk.CTkLabel(
|
||||
frame,
|
||||
text="📋 日常挂机配置",
|
||||
font=ctk.CTkFont(size=18, weight="bold")
|
||||
)
|
||||
title.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="w")
|
||||
|
||||
# 说明
|
||||
desc = ctk.CTkLabel(
|
||||
frame,
|
||||
text="配置每日自动执行的日常任务",
|
||||
font=ctk.CTkFont(size=12),
|
||||
text_color="gray"
|
||||
)
|
||||
desc.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="w")
|
||||
|
||||
# 任务列表
|
||||
tasks_frame = ctk.CTkFrame(frame)
|
||||
tasks_frame.grid(row=2, column=0, sticky="ew", padx=20, pady=10)
|
||||
tasks_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
tasks = self.task_runner.get_tasks()
|
||||
for idx, (task_id, config) in enumerate(tasks.items()):
|
||||
self._create_task_item(tasks_frame, task_id, config, idx)
|
||||
|
||||
# 保存按钮
|
||||
save_btn = ctk.CTkButton(
|
||||
frame,
|
||||
text="💾 保存配置",
|
||||
command=self._save_config
|
||||
)
|
||||
save_btn.grid(row=3, column=0, padx=20, pady=20, sticky="e")
|
||||
|
||||
return frame
|
||||
|
||||
def _create_task_item(self, parent, task_id: str, config: dict, row: int):
|
||||
"""创建任务项"""
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
frame.grid(row=row, column=0, sticky="ew", padx=10, pady=5)
|
||||
frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
# 开关
|
||||
switch = ctk.CTkSwitch(
|
||||
frame,
|
||||
text="",
|
||||
width=50
|
||||
)
|
||||
switch.grid(row=0, column=0, padx=(0, 10))
|
||||
if config["enabled"]:
|
||||
switch.select()
|
||||
self.switches[task_id] = switch
|
||||
|
||||
# 名称
|
||||
name_label = ctk.CTkLabel(
|
||||
frame,
|
||||
text=config["name"],
|
||||
font=ctk.CTkFont(size=13, weight="bold")
|
||||
)
|
||||
name_label.grid(row=0, column=1, sticky="w")
|
||||
|
||||
# 描述
|
||||
desc_label = ctk.CTkLabel(
|
||||
frame,
|
||||
text=config["description"],
|
||||
font=ctk.CTkFont(size=11),
|
||||
text_color="gray"
|
||||
)
|
||||
desc_label.grid(row=1, column=1, sticky="w")
|
||||
|
||||
def _save_config(self):
|
||||
"""保存配置"""
|
||||
for task_id, switch in self.switches.items():
|
||||
enabled = switch.get() == 1
|
||||
self.task_runner.update_task(task_id, enabled)
|
||||
self.app.set_status("日常配置已保存")
|
||||
99
src/ui/pages/misc_config_page.py
Normal file
99
src/ui/pages/misc_config_page.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
杂项功能配置页面
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from .base_page import BasePage
|
||||
from ...core.tasks.misc_tasks import MiscTaskRunner
|
||||
|
||||
|
||||
class MiscConfigPage(BasePage):
|
||||
"""杂项功能配置页面"""
|
||||
|
||||
def __init__(self, parent: ctk.CTkFrame, app: "LuoLuoApp"):
|
||||
super().__init__(parent, app)
|
||||
self.task_runner = MiscTaskRunner()
|
||||
self.switches = {}
|
||||
|
||||
def build(self) -> ctk.CTkFrame:
|
||||
"""构建页面"""
|
||||
frame = ctk.CTkFrame(self.parent, fg_color="transparent")
|
||||
frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 标题
|
||||
title = ctk.CTkLabel(
|
||||
frame,
|
||||
text="🔧 杂项功能配置",
|
||||
font=ctk.CTkFont(size=18, weight="bold")
|
||||
)
|
||||
title.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="w")
|
||||
|
||||
# 说明
|
||||
desc = ctk.CTkLabel(
|
||||
frame,
|
||||
text="配置挂机时的辅助功能",
|
||||
font=ctk.CTkFont(size=12),
|
||||
text_color="gray"
|
||||
)
|
||||
desc.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="w")
|
||||
|
||||
# 功能列表
|
||||
features_frame = ctk.CTkFrame(frame)
|
||||
features_frame.grid(row=2, column=0, sticky="ew", padx=20, pady=10)
|
||||
features_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
features = self.task_runner.get_features()
|
||||
for idx, (feature_id, config) in enumerate(features.items()):
|
||||
self._create_feature_item(features_frame, feature_id, config, idx)
|
||||
|
||||
# 保存按钮
|
||||
save_btn = ctk.CTkButton(
|
||||
frame,
|
||||
text="💾 保存配置",
|
||||
command=self._save_config
|
||||
)
|
||||
save_btn.grid(row=3, column=0, padx=20, pady=20, sticky="e")
|
||||
|
||||
return frame
|
||||
|
||||
def _create_feature_item(self, parent, feature_id: str, config: dict, row: int):
|
||||
"""创建功能项"""
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
frame.grid(row=row, column=0, sticky="ew", padx=10, pady=5)
|
||||
frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
# 开关
|
||||
switch = ctk.CTkSwitch(
|
||||
frame,
|
||||
text="",
|
||||
width=50
|
||||
)
|
||||
switch.grid(row=0, column=0, padx=(0, 10))
|
||||
if config["enabled"]:
|
||||
switch.select()
|
||||
self.switches[feature_id] = switch
|
||||
|
||||
# 名称
|
||||
name_label = ctk.CTkLabel(
|
||||
frame,
|
||||
text=config["name"],
|
||||
font=ctk.CTkFont(size=13, weight="bold")
|
||||
)
|
||||
name_label.grid(row=0, column=1, sticky="w")
|
||||
|
||||
# 描述
|
||||
desc_label = ctk.CTkLabel(
|
||||
frame,
|
||||
text=config["description"],
|
||||
font=ctk.CTkFont(size=11),
|
||||
text_color="gray"
|
||||
)
|
||||
desc_label.grid(row=1, column=1, sticky="w")
|
||||
|
||||
def _save_config(self):
|
||||
"""保存配置"""
|
||||
for feature_id, switch in self.switches.items():
|
||||
enabled = switch.get() == 1
|
||||
self.task_runner.update_feature(feature_id, enabled)
|
||||
self.app.set_status("杂项配置已保存")
|
||||
96
src/ui/pages/pending_config_page.py
Normal file
96
src/ui/pages/pending_config_page.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
待定功能配置页面
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from .base_page import BasePage
|
||||
from ...core.tasks.pending_tasks import PendingTaskRunner
|
||||
|
||||
|
||||
class PendingConfigPage(BasePage):
|
||||
"""待定功能配置页面"""
|
||||
|
||||
def __init__(self, parent: ctk.CTkFrame, app: "LuoLuoApp"):
|
||||
super().__init__(parent, app)
|
||||
self.task_runner = PendingTaskRunner()
|
||||
self.switches = {}
|
||||
|
||||
def build(self) -> ctk.CTkFrame:
|
||||
"""构建页面"""
|
||||
frame = ctk.CTkFrame(self.parent, fg_color="transparent")
|
||||
frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 标题
|
||||
title = ctk.CTkLabel(
|
||||
frame,
|
||||
text="⏳ 待定功能配置",
|
||||
font=ctk.CTkFont(size=18, weight="bold")
|
||||
)
|
||||
title.grid(row=0, column=0, padx=20, pady=(20, 10), sticky="w")
|
||||
|
||||
# 说明
|
||||
desc = ctk.CTkLabel(
|
||||
frame,
|
||||
text="预留功能配置(待开发)",
|
||||
font=ctk.CTkFont(size=12),
|
||||
text_color="gray"
|
||||
)
|
||||
desc.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="w")
|
||||
|
||||
# 功能列表
|
||||
features_frame = ctk.CTkFrame(frame)
|
||||
features_frame.grid(row=2, column=0, sticky="ew", padx=20, pady=10)
|
||||
features_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
features = self.task_runner.get_features()
|
||||
for idx, (feature_id, config) in enumerate(features.items()):
|
||||
self._create_feature_item(features_frame, feature_id, config, idx)
|
||||
|
||||
# 提示信息
|
||||
info_frame = ctk.CTkFrame(frame, fg_color=("#fff3cd", "#3d3a2a"))
|
||||
info_frame.grid(row=3, column=0, sticky="ew", padx=20, pady=20)
|
||||
|
||||
info_label = ctk.CTkLabel(
|
||||
info_frame,
|
||||
text="💡 这些功能正在开发中,敬请期待!",
|
||||
font=ctk.CTkFont(size=12)
|
||||
)
|
||||
info_label.pack(padx=20, pady=15)
|
||||
|
||||
return frame
|
||||
|
||||
def _create_feature_item(self, parent, feature_id: str, config: dict, row: int):
|
||||
"""创建功能项"""
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
frame.grid(row=row, column=0, sticky="ew", padx=10, pady=5)
|
||||
frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
# 开关(禁用状态)
|
||||
switch = ctk.CTkSwitch(
|
||||
frame,
|
||||
text="",
|
||||
width=50,
|
||||
state="disabled"
|
||||
)
|
||||
switch.grid(row=0, column=0, padx=(0, 10))
|
||||
if config["enabled"]:
|
||||
switch.select()
|
||||
self.switches[feature_id] = switch
|
||||
|
||||
# 名称
|
||||
name_label = ctk.CTkLabel(
|
||||
frame,
|
||||
text=config["name"],
|
||||
font=ctk.CTkFont(size=13, weight="bold")
|
||||
)
|
||||
name_label.grid(row=0, column=1, sticky="w")
|
||||
|
||||
# 描述
|
||||
desc_label = ctk.CTkLabel(
|
||||
frame,
|
||||
text=config["description"],
|
||||
font=ctk.CTkFont(size=11),
|
||||
text_color="gray"
|
||||
)
|
||||
desc_label.grid(row=1, column=1, sticky="w")
|
||||
200
src/ui/pages/run_page.py
Normal file
200
src/ui/pages/run_page.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
运行页面 - 主控制面板
|
||||
包含基础配置和运行控制
|
||||
"""
|
||||
|
||||
import customtkinter as ctk
|
||||
from src.utils.logger import logger
|
||||
|
||||
from .base_page import BasePage
|
||||
from ...core.automation import AutomationController
|
||||
|
||||
|
||||
class RunPage(BasePage):
|
||||
"""运行页面"""
|
||||
|
||||
def __init__(self, parent: ctk.CTkFrame, app: "LuoLuoApp"):
|
||||
super().__init__(parent, app)
|
||||
self.automation = AutomationController()
|
||||
self.automation.set_callback(self._on_automation_message)
|
||||
|
||||
self.log_textbox: Optional[ctk.CTkTextbox] = None
|
||||
|
||||
def build(self) -> ctk.CTkFrame:
|
||||
"""构建运行页面"""
|
||||
frame = ctk.CTkFrame(self.parent, fg_color="transparent")
|
||||
frame.grid_columnconfigure(0, weight=1)
|
||||
frame.grid_rowconfigure(3, weight=1)
|
||||
|
||||
# ===== 基础配置区域 =====
|
||||
config_frame = ctk.CTkFrame(frame)
|
||||
config_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
|
||||
config_frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
ctk.CTkLabel(
|
||||
config_frame,
|
||||
text="⚙️ 基础配置",
|
||||
font=ctk.CTkFont(size=14, weight="bold")
|
||||
).grid(row=0, column=0, columnspan=3, padx=10, pady=(10, 5), sticky="w")
|
||||
|
||||
# 游戏窗口
|
||||
ctk.CTkLabel(config_frame, text="游戏窗口:").grid(row=1, column=0, padx=10, pady=5, sticky="w")
|
||||
self.window_label = ctk.CTkLabel(config_frame, text="未捕获", text_color="gray")
|
||||
self.window_label.grid(row=1, column=1, padx=10, pady=5, sticky="w")
|
||||
self.capture_btn = ctk.CTkButton(
|
||||
config_frame,
|
||||
text="🔍 重新捕获",
|
||||
width=100,
|
||||
command=self._capture_window
|
||||
)
|
||||
self.capture_btn.grid(row=1, column=2, padx=10, pady=5)
|
||||
|
||||
# 运行热键
|
||||
ctk.CTkLabel(config_frame, text="运行热键:").grid(row=2, column=0, padx=10, pady=5, sticky="w")
|
||||
self.hotkey_entry = ctk.CTkEntry(config_frame, placeholder_text="F9")
|
||||
self.hotkey_entry.insert(0, "F9")
|
||||
self.hotkey_entry.grid(row=2, column=1, padx=10, pady=5, sticky="w")
|
||||
|
||||
# 日志等级
|
||||
ctk.CTkLabel(config_frame, text="日志等级:").grid(row=3, column=0, padx=10, pady=5, sticky="w")
|
||||
self.loglevel_combo = ctk.CTkComboBox(
|
||||
config_frame,
|
||||
values=["DEBUG", "INFO", "WARNING", "ERROR"],
|
||||
width=120
|
||||
)
|
||||
self.loglevel_combo.set("INFO")
|
||||
self.loglevel_combo.grid(row=3, column=1, padx=10, pady=5, sticky="w")
|
||||
|
||||
# ===== 窗口状态区域 =====
|
||||
status_frame = ctk.CTkFrame(frame)
|
||||
status_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5)
|
||||
|
||||
self.status_label = ctk.CTkLabel(
|
||||
status_frame,
|
||||
text="🎮 等待捕获游戏窗口...",
|
||||
font=ctk.CTkFont(size=12)
|
||||
)
|
||||
self.status_label.pack(padx=20, pady=15)
|
||||
|
||||
# ===== 控制按钮区域 =====
|
||||
control_frame = ctk.CTkFrame(frame, fg_color="transparent")
|
||||
control_frame.grid(row=2, column=0, sticky="ew", padx=5, pady=10)
|
||||
|
||||
self.start_btn = ctk.CTkButton(
|
||||
control_frame,
|
||||
text="🚀 开始挂机",
|
||||
font=ctk.CTkFont(size=14, weight="bold"),
|
||||
height=40,
|
||||
fg_color="#2ecc71",
|
||||
hover_color="#27ae60",
|
||||
command=self._start_automation
|
||||
)
|
||||
self.start_btn.pack(side="left", padx=5)
|
||||
|
||||
self.stop_btn = ctk.CTkButton(
|
||||
control_frame,
|
||||
text="⏹ 停止",
|
||||
font=ctk.CTkFont(size=14),
|
||||
height=40,
|
||||
fg_color="#e74c3c",
|
||||
hover_color="#c0392b",
|
||||
state="disabled",
|
||||
command=self._stop_automation
|
||||
)
|
||||
self.stop_btn.pack(side="left", padx=5)
|
||||
|
||||
self.pause_btn = ctk.CTkButton(
|
||||
control_frame,
|
||||
text="⏸ 暂停",
|
||||
font=ctk.CTkFont(size=14),
|
||||
height=40,
|
||||
state="disabled",
|
||||
command=self._pause_automation
|
||||
)
|
||||
self.pause_btn.pack(side="left", padx=5)
|
||||
|
||||
# ===== 日志区域 =====
|
||||
log_frame = ctk.CTkFrame(frame)
|
||||
log_frame.grid(row=3, column=0, sticky="nsew", padx=5, pady=5)
|
||||
log_frame.grid_columnconfigure(0, weight=1)
|
||||
log_frame.grid_rowconfigure(1, weight=1)
|
||||
|
||||
ctk.CTkLabel(
|
||||
log_frame,
|
||||
text="📋 运行日志",
|
||||
font=ctk.CTkFont(size=12, weight="bold")
|
||||
).grid(row=0, column=0, padx=10, pady=(10, 5), sticky="w")
|
||||
|
||||
self.log_textbox = ctk.CTkTextbox(
|
||||
log_frame,
|
||||
wrap="word",
|
||||
state="disabled",
|
||||
font=ctk.CTkFont(size=11)
|
||||
)
|
||||
self.log_textbox.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 10))
|
||||
|
||||
# 尝试自动捕获窗口
|
||||
self._capture_window()
|
||||
|
||||
return frame
|
||||
|
||||
def _capture_window(self):
|
||||
"""捕获游戏窗口"""
|
||||
if self.automation.window_manager.capture_window():
|
||||
size = self.automation.window_manager.client_size
|
||||
self.window_label.configure(
|
||||
text=f"桃源深处有人家 - {size[0]}x{size[1]}",
|
||||
text_color="#2ecc71"
|
||||
)
|
||||
self.status_label.configure(
|
||||
text=f"✅ 已捕获游戏窗口 ({size[0]}x{size[1]})",
|
||||
text_color="#2ecc71"
|
||||
)
|
||||
self.app.set_status("窗口已捕获")
|
||||
else:
|
||||
self.window_label.configure(text="未捕获", text_color="#e74c3c")
|
||||
self.status_label.configure(
|
||||
text="❌ 未找到游戏窗口,请确保游戏已运行",
|
||||
text_color="#e74c3c"
|
||||
)
|
||||
self.app.set_status("未找到游戏窗口")
|
||||
|
||||
def _start_automation(self):
|
||||
"""开始自动化"""
|
||||
self.automation.start()
|
||||
self.start_btn.configure(state="disabled")
|
||||
self.stop_btn.configure(state="normal")
|
||||
self.pause_btn.configure(state="normal", text="⏸ 暂停")
|
||||
self.app.set_status("运行中")
|
||||
|
||||
def _stop_automation(self):
|
||||
"""停止自动化"""
|
||||
self.automation.stop()
|
||||
self.start_btn.configure(state="normal")
|
||||
self.stop_btn.configure(state="disabled")
|
||||
self.pause_btn.configure(state="disabled", text="⏸ 暂停")
|
||||
self.app.set_status("已停止")
|
||||
|
||||
def _pause_automation(self):
|
||||
"""暂停/继续"""
|
||||
self.automation.pause()
|
||||
if self.automation.is_paused:
|
||||
self.pause_btn.configure(text="▶ 继续")
|
||||
self.app.set_status("已暂停")
|
||||
else:
|
||||
self.pause_btn.configure(text="⏸ 暂停")
|
||||
self.app.set_status("运行中")
|
||||
|
||||
def _on_automation_message(self, message: str):
|
||||
"""接收自动化消息"""
|
||||
self._add_log(message)
|
||||
|
||||
def _add_log(self, message: str):
|
||||
"""添加日志"""
|
||||
if self.log_textbox:
|
||||
from datetime import datetime
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
self.log_textbox.configure(state="normal")
|
||||
self.log_textbox.insert("end", f"[{timestamp}] {message}\n")
|
||||
self.log_textbox.see("end")
|
||||
self.log_textbox.configure(state="disabled")
|
||||
Reference in New Issue
Block a user