整理模块1界面代码,生成完整代码#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 标准库导入
import json
import os
import sys
import tkinter as tk
import time
import uuid
from pathlib import Path
from tkinter import ttk, messagebox, scrolledtext
import logging
from collections import defaultdict, Counter
import queue
import threading
# 项目路径设置
def add_project_to_path():
"""确保项目根目录在Python路径中"""
project_root = Path(__file__).parent.parent.absolute()
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
add_project_to_path()
# 核心模块导入
try:
from core.global_config import GlobalConfig
from core.event_system import Event, EventType
from core.event_center import event_center
print("成功导入核心模块")
except ImportError as import_err:
print(f"核心导入失败: {import_err}")
sys.exit(1)
# 本地模块导入
try:
from input_analysis import InputAnalysisModule
except ImportError as local_err:
print(f"输入分析模块导入失败: {local_err}")
sys.exit(1)
class InputAnalysisUI:
def __init__(self, parent_frame):
self.logger = logging.getLogger(f"{__name__}.InputAnalysisUI")
self.parent = parent_frame
self.token = str(uuid.uuid4())
self._is_analyzing = False
self._last_analysis_time = None
self._setup_dynamic_area()
self._register_dynamic_handlers()
try:
# 修复:正确初始化模块
self.module = InputAnalysisModule(event_center, EventType)
# 修复:正确注册回调函数
self.module.register_callback("analysis_result", self._handle_analysis_result)
except Exception as init_err:
messagebox.showerror("初始化错误", f"模块初始化失败: {init_err}")
raise
self.labels = ["排除号码", "前区", "后区", "推荐号码", "前区", "后区"]
self.dynamic_data = {
"排除号码": {"前区": [], "后区": []},
"推荐号码": {"前区": [], "后区": []}
}
self._setup_ui()
self._register_event_handlers()
self._load_saved_data()
def _setup_dynamic_area(self):
"""设置动态区UI"""
dynamic_frame = ttk.LabelFrame(self.frame, text=" 动态区 ")
dynamic_frame.pack(fill=tk.X, padx=20, pady=10)
# 推荐号码标签
ttk.Label(dynamic_frame, text="推荐号码").pack(anchor=tk.W, padx=10, pady=5)
# 前区推荐
front_frame = ttk.Frame(dynamic_frame)
front_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(front_frame, text="前区:").pack(side=tk.LEFT)
self.dynamic_front_var = tk.StringVar()
ttk.Label(front_frame, textvariable=self.dynamic_front_var).pack(side=tk.LEFT, padx=5)
# 后区推荐
back_frame = ttk.Frame(dynamic_frame)
back_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(back_frame, text="后区:").pack(side=tk.LEFT)
self.dynamic_back_var = tk.StringVar()
ttk.Label(back_frame, textvariable=self.dynamic_back_var).pack(side=tk.LEFT, padx=5)
# 动态区按钮
btn_frame = ttk.Frame(dynamic_frame)
btn_frame.pack(fill=tk.X, pady=5)
buttons = [
("运行", self.run_analysis),
("清除", self.clear_dynamic_data),
("保存", self.save_dynamic_data),
("刷新", self.refresh_to_pool)
]
for text, cmd in buttons:
ttk.Button(btn_frame, text=text, command=cmd, width=8).pack(
side=tk.LEFT, padx=5
)
def _register_dynamic_handlers(self):
"""注册动态区事件处理器"""
event_center.subscribe(
event_type=EventType.DYNAMIC_UPDATE,
token=self.token,
callback=self._handle_dynamic_update
)
def _handle_dynamic_update(self, event: Event):
"""处理动态区更新事件"""
if event.source != GlobalConfig.MODULE1_ID:
return
data = event.data.get("推荐号码", {})
self.dynamic_labels["推荐号码"] = data
# 更新UI
self.dynamic_front_var.set(", ".join(map(str, data.get("前区", []))))
self.dynamic_back_var.set(", ".join(map(str, data.get("后区", []))))
def run_analysis(self):
"""运行动态区分析"""
self._on_analyze_clicked()
def clear_dynamic_data(self):
"""清除动态区数据"""
self.dynamic_labels["推荐号码"] = {"前区": [], "后区": []}
self.dynamic_front_var.set("")
self.dynamic_back_var.set("")
logger.info("动态区数据已清除")
def save_dynamic_data(self):
"""保存动态区数据"""
try:
save_path = os.path.join(GlobalConfig.DESKTOP_PATH, "动态区数据.json")
with open(save_path, 'w', encoding='utf-8') as f:
json.dump(self.dynamic_labels, f, ensure_ascii=False, indent=2)
logger.info(f"动态区数据已保存至: {save_path}")
messagebox.showinfo("保存成功", f"数据已保存至:\n{save_path}")
except Exception as e:
logger.error(f"保存失败: {e}")
messagebox.showerror("保存失败", str(e))
def refresh_to_pool(self):
"""刷新数据到号码池"""
# 创建刷新事件
refresh_event = Event(
event_id=str(uuid.uuid4()),
type=EventType.POOL_UPDATE,
source=self.token,
target='number_pool',
data={
"前区:": self.dynamic_labels["推荐号码"].get("前区", []),
"后区:": self.dynamic_labels["推荐号码"].get("后区", [])
}
)
event_center.publish(refresh_event)
logger.info("动态区数据已刷新到号码池")
messagebox.showinfo("刷新成功", "数据已刷新到号码池")
@staticmethod
def _validate_number(text: str, min_val: int, max_val: int) -> bool:
"""验证输入的数字是否在指定范围内"""
if not text:
return True
return text.isdigit() and min_val <= int(text) <= max_val
def _validate_inputs(self):
"""验证所有输入有效性"""
valid = True
for entry in self.exclude_front_entries:
if entry.get() and not self._validate_number(entry.get(), 1, 35):
entry.config(foreground='red')
valid = False
else:
entry.config(foreground='black')
for entry in self.exclude_back_entries:
if entry.get() and not self._validate_number(entry.get(), 1, 12):
entry.config(foreground='red')
valid = False
else:
entry.config(foreground='black')
return valid
@staticmethod
def _format_number(num_str: str) -> str:
"""格式化数字为两位数"""
if not num_str.isdigit():
return num_str
num = int(num_str)
return f"{num:02d}" if 1 <= num <= 9 else str(num)
def _setup_ui(self):
"""初始化UI组件 - 按照模块3的成功设计"""
self.frame = ttk.LabelFrame(
self.parent, text="输入分析模块", font=('微软雅黑', 12, 'bold')
)
self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 模块ID标识
module_id_label = ttk.Label(
self.frame, text=f"模块ID: {GlobalConfig.MODULE_IDS['MODULE1_ID']}",
font=('微软雅黑', 8), foreground='gray'
)
module_id_label.pack(anchor=tk.NE, padx=10, pady=5)
# === 排除号码区 ===
exclude_frame = ttk.LabelFrame(self.frame, text=" 排除号码 ", font=('微软雅黑', 10, 'bold'))
exclude_frame.pack(fill=tk.X, padx=20, pady=10, ipady=5)
# 排除号码标题
ttk.Label(exclude_frame, text=self.labels[0], font=('微软雅黑', 10, 'bold')).pack(
anchor=tk.W, padx=10, pady=(5, 0))
# 前区排除
ttk.Label(exclude_frame, text=self.labels[1], font=('微软雅黑', 10)).pack(
anchor=tk.W, padx=10, pady=5)
front_entries_frame = ttk.Frame(exclude_frame)
front_entries_frame.pack(fill=tk.X, padx=10, pady=5)
self.exclude_front_entries = []
for i in range(10): # 10个前区输入框
entry_frame = ttk.Frame(front_entries_frame)
entry_frame.pack(side=tk.LEFT, padx=2)
ttk.Label(entry_frame, text=f"{i + 1}:").pack(side=tk.LEFT)
entry = ttk.Entry(entry_frame, width=3, font=('微软雅黑', 10))
entry.pack(side=tk.LEFT)
# 添加验证函数 - 前区限制1-35
entry.config(validate="key", validatecommand=(
entry.register(lambda text: self._validate_number(text, 1, 35)), '%P'))
entry.bind("<KeyRelease>", self._auto_format_entry)
entry.bind("<Left>", lambda e, d=-1: self._navigate_entry(e, d))
entry.bind("<Right>", lambda e, d=1: self._navigate_entry(e, d))
self.exclude_front_entries.append(entry)
# 后区排除
ttk.Label(exclude_frame, text=self.labels[2], font=('微软雅黑', 10)).pack(
anchor=tk.W, padx=10, pady=(10, 5))
back_entries_frame = ttk.Frame(exclude_frame)
back_entries_frame.pack(fill=tk.X, padx=10, pady=5)
self.exclude_back_entries = []
for i in range(10): # 10个后区输入框
entry_frame = ttk.Frame(back_entries_frame)
entry_frame.pack(side=tk.LEFT, padx=2)
ttk.Label(entry_frame, text=f"{i + 1}:").pack(side=tk.LEFT)
entry = ttk.Entry(entry_frame, width=3, font=('微软雅黑', 10))
entry.pack(side=tk.LEFT)
# 添加验证函数 - 后区限制1-12
entry.config(validate="key", validatecommand=(
entry.register(lambda text: self._validate_number(text, 1, 12)), '%P'))
entry.bind("<KeyRelease>", self._auto_format_entry)
entry.bind("<Left>", lambda e, d=-1: self._navigate_entry(e, d))
entry.bind("<Right>", lambda e, d=1: self._navigate_entry(e, d))
self.exclude_back_entries.append(entry)
# === 推荐号码区 ===
recommend_frame = ttk.LabelFrame(self.frame, text=" 推荐号码 ", font=('微软雅黑', 10, 'bold'))
recommend_frame.pack(fill=tk.X, padx=20, pady=10, ipady=5)
# 推荐号码标题
ttk.Label(recommend_frame, text=self.labels[3], font=('微软雅黑', 10, 'bold')).pack(
anchor=tk.W, padx=10, pady=(5, 0))
# 前区推荐
front_rec_frame = ttk.Frame(recommend_frame)
front_rec_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(front_rec_frame, text=self.labels[4], font=('微软雅黑', 10)).pack(side=tk.LEFT)
self.recommend_front_var = tk.StringVar()
ttk.Entry(
front_rec_frame, textvariable=self.recommend_front_var,
state='readonly', font=('微软雅黑', 10),
readonlybackground='#f0f5f0'
).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
# 后区推荐
back_rec_frame = ttk.Frame(recommend_frame)
back_rec_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(back_rec_frame, text=self.labels[5], font=('微软雅黑', 10)).pack(side=tk.LEFT)
self.recommend_back_var = tk.StringVar()
ttk.Entry(
back_rec_frame, textvariable=self.recommend_back_var,
state='readonly', font=('微软雅黑', 10),
readonlybackground='#f0f5f0'
).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
# === 结果区 ===
result_frame = ttk.LabelFrame(self.frame, text=" 分析结果 ", font=('微软雅黑', 10, 'bold'))
result_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10, ipady=5)
# 结果文本框
self.result_text = scrolledtext.ScrolledText(
result_frame, wrap=tk.WORD, font=('微软雅黑', 10), height=6
)
self.result_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
self.result_text.insert(tk.END, "请设置排除号码后点击'运行分析'按钮开始分析\n")
self.result_text.config(state=tk.DISABLED)
# === 控制按钮区域(右下角对齐)===
btn_frame = ttk.Frame(self.frame)
btn_frame.pack(fill=tk.X, pady=10, anchor=tk.SE)
# 按钮容器(右对齐)
btn_container = ttk.Frame(btn_frame)
btn_container.pack(side=tk.RIGHT, padx=5)
buttons = [
("运行", self._on_analyze_clicked),
("保存", self._save_config),
("清除", self._clear_entries),
("刷新", self._refresh_data)
]
for text, command in buttons:
btn = ttk.Button(btn_container, text=text, width=10, command=command)
btn.pack(side=tk.LEFT, padx=2)
self.analyze_btn = btn_container.winfo_children()[0] # 获取运行分析按钮引用
def _safe_update_ui(self, callback, *args):
"""线程安全的UI更新"""
if not self.frame.winfo_exists():
return
self.frame.after(0, lambda: callback(*args))
def _register_event_handlers(self):
"""注册事件处理器"""
if hasattr(EventType, 'ANALYSIS_RESULT'):
event_center.subscribe(
event_type=EventType.ANALYSIS_RESULT,
token=self.token,
callback=self._handle_analysis_result
)
else:
self.logger.warning("EventType缺少ANALYSIS_RESULT定义")
def _handle_module_run(self, event):
"""处理模块运行事件"""
if not hasattr(event, 'data'):
self.logger.error("收到无效的运行事件: 缺少data属性")
return
if event.target != getattr(self.module, 'MODULE_ID', 'input_analysis'):
return
self._on_analyze_clicked()
def _load_saved_data(self):
"""加载保存的数据"""
save_path = GlobalConfig.MODULE1_SAVE_PATH
if not os.path.exists(save_path):
return
try:
with open(save_path, 'r', encoding='utf-8') as f:
saved_data = json.load(f)
# 恢复前区排除号码
if 'exclude_front' in saved_data:
for i, num in enumerate(saved_data['exclude_front']):
if i < len(self.exclude_front_entries) and num:
try:
formatted_num = f"{int(num):02d}"
self.exclude_front_entries[i].delete(0, tk.END)
self.exclude_front_entries[i].insert(0, formatted_num)
except ValueError:
continue
# 恢复后区排除号码
if 'exclude_back' in saved_data:
for i, num in enumerate(saved_data['exclude_back']):
if i < len(self.exclude_back_entries) and num:
try:
formatted_num = f"{int(num):02d}"
self.exclude_back_entries[i].delete(0, tk.END)
self.exclude_back_entries[i].insert(0, formatted_num)
except ValueError:
continue
except Exception as load_err:
self.logger.error(f"加载保存数据失败: {load_err}")
self._update_result_text(f"加载保存数据失败: {load_err}")
def _save_config(self):
"""保存当前配置"""
save_data = {
"exclude_front": [],
"exclude_back": []
}
# 收集前区排除号码
for entry in self.exclude_front_entries:
if entry.get().strip():
try:
save_data["exclude_front"].append(int(entry.get()))
except ValueError:
pass
# 收集后区排除号码
for entry in self.exclude_back_entries:
if entry.get().strip():
try:
save_data["exclude_back"].append(int(entry.get()))
except ValueError:
pass
# 保存到文件
try:
with open(GlobalConfig.MODULE1_SAVE_PATH, 'w', encoding='utf-8') as f:
json.dump(save_data, f, ensure_ascii=False, indent=2)
self._update_result_text("配置保存成功")
except Exception as save_err:
self.logger.error(f"保存配置失败: {save_err}")
self._update_result_text(f"保存配置失败: {save_err}")
def _clear_entries(self):
"""清除所有输入框"""
for entry in self.exclude_front_entries + self.exclude_back_entries:
entry.delete(0, tk.END)
self._update_result_text("输入已清除", clear=True)
def _refresh_data(self):
"""刷新数据"""
self._load_saved_data()
self._update_result_text("数据已刷新")
def _on_analyze_clicked(self):
"""处理分析按钮点击事件"""
if not self._validate_inputs():
messagebox.showwarning("输入错误", "请检查输入的数字是否在有效范围内")
return
exclude_front = []
exclude_back = []
try:
# 收集前区排除号码
for entry in self.exclude_front_entries:
if entry.get().strip():
try:
exclude_front.append(int(entry.get()))
except ValueError:
pass
# 收集后区排除号码
for entry in self.exclude_back_entries:
if entry.get().strip():
try:
exclude_back.append(int(entry.get()))
except ValueError:
pass
# 准备分析数据
analysis_data = {
'exclude_front': exclude_front,
'exclude_back': exclude_back
}
# 确定事件类型
run_event_type = getattr(EventType, 'MODULE_RUN', getattr(EventType, 'MODULE_READY', 'module_run'))
# 修复:正确创建事件对象
command_event = Event(
event_id=str(uuid.uuid4()),
type=run_event_type,
source=self.token,
target=getattr(self.module, 'MODULE_ID', 'input_analysis'),
data=analysis_data # 确保提供data参数
)
event_center.publish(command_event)
self.analyze_btn.config(state='disabled')
self._update_result_text("分析中...", clear=True)
except Exception as analyze_error:
messagebox.showerror("输入错误", f"无效的输入格式: {analyze_error}")
self.analyze_btn.config(state='normal')
def run_analysis(self):
"""运行分析"""
if self.module:
result = self.module.analyze()
print(result)
else:
print("分析模块未初始化")
def _handle_analysis_result(self, event: Event):
"""处理分析结果事件"""
if not hasattr(event, 'data'):
self.logger.error("收到无效的分析结果事件: 缺少data属性")
return
self.frame.after(0, lambda: self._display_results(event.data))
def _handle_error(self, event: Event):
"""处理错误事件"""
error_msg = event.data.get('error', '未知错误') if hasattr(event, 'data') else '未知错误'
self.frame.after(0, lambda: self._show_error(error_msg))
self.frame.after(0, lambda: self.analyze_btn.config(state='normal'))
def _display_results(self, data):
"""显示分析结果"""
if not isinstance(data, dict):
self.logger.error(f"无效的结果数据类型: {type(data)}")
return
try:
# 更新推荐号码
if 'recommended_fronts' in data:
self.recommend_front_var.set(' '.join(map(str, data['recommended_fronts'])))
if 'recommended_backs' in data:
self.recommend_back_var.set(' '.join(map(str, data['recommended_backs'])))
# 更新结果文本框
result_text = f"分析完成: {time.strftime('%H:%M:%S')}\n"
if 'recommended_fronts' in data:
result_text += f"前区推荐: {', '.join(map(str, data['recommended_fronts']))}\n"
if 'recommended_backs' in data:
result_text += f"后区推荐: {', '.join(map(str, data['recommended_backs']))}\n"
self._update_result_text(result_text)
self.analyze_btn.config(state='normal')
except Exception as display_err:
self._show_error(f"结果显示错误: {display_err}")
def _update_result_text(self, text, clear=False):
"""更新结果文本框内容"""
self.result_text.config(state=tk.NORMAL)
if clear:
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, text + "\n")
self.result_text.see(tk.END)
self.result_text.config(state=tk.DISABLED)
def _show_error(self, error_msg):
"""显示错误信息"""
messagebox.showerror("分析错误", error_msg)
self._update_result_text(f"分析失败: {error_msg}")
self.recommend_front_var.set("分析失败")
self.recommend_back_var.set("分析失败")
def _auto_format_entry(self, event):
"""自动格式化输入框内容"""
entry = event.widget
current = entry.get().strip()
if current.isdigit():
formatted = self._format_number(current)
if formatted != current:
entry.delete(0, tk.END)
entry.insert(0, formatted)
if len(current) == 2:
self._focus_adjacent_entry(entry, 1)
def _focus_adjacent_entry(self, current_entry, direction):
"""焦点跳转到相邻输入框"""
all_entries = self.exclude_front_entries + self.exclude_back_entries
try:
current_index = all_entries.index(current_entry)
target_index = current_index + direction
if 0 <= target_index < len(all_entries):
all_entries[target_index].focus()
except ValueError:
pass
def _navigate_entry(self, event, direction):
"""使用方向键在输入框间导航"""
self._focus_adjacent_entry(event.widget, direction)
if __name__ == "__main__":
root = tk.Tk()
root.title("输入分析模块测试")
root.geometry("600x600")
main_frame = ttk.Frame(root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
try:
InputAnalysisUI(main_frame)
except Exception as e:
messagebox.showerror("致命错误", f"应用程序初始化失败: {e}")
sys.exit(1)
root.mainloop()
最新发布