| 方案 | 能否打包到单个exe | 实际打包方案 | 最终体积 |
|---|---|---|---|
| C++方案(Qt + QWebEngine) | ❌ 不能 | 安装包包含Chromium | ~200-300MB |
| Python方案(PyQt6 + Playwright) | ❌ 不能 | 安装包包含Chrome | ~150-250MB |
重要说明:
QWebEngineView特点:
目录结构:
BuffApp-Setup.exe (安装包)
└── 安装后目录结构:
├── BuffApp.exe (主程序,~10MB)
├── Qt6Core.dll
├── Qt6Widgets.dll
├── Qt6WebEngineWidgets.dll
├── Qt6WebEngineCore.dll
├── resources/
│ ├── qtwebengine_resources.pak
│ ├── qtwebengine_devtools_resources.pak
│ ├── icudtl.dat
│ ├── locales/ (语言包)
│ └── chromium/ (Chromium核心文件)
│ ├── chrome.exe
│ ├── chrome_100_percent.pak
│ ├── chrome_200_percent.pak
│ ├── resources.pak
│ └── ... (其他Chromium文件)
├── extensions/ (Chrome插件)
└── sda/ (Steam工具)
CMakeLists.txt配置:
# 安装规则
install(TARGETS BuffApp DESTINATION bin)
# 安装Qt库
install(FILES
${Qt6_DIR}/bin/Qt6Core.dll
${Qt6_DIR}/bin/Qt6Widgets.dll
${Qt6_DIR}/bin/Qt6WebEngineWidgets.dll
${Qt6_DIR}/bin/Qt6WebEngineCore.dll
DESTINATION bin
)
# 安装Qt WebEngine资源
install(DIRECTORY ${Qt6_DIR}/resources/
DESTINATION bin/resources
FILES_MATCHING PATTERN "qtwebengine*"
)
# 安装Chromium
install(DIRECTORY ${CMAKE_SOURCE_DIR}/resources/chromium/
DESTINATION bin/resources/chromium
)
# 安装其他资源
install(DIRECTORY ${CMAKE_SOURCE_DIR}/resources/extensions/
DESTINATION bin/resources/extensions
)
NSIS安装脚本:
; installer.nsi
!include "MUI2.nsh"
Name "Buff App"
OutFile "BuffApp-Setup-1.0.0.exe"
InstallDir "$PROGRAMFILES\BuffApp"
; 设置压缩方式(LZMA压缩率最高)
SetCompressor /SOLID lzma
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
; 主程序
File "build\release\BuffApp.exe"
; Qt库
File "Qt6Core.dll"
File "Qt6Widgets.dll"
File "Qt6WebEngineWidgets.dll"
File "Qt6WebEngineCore.dll"
; Qt WebEngine资源
SetOutPath "$INSTDIR\resources"
File /r "resources\qtwebengine_*"
File /r "resources\icudtl.dat"
File /r "resources\locales\*"
; Chromium浏览器
SetOutPath "$INSTDIR\resources\chromium"
File /r "resources\chromium\*"
; Chrome插件
SetOutPath "$INSTDIR\resources\extensions"
File /r "resources\extensions\*"
; Steam工具
SetOutPath "$INSTDIR\resources\sda"
File /r "resources\sda\*"
; 创建快捷方式
CreateShortcut "$DESKTOP\Buff App.lnk" "$INSTDIR\BuffApp.exe"
CreateShortcut "$SMPROGRAMS\Buff App.lnk" "$INSTDIR\BuffApp.exe"
; 写入注册表
WriteRegStr HKLM "Software\BuffApp" "InstallDir" "$INSTDIR"
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
最终安装包体积:
适用场景:
实现方式:
// updater/chromium_downloader.cpp
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QFile>
#include <QDir>
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonObject>
class ChromiumDownloader : public QObject {
Q_OBJECT
public:
void downloadChromiumIfNeeded() {
QString chromiumPath = getChromiumPath();
if (QFile::exists(chromiumPath + "/chrome.exe")) {
// 已存在,检查版本
if (checkVersion()) {
return; // 版本正确,无需下载
}
}
// 下载Chromium
downloadChromium();
}
private:
QString getChromiumPath() {
QString appDataPath = QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation
);
return appDataPath + "/chromium";
}
void downloadChromium() {
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
// 从服务器获取下载链接
QNetworkRequest request(QUrl("https://your-server.com/api/chromium-download-url"));
QNetworkReply* reply = manager->get(request);
connect(reply, &QNetworkReply::finished, [this, reply]() {
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
QJsonObject obj = doc.object();
QString downloadUrl = obj["url"].toString();
// 下载Chromium压缩包
downloadFile(downloadUrl);
});
}
void downloadFile(const QString& url) {
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
QNetworkRequest request(QUrl(url));
QNetworkReply* reply = manager->get(request);
QString savePath = getChromiumPath() + "/chromium.zip";
QFile* file = new QFile(savePath, this);
file->open(QIODevice::WriteOnly);
connect(reply, &QNetworkReply::readyRead, [reply, file]() {
file->write(reply->readAll());
});
connect(reply, &QNetworkReply::finished, [this, reply, file, savePath]() {
file->close();
// 解压文件
extractZip(savePath);
});
}
};
优点:
缺点:
Playwright特点:
目录结构:
BuffApp-Setup.exe (安装包)
└── 安装后目录结构:
├── BuffApp.exe (主程序,~50MB,包含Python运行时)
├── _internal/ (PyInstaller解压目录)
│ ├── python310.dll
│ ├── PyQt6相关DLL
│ └── ...
├── resources/
│ ├── chromium/ (Chrome浏览器)
│ │ ├── chrome.exe
│ │ ├── chrome_100_percent.pak
│ │ ├── chrome_200_percent.pak
│ │ ├── resources.pak
│ │ └── ... (其他Chrome文件)
│ ├── extensions/ (Chrome插件)
│ └── sda/ (Steam工具)
PyInstaller spec文件配置:
# BuffApp.spec
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[
# PyQt6资源文件
('C:/Python310/Lib/site-packages/PyQt6/Qt6/plugins', 'PyQt6/Qt6/plugins'),
('C:/Python310/Lib/site-packages/PyQt6/Qt6/resources', 'PyQt6/Qt6/resources'),
# Chrome浏览器
('resources/chromium', 'resources/chromium'),
# Chrome插件
('resources/extensions', 'resources/extensions'),
# Steam工具
('resources/sda', 'resources/sda'),
],
hiddenimports=[
'PyQt6',
'PyQt6.QtCore',
'PyQt6.QtWidgets',
'PyQt6.QtWebEngineWidgets',
'playwright',
],
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,
a.binaries,
a.zipfiles,
a.datas,
[],
name='BuffApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True, # 使用UPX压缩
upx_exclude=[],
runtime_tmpdir=None,
console=False, # 无控制台窗口
icon='resources/icon.ico',
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
# 如果需要安装包,使用NSIS
# coll = COLLECT(...)
打包命令:
# 1. 生成spec文件
pyinstaller --name=BuffApp --windowed --icon=resources/icon.ico main.py
# 2. 编辑spec文件,添加资源文件
# 3. 打包
pyinstaller BuffApp.spec
# 4. 使用NSIS创建安装包
makensis installer.nsi
NSIS安装脚本:
; installer.nsi
!include "MUI2.nsh"
Name "Buff App"
OutFile "BuffApp-Setup-1.0.0.exe"
InstallDir "$PROGRAMFILES\BuffApp"
; 设置压缩方式
SetCompressor /SOLID lzma
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
; 主程序(PyInstaller打包的exe)
File "dist\BuffApp.exe"
; Chrome浏览器
SetOutPath "$INSTDIR\resources\chromium"
File /r "resources\chromium\*"
; Chrome插件
SetOutPath "$INSTDIR\resources\extensions"
File /r "resources\extensions\*"
; Steam工具
SetOutPath "$INSTDIR\resources\sda"
File /r "resources\sda\*"
; 创建快捷方式
CreateShortcut "$DESKTOP\Buff App.lnk" "$INSTDIR\BuffApp.exe"
CreateShortcut "$SMPROGRAMS\Buff App.lnk" "$INSTDIR\BuffApp.exe"
; 写入注册表
WriteRegStr HKLM "Software\BuffApp" "InstallDir" "$INSTDIR"
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
最终安装包体积:
Playwright可以自动下载浏览器:
# 首次运行时,Playwright会自动下载Chrome
from playwright.sync_api import sync_playwright
playwright = sync_playwright().start()
# 首次调用时会自动下载Chrome到用户目录
browser = playwright.chromium.launch()
下载位置:
%USERPROFILE%\AppData\Local\ms-playwright\优点:
缺点:
| 对比项 | C++方案(Qt + QWebEngine) | Python方案(PyQt6 + Playwright) |
|---|---|---|
| 浏览器引擎 | Chromium(Qt内置) | Chrome(Playwright管理) |
| 浏览器体积 | ~150MB | ~120MB |
| 打包方式 | 安装包包含 | 安装包包含或首次下载 |
| 安装包体积 | ~220MB | ~180MB(包含)或 ~60MB(下载) |
| 启动速度 | 快(本地加载) | 中等(本地加载)或慢(首次下载) |
| 维护成本 | 需要手动更新Chromium | Playwright自动管理版本 |
| 用户体验 | 安装后即可使用 | 安装后即可使用(或首次下载) |
理由:
实施步骤:
resources/chromium/理由:
备选方案:首次启动下载
# 1. 构建项目
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release
# 2. 准备资源文件
# 下载Chromium到 resources/chromium/
# 准备插件到 resources/extensions/
# 准备SDA到 resources/sda/
# 3. 创建安装包
makensis ../installer.nsi
# 4. 输出:BuffApp-Setup-1.0.0.exe (~220MB)
# 1. 安装依赖
pip install -r requirements.txt
pip install pyinstaller
# 2. 下载Chrome浏览器(Playwright)
playwright install chromium
# 3. 复制Chrome到resources目录
# 从 %USERPROFILE%\AppData\Local\ms-playwright\ 复制到 resources/chromium/
# 4. 生成spec文件
pyinstaller --name=BuffApp --windowed --icon=resources/icon.ico main.py
# 5. 编辑BuffApp.spec,添加资源文件
# 6. 打包
pyinstaller BuffApp.spec
# 7. 创建安装包
makensis installer.nsi
# 8. 输出:BuffApp-Setup-1.0.0.exe (~180MB)
C++方案:
Python方案:
备选方案(如果体积敏感):
A: 可以,但效果有限:
A: 部分可行:
A: 可以,但有限制:
A: 解决方案:
结论:浏览器无法打包到单个exe,但可以打包到安装包中,这是标准做法,用户体验良好。