Files
guba-indicator/main.py

202 lines
6.1 KiB
Python
Raw 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.
"""
主程序入口 - 股吧人气指示器
"""
import sys
import logging
import time
from datetime import datetime
from PySide6.QtWidgets import QApplication
from PySide6.QtCore import QTimer, Signal, QObject
from config_manager import ConfigManager
from database import DatabaseManager
from spider import SpiderManager
from llm_analyzer import LLMAnalyzer
from main_window import MainWindow
class BackendWorker(QObject):
"""后台工作器 - 处理爬取和分析任务"""
fetch_finished = Signal(list)
analysis_finished = Signal(float)
error_occurred = Signal(str)
status_update = Signal(str)
def __init__(self, config_manager: ConfigManager, db_manager: DatabaseManager,
spider: SpiderManager, analyzer: LLMAnalyzer):
super().__init__()
self.config = config_manager
self.db = db_manager
self.spider = spider
self.analyzer = analyzer
self.running = False
self.last_fetch_time = 0
self.fetch_interval = 60 # 默认60秒
self.no_new_content_count = 0 # 无新内容计数
def start(self):
"""启动后台任务"""
self.running = True
self._run_cycle()
def stop(self):
"""停止后台任务"""
self.running = False
def _run_cycle(self):
"""运行一个周期"""
if not self.running:
return
try:
# 1. 爬取评论
self.status_update.emit("正在爬取评论...")
comments = self.spider.fetch()
if not comments:
self.no_new_content_count += 1
interval = self.fetch_interval * (1 + min(self.no_new_content_count * 0.5, 2))
self.status_update.emit(f"无新内容,{int(interval)}秒后重试...")
QTimer.singleShot(int(interval * 1000), self._run_cycle)
return
# 2. 写入数据库
self.status_update.emit(f"获取到 {len(comments)} 条评论...")
new_ids = self.db.add_comments_batch(comments)
if new_ids:
self.no_new_content_count = 0
self.status_update.emit(f"新增 {len(new_ids)} 条评论")
# 3. 获取未分析评论并分析
unanalyzed = self.db.get_unanalyzed_comments(limit=10)
if unanalyzed:
self.status_update.emit(f"开始分析 {len(unanalyzed)} 条评论...")
self._analyze_comments(unanalyzed)
else:
self.no_new_content_count += 1
# 4. 更新指示器
self._update_indicator()
except Exception as e:
self.error_occurred.emit(f"运行错误: {str(e)}")
# 安排下一次执行
if self.running:
interval = self.fetch_interval * (1 + min(self.no_new_content_count * 0.5, 2))
QTimer.singleShot(int(interval * 1000), self._run_cycle)
def _analyze_comments(self, comments):
"""分析评论"""
for i, comment in enumerate(comments):
if not self.running:
break
try:
self.status_update.emit(f"分析 {i+1}/{len(comments)}...")
score, label = self.analyzer.analyze(comment['content'])
if score is not None:
self.db.mark_analyzed(comment['id'], score, label)
time.sleep(1.0) # 延迟避免API限流
else:
self.db.mark_analyzed(comment['id'], 50, "无法判断")
except Exception as e:
self.error_occurred.emit(f"分析失败: {str(e)}")
self.db.mark_analyzed(comment['id'], 50, "分析异常")
def _update_indicator(self):
"""更新指示器显示"""
scores = self.db.get_all_scores()
if not scores:
return
# 计算平均分
avg_score = sum(scores) / len(scores)
# 根据阈值确定标签
thresholds = self.config.get('ui', 'thresholds', default={'cold': 30, 'warm': 70})
cold = thresholds.get('cold', 30)
warm = thresholds.get('warm', 70)
if avg_score < cold:
label = "看跌"
elif avg_score > warm:
label = "看涨"
else:
label = "中性"
self.analysis_finished.emit(avg_score)
def manual_refresh(self):
"""手动刷新"""
self.no_new_content_count = 0
self._run_cycle()
def update_fetch_interval(self, interval: int):
"""更新爬取间隔"""
self.fetch_interval = interval
def setup_logging(log_path: str, level: str = "INFO"):
"""配置日志"""
logging.basicConfig(
level=getattr(logging, level.upper(), logging.INFO),
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_path, encoding='utf-8'),
logging.StreamHandler()
]
)
def main():
"""主函数"""
# 创建应用
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False) # 允许最小化到托盘
# 加载配置
config = ConfigManager("config.json")
# 配置日志
log_config = config.logging_config
setup_logging(log_config.get('path', 'guba.log'), log_config.get('level', 'INFO'))
# 初始化组件
db = DatabaseManager(config.database_config.get('path', 'guba.db'))
spider = SpiderManager(config.spider_config)
analyzer = LLMAnalyzer(config.llm_api_config)
# 创建后台工作器
worker = BackendWorker(config, db, spider, analyzer)
worker.update_fetch_interval(config.spider_config.get('fetch_interval', 60))
# 创建主窗口
window = MainWindow(config)
window.show()
# 连接信号
worker.status_update.connect(window.update_status)
worker.analysis_finished.connect(window.update_indicator)
worker.error_occurred.connect(lambda msg: window.show_message("错误", msg))
# 设置按钮回调
window.set_refresh_callback(worker.manual_refresh)
window.set_config_callback(window.show_config)
# 启动后台任务
worker.start()
# 运行应用
sys.exit(app.exec())
if __name__ == "__main__":
main()