first commit
This commit is contained in:
commit
4890793ce5
1029 files changed
+5992
No files matched your search
@@ -0,0 +1,495 @@
|
||||
import customtkinter as ctk
|
||||
import logging
|
||||
import traceback
|
||||
import os
|
||||
from douyin_imbot.browser.browser_manager import BrowserManager
|
||||
from douyin_imbot.ui.profile_manager_panel import ProfileManagerPanel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MainWindow(ctk.CTk):
|
||||
"""抖音 IM 机器人主窗口."""
|
||||
|
||||
def __init__(self, settings=None):
|
||||
"""
|
||||
初始化主窗口.
|
||||
|
||||
Args:
|
||||
settings: 应用程序配置
|
||||
"""
|
||||
try:
|
||||
super().__init__()
|
||||
self.settings = settings or {}
|
||||
self.title("抖音自动回复机器人")
|
||||
self.geometry("1100x700")
|
||||
|
||||
# 设置主题和外观
|
||||
ctk.set_appearance_mode("System") # 系统主题
|
||||
ctk.set_default_color_theme("blue") # 蓝色主题
|
||||
|
||||
# 创建浏览器管理器
|
||||
self.browser_manager = BrowserManager(settings)
|
||||
|
||||
# 初始化变量
|
||||
self.current_profile = None
|
||||
self.profile_dialog = None
|
||||
|
||||
# 创建UI
|
||||
self._create_widgets()
|
||||
|
||||
logger.info("主窗口初始化完成")
|
||||
except Exception as e:
|
||||
logger.error(f"主窗口初始化失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
def _create_widgets(self):
|
||||
"""创建UI组件"""
|
||||
try:
|
||||
# 配置主网格
|
||||
self.grid_columnconfigure(1, weight=1)
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
|
||||
# 创建左侧导航栏
|
||||
self.sidebar_frame = ctk.CTkFrame(self, width=200, corner_radius=0)
|
||||
self.sidebar_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.sidebar_frame.grid_rowconfigure(5, weight=1) # 增加行数以适应新的按钮
|
||||
|
||||
# 标题
|
||||
self.app_title = ctk.CTkLabel(
|
||||
self.sidebar_frame,
|
||||
text="抖音 IM 机器人",
|
||||
font=ctk.CTkFont(size=20, weight="bold"),
|
||||
)
|
||||
self.app_title.grid(row=0, column=0, padx=20, pady=(20, 20))
|
||||
|
||||
# 状态指示器
|
||||
self.status_frame = ctk.CTkFrame(self.sidebar_frame)
|
||||
self.status_frame.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="ew")
|
||||
|
||||
self.status_label = ctk.CTkLabel(self.status_frame, text="状态:")
|
||||
self.status_label.grid(row=0, column=0, padx=10, pady=5, sticky="w")
|
||||
|
||||
self.status_value = ctk.CTkLabel(
|
||||
self.status_frame, text="未启动", text_color="gray"
|
||||
)
|
||||
self.status_value.grid(row=0, column=1, padx=10, pady=5, sticky="e")
|
||||
|
||||
# 导航按钮 - 修改为只有控制面板和设置按钮
|
||||
self.dashboard_button = ctk.CTkButton(
|
||||
self.sidebar_frame,
|
||||
text="控制面板",
|
||||
height=40,
|
||||
command=self.show_dashboard,
|
||||
)
|
||||
self.dashboard_button.grid(row=2, column=0, padx=20, pady=10, sticky="ew")
|
||||
|
||||
self.settings_button = ctk.CTkButton(
|
||||
self.sidebar_frame, text="设置", height=40, command=self.show_settings
|
||||
)
|
||||
self.settings_button.grid(row=3, column=0, padx=20, pady=10, sticky="ew")
|
||||
|
||||
self.about_button = ctk.CTkButton(
|
||||
self.sidebar_frame, text="关于", height=40, command=self.show_about
|
||||
)
|
||||
self.about_button.grid(row=4, column=0, padx=20, pady=10, sticky="ew")
|
||||
|
||||
# 主内容区域 - 使用帧切换而不是选项卡
|
||||
self.main_content = ctk.CTkFrame(self)
|
||||
self.main_content.grid(row=0, column=1, padx=20, pady=20, sticky="nsew")
|
||||
self.main_content.grid_columnconfigure(0, weight=1)
|
||||
self.main_content.grid_rowconfigure(0, weight=1)
|
||||
|
||||
# 创建各个内容区域的帧,但暂时不显示
|
||||
self.dashboard_frame = ctk.CTkFrame(self.main_content)
|
||||
self.settings_frame = ctk.CTkFrame(self.main_content)
|
||||
self.about_frame = ctk.CTkFrame(self.main_content)
|
||||
|
||||
# 初始化各个内容区域
|
||||
self.setup_dashboard()
|
||||
self.setup_settings_panel()
|
||||
self.setup_about_panel()
|
||||
|
||||
# 默认显示控制面板
|
||||
self.show_dashboard()
|
||||
|
||||
# 确保配置文件列表在UI加载后刷新
|
||||
self.after(500, self.refresh_profile_list)
|
||||
|
||||
logger.info("UI 组件创建完成")
|
||||
except Exception as e:
|
||||
logger.error(f"创建UI组件失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"UI初始化错误: {str(e)}")
|
||||
|
||||
def setup_dashboard(self):
|
||||
"""设置控制面板."""
|
||||
try:
|
||||
# 配置控制面板帧
|
||||
self.dashboard_frame.grid_columnconfigure(0, weight=1)
|
||||
self.dashboard_frame.grid_rowconfigure(
|
||||
2, weight=1
|
||||
) # 减少行数,因为移除了控制按钮区域
|
||||
|
||||
|
||||
# 集成浏览器配置管理面板
|
||||
self.profile_manager_frame = ctk.CTkFrame(self.dashboard_frame)
|
||||
self.profile_manager_frame.grid(
|
||||
row=1, column=0, padx=10, pady=10, sticky="ew"
|
||||
)
|
||||
|
||||
# 创建配置管理器
|
||||
self.profile_manager = ProfileManagerPanel(
|
||||
self.profile_manager_frame,
|
||||
self.browser_manager,
|
||||
on_profile_change=self.on_profile_change,
|
||||
)
|
||||
self.profile_manager.pack(fill="both", expand=True)
|
||||
|
||||
# 移除控制按钮区域
|
||||
|
||||
# 日志区域
|
||||
log_frame = ctk.CTkFrame(self.dashboard_frame)
|
||||
log_frame.grid(row=2, column=0, padx=10, pady=10, sticky="nsew")
|
||||
log_frame.grid_columnconfigure(0, weight=1)
|
||||
log_frame.grid_rowconfigure(1, weight=1)
|
||||
|
||||
log_label = ctk.CTkLabel(
|
||||
log_frame, text="运行日志:", anchor="w", font=ctk.CTkFont(size=16)
|
||||
)
|
||||
log_label.grid(row=0, column=0, padx=10, pady=(10, 5), sticky="w")
|
||||
|
||||
self.log_textbox = ctk.CTkTextbox(log_frame, height=200)
|
||||
self.log_textbox.grid(row=1, column=0, padx=10, pady=(0, 10), sticky="nsew")
|
||||
self.log_textbox.insert("0.0", "准备就绪...\n")
|
||||
self.log_textbox.configure(state="disabled")
|
||||
|
||||
# 清除日志按钮
|
||||
self.clear_log_button = ctk.CTkButton(
|
||||
log_frame, text="清除日志", command=self.clear_log, width=100
|
||||
)
|
||||
self.clear_log_button.grid(
|
||||
row=2, column=0, padx=10, pady=(0, 10), sticky="e"
|
||||
)
|
||||
|
||||
logger.info("控制面板设置完成")
|
||||
except Exception as e:
|
||||
logger.error(f"设置控制面板失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"控制面板初始化错误: {str(e)}")
|
||||
|
||||
def setup_settings_panel(self):
|
||||
"""设置设置面板."""
|
||||
try:
|
||||
self.settings_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 标题
|
||||
title = ctk.CTkLabel(
|
||||
self.settings_frame,
|
||||
text="设置",
|
||||
font=ctk.CTkFont(size=24, weight="bold"),
|
||||
)
|
||||
title.grid(row=0, column=0, padx=20, pady=20)
|
||||
|
||||
# 设置选项
|
||||
options_frame = ctk.CTkFrame(self.settings_frame)
|
||||
options_frame.grid(row=1, column=0, padx=20, pady=10, sticky="ew")
|
||||
options_frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
# API 密钥设置
|
||||
api_label = ctk.CTkLabel(options_frame, text="API 密钥:", width=150)
|
||||
api_label.grid(row=0, column=0, padx=10, pady=15, sticky="w")
|
||||
|
||||
self.entry_api_key = ctk.CTkEntry(options_frame, width=400)
|
||||
self.entry_api_key.grid(row=0, column=1, padx=10, pady=15, sticky="ew")
|
||||
self.entry_api_key.insert(0, self.settings.get("api_key", ""))
|
||||
|
||||
# 消息间隔设置
|
||||
interval_label = ctk.CTkLabel(
|
||||
options_frame, text="消息间隔 (秒):", width=150
|
||||
)
|
||||
interval_label.grid(row=1, column=0, padx=10, pady=15, sticky="w")
|
||||
|
||||
self.entry_message_interval = ctk.CTkEntry(options_frame, width=400)
|
||||
self.entry_message_interval.grid(
|
||||
row=1, column=1, padx=10, pady=15, sticky="ew"
|
||||
)
|
||||
self.entry_message_interval.insert(
|
||||
0, self.settings.get("message_interval", "5")
|
||||
)
|
||||
|
||||
# 高级设置区域
|
||||
advanced_frame = ctk.CTkFrame(self.settings_frame)
|
||||
advanced_frame.grid(row=2, column=0, padx=20, pady=(20, 10), sticky="ew")
|
||||
|
||||
advanced_label = ctk.CTkLabel(
|
||||
advanced_frame,
|
||||
text="高级设置",
|
||||
font=ctk.CTkFont(size=18, weight="bold"),
|
||||
)
|
||||
advanced_label.pack(pady=10, padx=10, anchor="w")
|
||||
|
||||
# 保存按钮
|
||||
save_button = ctk.CTkButton(
|
||||
self.settings_frame,
|
||||
text="保存设置",
|
||||
command=self.save_settings,
|
||||
width=200,
|
||||
height=40,
|
||||
font=ctk.CTkFont(size=16),
|
||||
)
|
||||
save_button.grid(row=3, column=0, pady=30)
|
||||
|
||||
logger.info("设置面板设置完成")
|
||||
except Exception as e:
|
||||
logger.error(f"设置设置面板失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"设置面板初始化错误: {str(e)}")
|
||||
|
||||
def setup_about_panel(self):
|
||||
"""设置关于面板."""
|
||||
try:
|
||||
self.about_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 标题
|
||||
title = ctk.CTkLabel(
|
||||
self.about_frame, text="关于", font=ctk.CTkFont(size=24, weight="bold")
|
||||
)
|
||||
title.grid(row=0, column=0, padx=20, pady=20)
|
||||
|
||||
# 信息卡片
|
||||
info_frame = ctk.CTkFrame(self.about_frame)
|
||||
info_frame.grid(row=1, column=0, padx=40, pady=20, sticky="ew")
|
||||
|
||||
version_label = ctk.CTkLabel(
|
||||
info_frame, text="抖音 IM 机器人 v1.0.0", font=ctk.CTkFont(size=18)
|
||||
)
|
||||
version_label.pack(pady=(20, 10))
|
||||
|
||||
description = ctk.CTkLabel(
|
||||
info_frame,
|
||||
text="抖音 IM 机器人是一个自动回复工具,可以帮助你自动处理抖音私信。",
|
||||
wraplength=500,
|
||||
font=ctk.CTkFont(size=14),
|
||||
)
|
||||
description.pack(pady=10, padx=30)
|
||||
|
||||
# 添加更多关于信息
|
||||
features_label = ctk.CTkLabel(
|
||||
info_frame,
|
||||
text="主要功能:",
|
||||
font=ctk.CTkFont(size=16, weight="bold"),
|
||||
anchor="w",
|
||||
)
|
||||
features_label.pack(pady=(20, 10), padx=30, anchor="w")
|
||||
|
||||
features_text = """• 自动回复抖音私信
|
||||
• 支持多种浏览器配置
|
||||
• 自定义回复内容
|
||||
• 日志记录与监控"""
|
||||
|
||||
features = ctk.CTkLabel(
|
||||
info_frame,
|
||||
text=features_text,
|
||||
font=ctk.CTkFont(size=14),
|
||||
justify="left",
|
||||
anchor="w",
|
||||
)
|
||||
features.pack(pady=5, padx=30, anchor="w")
|
||||
|
||||
copyright_label = ctk.CTkLabel(
|
||||
info_frame, text="© 2023 版权所有", font=ctk.CTkFont(size=12)
|
||||
)
|
||||
copyright_label.pack(pady=20)
|
||||
|
||||
logger.info("关于面板设置完成")
|
||||
except Exception as e:
|
||||
logger.error(f"设置关于面板失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"关于面板初始化错误: {str(e)}")
|
||||
|
||||
def show_dashboard(self):
|
||||
"""显示控制面板."""
|
||||
try:
|
||||
self._hide_all_frames()
|
||||
self.dashboard_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.highlight_button(self.dashboard_button)
|
||||
logger.debug("显示控制面板")
|
||||
except Exception as e:
|
||||
logger.error(f"显示控制面板失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def show_settings(self):
|
||||
"""显示设置界面."""
|
||||
try:
|
||||
self._hide_all_frames()
|
||||
self.settings_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.highlight_button(self.settings_button)
|
||||
logger.debug("显示设置面板")
|
||||
except Exception as e:
|
||||
logger.error(f"显示设置面板失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def show_about(self):
|
||||
"""显示关于界面."""
|
||||
try:
|
||||
self._hide_all_frames()
|
||||
self.about_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.highlight_button(self.about_button)
|
||||
logger.debug("显示关于面板")
|
||||
except Exception as e:
|
||||
logger.error(f"显示关于面板失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def _hide_all_frames(self):
|
||||
"""隐藏所有内容帧."""
|
||||
try:
|
||||
for frame in [
|
||||
self.dashboard_frame,
|
||||
self.settings_frame,
|
||||
self.about_frame,
|
||||
]:
|
||||
frame.grid_forget()
|
||||
except Exception as e:
|
||||
logger.error(f"隐藏内容帧失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def highlight_button(self, active_button):
|
||||
"""高亮当前活动按钮."""
|
||||
try:
|
||||
for button in [
|
||||
self.dashboard_button,
|
||||
self.settings_button,
|
||||
self.about_button,
|
||||
]:
|
||||
if button == active_button:
|
||||
button.configure(
|
||||
fg_color=["#3a7ebf", "#1f538d"]
|
||||
) # 使用主题较深的颜色
|
||||
else:
|
||||
button.configure(fg_color=["#3B8ED0", "#1E6FBA"]) # 恢复默认颜色
|
||||
except Exception as e:
|
||||
logger.error(f"高亮按钮失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def update_log(self, message):
|
||||
"""更新日志文本框."""
|
||||
try:
|
||||
if hasattr(self, "log_textbox") and self.log_textbox:
|
||||
self.log_textbox.configure(state="normal")
|
||||
self.log_textbox.insert("end", f"{message}\n")
|
||||
self.log_textbox.see("end")
|
||||
self.log_textbox.configure(state="disabled")
|
||||
else:
|
||||
logger.warning(f"日志无法更新,日志组件未初始化: {message}")
|
||||
except Exception as e:
|
||||
logger.error(f"更新日志失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def clear_log(self):
|
||||
"""清除日志内容."""
|
||||
try:
|
||||
self.log_textbox.configure(state="normal")
|
||||
self.log_textbox.delete("0.0", "end")
|
||||
self.log_textbox.insert("0.0", "日志已清除...\n")
|
||||
self.log_textbox.configure(state="disabled")
|
||||
logger.info("日志已清除")
|
||||
except Exception as e:
|
||||
logger.error(f"清除日志失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def save_settings(self):
|
||||
"""保存设置."""
|
||||
try:
|
||||
# 收集所有设置项
|
||||
for key in ["api_key", "message_interval"]:
|
||||
entry = getattr(self, f"entry_{key}", None)
|
||||
if entry:
|
||||
self.settings[key] = entry.get()
|
||||
|
||||
# 保存设置到文件
|
||||
from douyin_imbot.config.settings import save_settings
|
||||
|
||||
if save_settings(self.settings):
|
||||
logger.info("设置已保存")
|
||||
self.update_log("设置已保存")
|
||||
else:
|
||||
logger.error("保存设置失败")
|
||||
self.update_log("保存设置失败")
|
||||
except Exception as e:
|
||||
logger.error(f"保存设置失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"保存设置失败: {str(e)}")
|
||||
|
||||
def on_profile_selected(self, profile_name):
|
||||
"""当用户选择不同的配置文件时触发"""
|
||||
try:
|
||||
if not profile_name:
|
||||
logger.warning("配置文件名为空")
|
||||
self.update_log("选择了无效的配置文件")
|
||||
return
|
||||
|
||||
if profile_name == "无可用配置":
|
||||
logger.warning("选择了无效的配置文件")
|
||||
self.update_log("选择了无效的配置文件")
|
||||
return
|
||||
|
||||
self.current_profile = profile_name
|
||||
logger.info(f"用户选择配置文件: {profile_name}")
|
||||
self.update_log(f"已选择配置文件: {profile_name}")
|
||||
|
||||
# 更新配置文件详情显示
|
||||
try:
|
||||
profile_details = (
|
||||
self.browser_manager.get_profile_details(profile_name) or {}
|
||||
)
|
||||
details_text = (
|
||||
f"用户目录: {profile_details.get('user_data_dir', '无')}\n"
|
||||
)
|
||||
details_text += f"上次使用: {profile_details.get('last_used', '从未')}"
|
||||
except Exception as detail_error:
|
||||
logger.error(f"获取配置详情失败: {str(detail_error)}")
|
||||
logger.error(traceback.format_exc())
|
||||
details_text = "获取配置详情失败"
|
||||
|
||||
# 检查组件是否已创建
|
||||
if hasattr(self, "profile_details_label") and self.profile_details_label:
|
||||
self.profile_details_label.configure(text=details_text)
|
||||
else:
|
||||
logger.warning("配置详情标签未创建")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"选择配置文件时出错: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"选择配置文件时出错: {str(e)}")
|
||||
|
||||
def refresh_profile_list(self):
|
||||
"""刷新配置文件列表"""
|
||||
try:
|
||||
# 使用配置管理器刷新配置
|
||||
if hasattr(self, "profile_manager") and self.profile_manager:
|
||||
self.profile_manager.refresh_profiles()
|
||||
self.update_log("配置文件列表已刷新")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"刷新配置文件列表失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"刷新配置文件列表失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def on_profile_change(self):
|
||||
"""
|
||||
当浏览器配置文件发生变化时调用
|
||||
可以在这里更新UI或执行其他操作
|
||||
"""
|
||||
try:
|
||||
if hasattr(self, "refresh_profile_list"):
|
||||
# 使用after方法避免在对话框销毁期间刷新UI
|
||||
self.after(100, self.refresh_profile_list)
|
||||
logger.info("配置文件已更改,列表已刷新")
|
||||
except Exception as e:
|
||||
logger.error(f"响应配置文件变化失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
self.update_log(f"更新配置列表失败: {str(e)}")
|
||||
Reference in new issue
Block a user