v2.6.1: Minor fixes and stability updates

This commit is contained in:
QwejayHuang committed 2026-08-07 01:39:25 +08:00
1 parent b9ab8f6896
commit 2c02d9db73
3 files changed
+66 -28

No files matched your search

+24 -12
View File
@@ -26,8 +26,9 @@ QPyPack 是一款致力于简化 Python 应用打包流程的可视化工具。
## 📷 界面预览 (Screenshots)
<p align="center">
<img width="1023" height="977" alt="image" src="https://github.com/user-attachments/assets/5201ce6f-e908-443b-b6aa-733cf4d4c4ad" />
<img width="1023" height="977" alt="image" src="https://github.com/user-attachments/assets/cfa93356-1def-4b39-9cc9-5cb6914c783e" />
<img width="1143" height="1067" alt="image" src="https://github.com/user-attachments/assets/8658aaef-5867-470d-ba4d-07892309ab1a" />
<img width="1142" height="1067" alt="image" src="https://github.com/user-attachments/assets/b64d9b89-8bfd-4bf8-8fec-9c75f61305dd" />
</p>
---
@@ -79,6 +80,12 @@ qpypack
## 📅 更新日志 (Changelog)
### [2.6.1] - 2026-07-25
* **消除依赖污染**:修复了开启“精简模式”时,AST 引擎错误遍历整个父目录的 Bug,解决打包产物被混入无关依赖的问题。
* **构建沙盒隔离**:默认将目标脚本放入独立沙盒中解析,杜绝同目录其他脚本的依赖干扰;同时在 UI 界面新增“允许扫描同目录所有文件”开关。
* **MSVC 智能检测**:引入本地 C++ 编译环境智能嗅探。若系统已安装 MSVC,Nuitka 将自动启用 --msvc=latest,优先使用系统原生编译器,提升兼容性并免除耗时的 MinGW 下载。
* **UPX 警告静默**:自动识别并跳过系统核心动态库的 UPX 压缩报错,并小幅提升了构建速度。
### [2.6.0] - 2026-07-24
@@ -208,18 +215,23 @@ qpypack
---
## 📄 开源协议 (License)
## 💖 赞助支持 (Sponsorship)
本项目基于 **GPL-3.0** 开源许可协议。在遵守该开源许可协议的前提下,您可以自由分发、修改和二次开发。
QPyPack 是一个完全开源且免费的项目,由个人利用业余时间开发与维护。如果本项目帮助您提高了开发效率或解决了打包难题,欢迎通过以下方式对项目进行**自愿赞助**。您的支持将是本项目持续更新与维护的重要动力:
- ⚡ **请作者喝咖啡**:[赞助支持 QPyPack](https://www.ifdian.net/a/qwejay)(支持微信 / 支付宝)
> **赞助说明**:赞助完全出于自愿,属于对开源项目的无偿鼓励,不包含任何商业服务绑定或特定功能开发承诺。非常感谢每一位支持开源创作的朋友!
---
## 📈 Star History
## 📄 开源协议 (License)
<a href="https://www.star-history.com/?repos=Qwejay%2FQPyPack&type=date&legend=bottom-right">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Qwejay/QPyPack&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Qwejay/QPyPack&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Qwejay/QPyPack&type=date&legend=top-left" />
</picture>
</a>
本项目基于 [GNU General Public License v3.0](LICENSE) 开源,允许在遵循协议条款的前提下自由分发、修改和二次开发。
> [!NOTE]
> **关于打包产物的版权**:
> 使用 QPyPack 构建生成的二进制文件/应用程序,其版权和开源许可**完全由使用者自行决定**,QPyPack 的 GPL-3.0 协议不会影响用户打包后的程序。
Copyright (C) 2026 QwejayHuang.
```
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "qpypack"
version = "2.6.0"
version = "2.6.1"
description = "A cross-platform GUI packaging and compilation suite for PyInstaller and Nuitka."
readme = "README.md"
authors = [
+41 -15
View File
@@ -47,7 +47,7 @@ except ImportError:
HAS_QT_AUDIO = False
__app_name__ = "QPyPack"
__version__ = "2.6.0"
__version__ = "2.6.1"
__author__ = "QwejayHuang"
__company__ = "QwejayHuang"
__description__ = "基于 PyInstaller 与 Nuitka 的跨平台 Python 应用打包构建工具"
@@ -150,7 +150,7 @@ def load_config():
'pip_index_backup': 'https://mirrors.aliyun.com/pypi/simple/',
'onefile': 'True', 'noconsole': 'True', 'clean_all': 'True',
'auto_icon': 'True', 'use_venv': 'True', 'use_reqs': 'True',
'use_pipreqs': 'True', 'upx': 'False', 'concise_log': 'True',
'use_pipreqs': 'True', 'use_pipreqs_dir': 'False', 'upx': 'False', 'concise_log': 'True',
'cpu_cores': str(os.cpu_count() or 2), 'upx_path': '',
'exclude_modules': '', 'out_mode': '0', 'custom_out_dir': '',
'sound_notify': 'True', 'auto_save_log': 'False',
@@ -1695,9 +1695,11 @@ class SettingsPanel(QWidget):
self.venv_check = QCheckBox("启用独立虚拟环境打包 (推荐)")
self.reqs_check = QCheckBox("同步安装 requirements.txt 声明")
self.pipreqs_check = QCheckBox("自动分析并补全项目依赖 (pipreqs)")
self.pipreqs_dir_check = QCheckBox("允许 pipreqs 扫描同目录所有文件")
g_dep.addWidget(self.venv_check, 0, 0)
g_dep.addWidget(self.reqs_check, 0, 1)
g_dep.addWidget(self.pipreqs_check, 1, 0, 1, 2)
g_dep.addWidget(self.pipreqs_check, 1, 0)
g_dep.addWidget(self.pipreqs_dir_check, 1, 1)
c_lay_deps.addLayout(g_dep)
lay_sub2.addWidget(card_deps)
@@ -2002,6 +2004,7 @@ class SettingsPanel(QWidget):
"use_venv": self.venv_check.isChecked(),
"use_reqs": self.reqs_check.isChecked(),
"use_pipreqs": self.pipreqs_check.isChecked(),
"use_pipreqs_dir": self.pipreqs_dir_check.isChecked(),
"reqs_file": self.reqs_file_edit.text(),
"pip_source": self._get_url_from_combo(self.pip_source_combo),
"pip_backup": self._get_url_from_combo(self.pip_backup_combo),
@@ -2031,6 +2034,7 @@ class SettingsPanel(QWidget):
if "use_venv" in data: self.venv_check.setChecked(bool(data["use_venv"]))
if "use_reqs" in data: self.reqs_check.setChecked(bool(data["use_reqs"]))
if "use_pipreqs" in data: self.pipreqs_check.setChecked(bool(data["use_pipreqs"]))
if "use_pipreqs_dir" in data: self.pipreqs_dir_check.setChecked(bool(data["use_pipreqs_dir"]))
if "reqs_file" in data: self.reqs_file_edit.setText(data["reqs_file"])
if "pip_source" in data: self._set_combo_value(self.pip_source_combo, data["pip_source"])
if "pip_backup" in data: self._set_combo_value(self.pip_backup_combo, data["pip_backup"])
@@ -2063,6 +2067,7 @@ class SettingsPanel(QWidget):
self.venv_check.setChecked(s.getboolean('use_venv', True))
self.reqs_check.setChecked(s.getboolean('use_reqs', True))
self.pipreqs_check.setChecked(s.getboolean('use_pipreqs', True))
self.pipreqs_dir_check.setChecked(s.getboolean('use_pipreqs_dir', False))
self.reqs_file_edit.setText(s.get('use_reqs_file', ''))
self.python_path_combo.setCurrentText(s.get('custom_python_path', ''))
@@ -2112,6 +2117,7 @@ class SettingsPanel(QWidget):
s['use_venv'] = str(self.venv_check.isChecked())
s['use_reqs'] = str(self.reqs_check.isChecked())
s['use_pipreqs'] = str(self.pipreqs_check.isChecked())
s['use_pipreqs_dir'] = str(self.pipreqs_dir_check.isChecked())
s['use_reqs_file'] = self.reqs_file_edit.text().strip()
raw_py = self.python_path_combo.currentText().strip()
@@ -2405,6 +2411,8 @@ class PackingThread(QThread):
l_lower = l.lower()
if "error:" in l_lower:
return False
if "upx" in l_lower and ("subprocess.calledprocesserror" in l_lower or "notcompressibleexception" in l_lower):
return True
return any(kw in l_lower for kw in ["warning:", "info:", "deprecation:", "userwarning:", "futurewarning:"])
for line in self.process.stdout:
@@ -2574,16 +2582,6 @@ class PackingThread(QThread):
script_imports = set()
try:
script_imports = extract_imports_via_ast(script_posix, system_python_exe)
if self.params.get('lite_mode'):
scan_count = 0
for py_file in script_dir.rglob("*.py"):
if any(p.startswith('.') or p.lower() in ('venv', 'env', 'site-packages', 'node_modules', '__pycache__') for p in py_file.parts):
continue
if scan_count > 500: break
try:
script_imports.update(extract_imports_via_ast(py_file.as_posix(), system_python_exe))
scan_count += 1
except: pass
except Exception as e:
self.progress.emit(f"[WARN] 源码 AST 分析异常: {e}")
@@ -2639,6 +2637,16 @@ class PackingThread(QThread):
if self.params.get('use_pipreqs'):
self.progress.emit("[INFO] 依赖安装 [2/3]: 正在调用 pipreqs 分析项目依赖...")
sandbox_dir = None
if not self.params.get('use_pipreqs_dir', False):
sandbox_dir = Path(tempfile.mkdtemp(prefix="qpypack_sandbox_")).resolve()
shutil.copy2(build_script_path, sandbox_dir / build_script_path.name)
target_scan_dir = sandbox_dir
self.progress.emit("[INFO] 已启用单文件沙盒模式:仅对当前脚本解析,防止同目录其他文件污染。")
else:
target_scan_dir = script_dir
self.progress.emit("[WARN] 已允许全目录扫描模式:正在扫描当前目录下所有 Python 文件...")
pipreqs_pkg = "pipreqs"
if self.params.get('pipreqs_version'):
pipreqs_pkg = f"pipreqs=={self.params['pipreqs_version']}"
@@ -2651,7 +2659,7 @@ class PackingThread(QThread):
pypi_server = re.sub(r'/simple/?$', '/pypi', pip_idx, flags=re.I).rstrip('/')
pipreqs_cmd = [
python_exe, "-m", "pipreqs.pipreqs", script_dir.as_posix(),
python_exe, "-m", "pipreqs.pipreqs", target_scan_dir.as_posix(),
"--encoding", "utf-8", "--force", "--savepath", temp_pipreqs.as_posix()
]
if pypi_server:
@@ -2677,6 +2685,9 @@ class PackingThread(QThread):
if success_pipreqs and temp_pipreqs.exists():
self.run_pip_install(python_exe, ["-q", "-r", temp_pipreqs.as_posix()])
temp_pipreqs.unlink(missing_ok=True)
if sandbox_dir and sandbox_dir.exists():
robust_rmtree(sandbox_dir)
config = load_config()
known_mappings = DEFAULT_MAPPINGS.copy()
@@ -2772,6 +2783,8 @@ class PackingThread(QThread):
else:
upx_dir_default = (Path.cwd() / "upx").resolve()
if upx_dir_default.exists(): cmd.append(f"--upx-dir={upx_dir_default.as_posix()}")
if os.name == "nt":
cmd.extend(["--upx-exclude=python3.dll", "--upx-exclude=python*.dll", "--upx-exclude=vcruntime140.dll"])
else:
cmd.append("--noupx")
@@ -2794,6 +2807,18 @@ class PackingThread(QThread):
f"--output-filename={app_name}{ext}"
]
if os.name == "nt":
has_msvc = shutil.which('cl.exe') is not None
if not has_msvc:
vswhere = Path(os.environ.get("ProgramFiles(x86)", "C:\\Program Files (x86)")) / "Microsoft Visual Studio/Installer/vswhere.exe"
if vswhere.exists():
try:
res = subprocess.run([vswhere.as_posix(), "-latest", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"], capture_output=True, text=True, creationflags=subprocess.CREATE_NO_WINDOW)
if res.stdout.strip(): has_msvc = True
except: pass
if has_msvc:
cmd.append("--msvc=latest")
self.progress.emit("[INFO] 发现本地 MSVC 环境,优先使用原生 C++ 编译器。")
cores = self.params.get('cpu_cores', os.cpu_count() or 2)
cmd.append(f"--jobs={cores}")
@@ -3375,6 +3400,7 @@ class MainWindow(QMainWindow):
'icon': icon_path_str,
'use_reqs': sp.reqs_check.isChecked(),
'use_pipreqs': sp.pipreqs_check.isChecked(),
'use_pipreqs_dir': sp.pipreqs_dir_check.isChecked(),
'reqs_file': sp.reqs_file_edit.text().strip(),
'hidden_imports': sp.hidden_edit.text(),
'add_data_list': add_data_items,
@@ -3576,4 +3602,4 @@ def main():
sys.exit(app.exec())
if __name__ == "__main__":
main()
main()