diff --git a/app/core/config.py b/app/core/config.py index fe1db9b..7516600 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -522,7 +522,9 @@ class AppConfig: if current_stage: item_match: List[str] = re.findall( - r"^(?!\[)([\u4e00-\u9fa5A-Za-z0-9\-]+)\s*:\s*([\d,]+)(?:\s*\(\+[\d,]+\))?", line, re.M + r"^(?!\[)([\u4e00-\u9fa5A-Za-z0-9\-]+)\s*:\s*([\d,]+)(?:\s*\(\+[\d,]+\))?", + line, + re.M, ) for item, total in item_match: # 解析数值时去掉逗号 (如 2,160 -> 2160) @@ -747,6 +749,12 @@ class AppConfig: class GlobalConfig(QConfig): """全局配置""" + function_HomeImageMode = OptionsConfigItem( + "Function", + "HomeImageMode", + "默认", + OptionsValidator(["默认", "自定义", "主题图像"]), + ) function_HistoryRetentionTime = OptionsConfigItem( "Function", "HistoryRetentionTime", 0, OptionsValidator([7, 15, 30, 60, 0]) ) diff --git a/app/ui/Widget.py b/app/ui/Widget.py index a7bc31c..1fe9610 100644 --- a/app/ui/Widget.py +++ b/app/ui/Widget.py @@ -463,14 +463,22 @@ class IconButton(TransparentToolButton): class Banner(QWidget): """展示带有圆角的固定大小横幅小部件""" - def __init__(self, image_path: str, parent=None): + def __init__(self, image_path: str = None, parent=None): QWidget.__init__(self, parent) + self.image_path = None + self.banner_image = None + self.scaled_image = None + + if image_path: + self.set_banner_image(image_path) + + def set_banner_image(self, image_path: str): + """设置横幅图片""" self.image_path = image_path self.banner_image = self.load_banner_image(image_path) - self.scaled_image = None self.update_scaled_image() - def load_banner_image(self, image_path: str): + def load_banner_image(self, image_path: str) -> QPixmap: """加载横幅图片,或创建渐变备用图片""" if os.path.isfile(image_path): return QPixmap(image_path) diff --git a/app/ui/home.py b/app/ui/home.py index 07aac13..031b3c7 100644 --- a/app/ui/home.py +++ b/app/ui/home.py @@ -32,12 +32,19 @@ from PySide6.QtWidgets import ( QHBoxLayout, QSpacerItem, QSizePolicy, + QFileDialog, ) from PySide6.QtCore import Qt, QSize, QUrl from PySide6.QtGui import QDesktopServices, QColor -from qfluentwidgets import FluentIcon, ScrollArea, SimpleCardWidget +from qfluentwidgets import FluentIcon, ScrollArea, SimpleCardWidget, PrimaryToolButton +import shutil +import requests +import json +import time +from datetime import datetime +from pathlib import Path -from app.core import Config +from app.core import Config, MainInfoBar from .Widget import Banner, IconButton @@ -47,12 +54,12 @@ class Home(QWidget): super().__init__(parent) self.setObjectName("主页") - widget = Banner(str(Config.app_path / "resources/images/Home.png")) - widget.set_percentage_size( + self.banner = Banner() + self.banner.set_percentage_size( 0.8, 0.5 ) # 设置 Banner 大小为窗口的 80% 宽度和 50% 高度 - v_layout = QVBoxLayout(widget) + v_layout = QVBoxLayout(self.banner) v_layout.setContentsMargins(0, 0, 0, 15) v_layout.setSpacing(5) v_layout.setAlignment(Qt.AlignmentFlag.AlignTop) @@ -88,13 +95,216 @@ class Home(QWidget): ) v_layout.addStretch() + # 中间留白区域 + v_layout.addItem( + QSpacerItem(10, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) + ) + v_layout.addStretch() + + # 底部部分 (图片切换按钮) + h2_layout = QHBoxLayout() + h2_layout.setAlignment(Qt.AlignmentFlag.AlignTop) + + # 左边留白区域 + h2_layout.addItem( + QSpacerItem(20, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) + ) + + # # 公告卡片 + # noticeCard = NoticeCard() + # h2_layout.addWidget(noticeCard) + + h2_layout.addStretch() + + # 自定义图像按钮布局 + self.imageButton = PrimaryToolButton(FluentIcon.IMAGE_EXPORT) + self.imageButton.setFixedSize(56, 56) + self.imageButton.setIconSize(QSize(32, 32)) + self.imageButton.clicked.connect(self.get_home_image) + + v1_layout = QVBoxLayout() + v1_layout.addWidget(self.imageButton, alignment=Qt.AlignmentFlag.AlignBottom) + + h2_layout.addLayout(v1_layout) + + # 空白占位符 + h2_layout.addItem( + QSpacerItem(25, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) + ) + + # 将底部水平布局添加到垂直布局 + v_layout.addLayout(h2_layout) + layout = QVBoxLayout() scrollArea = ScrollArea() scrollArea.setWidgetResizable(True) - scrollArea.setWidget(widget) + scrollArea.setWidget(self.banner) layout.addWidget(scrollArea) self.setLayout(layout) + self.set_banner() + + def get_home_image(self) -> None: + """获取主页图片""" + + if ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "默认" + ): + pass + elif ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "自定义" + ): + + file_path, _ = QFileDialog.getOpenFileName( + self, "打开自定义主页图片", "", "图片文件 (*.png *.jpg *.bmp)" + ) + if file_path: + + for file in Config.app_path.glob( + "resources/images/Home/BannerCustomize.*" + ): + file.unlink() + + shutil.copy( + file_path, + Config.app_path + / f"resources/images/Home/BannerCustomize{Path(file_path).suffix}", + ) + + logger.info(f"自定义主页图片更换成功:{file_path}") + MainInfoBar.push_info_bar( + "success", + "主页图片更换成功", + "自定义主页图片更换成功!", + 3000, + ) + + else: + logger.warning("自定义主页图片更换失败:未选择图片文件") + MainInfoBar.push_info_bar( + "warning", + "主页图片更换失败", + "未选择图片文件!", + 5000, + ) + elif ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "主题图像" + ): + + # 从远程服务器获取最新主题图像 + for _ in range(3): + try: + response = requests.get( + "https://gitee.com/DLmaster_361/AUTO_MAA/raw/server/theme_image.json" + ) + theme_image = response.json() + break + except Exception as e: + err = e + time.sleep(0.1) + else: + logger.error(f"获取最新主题图像时出错:\n{err}") + MainInfoBar.push_info_bar( + "error", + "主题图像获取失败", + f"获取最新主题图像信息时出错:\n{err}", + -1, + ) + return None + + if (Config.app_path / "resources/theme_image.json").exists(): + with (Config.app_path / "resources/theme_image.json").open( + mode="r", encoding="utf-8" + ) as f: + theme_image_local = json.load(f) + time_local = datetime.strptime( + theme_image_local["time"], "%Y-%m-%d %H:%M" + ) + else: + time_local = datetime.strptime("2000-01-01 00:00", "%Y-%m-%d %H:%M") + + if not ( + Config.app_path / "resources/images/Home/BannerTheme.jpg" + ).exists() or ( + datetime.now() + > datetime.strptime(theme_image["time"], "%Y-%m-%d %H:%M") + and datetime.strptime(theme_image["time"], "%Y-%m-%d %H:%M") + > time_local + ): + + response = requests.get(theme_image["url"]) + if response.status_code == 200: + + with open( + Config.app_path / "resources/images/Home/BannerTheme.jpg", "wb" + ) as file: + file.write(response.content) + + logger.info("主题图像下载成功") + MainInfoBar.push_info_bar( + "success", + "主题图像下载成功", + "主题图像下载成功!", + 3000, + ) + + else: + + logger.error("主题图像下载失败") + MainInfoBar.push_info_bar( + "error", + "主题图像下载失败", + f"主题图像下载失败:{response.status_code}", + -1, + ) + + with (Config.app_path / "resources/theme_image.json").open( + mode="w", encoding="utf-8" + ) as f: + json.dump(theme_image, f, ensure_ascii=False, indent=4) + + else: + + logger.info("主题图像已是最新") + MainInfoBar.push_info_bar( + "info", + "主题图像已是最新", + "主题图像已是最新!", + 3000, + ) + + self.set_banner() + + def set_banner(self): + """设置主页图像""" + if ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "默认" + ): + self.banner.set_banner_image( + str(Config.app_path / "resources/images/Home/BannerDefault.png") + ) + self.imageButton.hide() + elif ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "自定义" + ): + for file in Config.app_path.glob("resources/images/Home/BannerCustomize.*"): + self.banner.set_banner_image(str(file)) + break + self.imageButton.show() + elif ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "主题图像" + ): + self.banner.set_banner_image( + str(Config.app_path / "resources/images/Home/BannerTheme.jpg") + ) + self.imageButton.show() + class ButtonGroup(SimpleCardWidget): """显示主页和 GitHub 按钮的竖直按钮组""" diff --git a/app/ui/main_window.py b/app/ui/main_window.py index d7d1265..fc2b2ae 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -193,6 +193,11 @@ class AUTO_MAA(MSFluentWindow): lambda: self.show_ui("配置托盘") ) self.setting.ui.card_IfToTray.checkedChanged.connect(self.set_min_method) + self.setting.function.card_HomeImageMode.comboBox.currentIndexChanged.connect( + lambda index: ( + self.home.get_home_image() if index == 2 else self.home.set_banner() + ) + ) self.splashScreen.finish() @@ -229,6 +234,12 @@ class AUTO_MAA(MSFluentWindow): # 获取公告 self.setting.show_notice(if_show=False) + if ( + Config.global_config.get(Config.global_config.function_HomeImageMode) + == "主题图像" + ): + self.home.get_home_image() + # 检查更新 if Config.global_config.get(Config.global_config.update_IfAutoUpdate): result = self.setting.get_update_info() diff --git a/app/ui/setting.py b/app/ui/setting.py index 80270e6..e47b0e2 100644 --- a/app/ui/setting.py +++ b/app/ui/setting.py @@ -409,6 +409,13 @@ class FunctionSettingCard(HeaderCardWidget): super().__init__(parent) self.setTitle("功能") + self.card_HomeImageMode = ComboBoxSettingCard( + configItem=Config.global_config.function_HomeImageMode, + icon=FluentIcon.PAGE_RIGHT, + title="主页背景图模式", + content="选择主页背景图的来源", + texts=["默认", "自定义", "主题图像"], + ) self.card_HistoryRetentionTime = ComboBoxSettingCard( configItem=Config.global_config.function_HistoryRetentionTime, icon=FluentIcon.PAGE_RIGHT, @@ -431,6 +438,7 @@ class FunctionSettingCard(HeaderCardWidget): ) Layout = QVBoxLayout() + Layout.addWidget(self.card_HomeImageMode) Layout.addWidget(self.card_HistoryRetentionTime) Layout.addWidget(self.card_IfAllowSleep) Layout.addWidget(self.card_IfSilence) diff --git a/resources/images/Home/BannerDefault.png b/resources/images/Home/BannerDefault.png new file mode 100644 index 0000000..0795101 Binary files /dev/null and b/resources/images/Home/BannerDefault.png differ