feat: 完成macOS应用打包功能
This commit is contained in:
171
build.py
171
build.py
@@ -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()
|
||||||
|
|||||||
@@ -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__":
|
||||||
app = QApplication(sys.argv)
|
try:
|
||||||
window = CursorGUI()
|
# 初始化日志系统
|
||||||
window.show()
|
setup_logging()
|
||||||
sys.exit(app.exec())
|
|
||||||
|
# 记录启动信息
|
||||||
|
logging.info("=== 应用程序启动 ===")
|
||||||
|
logging.info(f"Python 版本: {sys.version}")
|
||||||
|
logging.info(f"操作系统: {sys.platform}")
|
||||||
|
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
window = CursorGUI()
|
||||||
|
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())
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"程序启动失败: {str(e)}")
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
raise
|
||||||
BIN
icons/logo.icns
Normal file
BIN
icons/logo.icns
Normal file
Binary file not shown.
14
icons/logo.svg
Normal file
14
icons/logo.svg
Normal 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 |
@@ -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
4
version.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"version": "3.0.5",
|
||||||
|
"build": 5
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user