分支后首次推送;更新所有代码至内部最新测试版

This commit is contained in:
hyb-oyqq
2025-07-15 16:30:50 +08:00
parent 3407c88661
commit 40ac80b8f6
42 changed files with 9257 additions and 415 deletions

View File

@@ -1,29 +1,23 @@
# 🍓FRAISEMOE Addons Installer🍓
# 🍓FRAISEMOE2-Installer🍓
```
🔊 通知“FraiseMoe Addons Installer”完成了其作为测试版本的用途感谢广大用户的支持与陪伴。
将于2025年7月14日00时00分起“FraiseMoe Addons Installer”GitHub仓库将进行归档下载服务将永久停止使用。
本应用程式的完全重构版本将于2025年7月14日23时59分将在“https://github.com/Yanam1Anna/FRAISEMOE-Addons-Manager”发布。
因原 CDN 服务成本高且服务器长期高负载,我们无力继续提供免费服务。且此版本开源效果不佳,故全新版本采用闭源方式发布。
自新版本发布,此版本归档之日起,为维持服务器收支平衡,可能引入收费机制以保障持续运营,或项目将进入生命周期管理阶段。
🔊 注意:本库仍然努力更新中,大部分文档不可用,敬请谅解。
```
<!-- PROJECT SHIELDS -->
<p align="center">
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/stargazers" class="shield">
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/stargazers" class="shield">
<img src="https://img.shields.io/github/stars/Yanam1Anna/FRAISEMOE-Addons-Installer?style=flat-square&label=%E2%AD%90%20Stars&color=blue" alt="Stars">
</a>
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/issues" class="shield">
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/issues" class="shield">
<img src="https://img.shields.io/github/issues/Yanam1Anna/FRAISEMOE-Addons-Installer?style=flat-square&label=%F0%9F%92%AC%20%E6%8A%A5%E5%91%8A%E9%97%AE%E9%A2%98%2F%E6%8B%89%E5%8F%96%E8%AF%B7%E6%B1%82&color=blue" alt="Issues/Pull Requests">
</a>
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/releases/latest" class="shield">
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/releases/latest" class="shield">
<img src="https://img.shields.io/github/v/release/Yanam1Anna/FRAISEMOE-Addons-Installer?style=flat-square&label=%F0%9F%92%AF%20%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC&color=blue" alt="Latest Release">
</a>
<img src="https://img.shields.io/github/downloads/Yanam1Anna/FRAISEMOE-Addons-Installer/total?style=flat-square&label=%F0%9F%93%A5%20%E4%B8%8B%E8%BD%BD%E6%AC%A1%E6%95%B0&color=blue" alt="Download Count" class="shield">
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/LICENSE" class="shield">
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE" class="shield">
<img src="https://img.shields.io/github/license/Yanam1Anna/FRAISEMOE-Addons-Installer?style=flat-square&label=%F0%9F%93%96%20%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE&color=blue" alt="License">
</a>
<img src="https://img.shields.io/badge/Python-3.12.10-blue?style=flat-square&logo=python" alt="Python" class="shield">
@@ -43,21 +37,21 @@
⚠️ 本工具为非官方工具,不代表官方立场 ⚠️
<br />
<br />
<strong><a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer">探索本项目的文档 »</a></strong>
⚠️ 本NEXT版本目前处于持续开发阶段不保证任何稳定性 ⚠️
<br />
<br />
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/issues">报告Bug</a>
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/issues">报告Bug</a>
·
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/issues">提出新特性</a>
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/issues">提出新特性</a>
·
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/FAQ.md">【用前必看】使用须知</a>
<a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/FAQ.md">【用前必看】使用须知</a>
<br />
</p>
<!-- LANGUAGE -->
<p align="center">
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer">中文</a> |
<a href="https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/README-en.md">English</a>
<a href="#">English</a>
</p>
---
@@ -79,42 +73,34 @@
### 📥 安装
请从 [应用发布页面](https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/releases) 下载最新版本的应用程序。
请从 [应用发布页面](https://github.com/hyb-oyqq/FRAISEMOE2-Installer/releases) 下载最新版本的应用程序。
![preview](https://raw.githubusercontent.com/Yanam1Anna/FRAISEMOE-Addons-Installer/master/introduction_imgs/preview.png)
### ❗ 使用步骤
1. **重要提示**:使用前请务必阅读 [使用须知文档](https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/FAQ.md)。
1. **重要提示**:使用前请务必阅读 [使用须知文档](https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/FAQ.md)。
2. **详细教程**:参考 [视频教学](https://www.bilibili.com/video/BV1hn9UYwE6p/)。
### ⭕ 版本控制
该项目使用 Git 进行版本管理,您可以在 [应用发布页面](https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/releases) 查看当前可用版本。
该项目使用 Git 进行版本管理,您可以在 [应用发布页面](https://github.com/hyb-oyqq/FRAISEMOE2-Installer/releases) 查看当前可用版本。
---
## 💡 注意事项
1. 请勿使用经过二次修改的应用:若使用未知来源或修改后的应用导致个人利益受损,作者和开发人员不承担任何责任。
2. 请遵循所有规则:请严格遵守 [使用须知文档](https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/FAQ.md) 和本文档中的规则,如有违反,作者和开发人员不承担责任。
2. 请遵循所有规则:请严格遵守 [使用须知文档](https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/FAQ.md) 和本文档中的规则,如有违反,作者和开发人员不承担责任。
3. 免费开源:本应用免费、开源,如有通过非免费途径获取,请立即向来源申请退款并积极维权。
---
## 📝 作者
- [Yanam1Anna](https://github.com/Yanam1Anna) &ensp;&ensp;Email: [icon.hirasawayui@gmail.com](mailto:icon.hirasawayui@gmail.com)
## 🧧 捐助(遵循自愿原则,非强制,一经赞赏无法退还)
<img src="https://raw.githubusercontent.com/Yanam1Anna/FRAISEMOE-Addons-Installer/refs/heads/master/introduction_imgs/donations.png" alt="Donations" width="500">
## 🎉 特别鸣谢
- [Yanam1Anna](https://github.com/Yanam1Anna): 本项目的原作者,提供了大量代码和资源。
- [HTony03](https://github.com/HTony03):对于本项目部分源码的重构、逻辑优化和功能实现提供了大力支持。
- [Akatsuki Misaki](https://github.com/Akatsuki-Misaki):对于本项目云端资源存储提供了大力支持。
## 📖 协议
此应用根据 [GPL-3.0](https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/LICENSE) 许可证授权。请参阅 [LICENSE](https://github.com/Yanam1Anna/FRAISEMOE-Addons-Installer/blob/master/LICENSE) 文件了解更多信息。
此应用根据 [GPL-3.0](https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE) 许可证授权。请参阅 [LICENSE](https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE) 文件了解更多信息。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

View File

@@ -1,236 +0,0 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'Main.ui'
##
## Created by: Qt User Interface Compiler version 6.7.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (
QCoreApplication,
QDate,
QDateTime,
QLocale,
QMetaObject,
QObject,
QPoint,
QRect,
QSize,
QTime,
QUrl,
Qt,
)
from PySide6.QtGui import (
QBrush,
QColor,
QConicalGradient,
QCursor,
QFont,
QFontDatabase,
QGradient,
QIcon,
QImage,
QKeySequence,
QLinearGradient,
QPainter,
QPalette,
QPixmap,
QRadialGradient,
QTransform,
)
from PySide6.QtWidgets import (
QApplication,
QLabel,
QPushButton,
QSizePolicy,
QSplitter,
QVBoxLayout,
QWidget,
)
from pic_data import img_data
from PySide6.QtCore import QByteArray
import base64
class Ui_mainwin(object):
def setupUi(self, mainwin):
if not mainwin.objectName():
mainwin.setObjectName("mainwin")
mainwin.setWindowModality(Qt.WindowModality.NonModal)
mainwin.resize(1280, 720)
mainwin.setMinimumSize(QSize(1280, 720))
mainwin.setMaximumSize(QSize(1280, 720))
pixmap = QPixmap()
icon = QIcon()
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["icon"])))
icon.addPixmap(pixmap)
mainwin.setWindowIcon(icon)
self.mainbg = QLabel(mainwin)
self.mainbg.setObjectName("mainbg")
self.mainbg.setEnabled(True)
self.mainbg.setGeometry(QRect(0, 0, 1280, 720))
self.mainbg.setMinimumSize(QSize(1280, 720))
self.mainbg.setMaximumSize(QSize(1280, 720))
self.mainbg.setTextFormat(Qt.TextFormat.AutoText)
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["bg"])))
self.mainbg.setPixmap(pixmap)
self.mainbg.setScaledContents(False)
self.mainbg.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.cover = QSplitter(mainwin)
self.cover.setObjectName("cover")
self.cover.setGeometry(QRect(0, 320, 213, 400))
self.cover.setOrientation(Qt.Orientation.Vertical)
self.Cover_1 = QLabel(self.cover)
self.Cover_1.setObjectName("Cover_1")
self.Cover_1.setMinimumSize(QSize(213, 100))
self.Cover_1.setMaximumSize(QSize(213, 100))
self.Cover_1.setTextFormat(Qt.TextFormat.AutoText)
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_1_cover"])))
self.Cover_1.setPixmap(pixmap)
self.Cover_1.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.cover.addWidget(self.Cover_1)
self.Cover_2 = QLabel(self.cover)
self.Cover_2.setObjectName("Cover_2")
self.Cover_2.setMinimumSize(QSize(213, 100))
self.Cover_2.setMaximumSize(QSize(213, 100))
self.Cover_2.setTextFormat(Qt.TextFormat.AutoText)
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_2_cover"])))
self.Cover_2.setPixmap(pixmap)
self.Cover_2.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.cover.addWidget(self.Cover_2)
self.Cover_3 = QLabel(self.cover)
self.Cover_3.setObjectName("Cover_3")
self.Cover_3.setMinimumSize(QSize(213, 100))
self.Cover_3.setMaximumSize(QSize(213, 100))
self.Cover_3.setTextFormat(Qt.TextFormat.AutoText)
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_3_cover"])))
self.Cover_3.setPixmap(pixmap)
self.Cover_3.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.cover.addWidget(self.Cover_3)
self.Cover_4 = QLabel(self.cover)
self.Cover_4.setObjectName("Cover_4")
self.Cover_4.setMinimumSize(QSize(213, 100))
self.Cover_4.setMaximumSize(QSize(213, 100))
self.Cover_4.setTextFormat(Qt.TextFormat.AutoText)
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["vol_4_cover"])))
self.Cover_4.setPixmap(pixmap)
self.Cover_4.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.cover.addWidget(self.Cover_4)
self.menubg = QLabel(mainwin)
self.menubg.setObjectName("menubg")
self.menubg.setGeometry(QRect(800, 0, 480, 720))
self.menubg.setMinimumSize(QSize(480, 720))
self.menubg.setMaximumSize(QSize(480, 720))
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["menubg"])))
self.menubg.setPixmap(pixmap)
self.menubg.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.layoutWidget = QWidget(mainwin)
self.layoutWidget.setObjectName("layoutWidget")
self.layoutWidget.setGeometry(QRect(940, 340, 212, 204))
self.button = QVBoxLayout(self.layoutWidget)
self.button.setObjectName("button")
self.button.setContentsMargins(0, 0, 0, 0)
self.startbtn = QPushButton(self.layoutWidget)
self.startbtn.setObjectName("startbtn")
self.startbtn.setMinimumSize(QSize(210, 98))
self.startbtn.setMaximumSize(QSize(210, 98))
icon1 = QIcon()
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["btn01bg"])))
icon1.addPixmap(pixmap)
self.startbtn.setIcon(icon1)
self.startbtn.setIconSize(QSize(210, 98))
self.startbtn.setFlat(True)
self.button.addWidget(self.startbtn)
self.exitbtn = QPushButton(self.layoutWidget)
self.exitbtn.setObjectName("exitbtn")
self.exitbtn.setMinimumSize(QSize(210, 98))
self.exitbtn.setMaximumSize(QSize(210, 98))
icon2 = QIcon()
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["btn02bg"])))
icon2.addPixmap(pixmap)
self.exitbtn.setIcon(icon2)
self.exitbtn.setIconSize(QSize(210, 98))
self.exitbtn.setFlat(True)
self.button.addWidget(self.exitbtn)
self.sup_info = QLabel(mainwin)
self.sup_info.setObjectName("sup_info")
self.sup_info.setGeometry(QRect(0, 230, 213, 100))
self.sup_info.setMinimumSize(QSize(213, 100))
self.sup_info.setMaximumSize(QSize(213, 100))
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["support_info"])))
self.sup_info.setPixmap(pixmap)
self.future_info = QLabel(mainwin)
self.future_info.setObjectName("future_info")
self.future_info.setGeometry(QRect(860, 700, 370, 16))
self.future_info.setMinimumSize(QSize(370, 16))
self.future_info.setMaximumSize(QSize(370, 16))
font = QFont()
font.setFamilies(["Consolas"])
font.setBold(True)
font.setItalic(True)
self.future_info.setFont(font)
self.future_info.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.mainbg.raise_()
self.cover.raise_()
self.menubg.raise_()
self.layoutWidget.raise_()
self.future_info.raise_()
self.sup_info.raise_()
self.retranslateUi(mainwin)
QMetaObject.connectSlotsByName(mainwin)
# setupUi
def retranslateUi(self, mainwin):
mainwin.setWindowTitle(
QCoreApplication.translate(
"mainwin", "FRAISEMOE Addons Installer V4.10.0.17496", None
)
)
self.mainbg.setText("")
self.Cover_1.setText("")
self.Cover_2.setText("")
self.Cover_3.setText("")
self.Cover_4.setText("")
self.menubg.setText("")
self.startbtn.setText("")
self.exitbtn.setText("")
self.sup_info.setText("")
self.future_info.setText(
QCoreApplication.translate(
"mainwin", "Nekopara After La Vraie Famille COMMING SOON IN 2025", None
)
)
# retranslateUi

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
source/IMG/BG/bg1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

