Initial commit: 股吧人气指示器 - PySide6桌面悬浮工具
This commit is contained in:
201
main.py
Normal file
201
main.py
Normal file
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
主程序入口 - 股吧人气指示器
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user