修改::方法必须有第一个形参,通常叫做 'self',,未解析的引用 'self',,未解析的引用 'self',未解析的引用 'self',类 'FollowAnalysisModule' 的未解析的特性引用 'run_analysis',,重复的代码段(150 行长),,重复的代码段(4 行长),,重复的代码段(8 行长),,,未使用局部变量 'follow_engine' 的值,,代码# ====== 跟随分析模块完整代码 ======
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
彩票跟随分析模块(独立完整版)
包含分析引擎和UI,无需外部依赖
"""
import json
import os
import sys
import time
import logging
import threading
import random
import pandas as pd
from pathlib import Path
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from collections import defaultdict, Counter
from typing import List, Dict, Optional, Tuple
import chardet
CORE_DIR = Path(__file__).parent.parent / "core"
if str(CORE_DIR) not in sys.path:
sys.path.insert(0, str(CORE_DIR))
# 配置日志系统
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger('FollowAnalysisSystem')
# ====== 事件类型定义 ======
class EventType:
DATA_SUBMIT = "data_submit"
ANALYSIS_RESULT = "analysis_result"
MODULE_RUN = "module_run"
MODULE_READY = "module_ready"
MODULE_COMPLETE = "module_complete"
UI_UPDATE = "ui_update"
ERROR = "error"
class Event:
def __init__(self, event_id: int, event_type: str, source: str, target: str, data: dict = None, token: str = None):
self.event_id = event_id
self.type = event_type
self.source = source
self.target = target
self.data = data or {}
self.token = token
# ====== 事件中心 ======
class EventCenter:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._subscribers = defaultdict(list)
return cls._instance
def subscribe(self, event_type, handler, token=None):
if not callable(handler):
raise ValueError("handler必须是可调用函数")
self._subscribers[event_type].append((handler, token))
def publish(self, event):
for handler, token in self._subscribers.get(event.type, []):
if token is None or token == event.token:
try:
handler(event)
except Exception as e:
logger.error(f"事件处理失败: {str(e)}")
# 创建全局事件中心实例
event_center = EventCenter()
# ====== 前端加载器 ======
class FrontendLoader:
def __init__(self):
self._ui_instance = None
logger.info("前端加载器已初始化")
logger.info("[模块状态] FrontendLoader: ✅ 已加载")
@property
def ui(self):
return self._ui_instance
@ui.setter
def ui(self, value):
self._ui_instance = value
logger.info("UI实例已设置到前端加载器")
# ====== 彩票数据接口类 ======
class LotteryDataInterface:
"""彩票数据接口类"""
@staticmethod
def detect_encoding(file_path: str) -> str:
"""检测文件编码"""
try:
with open(file_path, 'rb') as f:
rawdata = f.read(100000)
result = chardet.detect(rawdata)
encoding = result['encoding'] or 'gb2312'
logger.info(f"检测到文件编码: {encoding} (置信度: {result['confidence']:.1%})")
return encoding
except Exception as error:
logger.warning(f"编码检测失败, 使用默认编码: {str(error)}")
return 'gb2312'
@staticmethod
def preprocess_data(df: pd.DataFrame) -> pd.DataFrame:
"""预处理数据"""
try:
# 重命名列
column_map = {
'开奖期号': '期号',
'前区1': '前1', '前区2': '前2', '前区3': '前3',
'前区4': '前4', '前区5': '前5',
'后区1': '后1', '后区2': '后2'
}
for orig_col, new_col in column_map.items():
if orig_col in df.columns:
df.rename(columns={orig_col: new_col}, inplace=True)
# 合并号码列
front_cols = [col for col in ['前1', '前2', '前3', '前4', '前5'] if col in df.columns]
back_cols = [col for col in ['后1', '后2'] if col in df.columns]
if front_cols:
df['前区号码'] = df[front_cols].values.tolist()
if back_cols:
df['后区号码'] = df[back_cols].values.tolist()
# 按期号排序
if '期号' in df.columns:
df = df.sort_values('期号')
logger.info(f"数据预处理完成,共{len(df)}期数据")
return df
except Exception as error:
logger.error(f"数据预处理失败: {str(error)}")
raise
@staticmethod
def load_data(file_path: str) -> Optional[pd.DataFrame]:
"""加载数据文件"""
if not os.path.exists(file_path):
logger.error(f"数据文件不存在: {file_path}")
return None
encodings = ['gb2312', 'utf-8', 'gbk', 'utf-16']
detected_encoding = LotteryDataInterface.detect_encoding(file_path)
if detected_encoding not in encodings:
encodings.insert(0, detected_encoding)
for encoding in encodings:
try:
df = pd.read_csv(file_path, encoding=encoding)
logger.info(f"成功使用 {encoding} 编码加载数据")
return LotteryDataInterface.preprocess_data(df)
except UnicodeDecodeError:
continue
except Exception as decode_error:
logger.error(f"加载数据时出错: {str(decode_error)}")
continue
logger.error("尝试了所有编码方式仍无法加载数据")
return None
# ====== 跟随分析核心类 ======
class FollowingAnalyzer:
"""跟随分析核心类"""
def __init__(self, df: pd.DataFrame):
self.df = df.tail(500).copy() if len(df) >= 500 else df.copy()
self.last_update_time = time.time()
logger.info(f"初始化分析器,使用{len(self.df)}期数据")
def analyze_front_more(self) -> List[str]:
"""分析前区多推荐"""
front_following, _ = self._analyze_following()
return self._get_most_common_numbers(front_following, "前推荐多")
def analyze_front_less(self) -> List[str]:
"""分析前区少推荐"""
front_following, _ = self._analyze_following()
return self._get_least_common_numbers(front_following, "前推荐少")
def analyze_back_more(self) -> List[str]:
"""分析后区多推荐"""
_, back_following = self._analyze_following()
return self._get_most_common_numbers(back_following, "后推荐多")
def analyze_back_less(self) -> List[str]:
"""分析后区少推荐"""
_, back_following = self._analyze_following()
return self._get_least_common_numbers(back_following, "后推荐少")
def _analyze_following(self) -> Tuple[Dict[int, Counter], Dict[int, Counter]]:
"""分析前后区号码的跟随关系"""
front_following = defaultdict(Counter)
back_following = defaultdict(Counter)
for i in range(len(self.df) - 1):
current = self.df.iloc[i]
next_draw = self.df.iloc[i + 1]
if '前区号码' in current and '前区号码' in next_draw:
current_front = current['前区号码']
next_front = next_draw['前区号码']
for num in current_front:
front_following[num].update(next_front)
if '后区号码' in current and '后区号码' in next_draw:
current_back = current['后区号码']
next_back = next_draw['后区号码']
for num in current_back:
back_following[num].update(next_back)
logger.info(f"跟随分析完成,前区{len(front_following)}种,后区{len(back_following)}种跟随关系")
return front_following, back_following
@staticmethod
def _get_most_common_numbers(following_dict: Dict[int, Counter], label: str, top_n: int = 5) -> List[str]:
all_numbers = Counter()
for counter in following_dict.values():
all_numbers.update(counter)
most_common = [f"{num:02d}" for num, _ in all_numbers.most_common(top_n)]
logger.debug(f"{label}号码: {most_common}")
return most_common
@staticmethod
def _get_least_common_numbers(following_dict: Dict[int, Counter], label: str, top_n: int = 5) -> List[str]:
all_numbers = Counter()
for counter in following_dict.values():
all_numbers.update(counter)
least_common = [f"{num:02d}" for num, _ in all_numbers.most_common()[-top_n:]]
logger.debug(f"{label}号码: {least_common}")
return least_common
# ====== 跟随分析引擎 ======
class FollowAnalysisEngine:
def __init__(self):
self.module_name = "follow_analysis_engine"
self._setup_subscriptions()
logger.info("跟随分析引擎已初始化")
logger.info("跟随分析引擎已启动,等待UI指令...")
logger.info(f"模块ID: {self.module_name}")
def _setup_subscriptions(self):
"""设置事件订阅"""
event_center.subscribe(EventType.MODULE_RUN, self._handle_module_run, token=self.module_name)
def _handle_module_run(self, event):
"""处理模块运行事件"""
if event.target != self.module_name:
return
logger.info("跟随分析引擎开始处理")
try:
# 模拟跟随分析逻辑
front_rec_more = sorted(random.sample(range(1, 36), 5))
front_rec_less = sorted(random.sample(range(1, 36), 5))
back_rec_more = sorted(random.sample(range(1, 13), 2))
back_rec_less = sorted(random.sample(range(1, 13), 2))
# 发布分析结果
result_event = Event(
event_id=int(time.time()),
event_type=EventType.ANALYSIS_RESULT,
source=self.module_name,
target='pool',
data={
"前推荐多": front_rec_more,
"前推荐少": front_rec_less,
"后推荐多": back_rec_more,
"后推荐少": back_rec_less
},
token=self.module_name
)
event_center.publish(result_event)
logger.info("跟随分析结果已发布")
except Exception as e:
logger.error(f"跟随分析失败: {str(e)}")
error_event = Event(
event_id=int(time.time()),
event_type=EventType.ERROR,
source=self.module_name,
target='pool',
data={"error": str(e)},
token=self.module_name
)
event_center.publish(error_event)
# ====== 跟随分析UI模块 ======
class FollowAnalysisModule:
FIXED_LABELS = ["前推荐多", "前推荐少", "后推荐多", "后推荐少"]
def __init__(self, module_name: str = "follow_analysis", master=None):
self.module_name = module_name
self.master = master or self._create_root_window()
self.value_vars = {}
self.data_lock = threading.Lock()
self.analysis_start_time = 0
self.run_button = None
self.dynamic_data = {label: "" for label in self.FIXED_LABELS}
self.result_text = None
logger.info("跟随分析UI模块已初始化")
self._create_ui()
self._setup_event_handlers()
self._setup_timeout_detection()
logger.info("[模块状态] follow_analysis.FollowAnalysisModule: ✅ 已加载")
@staticmethod
def _create_root_window():
"""创建主窗口"""
main_window = tk.Tk()
main_window.title("跟随分析模块")
main_window.geometry("800x500")
return main_window
def _create_ui(self):
"""创建用户界面"""
main_frame = ttk.Frame(self.master)
main_frame.pack(fill='both', expand=True, padx=10, pady=5)
# 标题区域
title_frame = ttk.Frame(main_frame)
title_frame.pack(fill='x', pady=5)
ttk.Label(title_frame, text="跟随分析", font=("楷体", 14, "bold")).pack(side='left')
ttk.Label(title_frame, text=f"模块ID: {self.module_name}", font=("宋体", 10)).pack(side='right', padx=10)
# 动态数据区域
self._create_dynamic_section(main_frame)
# 结果展示区域
self._create_result_section(main_frame)
# 按钮区域
self._create_button_section(main_frame)
def _create_dynamic_section(self, parent):
"""创建动态数据显示区域"""
dynamic_frame = ttk.LabelFrame(parent, text="动态区")
dynamic_frame.pack(fill='x', padx=10, pady=5, ipadx=5, ipady=5)
for label in self.FIXED_LABELS:
frame = ttk.Frame(dynamic_frame)
frame.pack(fill='x', padx=5, pady=2)
ttk.Label(frame, text=f"{label}:", width=10, anchor='e').pack(side='left', padx=(0, 5))
self.value_vars[label] = tk.StringVar()
ttk.Entry(frame, textvariable=self.value_vars[label], state='readonly', width=30).pack(side='left',
fill='x',
expand=True)
def _create_result_section(self, parent):
"""创建结果展示区域"""
result_frame = ttk.LabelFrame(parent, text="分析结果")
result_frame.pack(fill='both', expand=True, padx=10, pady=5)
self.result_text = scrolledtext.ScrolledText(result_frame, wrap=tk.WORD, height=10)
self.result_text.pack(fill='both', expand=True, padx=5, pady=5)
self._update_result_text("点击'运行'按钮开始分析…")
def _create_button_section(self, parent):
"""创建按钮区域"""
button_frame = ttk.Frame(parent)
button_frame.pack(fill='x', padx=10, pady=10)
btn_container = ttk.Frame(button_frame)
btn_container.pack(side='right', padx=5)
buttons_info = [
("运行", self.run_analysis),
("清除", self._clear_data),
("保存", self.save_data),
("刷新", self._refresh_data)
]
for btn_text, command in buttons_info:
btn = ttk.Button(btn_container, text=btn_text, command=command)
if btn_text == "运行":
self.run_button = btn
btn.pack(side='left', padx=5)
def _setup_event_handlers(self):
"""设置事件处理器"""
event_center.subscribe(EventType.ANALYSIS_RESULT, self._handle_analysis_result, token=self.module_name)
event_center.subscribe(EventType.ERROR, self._handle_error, token=self.module_name)
logger.info("事件处理器已注册")
def _setup_timeout_detection(self):
"""设置超时检测机制"""
def check_timeout():
if self.analysis_start_time > 0:
elapsed = time.time() - self.analysis_start_time
if elapsed > 15: # 15秒超时
self._handle_analysis_timeout()
self.master.after(1000, check_timeout)
self.master.after(1000, check_timeout)
logger.info("超时检测已启动")
def _handle_analysis_result(self, event):
"""处理分析结果事件"""
if event.source != "follow_analysis_engine":
return
self._update_dynamic_data(event.data)
self._update_ui()
self._complete_analysis()
def _handle_error(self, event):
"""处理错误事件"""
error_msg = event.data.get('error', '未知错误')
logger.error(f"接收到错误: {error_msg}")
self._update_result_text(f"错误: {error_msg}")
self._complete_analysis()
def _handle_analysis_timeout(self):
"""处理分析超时"""
self.analysis_start_time = 0
if self.run_button:
self.run_button.config(state='normal')
self._update_result_text("分析超时,请检查分析引擎状态")
logger.error("分析超时")
def _complete_analysis(self):
"""完成分析后的通用操作"""
self.analysis_start_time = 0
if self.run_button:
self.run_button.config(state='normal')
def run_analysis_threaded():
self.run_button.config(state=tk.DISABLED)
self.analysis_start_time = time.time()
self._update_result_text("分析中,请稍候…")
thread = threading.Thread(target=self._run_analysis_task)
thread.start()
def _run_analysis_task(self):
"""运行分析"""
try:
if self.run_button:
self.run_button.config(state=tk.DISABLED)
self.analysis_start_time = time.time()
self._update_result_text("分析中,请稍候…")
# 发送运行事件
run_event = Event(
event_id=int(time.time()),
event_type=EventType.MODULE_RUN,
source=self.module_name,
target="follow_analysis_engine",
data={"command": "start_analysis"},
token=self.module_name
)
event_center.publish(run_event)
logger.info("已发送运行指令到跟随分析引擎")
except Exception as ex:
logger.error(f"运行分析时出错: {str(ex)}")
self._update_result_text(f"错误: {str(ex)}")
if self.run_button:
self.run_button.config(state='normal')
def _update_dynamic_data(self, result):
"""更新动态数据"""
with self.data_lock:
for label in self.FIXED_LABELS:
if label in result:
value = result[label]
display_value = ', '.join(map(str, value)) if isinstance(value, list) else str(value)
self.dynamic_data[label] = display_value
self.value_vars[label].set(display_value)
def _update_ui(self):
"""更新结果界面"""
result_str = "=== 分析完成 ===\n\n"
result_str += f"时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n"
for label in self.FIXED_LABELS:
value = self.dynamic_data.get(label, "无数据")
result_str += f"{label}: {value}\n"
result_str += "\n=== 分析结束 ==="
self._update_result_text(result_str)
def _update_result_text(self, message):
"""更新结果文本框"""
if self.result_text:
self.result_text.config(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, message)
self.result_text.config(state=tk.DISABLED)
def _clear_data(self):
"""清除数据"""
for label in self.FIXED_LABELS:
self.value_vars[label].set("")
self.dynamic_data[label] = ""
self._update_result_text("数据已清除")
logger.info("动态区数据已清除")
def save_data(self):
"""保存数据到指定路径"""
try:
# 指定保存路径
save_path = r"C:\Users\Administrator\Desktop\project\modules"
os.makedirs(save_path, exist_ok=True)
filename = os.path.join(save_path, f"follow_analysis_{time.strftime('%Y%m%d_%H%M%S')}.json")
with open(filename, 'w', encoding='utf-8') as f:
json.dump(self.dynamic_data, f, indent=2, ensure_ascii=False)
self._update_result_text(f"数据已保存到: {filename}\n")
logger.info(f"数据已保存到: {filename}")
except Exception as ex:
logger.error(f"保存失败: {ex}")
messagebox.showerror("保存错误", f"保存数据时出错: {str(ex)}")
def _refresh_data(self):
"""刷新数据"""
self._update_ui()
self._update_result_text("数据已刷新\n")
logger.info("数据已刷新")
def update_ui(self, data):
"""更新UI(供前端加载器调用)"""
if 'update_type' in data and data['update_type'] == 'follow_data':
self._update_dynamic_data(data.get('data', {}))
self._update_ui()
# ====== 主函数 ======
def main():
"""主函数"""
try:
# 初始化根窗口
root = tk.Tk()
root.title("跟随分析模块")
root.geometry("800x500")
# 初始化前端加载器
frontend_loader = FrontendLoader()
# 初始化跟随分析模块
follow_module = FollowAnalysisModule(master=root)
# 设置UI实例到前端加载器
frontend_loader.ui = follow_module
# 初始化跟随分析引擎
follow_engine = FollowAnalysisEngine()
logger.info("等待来自 'follow_analysis' 的指令")
root.mainloop()
except Exception as e:
logger.error(f"应用启动失败: {str(e)}")
messagebox.showerror("错误", f"应用启动失败: {str(e)}")
if __name__ == "__main__":
main()