BIN
source/IMG/BG/bg2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

BIN
source/IMG/BG/bg3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
source/IMG/BG/bg4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
source/IMG/BG/menubg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 264 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -8,27 +8,30 @@ import base64
import psutil
import ctypes
import concurrent.futures
from PySide6 import QtWidgets, QtCore
from PySide6.QtCore import Qt, QByteArray, QThread, Signal
from PySide6.QtWidgets import (
QApplication,
QWidget,
QMessageBox,
QProgressBar,
QVBoxLayout,
QLabel,
QDialog,
)
from PySide6.QtGui import QIcon, QPixmap
from PySide6.QtGui import QIcon
from collections import deque
from pic_data import img_data
from GUI import Ui_mainwin
from PySide6.QtCore import ( Qt,
Signal, QThread, QTimer)
from PySide6.QtGui import (QIcon, QPixmap, )
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QMessageBox,
QProgressBar, QVBoxLayout, QFileDialog, QDialog)
from Ui_install import Ui_MainWindows
from animations import MultiStageAnimations
import sys
import os
def load_base64_image(base64_str):
pixmap = QPixmap()
pixmap.loadFromData(base64.b64decode(base64_str))
return pixmap
# 配置信息
app_data = {
"APP_VERSION": "4.10.0.17496",
"APP_NAME": "@FRAISEMOE Addons Installer",
"APP_NAME": "@FRAISEMOE2 Addons Installer",
"TEMP": "TEMP",
"CACHE": "FRAISEMOE",
"PLUGIN": "PLUGIN",
@@ -59,15 +62,21 @@ app_data = {
"install_path": "NEKOPARA Vol. 4/vol4adult.xp3",
"plugin_path": "vol.4/vol4adult.xp3",
},
"NEKOPARA After": {
"exe": "nekopara_after.exe",
"hash": "eb26ff6850096a240af8340ba21c5c3232e90f29fb8191e24b6ce701acae0aa9",
"install_path": "NEKOPARA After/afteradult.xp3",
"plugin_path": "after/afteradult.xp3",
"sig_path": "after/afteradult.xp3.sig"
},
},
}
# Base64解码
def decode_base64(encoded_str):
return base64.b64decode(encoded_str).decode("utf-8")
# 全局变量
APP_VERSION = app_data["APP_VERSION"]
APP_NAME = app_data["APP_NAME"]
TEMP = os.getenv(app_data["TEMP"])
@@ -81,26 +90,29 @@ HASH_SIZE = 134217728
PLUGIN_HASH = {game: info["hash"] for game, info in GAME_INFO.items()}
PROCESS_INFO = {info["exe"]: game for game, info in GAME_INFO.items()}
# 弹窗框架
def msgbox_frame(title, text, buttons=QMessageBox.StandardButton.NoButton):
msg_box = QMessageBox()
msg_box.setWindowTitle(title)
pixmap = QPixmap()
pixmap.loadFromData(QByteArray(base64.b64decode(img_data["icon"])))
icon = QIcon(pixmap)
msg_box.setWindowIcon(icon)
# 设置弹窗图标
icon_data = img_data.get("icon")
if icon_data:
pixmap = load_base64_image(icon_data)
if not pixmap.isNull():
msg_box.setWindowIcon(QIcon(pixmap))
msg_box.setIconPixmap(pixmap.scaled(64, 64, Qt.KeepAspectRatio))
else:
msg_box.setIcon(QMessageBox.Information)
msg_box.setText(text)
msg_box.setStandardButtons(buttons)
return msg_box
# 哈希值计算类
class HashManager:
def __init__(self, HASH_SIZE):
self.HASH_SIZE = HASH_SIZE
# 哈希值计算
def hash_calculate(self, file_path):
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
@@ -108,7 +120,6 @@ class HashManager:
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
# 使用多线程优化哈希值计算
def calculate_hashes_in_parallel(self, file_paths):
with concurrent.futures.ThreadPoolExecutor() as executor:
future_to_file = {
@@ -129,17 +140,13 @@ class HashManager:
msg_box.exec()
return results
# 哈希值计算时的窗口
def hash_pop_window(self):
msg_box = msgbox_frame(f"通知 {APP_NAME}", "\n正在检验文件状态...\n")
msg_box.show()
QApplication.processEvents()
return msg_box
# 下载前比对已有文件哈希值
def cfg_pre_hash_compare(
self, install_path, game_version, plugin_hash, installed_status
):
def cfg_pre_hash_compare(self, install_path, game_version, plugin_hash, installed_status):
if not os.path.exists(install_path):
installed_status[game_version] = False
return
@@ -157,7 +164,6 @@ class HashManager:
else:
installed_status[game_version] = True
# 下载完成后比对哈希值
def cfg_after_hash_compare(self, install_paths, plugin_hash, installed_status):
passed = True
file_paths = [
@@ -180,26 +186,23 @@ class HashManager:
break
return passed
# 管理员权限检查类
class AdminPrivileges:
# 进程列表
def __init__(self):
self.required_exes = [
"nekopara_vol1.exe",
"nekopara_vol2.exe",
"NEKOPARAvol3.exe",
"nekopara_vol4.exe",
"nekopara_after.exe",
]
# 检查管理员权限
def is_admin(self):
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
# 请求管理员权限
def request_admin_privileges(self):
if not self.is_admin():
msg_box = msgbox_frame(
@@ -230,7 +233,6 @@ class AdminPrivileges:
msg_box.exec()
sys.exit(1)
# 检查并终止进程
def check_and_terminate_processes(self):
for proc in psutil.process_iter(["pid", "name"]):
if proc.info["name"] in self.required_exes:
@@ -261,18 +263,16 @@ class AdminPrivileges:
msg_box.exec()
sys.exit(1)
# 下载线程类
class DownloadThread(QThread):
progress = Signal(int) # 进度信号
finished = Signal(bool, str) # 完成信号
progress = Signal(int)
finished = Signal(bool, str)
def __init__(self, url, _7z_path, parent=None):
super().__init__(parent)
self.url = url # 下载地址
self._7z_path = _7z_path # 7z文件路径
self.url = url
self._7z_path = _7z_path
# 下载线程运行
def run(self):
try:
headers = {"User-Agent": UA}
@@ -282,14 +282,13 @@ class DownloadThread(QThread):
with open(self._7z_path, "wb") as f:
for chunk in r.iter_content(chunk_size=BLOCK_SIZE):
f.write(chunk)
self.progress.emit(f.tell() * 100 // total_size) # 发送进度信号
self.finished.emit(True, "") # 发送完成信号
self.progress.emit(f.tell() * 100 // total_size)
self.finished.emit(True, "")
except requests.exceptions.RequestException as e:
self.finished.emit(False, f"\n网络请求错误\n\n【错误信息】: {e}\n")
except Exception as e:
self.finished.emit(False, f"\n未知错误\n\n【错误信息】: {e}\n")
# 下载进度窗口类
class ProgressWindow(QDialog):
def __init__(self, parent=None):
@@ -297,12 +296,8 @@ class ProgressWindow(QDialog):
self.setWindowTitle(f"下载进度 {APP_NAME}")
self.resize(400, 100)
self.progress_bar_max = 100
self.setWindowFlags(
self.windowFlags() & ~Qt.WindowCloseButtonHint
) # 禁用关闭按钮
self.setWindowFlags(
self.windowFlags() & ~Qt.WindowSystemMenuHint
) # 禁用系统菜单
self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
self.setWindowFlags(self.windowFlags() & ~Qt.WindowSystemMenuHint)
layout = QVBoxLayout()
self.progress_bar = QProgressBar()
@@ -312,26 +307,39 @@ class ProgressWindow(QDialog):
layout.addWidget(self.progress_bar)
self.setLayout(layout)
# 设置进度条最大值
def setmaxvalue(self, value):
self.progress_bar_max = value
self.progress_bar.setMaximum(value)
# 设置进度条值
def setprogressbarval(self, value):
self.progress_bar.setValue(value)
if value == self.progress_bar_max: # 下载完成后关闭窗口
QtCore.QTimer.singleShot(2000, self.close)
if value == self.progress_bar_max:
QTimer.singleShot(2000, self.close)
# 主窗口类
class MyWindow(QWidget, Ui_mainwin):
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
# 初始化UI (从Ui_install.py导入)
self.ui = Ui_MainWindows()
self.ui.setupUi(self)
icon_data = img_data.get("icon")
if icon_data:
pixmap = load_base64_image(icon_data)
self.setWindowIcon(QIcon(pixmap))
# 设置窗口标题为APP_NAME加版本号
self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
# 初始化动画系统 (从animations.py导入)
self.animator = MultiStageAnimations(self.ui)
# 初始化功能变量
self.selected_folder = ""
self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)}
self.installed_status["NEKOPARA After"] = False # 添加After的状态
self.download_queue = deque()
self.current_download_thread = None
self.hash_manager = HashManager(BLOCK_SIZE)
@@ -340,6 +348,7 @@ class MyWindow(QWidget, Ui_mainwin):
admin_privileges = AdminPrivileges()
admin_privileges.request_admin_privileges()
admin_privileges.check_and_terminate_processes()
# 创建缓存目录
if not os.path.exists(PLUGIN):
try:
@@ -351,20 +360,28 @@ class MyWindow(QWidget, Ui_mainwin):
f"\n无法创建缓存位置\n\n使用管理员身份运行或检查文件读写权限\n\n【错误信息】:{e}\n",
)
sys.exit(1)
# 连接信号 & UI按钮
self.startbtn.clicked.connect(self.file_dialog)
self.exitbtn.clicked.connect(self.shutdown_app)
# 获取游戏安装路径
# 连接信号 (使用Ui_install.py中的组件名称)
self.ui.start_install_btn.clicked.connect(self.file_dialog)
self.ui.exit_btn.clicked.connect(self.shutdown_app)
# 在窗口显示前设置初始状态
self.animator.initialize()
# 窗口显示后延迟100ms启动动画
QTimer.singleShot(100, self.start_animations)
def start_animations(self):
self.animator.start_animations()
def get_install_paths(self):
return {
game: os.path.join(self.selected_folder, info["install_path"])
for game, info in GAME_INFO.items()
}
# 获取游戏目录
def file_dialog(self):
self.selected_folder = QtWidgets.QFileDialog.getExistingDirectory(
self.selected_folder = QFileDialog.getExistingDirectory(
self, f"选择游戏所在【上级目录】 {APP_NAME}"
)
if not self.selected_folder:
@@ -374,23 +391,22 @@ class MyWindow(QWidget, Ui_mainwin):
return
self.download_action()
# 获取下载配置文件
def get_download_url(self) -> dict:
try:
headers = {"User-Agent": UA}
response = requests.get(CONFIG_URL, headers=headers, timeout=10)
response.raise_for_status()
config_data = response.json()
if not all(f"vol.{i+1}.data" in config_data for i in range(4)):
if not all(f"vol.{i+1}.data" in config_data for i in range(4)) or "after.data" not in config_data:
raise ValueError("配置文件数据异常")
return {
f"vol{i+1}": config_data[f"vol.{i+1}.data"]["url"] for i in range(4)
} | {
"after": config_data["after.data"]["url"]
}
except requests.exceptions.RequestException as e:
# 获取 HTTP 状态码
status_code = e.response.status_code if e.response is not None else "未知"
try:
# 尝试从响应中解析 JSON 并提取 title 和 message
error_response = e.response.json() if e.response else {}
json_title = error_response.get("title", "无错误类型")
json_message = error_response.get("message", "无附加错误信息")
@@ -412,7 +428,6 @@ class MyWindow(QWidget, Ui_mainwin):
)
return {}
# 下载参数设置
def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path):
game_exe = {
game: os.path.join(
@@ -420,7 +435,7 @@ class MyWindow(QWidget, Ui_mainwin):
)
for game, info in GAME_INFO.items()
}
# 判断游戏是否存在,不存在则跳过
if (
game_version not in game_exe
or not os.path.exists(game_exe[game_version])
@@ -429,10 +444,10 @@ class MyWindow(QWidget, Ui_mainwin):
self.installed_status[game_version] = False
self.show_result()
return
# 下载时显示进度窗口
progress_window = ProgressWindow(self)
progress_window.show()
# 启用下载线程
self.current_download_thread = DownloadThread(url, _7z_path, self)
self.current_download_thread.progress.connect(progress_window.setprogressbarval)
self.current_download_thread.finished.connect(
@@ -448,7 +463,6 @@ class MyWindow(QWidget, Ui_mainwin):
)
self.current_download_thread.start()
# 安装设置
def install_setting(
self,
success,
@@ -466,7 +480,18 @@ class MyWindow(QWidget, Ui_mainwin):
QApplication.processEvents()
with py7zr.SevenZipFile(_7z_path, mode="r") as archive:
archive.extractall(path=PLUGIN)
# 创建游戏目录(如果不存在)
os.makedirs(game_folder, exist_ok=True)
# 复制主文件
shutil.copy(plugin_path, game_folder)
# 如果是After版本还需要复制签名文件
if game_version == "NEKOPARA After":
sig_path = os.path.join(PLUGIN, GAME_INFO[game_version]["sig_path"])
shutil.copy(sig_path, game_folder)
self.installed_status[game_version] = True
QMessageBox.information(
self, f"通知 {APP_NAME}", f"\n{game_version} 补丁已安装\n"
@@ -487,7 +512,6 @@ class MyWindow(QWidget, Ui_mainwin):
)
self.next_download_task()
# 下载前比对已有文件哈希值
def pre_hash_compare(self, install_path, game_version, plugin_hash):
msg_box = self.hash_manager.hash_pop_window()
self.hash_manager.cfg_pre_hash_compare(
@@ -495,7 +519,6 @@ class MyWindow(QWidget, Ui_mainwin):
)
msg_box.close()
# 开始下载文件
def download_action(self):
install_paths = self.get_install_paths()
for game_version, install_path in install_paths.items():
@@ -508,6 +531,7 @@ class MyWindow(QWidget, Ui_mainwin):
)
return
# 处理1-4卷
for i in range(1, 5):
game_version = f"NEKOPARA Vol.{i}"
if not self.installed_status[game_version]:
@@ -521,19 +545,28 @@ class MyWindow(QWidget, Ui_mainwin):
(url, game_folder, game_version, _7z_path, plugin_path)
)
# 处理After
game_version = "NEKOPARA After"
if not self.installed_status[game_version]:
url = config["after"]
game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
_7z_path = os.path.join(PLUGIN, "after.7z")
plugin_path = os.path.join(
PLUGIN, GAME_INFO[game_version]["plugin_path"]
)
self.download_queue.append(
(url, game_folder, game_version, _7z_path, plugin_path)
)
self.next_download_task()
# 开始下载队列中的下一个任务
def next_download_task(self):
if not self.download_queue:
self.after_hash_compare(PLUGIN_HASH)
return
url, game_folder, game_version, _7z_path, plugin_path = (
self.download_queue.popleft()
)
url, game_folder, game_version, _7z_path, plugin_path = self.download_queue.popleft()
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
# 下载完成后比对哈希值
def after_hash_compare(self, plugin_hash):
msg_box = self.hash_manager.hash_pop_window()
result = self.hash_manager.cfg_after_hash_compare(
@@ -543,7 +576,6 @@ class MyWindow(QWidget, Ui_mainwin):
self.show_result()
return result
# 显示最终安装结果
def show_result(self):
installed_version = "\n".join(
[i for i in self.installed_status if self.installed_status[i]]
@@ -558,11 +590,9 @@ class MyWindow(QWidget, Ui_mainwin):
f"安装成功的版本:\n{installed_version}\n尚未持有或未使用本工具安装补丁的版本:\n{failed_ver}\n",
)
# 关闭程序-窗口
def closeEvent(self, event):
self.shutdown_app(event)
# 关闭程序-按钮
def shutdown_app(self, event=None):
reply = QMessageBox.question(
self,
@@ -609,9 +639,8 @@ class MyWindow(QWidget, Ui_mainwin):
if event:
event.ignore()
if __name__ == "__main__":
app = QApplication([])
window = MyWindow()
window = MainWindow()
window.show()
sys.exit(app.exec())

169
source/Ui_install.py Normal file
View File

@@ -0,0 +1,169 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'install.ui'
##
## Created by: Qt User Interface Compiler version 6.9.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from pic_data import img_data
from PySide6.QtGui import QPixmap
import base64
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QCursor, QFont, QFontDatabase, QGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QMenu,
QMenuBar, QPushButton, QSizePolicy, QWidget)
def load_base64_image(base64_str):
pixmap = QPixmap()
pixmap.loadFromData(base64.b64decode(base64_str))
return pixmap
class Ui_MainWindows(object):
def setupUi(self, MainWindows):
if not MainWindows.objectName():
MainWindows.setObjectName(u"MainWindows")
MainWindows.setEnabled(True)
MainWindows.resize(1024, 576)
MainWindows.setMinimumSize(QSize(1024, 576))
MainWindows.setMaximumSize(QSize(1024, 576))
MainWindows.setMouseTracking(False)
MainWindows.setTabletTracking(False)
MainWindows.setAcceptDrops(True)
MainWindows.setAutoFillBackground(True)
MainWindows.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
MainWindows.setAnimated(True)
MainWindows.setDocumentMode(False)
MainWindows.setDockNestingEnabled(False)
self.action_2 = QAction(MainWindows)
self.action_2.setObjectName(u"action_2")
self.centralwidget = QWidget(MainWindows)
self.centralwidget.setObjectName(u"centralwidget")
self.centralwidget.setAutoFillBackground(True)
self.loadbg = QLabel(self.centralwidget)
self.loadbg.setObjectName(u"loadbg")
self.loadbg.setGeometry(QRect(0, 0, 1031, 561))
self.loadbg.setPixmap(load_base64_image(img_data["loadbg"]))
self.loadbg.setScaledContents(True)
self.vol1bg = QLabel(self.centralwidget)
self.vol1bg.setObjectName(u"vol1bg")
self.vol1bg.setGeometry(QRect(0, 120, 93, 64))
self.vol1bg.setPixmap(load_base64_image(img_data["vol1"]))
self.vol1bg.setScaledContents(True)
self.vol2bg = QLabel(self.centralwidget)
self.vol2bg.setObjectName(u"vol2bg")
self.vol2bg.setGeometry(QRect(0, 180, 93, 64))
self.vol2bg.setPixmap(load_base64_image(img_data["vol2"]))
self.vol2bg.setScaledContents(True)
self.vol3bg = QLabel(self.centralwidget)
self.vol3bg.setObjectName(u"vol3bg")
self.vol3bg.setGeometry(QRect(0, 240, 93, 64))
self.vol3bg.setPixmap(load_base64_image(img_data["vol3"]))
self.vol3bg.setScaledContents(True)
self.vol4bg = QLabel(self.centralwidget)
self.vol4bg.setObjectName(u"vol4bg")
self.vol4bg.setGeometry(QRect(0, 300, 93, 64))
self.vol4bg.setPixmap(load_base64_image(img_data["vol4"]))
self.vol4bg.setScaledContents(True)
self.afterbg = QLabel(self.centralwidget)
self.afterbg.setObjectName(u"afterbg")
self.afterbg.setGeometry(QRect(0, 360, 93, 64))
self.afterbg.setPixmap(load_base64_image(img_data["after"]))
self.afterbg.setScaledContents(True)
self.Mainbg = QLabel(self.centralwidget)
self.Mainbg.setObjectName(u"Mainbg")
self.Mainbg.setGeometry(QRect(0, 0, 1031, 561))
self.Mainbg.setPixmap(load_base64_image(img_data["Mainbg"]))
self.Mainbg.setScaledContents(True)
self.start_install_btn = QPushButton(self.centralwidget)
self.start_install_btn.setObjectName(u"start_install_btn")
self.start_install_btn.setEnabled(True)
self.start_install_btn.setGeometry(QRect(780, 250, 191, 91))
self.start_install_btn.setAutoFillBackground(False)
start_install_icon = QIcon()
start_install_pixmap = load_base64_image(img_data["start_install_btn"])
if not start_install_pixmap.isNull():
start_install_icon.addPixmap(start_install_pixmap)
self.start_install_btn.setIcon(start_install_icon)
self.start_install_btn.setIcon(start_install_icon)
self.start_install_btn.setIconSize(QSize(189, 110))
self.start_install_btn.setCheckable(False)
self.start_install_btn.setAutoRepeat(False)
self.start_install_btn.setAutoDefault(False)
self.start_install_btn.setFlat(True)
self.exit_btn = QPushButton(self.centralwidget)
self.exit_btn.setObjectName(u"exit_btn")
self.exit_btn.setEnabled(True)
self.exit_btn.setGeometry(QRect(780, 340, 191, 91))
self.exit_btn.setAutoFillBackground(False)
exit_icon = QIcon()
exit_pixmap = load_base64_image(img_data["exit_btn"])
if not exit_pixmap.isNull():
exit_icon.addPixmap(exit_pixmap)
self.exit_btn.setIcon(exit_icon)
self.exit_btn.setIcon(exit_icon)
self.exit_btn.setIconSize(QSize(189, 110))
self.exit_btn.setCheckable(False)
self.exit_btn.setFlat(True)
self.menubg = QLabel(self.centralwidget)
self.menubg.setObjectName(u"menubg")
self.menubg.setGeometry(QRect(710, 0, 321, 561))
self.menubg.setPixmap(load_base64_image(img_data["menubg"]))
self.menubg.setScaledContents(True)
MainWindows.setCentralWidget(self.centralwidget)
self.loadbg.raise_()
self.vol1bg.raise_()
self.vol2bg.raise_()
self.vol3bg.raise_()
self.vol4bg.raise_()
self.afterbg.raise_()
self.Mainbg.raise_()
self.menubg.raise_()
self.start_install_btn.raise_()
self.exit_btn.raise_()
self.menubar = QMenuBar(MainWindows)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 1024, 21))
self.menu = QMenu(self.menubar)
self.menu.setObjectName(u"menu")
self.menu_2 = QMenu(self.menubar)
self.menu_2.setObjectName(u"menu_2")
MainWindows.setMenuBar(self.menubar)
self.menubar.addAction(self.menu.menuAction())
self.menubar.addAction(self.menu_2.menuAction())
self.menu.addSeparator()
self.menu.addAction(self.action_2)
self.retranslateUi(MainWindows)
QMetaObject.connectSlotsByName(MainWindows)
# setupUi
def retranslateUi(self, MainWindows):
MainWindows.setWindowTitle(QCoreApplication.translate("MainWindows", u" UI Test", None))
self.action_2.setText(QCoreApplication.translate("MainWindows", u"\u68c0\u67e5\u66f4\u65b0(\u672a\u5b8c\u6210)", None))
self.loadbg.setText("")
self.vol1bg.setText("")
self.vol2bg.setText("")
self.vol3bg.setText("")
self.vol4bg.setText("")
self.afterbg.setText("")
self.Mainbg.setText("")
#if QT_CONFIG(accessibility)
self.start_install_btn.setAccessibleDescription("")
#endif // QT_CONFIG(accessibility)
self.start_install_btn.setText("")
self.exit_btn.setText("")
self.menubg.setText("")
self.menu.setTitle(QCoreApplication.translate("MainWindows", u"\u8bbe\u7f6e", None))
self.menu_2.setTitle(QCoreApplication.translate("MainWindows", u"\u5173\u4e8e", None))
# retranslateUi

