v2.5.5: Docs and pyproject updates

This commit is contained in:
QwejayHuang committed 2026-08-07 01:39:24 +08:00
1 parent da77d5cd56
commit 69a8cf6588
3 files changed
+45 -42

No files matched your search

+15 -2
View File
@@ -58,7 +58,7 @@ QPyPack 是一款致力于简化 Python 应用打包流程的可视化工具。
您可以通过以下两种方式之使用 QPyPack:
### 方法一:通过 pip 安装并运行(推荐开发者使用)
### 方法一:通过 pip 安装并运行
在 Python >= 3.8 的环境中,执行以下命令进行安装与启动:
@@ -70,7 +70,7 @@ pip install qpypack
qpypack
```
### 方法二:下载免安装独立运行版(推荐普通用户使用)
### 方法二:下载二进制文件
如果您不想配置本地 Python 环境,可直接在 Release 页面下载对应系统的预编译打包版本:
👉 [下载页面](https://github.com/Qwejay/QPyPack/releases)
@@ -79,6 +79,19 @@ qpypack
## 📅 更新日志 (Changelog)
### [2.5.5] - 2026-07-22
* **消除误删风险**:优化目录 `build/dist` 智能清理,保护桌面及工作区安全。
* **全沙盒隔离编译**:引入系统级临时目录隔离输出,彻底解决跨盘符与路径割裂异常。
* **修复路径映射与 UX**:构建 cwd 重定向至源码目录以完美解析相对路径;修正 `open_dist` 打开逻辑。
* **修复 pipreqs 崩溃**:尝试解决依赖自动扫描过程中的编码中断问题。
### [2.5.4] - 2026-07-18
* **新增精简模式**:自动剔除 `pip`、`unittest` 等冗余库并启用 Nuitka `-OO` 优化,大幅缩减产物体积;配套深度源码静态扫描(至多检索 500 个本地依赖),保障极致精简下的运行稳定性。
* **修复 UPX 假性报错问题**:当未勾选 UPX 时,显式向引擎传递 `--noupx` 指令,阻断 PyInstaller 静默调用系统 UPX 造成的兼容性报错与日志干扰。
* **修复日志中文乱码问题**:对底层命令行调用 (`subprocess`) 全面强制追加 `encoding="utf-8"` 参数,彻底解决 Windows 默认编码 (GBK) 解析字节流时引发的控制台乱码。。
### [2.5.3] - 2026-07-17
* **引擎版本锁定**:支持在高级设置中自定义 Nuitka/PyInstaller/pipreqs 的版本,杜绝上游更新导致的打包失败。
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "qpypack"
version = "2.5.4"
version = "2.5.5"
description = "A cross-platform GUI packaging and compilation suite for PyInstaller and Nuitka."
readme = "README.md"
authors = [
+29 -39
View File
@@ -36,7 +36,7 @@ from PySide6.QtGui import QFont, QDragEnterEvent, QDropEvent, QTextCursor, QIcon
from PySide6.QtSvg import QSvgRenderer
__app_name__ = "QPyPack"
__version__ = "2.5.4"
__version__ = "2.5.5"
__author__ = "QwejayHuang"
__company__ = "QwejayHuang"
__description__ = "基于 PyInstaller 与 Nuitka 的跨平台 Python 应用打包构建工具"
@@ -1421,11 +1421,11 @@ class SettingsPanel(QWidget):
card2, lay2 = self._create_card("编译选项")
lay2.setSpacing(16)
self.concise_log_check = QCheckBox("精简日志输出 (过滤构建引擎产生的警告信息)")
self.auto_save_log_check = QCheckBox("自动保存编译日志 (构建失败时在输出目录落盘生成日志文件)")
self.auto_icon_check = QCheckBox("自动检索图标 (自动匹配并绑定源文件同名 ICO/SVG 图标)")
self.clean_all_check = QCheckBox("清理构建缓存 (构建完成后自动删除临时目录与缓存文件)")
self.sound_notify_check = QCheckBox("启用构建提示音 (构建成功或失败时播放系统音效)")
self.concise_log_check = QCheckBox("精简日志输出")
self.auto_save_log_check = QCheckBox("自动保存编译日志")
self.auto_icon_check = QCheckBox("自动检索图标")
self.clean_all_check = QCheckBox("清理构建缓存")
self.sound_notify_check = QCheckBox("启用构建提示音")
for chk in (self.concise_log_check, self.auto_save_log_check, self.auto_icon_check, self.clean_all_check, self.sound_notify_check):
lay2.addWidget(chk)
@@ -1685,6 +1685,7 @@ class PackingThread(QThread):
self.venv_dir = None
self.temp_workpath = None
self.temp_out_dir = None
self.temp_dist_dir = None
self.all_raw_logs = []
def cancel(self):
@@ -1695,7 +1696,7 @@ class PackingThread(QThread):
else: self.process.kill()
except: pass
def run_cmd(self, cmd, cwd=None, timeout=None):
def run_cmd(self, cmd, cwd=None, timeout=None, silent_error=False):
if self._is_cancelled: return False
timer = None
@@ -1769,7 +1770,7 @@ class PackingThread(QThread):
success = self.process.returncode == 0
if not success and self.params.get('concise_log', True) and cmd_raw_lines:
if not success and not silent_error and self.params.get('concise_log', True) and cmd_raw_lines:
self.progress.emit("\n" + "!"*10 + " [诊断回溯: 以下是由于该环节执行异常产生的完整原始日志] " + "!"*10)
self.progress.emit('\n'.join(cmd_raw_lines))
self.progress.emit("!"*60 + "\n")
@@ -1867,9 +1868,7 @@ class PackingThread(QThread):
ext = ".exe" if os.name == "nt" else ""
try:
self.progress.emit("[INFO] 正在初始化工作空间并清除历史构建缓存...")
robust_rmtree(Path.cwd() / "build")
robust_rmtree(Path.cwd() / "dist")
self.progress.emit("[INFO] 正在初始化隔离构建环境...")
script_path = Path(self.params['script_path']).resolve()
script_dir = script_path.parent
@@ -1970,15 +1969,14 @@ class PackingThread(QThread):
self.progress.emit(f"[INFO] 依赖分析服务源地址: {pypi_server}")
self.progress.emit("[INFO] 正在查询各依赖库的版本,请稍候...")
success_pipreqs = self.run_cmd(pipreqs_cmd, timeout=15)
success_pipreqs = self.run_cmd(pipreqs_cmd, timeout=15, silent_error=True)
if not success_pipreqs:
self.progress.emit("[INFO] UTF-8 扫描受阻,正在尝试利用系统本地编码重新扫描...")
fallback_cmd = [c for c in pipreqs_cmd if c != "utf-8"]
if "--encoding" in fallback_cmd:
idx = fallback_cmd.index("--encoding")
fallback_cmd.pop(idx)
success_pipreqs = self.run_cmd(fallback_cmd, timeout=15)
self.progress.emit("[INFO] UTF-8 扫描受阻,正在尝试利用兼容编码重新扫描...")
fallback_cmd = ["iso-8859-1" if c == "utf-8" else c for c in pipreqs_cmd]
success_pipreqs = self.run_cmd(fallback_cmd, timeout=15, silent_error=True)
if not success_pipreqs:
self.progress.emit("[WARN] pipreqs 深度扫描因编码异常中断,已跳过。将由后续 AST 扫描引擎补全依赖。")
if success_pipreqs and temp_pipreqs.exists():
self.run_cmd([python_exe, "-m", "pip", "install", "-q", "-r", temp_pipreqs.as_posix()] + pip_args)
@@ -2014,10 +2012,12 @@ class PackingThread(QThread):
if engine == "PyInstaller":
self.temp_workpath = Path(tempfile.mkdtemp(prefix="qpypack_build_")).resolve()
self.temp_dist_dir = Path(tempfile.mkdtemp(prefix="qpypack_dist_")).resolve()
cmd = [
python_exe, "-m", "PyInstaller",
"--clean",
"--noconfirm",
f"--distpath={self.temp_dist_dir.as_posix()}",
f"--workpath={self.temp_workpath.as_posix()}",
f"--name={app_name}"
]
@@ -2160,18 +2160,17 @@ class PackingThread(QThread):
cmd.append(script_posix)
success = self.run_cmd(cmd, cwd=Path.cwd().resolve().as_posix())
success = self.run_cmd(cmd, cwd=script_dir.as_posix())
if self._is_cancelled: return self.finished.emit(False, "[INFO] 构建已被终止。")
self.progress.emit("[INFO] 编译核心完成,正在提取并归档编译产物...")
cwd = Path.cwd().resolve()
src_out = None
if engine == "PyInstaller":
if sys.platform == "darwin" and self.params['noconsole']:
src_out = cwd / "dist" / f"{app_name}.app"
src_out = self.temp_dist_dir / f"{app_name}.app"
else:
src_out = cwd / "dist" / (f"{app_name}{ext}" if self.params['onefile'] else app_name)
src_out = self.temp_dist_dir / (f"{app_name}{ext}" if self.params['onefile'] else app_name)
elif engine == "Nuitka":
if self.params['onefile']:
if sys.platform == "darwin" and self.params['noconsole']:
@@ -2263,27 +2262,20 @@ class PackingThread(QThread):
except: pass
if self.params['clean_all']:
self.progress.emit("[INFO] 正在释放空间,删除虚拟环境及临时编译缓存...")
self.progress.emit("[INFO] 正在释放空间,安全清除临时构建环境...")
for p in [self.venv_dir, self.temp_workpath, self.temp_out_dir]:
for p in [self.venv_dir, self.temp_workpath, self.temp_out_dir, self.temp_dist_dir]:
if p and p.exists(): robust_rmtree(p)
cwd = Path.cwd().resolve()
app_name = self.params.get('app_name', 'app')
robust_rmtree(cwd / "dist")
for p in ["build", "__pycache__", f"{app_name}.build", f"{app_name}.dist", f"{app_name}.onefile-build"]:
robust_rmtree(cwd / p)
for p in ["__pycache__", f"{app_name}.build", f"{app_name}.dist", f"{app_name}.onefile-build"]:
robust_rmtree(script_dir / p)
spec_file = cwd / f"{app_name}.spec"
spec_file = script_dir / f"{app_name}.spec"
if spec_file.exists():
try: spec_file.unlink()
except: pass
crash_xml = cwd / "nuitka-crash-report.xml"
if crash_xml.exists():
try: crash_xml.unlink()
except: pass
class MainWindow(QMainWindow):
def __init__(self):
@@ -2713,12 +2705,10 @@ class MainWindow(QMainWindow):
self.update_ui_state("failed")
def open_dist(self):
if self.settings_panel.out_mode_combo.currentIndex() == 1:
if self.settings_panel.out_mode_combo.currentIndex() == 1 and self.settings_panel.out_dir_edit.text().strip():
target = Path(self.settings_panel.out_dir_edit.text().strip())
elif self.settings_panel.clean_all_check.isChecked() and self.script_path:
target = Path(self.script_path).parent
else:
target = Path.cwd() / "dist"
else:
target = Path(self.script_path).parent if self.script_path else Path.cwd()
if target.exists():
try: