feat: 完成macOS应用打包功能

This commit is contained in:
ruisu
2025-02-19 16:29:59 +08:00
parent 6a00193333
commit 0b6b3f13aa
7 changed files with 253 additions and 8 deletions

171
build.py
View File

@@ -1,9 +1,15 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import warnings import warnings
import os import os
import platform import platform
import subprocess import subprocess
import time import time
import threading import threading
import json
import sys
from pathlib import Path
# Ignore specific SyntaxWarning # Ignore specific SyntaxWarning
warnings.filterwarnings("ignore", category=SyntaxWarning, module="DrissionPage") warnings.filterwarnings("ignore", category=SyntaxWarning, module="DrissionPage")
@@ -79,6 +85,169 @@ def filter_output(output):
return "\n".join(important_lines) return "\n".join(important_lines)
def increment_version():
"""增加构建版本号"""
version_file = Path("version.json")
if version_file.exists():
with open(version_file, "r") as f:
version_data = json.load(f)
# 增加构建号
version_data["build"] += 1
# 更新版本号的最后一位
version_parts = version_data["version"].split(".")
version_parts[-1] = str(version_data["build"])
version_data["version"] = ".".join(version_parts)
# 保存更新后的版本信息
with open(version_file, "w") as f:
json.dump(version_data, f, indent=4)
return version_data
else:
print("错误:未找到 version.json 文件")
sys.exit(1)
def create_icns():
"""将 SVG 转换为 ICNS 格式"""
if not Path("icons/logo.svg").exists():
print("错误:未找到 logo.svg 文件")
sys.exit(1)
# 创建临时目录
os.makedirs("icons/tmp.iconset", exist_ok=True)
# 转换 SVG 到 PNG
sizes = [16, 32, 64, 128, 256, 512, 1024]
for size in sizes:
# 普通分辨率
os.system(f"rsvg-convert -w {size} -h {size} icons/logo.svg > icons/tmp.iconset/icon_{size}x{size}.png")
# 高分辨率(@2x
if size <= 512:
os.system(f"rsvg-convert -w {size*2} -h {size*2} icons/logo.svg > icons/tmp.iconset/icon_{size}x{size}@2x.png")
# 生成 icns 文件
os.system("iconutil -c icns icons/tmp.iconset -o icons/logo.icns")
# 清理临时文件
os.system("rm -rf icons/tmp.iconset")
def update_spec(version_data):
"""更新 spec 文件中的版本信息"""
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['cursor_gui.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='听泉Cursor助手',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=True,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='icons/logo.icns',
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='听泉Cursor助手',
)
app = BUNDLE(
coll,
name='听泉Cursor助手.app',
icon='icons/logo.icns',
bundle_identifier='com.tingquan.cursor',
info_plist={{
'CFBundleShortVersionString': '{version_data["version"]}',
'CFBundleVersion': '{version_data["build"]}',
'NSHighResolutionCapable': 'True',
'LSMinimumSystemVersion': '10.13.0',
'CFBundleName': '听泉Cursor助手',
'CFBundleDisplayName': '听泉Cursor助手',
}},
)'''
with open("cursor_app.spec", "w") as f:
f.write(spec_content)
def build_app():
"""构建应用程序"""
# 增加版本号
version_data = increment_version()
# 创建图标
create_icns()
# 更新 spec 文件
update_spec(version_data)
# 更新主程序中的版本号
update_main_version(version_data["version"])
# 构建应用
os.system("pyinstaller cursor_app.spec")
# 创建压缩包
os.system(f'cd dist && zip -r "听泉Cursor助手_v{version_data["version"]}.zip" "听泉Cursor助手.app"')
print(f"\n构建完成版本号v{version_data['version']}")
print(f"应用程序位置dist/听泉Cursor助手.app")
print(f"压缩包位置dist/听泉Cursor助手_v{version_data['version']}.zip")
def update_main_version(version):
"""更新主程序中的版本号"""
with open("cursor_gui.py", "r") as f:
content = f.read()
# 替换版本号
content = content.replace(
'self.setWindowTitle("Cursor账号管理器 v3.5.3")',
f'self.setWindowTitle("听泉Cursor助手 v{version}")'
)
with open("cursor_gui.py", "w") as f:
f.write(content)
def build(): def build():
# Clear screen # Clear screen
os.system("cls" if platform.system().lower() == "windows" else "clear") os.system("cls" if platform.system().lower() == "windows" else "clear")
@@ -176,4 +345,4 @@ def build():
if __name__ == "__main__": if __name__ == "__main__":
build() build_app()

View File

@@ -7,6 +7,42 @@ from PyQt6.QtCore import Qt, QTimer, QPropertyAnimation
from PyQt6.QtGui import QIcon, QPixmap from PyQt6.QtGui import QIcon, QPixmap
import cursor_account_manager as backend import cursor_account_manager as backend
from logger import logging from logger import logging
import traceback
# 设置日志文件路径
def setup_logging():
try:
# 获取应用程序所在目录
if getattr(sys, 'frozen', False):
# 如果是打包后的应用程序
app_dir = os.path.dirname(sys.executable)
else:
# 如果是开发环境
app_dir = os.path.dirname(os.path.abspath(__file__))
# 创建日志目录
log_dir = os.path.join(app_dir, 'logs')
os.makedirs(log_dir, exist_ok=True)
# 设置日志文件路径
log_file = os.path.join(log_dir, 'app.log')
# 配置日志记录
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler()
]
)
logging.info('日志系统初始化成功')
logging.info(f'日志文件路径: {log_file}')
except Exception as e:
print(f"设置日志系统时出错: {str(e)}")
print(traceback.format_exc())
class SuccessDialog(QDialog): class SuccessDialog(QDialog):
def __init__(self, message, parent=None): def __init__(self, message, parent=None):
@@ -154,7 +190,7 @@ class PasswordDialog(QDialog):
class CursorGUI(QMainWindow): class CursorGUI(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("Cursor账号管理器 v3.5.3") self.setWindowTitle("听泉Cursor助手 v3.0.2")
self.setFixedSize(600, 600) self.setFixedSize(600, 600)
# 设置整体样式 # 设置整体样式
@@ -488,7 +524,27 @@ class CursorGUI(QMainWindow):
QMessageBox.information(self, "提示", "更新功能开发中") QMessageBox.information(self, "提示", "更新功能开发中")
if __name__ == "__main__": if __name__ == "__main__":
try:
# 初始化日志系统
setup_logging()
# 记录启动信息
logging.info("=== 应用程序启动 ===")
logging.info(f"Python 版本: {sys.version}")
logging.info(f"操作系统: {sys.platform}")
app = QApplication(sys.argv) app = QApplication(sys.argv)
window = CursorGUI() window = CursorGUI()
window.show() window.show()
# 捕获未处理的异常
def handle_exception(exc_type, exc_value, exc_traceback):
logging.error("未捕获的异常:", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_exception
sys.exit(app.exec()) sys.exit(app.exec())
except Exception as e:
logging.error(f"程序启动失败: {str(e)}")
logging.error(traceback.format_exc())
raise

BIN
icons/logo.icns Normal file

Binary file not shown.

14
icons/logo.svg Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<!-- 背景 -->
<rect width="1024" height="1024" rx="200" fill="#1e88e5"/>
<!-- 水波纹效果 -->
<path d="M312,512 Q512,412 712,512" stroke="white" stroke-width="20" fill="none" opacity="0.6"/>
<path d="M312,562 Q512,462 712,562" stroke="white" stroke-width="15" fill="none" opacity="0.4"/>
<path d="M312,612 Q512,512 712,612" stroke="white" stroke-width="10" fill="none" opacity="0.2"/>
<!-- 主文字 -->
<text x="512" y="480" font-family="Arial" font-size="180" font-weight="bold" text-anchor="middle" fill="white">听泉</text>
<text x="512" y="680" font-family="Arial" font-size="120" font-weight="bold" text-anchor="middle" fill="white">Cursor</text>
</svg>

After

Width:  |  Height:  |  Size: 838 B

BIN
logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,7 +1,9 @@
DrissionPage==4.1.0.9 DrissionPage==4.1.0.9
colorama==0.4.6 colorama==0.4.6
python-dotenv==1.0.0 python-dotenv==1.0.0
pyinstaller pyinstaller==6.3.0
PyQt6==6.6.1 PyQt6==6.6.1
PyQt6-Qt6==6.6.1 PyQt6-Qt6==6.6.1
PyQt6-sip==13.6.0 PyQt6-sip==13.6.0
requests==2.31.0
urllib3==2.1.0

4
version.json Normal file
View File

@@ -0,0 +1,4 @@
{
"version": "3.0.5",
"build": 5
}