168
source/animations.py Normal file
View File

@@ -0,0 +1,168 @@
from PySide6.QtCore import (QPropertyAnimation, QParallelAnimationGroup,
QPoint, QEasingCurve, QTimer)
from PySide6.QtWidgets import QGraphicsOpacityEffect
class MultiStageAnimations:
def __init__(self, ui):
self.ui = ui
# 获取画布尺寸
self.canvas_width = ui.centralwidget.width()
self.canvas_height = ui.centralwidget.height()
# 动画时序配置
self.animation_config = {
"logo": {
"delay_after": 2800
},
"mainbg": {
"delay_after": 500
}
}
# 第一阶段Logo动画配置
self.logo_widgets = [
{"widget": ui.vol1bg, "delay": 0, "duration": 500, "end_pos": QPoint(0, 120)},
{"widget": ui.vol2bg, "delay": 80, "duration": 500, "end_pos": QPoint(0, 180)},
{"widget": ui.vol3bg, "delay": 160, "duration": 500, "end_pos": QPoint(0, 240)},
{"widget": ui.vol4bg, "delay": 240, "duration": 500, "end_pos": QPoint(0, 300)},
{"widget": ui.afterbg, "delay": 320, "duration": 500, "end_pos": QPoint(0, 360)}
]
# 第二阶段:菜单元素
self.menu_widgets = [
{"widget": ui.menubg, "end_pos": QPoint(710, 0), "duration": 600},
{"widget": ui.start_install_btn, "end_pos": QPoint(780, 250), "duration": 600},
{"widget": ui.exit_btn, "end_pos": QPoint(780, 340), "duration": 600}
]
self.animations = []
self.timers = []
def initialize(self):
"""初始化所有组件状态"""
# 设置Mainbg初始状态
effect = QGraphicsOpacityEffect(self.ui.Mainbg)
effect.setOpacity(0)
self.ui.Mainbg.setGraphicsEffect(effect)
# 初始化Logo位置移到左侧外
for item in self.logo_widgets:
widget = item["widget"]
effect = QGraphicsOpacityEffect(widget)
effect.setOpacity(0)
widget.setGraphicsEffect(effect)
widget.move(-widget.width(), item["end_pos"].y())
widget.show()
print("初始化支持栏动画")
# 初始化菜单元素(底部外)
for item in self.menu_widgets:
widget = item["widget"]
effect = QGraphicsOpacityEffect(widget)
effect.setOpacity(0)
widget.setGraphicsEffect(effect)
widget.move(widget.x(), self.canvas_height + 100)
widget.show()
def start_logo_animations(self):
"""启动Logo动画序列"""
for item in self.logo_widgets:
timer = QTimer()
timer.setSingleShot(True)
timer.timeout.connect(
lambda w=item["widget"], d=item["duration"], pos=item["end_pos"]:
self.animate_logo(w, pos, d)
)
timer.start(item["delay"])
self.timers.append(timer)
def animate_logo(self, widget, end_pos, duration):
"""执行单个Logo动画"""
anim_group = QParallelAnimationGroup()
# 位置动画
pos_anim = QPropertyAnimation(widget, b"pos")
pos_anim.setDuration(duration)
pos_anim.setStartValue(QPoint(-widget.width(), end_pos.y()))
pos_anim.setEndValue(end_pos)
pos_anim.setEasingCurve(QEasingCurve.OutBack)
# 透明度动画
opacity_anim = QPropertyAnimation(widget.graphicsEffect(), b"opacity")
opacity_anim.setDuration(duration)
opacity_anim.setStartValue(0)
opacity_anim.setEndValue(1)
anim_group.addAnimation(pos_anim)
anim_group.addAnimation(opacity_anim)
# 最后一个Logo动画完成后添加延迟
if widget == self.logo_widgets[-1]["widget"]:
anim_group.finished.connect(
lambda: QTimer.singleShot(
self.animation_config["logo"]["delay_after"],
self.start_mainbg_animation
)
)
anim_group.start()
self.animations.append(anim_group)
def start_mainbg_animation(self):
"""启动主背景淡入动画"""
main_anim = QPropertyAnimation(self.ui.Mainbg.graphicsEffect(), b"opacity")
main_anim.setDuration(800)
main_anim.setStartValue(0)
main_anim.setEndValue(1)
main_anim.finished.connect(self.start_menu_animations)
main_anim.start()
self.animations.append(main_anim)
def start_mainbg_animation(self):
"""启动主背景淡入动画(带延迟)"""
main_anim = QPropertyAnimation(self.ui.Mainbg.graphicsEffect(), b"opacity")
main_anim.setDuration(800)
main_anim.setStartValue(0)
main_anim.setEndValue(1)
main_anim.finished.connect(
lambda: QTimer.singleShot(
self.animation_config["mainbg"]["delay_after"],
self.start_menu_animations
)
)
main_anim.start()
self.animations.append(main_anim)
def start_menu_animations(self):
"""启动菜单动画(从下往上)"""
for item in self.menu_widgets:
anim_group = QParallelAnimationGroup()
# 位置动画(从下往上)
pos_anim = QPropertyAnimation(item["widget"], b"pos")
pos_anim.setDuration(item["duration"])
pos_anim.setStartValue(QPoint(item["end_pos"].x(), self.canvas_height + 100))
pos_anim.setEndValue(item["end_pos"])
pos_anim.setEasingCurve(QEasingCurve.OutBack)
# 透明度动画
opacity_anim = QPropertyAnimation(item["widget"].graphicsEffect(), b"opacity")
opacity_anim.setDuration(item["duration"])
opacity_anim.setStartValue(0)
opacity_anim.setEndValue(1)
anim_group.addAnimation(pos_anim)
anim_group.addAnimation(opacity_anim)
anim_group.start()
self.animations.append(anim_group)
def start_animations(self):
"""启动完整动画序列"""
self.clear_animations()
self.start_logo_animations()
def clear_animations(self):
"""清理所有动画资源"""
for timer in self.timers:
timer.stop()
for anim in self.animations:
anim.stop()
self.timers.clear()
self.animations.clear()

