From 6f540036a08ad83657e2ca5e017f285a9fc79c68 Mon Sep 17 00:00:00 2001 From: DLmaster361 Date: Fri, 25 Apr 2025 22:47:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=8A=9F=E8=83=BD=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/config.py | 88 +++++++--- app/ui/Widget.py | 37 +++++ app/ui/history.py | 367 +++++++++++++++++++++++------------------ resources/version.json | 8 +- 4 files changed, 316 insertions(+), 184 deletions(-) diff --git a/app/core/config.py b/app/core/config.py index 6c3bcae..c18f732 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1289,8 +1289,21 @@ class AppConfig(GlobalConfig): self.user_info_changed.emit() + def save_history(self, key: str, content: dict) -> None: + """保存历史记录""" + + if key in self.queue_dict: + self.queue_dict[key]["Config"].set( + self.queue_dict[key]["Config"].Data_LastProxyTime, content["Time"] + ) + self.queue_dict[key]["Config"].set( + self.queue_dict[key]["Config"].Data_LastProxyHistory, content["History"] + ) + else: + logger.warning(f"保存历史记录时未找到调度队列: {key}") + def save_maa_log(self, log_path: Path, logs: list, maa_result: str) -> bool: - """保存MAA日志""" + """保存MAA日志并生成初步统计数据""" data: Dict[str, Union[str, Dict[str, Union[int, dict]]]] = { "recruit_statistics": defaultdict(int), @@ -1453,9 +1466,9 @@ class AppConfig(GlobalConfig): data["drop_statistics"][stage][item] = count # 合并MAA结果 - data["maa_result"][ - json_file.name.replace(".json", "").replace("-", ":") - ] = single_data["maa_result"] + data["maa_result"][json_file.stem.replace("-", ":")] = single_data[ + "maa_result" + ] # 生成汇总 JSON 文件 if mode == "所有项": @@ -1523,7 +1536,9 @@ class AppConfig(GlobalConfig): return data - def search_history(self) -> dict: + def search_history( + self, mode: str, start_date: datetime, end_date: datetime + ) -> dict: """搜索所有历史记录""" history_dict = {} @@ -1536,34 +1551,55 @@ class AppConfig(GlobalConfig): date = datetime.strptime(date_folder.name, "%Y-%m-%d") - history_dict[date.strftime("%Y年 %m月 %d日")] = list( - date_folder.glob("*.json") - ) + if not (start_date <= date <= end_date): + continue # 只统计在范围内的日期 + + if mode == "按日合并": + + history_dict[date.strftime("%Y年 %m月 %d日")] = list( + date_folder.glob("*.json") + ) + + elif mode == "按周合并": + + year, week, _ = date.isocalendar() + if f"{year}年 第{week}周" not in history_dict: + history_dict[f"{year}年 第{week}周"] = {} + + for user in date_folder.glob("*.json"): + + if user.stem not in history_dict[f"{year}年 第{week}周"]: + history_dict[f"{year}年 第{week}周"][user.stem] = list( + user.with_suffix("").glob("*.json") + ) + else: + history_dict[f"{year}年 第{week}周"][user.stem] += list( + user.with_suffix("").glob("*.json") + ) + + elif mode == "按月合并": + + if date.strftime("%Y年 %m月") not in history_dict: + history_dict[date.strftime("%Y年 %m月")] = {} + + for user in date_folder.glob("*.json"): + + if user.stem not in history_dict[date.strftime("%Y年 %m月")]: + history_dict[date.strftime("%Y年 %m月")][user.stem] = list( + user.with_suffix("").glob("*.json") + ) + else: + history_dict[date.strftime("%Y年 %m月")][user.stem] += list( + user.with_suffix("").glob("*.json") + ) except ValueError: logger.warning(f"非日期格式的目录: {date_folder}") return { k: v - for k, v in sorted( - history_dict.items(), - key=lambda x: datetime.strptime(x[0], "%Y年 %m月 %d日"), - reverse=True, - ) + for k, v in sorted(history_dict.items(), key=lambda x: x[0], reverse=True) } - def save_history(self, key: str, content: dict) -> None: - """保存历史记录""" - - if key in self.queue_dict: - self.queue_dict[key]["Config"].set( - self.queue_dict[key]["Config"].Data_LastProxyTime, content["Time"] - ) - self.queue_dict[key]["Config"].set( - self.queue_dict[key]["Config"].Data_LastProxyHistory, content["History"] - ) - else: - logger.warning(f"保存历史记录时未找到调度队列: {key}") - Config = AppConfig() diff --git a/app/ui/Widget.py b/app/ui/Widget.py index 93a81b0..45a9531 100644 --- a/app/ui/Widget.py +++ b/app/ui/Widget.py @@ -26,6 +26,7 @@ v4.3 """ from PySide6.QtWidgets import ( + QApplication, QWidget, QWidget, QLabel, @@ -60,6 +61,7 @@ from qfluentwidgets import ( TransparentToolButton, TeachingTipTailPosition, ExpandSettingCard, + ExpandGroupSettingCard, ToolButton, PushButton, PrimaryPushButton, @@ -1068,6 +1070,41 @@ class QuantifiedItemCard(CardWidget): self.Layout.addWidget(self.Numb) +class QuickExpandGroupCard(ExpandGroupSettingCard): + """全局配置""" + + def __init__( + self, + icon: Union[str, QIcon, FluentIcon], + title: str, + content: str = None, + parent=None, + ): + super().__init__(icon, title, content, parent) + + def setExpand(self, isExpand: bool): + """set the expand status of card""" + if self.isExpand == isExpand: + return + + # update style sheet + self.isExpand = isExpand + self.setProperty("isExpand", isExpand) + self.setStyle(QApplication.style()) + + # start expand animation + if isExpand: + h = self.viewLayout.sizeHint().height() + self.verticalScrollBar().setValue(h) + self.expandAni.setStartValue(h) + self.expandAni.setEndValue(0) + self.expandAni.start() + else: + self.setFixedHeight(self.viewportMargins().top()) + + self.card.expandButton.setExpand(isExpand) + + class IconButton(TransparentToolButton): """包含下拉框的自定义设置卡片类。""" diff --git a/app/ui/history.py b/app/ui/history.py index c8a73cc..808c114 100644 --- a/app/ui/history.py +++ b/app/ui/history.py @@ -36,7 +36,6 @@ from qfluentwidgets import ( FluentIcon, HeaderCardWidget, PushButton, - ExpandGroupSettingCard, TextBrowser, CardWidget, ComboBox, @@ -45,14 +44,15 @@ from qfluentwidgets import ( ) from PySide6.QtCore import Signal, QDate import os +import subprocess from datetime import datetime, timedelta from functools import partial from pathlib import Path -from typing import List +from typing import Union, List, Dict from app.core import Config -from .Widget import StatefulItemCard, QuantifiedItemCard +from .Widget import StatefulItemCard, QuantifiedItemCard, QuickExpandGroupCard class History(QWidget): @@ -77,7 +77,7 @@ class History(QWidget): self.history_card_list = [] - def reload_history(self, start_date: QDate, end_date: QDate, mode: str) -> None: + def reload_history(self, mode: str, start_date: QDate, end_date: QDate) -> None: """加载历史记录界面""" while self.content_layout.count() > 0: @@ -89,19 +89,15 @@ class History(QWidget): self.history_card_list = [] - history_dict = Config.search_history() - - selected_history = filter( - lambda item: datetime( - start_date.year(), start_date.month(), start_date.day() - ) - <= datetime.strptime(item[0], "%Y年 %m月 %d日") - <= datetime(end_date.year(), end_date.month(), end_date.day()), - history_dict.items(), + history_dict = Config.search_history( + mode, + datetime(start_date.year(), start_date.month(), start_date.day()), + datetime(end_date.year(), end_date.month(), end_date.day()), ) - for date, user_list in selected_history: - self.history_card_list.append(HistoryCard(date, user_list, self)) + for date, user in history_dict.items(): + + self.history_card_list.append(self.HistoryCard(mode, date, user, self)) self.content_layout.addWidget(self.history_card_list[-1]) self.content_layout.addStretch(1) @@ -109,9 +105,9 @@ class History(QWidget): class HistoryTopBar(CardWidget): """历史记录顶部工具栏""" - search_history = Signal(QDate, QDate, str) + search_history = Signal(str, QDate, QDate) - def __init__(self, parent=None, name: str = None): + def __init__(self, parent=None): super().__init__(parent) Layout = QHBoxLayout(self) @@ -127,18 +123,18 @@ class History(QWidget): ) self.mode = ComboBox() self.mode.setPlaceholderText("请选择查询模式") - self.mode.addItems(["按日期分类"]) + self.mode.addItems(["按日合并", "按周合并", "按月合并"]) - self.select_month = PushButton(FluentIcon.TAG, "选中最近一月") - self.select_week = PushButton(FluentIcon.TAG, "选中最近一周") + self.select_month = PushButton(FluentIcon.TAG, "最近一月") + self.select_week = PushButton(FluentIcon.TAG, "最近一周") self.search = PushButton(FluentIcon.SEARCH, "查询") self.select_month.clicked.connect(lambda: self.select_date("month")) self.select_week.clicked.connect(lambda: self.select_date("week")) self.search.clicked.connect( lambda: self.search_history.emit( + self.mode.currentText(), self.start_date.getDate(), self.end_date.getDate(), - self.mode.currentText(), ) ) @@ -153,7 +149,7 @@ class History(QWidget): Layout.addWidget(self.search) def select_date(self, date: str) -> None: - """选中最近一段时间""" + """选中最近一段时间并启动查询""" server_date = Config.server_date() if date == "week": @@ -168,168 +164,225 @@ class History(QWidget): QDate(server_date.year, server_date.month, server_date.day) ) + self.search.clicked.emit() -class HistoryCard(ExpandGroupSettingCard): - - def __init__(self, date: str, user_list: List[Path], parent=None): - super().__init__( - FluentIcon.HISTORY, date, f"{date}的历史运行记录与统计信息", parent - ) - - widget = QWidget() - Layout = QVBoxLayout(widget) - self.viewLayout.setContentsMargins(0, 0, 0, 0) - self.viewLayout.setSpacing(0) - self.addGroupWidget(widget) - - self.user_history_card_list = [] - - for user_path in user_list: - - self.user_history_card_list.append(self.UserHistoryCard(user_path, self)) - Layout.addWidget(self.user_history_card_list[-1]) - - class UserHistoryCard(HeaderCardWidget): + class HistoryCard(QuickExpandGroupCard): def __init__( self, - user_history_path: Path, + mode: str, + date: str, + user: Union[List[Path], Dict[str, List[Path]]], parent=None, ): - super().__init__(parent) + super().__init__( + FluentIcon.HISTORY, date, f"{date}的历史运行记录与统计信息", parent + ) - self.setTitle(user_history_path.name.replace(".json", "")) - - self.user_history_path = user_history_path - self.main_history = Config.load_maa_logs("总览", user_history_path) - - self.index_card = self.IndexCard(self.main_history["条目索引"], self) - self.statistics_card = QHBoxLayout() - self.log_card = self.LogCard(self) - - self.index_card.index_changed.connect(self.update_info) - - self.viewLayout.addWidget(self.index_card) - self.viewLayout.addLayout(self.statistics_card) - self.viewLayout.addWidget(self.log_card) + widget = QWidget() + Layout = QVBoxLayout(widget) self.viewLayout.setContentsMargins(0, 0, 0, 0) self.viewLayout.setSpacing(0) - self.viewLayout.setStretch(0, 1) - self.viewLayout.setStretch(2, 4) + self.addGroupWidget(widget) - self.update_info("数据总览") + self.user_history_card_list = [] - def update_info(self, index: str) -> None: - """更新信息""" + if mode == "按日合并": - if index == "数据总览": - - while self.statistics_card.count() > 0: - item = self.statistics_card.takeAt(0) - if item.spacerItem(): - self.statistics_card.removeItem(item.spacerItem()) - elif item.widget(): - item.widget().deleteLater() - - for name, item_list in self.main_history["统计数据"].items(): - - statistics_card = self.StatisticsCard(name, item_list, self) - self.statistics_card.addWidget(statistics_card) - - self.log_card.hide() - - else: - - single_history = Config.load_maa_logs( - "单项", - self.user_history_path.with_suffix("") - / f"{index.replace(":","-")}.json", - ) - - while self.statistics_card.count() > 0: - item = self.statistics_card.takeAt(0) - if item.spacerItem(): - self.statistics_card.removeItem(item.spacerItem()) - elif item.widget(): - item.widget().deleteLater() - - for name, item_list in single_history["统计数据"].items(): - - statistics_card = self.StatisticsCard(name, item_list, self) - self.statistics_card.addWidget(statistics_card) - - self.log_card.text.setText(single_history["日志信息"]) - self.log_card.button.clicked.disconnect() - self.log_card.button.clicked.connect( - lambda: os.startfile( - self.user_history_path.with_suffix("") - / f"{index.replace(":","-")}.log" + for user_path in user: + self.user_history_card_list.append( + self.UserHistoryCard(mode, user_path.stem, user_path, self) ) - ) - self.log_card.show() + Layout.addWidget(self.user_history_card_list[-1]) - self.viewLayout.setStretch(1, self.statistics_card.count()) + elif mode in ["按周合并", "按月合并"]: - self.setMinimumHeight(300) - - class IndexCard(HeaderCardWidget): - - index_changed = Signal(str) - - def __init__(self, index_list: list, parent=None): - super().__init__(parent) - self.setTitle("记录条目") - - self.Layout = QVBoxLayout() - self.viewLayout.addLayout(self.Layout) - self.viewLayout.setContentsMargins(3, 0, 3, 3) - - self.index_cards: List[StatefulItemCard] = [] - - for index in index_list: - - self.index_cards.append(StatefulItemCard(index)) - self.index_cards[-1].clicked.connect( - partial(self.index_changed.emit, index[0]) + for user, info in user.items(): + self.user_history_card_list.append( + self.UserHistoryCard(mode, user, info, self) ) - self.Layout.addWidget(self.index_cards[-1]) + Layout.addWidget(self.user_history_card_list[-1]) - self.Layout.addStretch(1) + class UserHistoryCard(HeaderCardWidget): + """用户历史记录卡片""" - class StatisticsCard(HeaderCardWidget): - - def __init__(self, name: str, item_list: list, parent=None): + def __init__( + self, + mode: str, + name: str, + user_history: Union[Path, List[Path]], + parent=None, + ): super().__init__(parent) + self.setTitle(name) - self.Layout = QVBoxLayout() - self.viewLayout.addLayout(self.Layout) - self.viewLayout.setContentsMargins(3, 0, 3, 3) + if mode == "按日合并": - self.item_cards: List[QuantifiedItemCard] = [] + self.user_history_path = user_history + self.main_history = Config.load_maa_logs("总览", user_history) - for item in item_list: + self.index_card = self.IndexCard( + self.main_history["条目索引"], self + ) + self.index_card.index_changed.connect(self.update_info) + self.viewLayout.addWidget(self.index_card) - self.item_cards.append(QuantifiedItemCard(item)) - self.Layout.addWidget(self.item_cards[-1]) + elif mode in ["按周合并", "按月合并"]: - if len(item_list) == 0: - self.Layout.addWidget(QuantifiedItemCard(["暂无记录", ""])) + history = Config.merge_maa_logs("指定项", user_history) - self.Layout.addStretch(1) + self.main_history = {} + self.main_history["统计数据"] = { + "公招统计": list(history["recruit_statistics"].items()) + } - class LogCard(HeaderCardWidget): + for game_id, drops in history["drop_statistics"].items(): + self.main_history["统计数据"][f"掉落统计:{game_id}"] = list( + drops.items() + ) - def __init__(self, parent=None): - super().__init__(parent) - self.setTitle("日志") + self.statistics_card = QHBoxLayout() + self.log_card = self.LogCard(self) - self.text = TextBrowser(self) - self.button = PushButton("打开日志文件", self) - self.button.clicked.connect(lambda: print("打开日志文件")) + self.viewLayout.addLayout(self.statistics_card) + self.viewLayout.addWidget(self.log_card) + self.viewLayout.setContentsMargins(0, 0, 0, 0) + self.viewLayout.setSpacing(0) + self.viewLayout.setStretch(0, 1) + self.viewLayout.setStretch(2, 4) - Layout = QVBoxLayout() - Layout.addWidget(self.text) - Layout.addWidget(self.button) - self.viewLayout.setContentsMargins(3, 0, 3, 3) - self.viewLayout.addLayout(Layout) + self.update_info("数据总览") + + def update_info(self, index: str) -> None: + """更新信息""" + + if index == "数据总览": + + while self.statistics_card.count() > 0: + item = self.statistics_card.takeAt(0) + if item.spacerItem(): + self.statistics_card.removeItem(item.spacerItem()) + elif item.widget(): + item.widget().deleteLater() + + for name, item_list in self.main_history["统计数据"].items(): + + statistics_card = self.StatisticsCard(name, item_list, self) + self.statistics_card.addWidget(statistics_card) + + self.log_card.hide() + + else: + + single_history = Config.load_maa_logs( + "单项", + self.user_history_path.with_suffix("") + / f"{index.replace(":","-")}.json", + ) + + while self.statistics_card.count() > 0: + item = self.statistics_card.takeAt(0) + if item.spacerItem(): + self.statistics_card.removeItem(item.spacerItem()) + elif item.widget(): + item.widget().deleteLater() + + for name, item_list in single_history["统计数据"].items(): + + statistics_card = self.StatisticsCard(name, item_list, self) + self.statistics_card.addWidget(statistics_card) + + self.log_card.text.setText(single_history["日志信息"]) + self.log_card.open_file.clicked.disconnect() + self.log_card.open_file.clicked.connect( + lambda: os.startfile( + self.user_history_path.with_suffix("") + / f"{index.replace(":","-")}.log" + ) + ) + self.log_card.open_dir.clicked.disconnect() + self.log_card.open_dir.clicked.connect( + lambda: subprocess.Popen( + [ + "explorer", + "/select,", + str( + self.user_history_path.with_suffix("") + / f"{index.replace(":","-")}.log" + ), + ] + ) + ) + self.log_card.show() + + self.viewLayout.setStretch(1, self.statistics_card.count()) + + self.setMinimumHeight(300) + + class IndexCard(HeaderCardWidget): + + index_changed = Signal(str) + + def __init__(self, index_list: list, parent=None): + super().__init__(parent) + self.setTitle("记录条目") + + self.Layout = QVBoxLayout() + self.viewLayout.addLayout(self.Layout) + self.viewLayout.setContentsMargins(3, 0, 3, 3) + + self.index_cards: List[StatefulItemCard] = [] + + for index in index_list: + + self.index_cards.append(StatefulItemCard(index)) + self.index_cards[-1].clicked.connect( + partial(self.index_changed.emit, index[0]) + ) + self.Layout.addWidget(self.index_cards[-1]) + + self.Layout.addStretch(1) + + class StatisticsCard(HeaderCardWidget): + + def __init__(self, name: str, item_list: list, parent=None): + super().__init__(parent) + self.setTitle(name) + + self.Layout = QVBoxLayout() + self.viewLayout.addLayout(self.Layout) + self.viewLayout.setContentsMargins(3, 0, 3, 3) + + self.item_cards: List[QuantifiedItemCard] = [] + + for item in item_list: + + self.item_cards.append(QuantifiedItemCard(item)) + self.Layout.addWidget(self.item_cards[-1]) + + if len(item_list) == 0: + self.Layout.addWidget(QuantifiedItemCard(["暂无记录", ""])) + + self.Layout.addStretch(1) + + class LogCard(HeaderCardWidget): + + def __init__(self, parent=None): + super().__init__(parent) + self.setTitle("日志") + + self.text = TextBrowser(self) + self.open_file = PushButton("打开日志文件", self) + self.open_file.clicked.connect(lambda: print("打开日志文件")) + self.open_dir = PushButton("打开所在目录", self) + self.open_dir.clicked.connect(lambda: print("打开所在文件")) + + Layout = QVBoxLayout() + h_layout = QHBoxLayout() + h_layout.addWidget(self.open_file) + h_layout.addWidget(self.open_dir) + Layout.addWidget(self.text) + Layout.addLayout(h_layout) + self.viewLayout.setContentsMargins(3, 0, 3, 3) + self.viewLayout.addLayout(Layout) diff --git a/resources/version.json b/resources/version.json index e7bd69b..0ec9b7d 100644 --- a/resources/version.json +++ b/resources/version.json @@ -4,8 +4,14 @@ "announcement": "\n## 新增功能\n- 屏蔽MuMu模拟器开屏广告功能上线\n- 更新器支持多线程下载\n- 添加强制关闭ADB与模拟器等增强任务项\n## 修复BUG\n- 修复统计信息HTML模板公招匹配错误\n- 修复密码显示按钮动画异常\n- 修复`检测到MAA未能实际执行任务`报错被异常屏蔽\n- 修复MAA超时判定异常失效\n## 程序优化\n- 关机等电源操作添加100s倒计时\n- 人工排查弹窗方法优化\n- 人工排查时自动屏蔽静默操作\n- 公告样式优化", "version_info": { "4.3.4.6": { + "新增功能": [ + "历史记录添加`按周合并`、`按月合并`功能" + ], + "修复BUG": [ + "修复历史记录选项卡无法回收的问题" + ], "程序优化": [ - "历史记录页面添加`选中最近一月`、`选中最近一周`选项" + "历史记录添加`选中最近一月`、`选中最近一周`选项与打开日志文件所在目录功能" ] }, "4.3.4.5": {