View File

@@ -1,45 +0,0 @@
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=(4, 9, 0, 17411),
prodvers=(4, 9, 0, 17411),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x40004,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
'040904b0',
[StringStruct('CompanyName', ''),
StringStruct('FileDescription', 'FRAISEMOE Addons Installer'),
StringStruct('FileVersion', '4.9.0.17411'),
StringStruct('InternalName', 'FRAISEMOE Addons Installer'),
StringStruct('LegalCopyright', ''),
StringStruct('OriginalFilename', ''),
StringStruct('ProductName', 'FRAISEMOE Addons Installer'),
StringStruct('ProductVersion', '4.9.0.17411'),
StringStruct('SquirrelAwareVersion', '1'),
StringStruct('', '')])
]),
VarFileInfo([VarStruct('Translation', [2052, 1200])])
]
)

BIN
source/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

333
source/install.ui Normal file
View File

@@ -0,0 +1,333 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindows</class>
<widget class="QMainWindow" name="MainWindows">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>576</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1024</width>
<height>576</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1024</width>
<height>576</height>
</size>
</property>
<property name="mouseTracking">
<bool>false</bool>
</property>
<property name="tabletTracking">
<bool>false</bool>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle">
<string> UI Test</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonStyle::ToolButtonIconOnly</enum>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="documentMode">
<bool>false</bool>
</property>
<property name="dockNestingEnabled">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralwidget">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<widget class="QLabel" name="loadbg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1031</width>
<height>561</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/BG/bg2.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="vol1bg">
<property name="geometry">
<rect>
<x>0</x>
<y>120</y>
<width>93</width>
<height>64</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/LOGO/vo01_logo.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="vol2bg">
<property name="geometry">
<rect>
<x>0</x>
<y>180</y>
<width>93</width>
<height>64</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/LOGO/vo02_logo.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="vol3bg">
<property name="geometry">
<rect>
<x>0</x>
<y>240</y>
<width>93</width>
<height>64</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/LOGO/vo03_logo.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="vol4bg">
<property name="geometry">
<rect>
<x>0</x>
<y>300</y>
<width>93</width>
<height>64</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/LOGO/vo04_logo.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="afterbg">
<property name="geometry">
<rect>
<x>0</x>
<y>360</y>
<width>93</width>
<height>64</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/LOGO/voaf_logo.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="Mainbg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1031</width>
<height>561</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/BG/bg3.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="start_install_btn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>780</x>
<y>250</y>
<width>191</width>
<height>91</height>
</rect>
</property>
<property name="accessibleDescription">
<string/>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>IMG/BTN/start_install.bmp</normaloff>IMG/BTN/start_install.bmp</iconset>
</property>
<property name="iconSize">
<size>
<width>189</width>
<height>110</height>
</size>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="autoRepeat">
<bool>false</bool>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="exit_btn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>780</x>
<y>340</y>
<width>191</width>
<height>91</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>IMG/BTN/exit.bmp</normaloff>IMG/BTN/exit.bmp</iconset>
</property>
<property name="iconSize">
<size>
<width>189</width>
<height>110</height>
</size>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="menubg">
<property name="geometry">
<rect>
<x>710</x>
<y>0</y>
<width>321</width>
<height>561</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>IMG/BG/menubg.jpg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
<zorder>loadbg</zorder>
<zorder>vol1bg</zorder>
<zorder>vol2bg</zorder>
<zorder>vol3bg</zorder>
<zorder>vol4bg</zorder>
<zorder>afterbg</zorder>
<zorder>Mainbg</zorder>
<zorder>menubg</zorder>
<zorder>start_install_btn</zorder>
<zorder>exit_btn</zorder>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menu">
<property name="title">
<string>设置</string>
</property>
<addaction name="separator"/>
<addaction name="action_2"/>
</widget>
<widget class="QMenu" name="menu_2">
<property name="title">
<string>关于</string>
</property>
</widget>
<addaction name="menu"/>
<addaction name="menu_2"/>
</widget>
<action name="action_2">
<property name="text">
<string>update - sd</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

19
source/popup.ui Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>270</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
</widget>
<resources/>
<connections/>
</ui>

Binary file not shown.