diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d793fd0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+# PyPack
+
+PyPack 是一个基于 PyQt5 开发的图形化 Python 脚本打包与构建辅助工具。它整合了 **PyInstaller**、**Nuitka** 和 **cx_Freeze** 三种主流打包引擎,旨在通过直观的 GUI 界面降低命令行打包的复杂度,并提供自动依赖分析与虚拟环境隔离等功能。
+
+
+
+
+---
+
+## 主要功能
+
+* **多引擎集成**:支持在界面中快速切换 PyInstaller、Nuitka 和 cx_Freeze 打包引擎。
+* **拖拽式操作**:提供支持拖放(Drag and Drop)的脚本装载区域,亦可手动浏览选择主脚本。
+* **自动依赖解析**:
+ * 优先读取项目同级目录下的 `requirements.txt`。
+ * 若无配置文件,则通过 Python 标准库中的 `ast` 模块静态分析脚本文件,提取第三方依赖(自动过滤标准库),同时提供基于正则表达式的备份扫描。
+* **隔离构建环境 (Venv)**:支持自动在临时目录下创建虚拟环境以隔离全局环境,降低依赖冲突的风险。
+* **自定义 PIP 镜像源**(**v1.0.1 新增**):高级选项中支持自定义设置 PIP 镜像源地址,默认提供清华大学镜像源以提升国内下载速度,修改后自动实时保存。
+* **元数据配置 (Windows)**:支持在界面配置版本号、发行公司、文件描述等元数据。对于 PyInstaller,会自动生成版本信息描述文件;对于 Nuitka,则自动映射为对应的编译器参数。
+* **多项细节优化**:
+ * 支持控制台隐藏、单文件/单目录输出切换、UPX 压缩等。
+ * 支持添加附加资源文件(格式为 `源路径:目标路径`),并提供交互式选择对话框。
+ * 支持图标预览功能(可自动识别脚本目录下的 `icon.ico` / `logo.ico`)。
+ * 采用 `QThread` 异步执行打包任务,保持 GUI 界面在打包过程中响应正常,并在日志窗口中实时输出控制台信息。
+
+---
+
+# 更新日志 (Changelog)
+
+### [1.0.1] - 2026-05-31
+#### 新增
+* **自定义 PIP 镜像源**:在“高级选项”界面中引入了 PIP 镜像源输入配置,默认使用清华大学开源软件镜像源(`https://pypi.tuna.tsinghua.edu.cn/simple`)。
+* **配置即时持久化**:PIP 镜像源输入支持修改即时生效,并利用 `QSettings` 自动存储于注册表/配置文件中,下次启动应用时自动读取。
+
+#### 优化
+* **构建逻辑统一**:优化了虚拟环境(Venv)初始化、打包引擎下载以及项目依赖解析时的命令行拼接逻辑,统一走用户配置的自定义 PIP 镜像源。
+
+---
+## 运行环境
+
+* **操作系统**:Windows
+* **Python 版本**:Python 3.x
+* **核心依赖**:
+ * `PyQt5`
+ * (根据选择的打包引擎)`pyinstaller` / `nuitka` / `cx_Freeze` / `pillow`
+
+---
+
+## 快速上手
+
+### 1. 克隆或下载项目
+将包含该源码的 Python 文件保存为 `main.py`。
+
+### 2. 安装运行所需的必要依赖
+在运行该工具前,您需要先安装 `PyQt5`:
+```bash
+pip install PyQt5
+```
+
+### 3. 启动应用
+执行以下命令启动图形化界面:
+```bash
+python main.py
+```
+
+### 或者使用打包好的二进制文件直接运行
+[GitHub Releases 下载链接](https://github.com/Qwejay/PyPack/releases)
+
+### 4. 打包您的项目
+1. **装载脚本**:将需要打包的 `.py` 文件拖拽至上方的拖拽区,或者点击主界面的“浏览...”按钮进行选择。
+2. **配置选项**:
+ * 选择您要使用的**构建引擎**。
+ * 根据需要调整**打包选项**(如是否开启虚拟环境、是否单文件输出、是否隐藏控制台等)。
+ * 如有需要,在“高级选项”选项卡中配置 **PIP 镜像源**、隐式依赖、附加资源和版本元数据。
+3. **开始构建**:点击右下角的**“开始构建”**。下方日志窗口会实时更新编译进度。
+4. **提取产物**:构建成功后,点击“打开输出目录”按钮即可直达打包好的可执行文件位置。
diff --git a/main.py b/main.py
index b48d3bb..6c9fb90 100644
--- a/main.py
+++ b/main.py
@@ -221,6 +221,7 @@ class PackingThread(QThread):
os.environ["NUITKA_ACCEPT_DOWNLOADS"] = "yes"
engine = self.params['engine']
+ pip_idx = self.params.get('pip_index_url', '').strip() or "https://pypi.tuna.tsinghua.edu.cn/simple"
try:
self.progress.emit("[预处理] 清理遗留缓存...")
robust_rmtree(Path.cwd() / "build")
@@ -251,17 +252,18 @@ class PackingThread(QThread):
engine_pkg = "cx_Freeze"
else:
engine_pkg = "pyinstaller"
- self.run_cmd_realtime([python_exe, "-m", "pip", "install", "-U", "pip", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "-q"])
- self.run_cmd_realtime([python_exe, "-m", "pip", "install", engine_pkg, "-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "-q"])
+ self.progress.emit(f"[环境配置] 正在通过镜像源 {pip_idx} 部署依赖包...")
+ self.run_cmd_realtime([python_exe, "-m", "pip", "install", "-U", "pip", "-i", pip_idx, "-q"])
+ self.run_cmd_realtime([python_exe, "-m", "pip", "install", engine_pkg, "-i", pip_idx, "-q"])
if engine == "PyInstaller":
- self.run_cmd_realtime([python_exe, "-m", "pip", "install", "pillow", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "-q"])
+ self.run_cmd_realtime([python_exe, "-m", "pip", "install", "pillow", "-i", pip_idx, "-q"])
# 依赖解析
if self.params.get('use_reqs'):
req_file = script_dir / "requirements.txt"
if req_file.exists():
self.progress.emit(f"[依赖解析] 同步工程内 {req_file.name}...")
- self.run_cmd_realtime([python_exe, "-m", "pip", "install", "-r", str(req_file), "-i", "https://pypi.tuna.tsinghua.edu.cn/simple"])
+ self.run_cmd_realtime([python_exe, "-m", "pip", "install", "-r", str(req_file), "-i", pip_idx])
else:
self.progress.emit("[依赖解析] 未找到 requirements.txt,执行 AST 静态扫描...")
try:
@@ -289,7 +291,7 @@ class PackingThread(QThread):
for pkg in third_party:
if self._is_cancelled: break
self.progress.emit(f"[自动同步] 正在安装 {pkg}...")
- self.run_cmd_realtime([python_exe, "-m", "pip", "install", pkg, "-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "-q"])
+ self.run_cmd_realtime([python_exe, "-m", "pip", "install", pkg, "-i", pip_idx, "-q"])
except Exception as e:
self.progress.emit(f"[扫描异常] {e}")
@@ -446,7 +448,7 @@ class MainWindow(QMainWindow):
self._load_window_icon()
def init_style(self):
- self.setWindowTitle("PyPack 1.0.0- 自动化构建工具")
+ self.setWindowTitle("PyPack 1.0.1 - 自动化构建工具")
self.setStyleSheet("""
QMainWindow { background-color: #fdfdfd; }
QLabel { color: #343a40; font-size: 9pt; }
@@ -696,6 +698,13 @@ class MainWindow(QMainWindow):
form.setSpacing(5)
form.setContentsMargins(0, 0, 0, 0)
+ # PIP 源配置(修改马上通过 QSettings 保存,在打包时生效)
+ self.pip_source_edit = QLineEdit()
+ self.pip_source_edit.setPlaceholderText("默认: https://pypi.tuna.tsinghua.edu.cn/simple")
+ self.pip_source_edit.setText(self.settings.value("pip_index_url", "https://pypi.tuna.tsinghua.edu.cn/simple"))
+ self.pip_source_edit.textChanged.connect(lambda text: self.settings.setValue("pip_index_url", text))
+ form.addRow("PIP 镜像源:", self.pip_source_edit)
+
self.hidden_edit = QLineEdit()
self.hidden_edit.setPlaceholderText("例如: pandas, PyQt5 (逗号分隔)")
scan_btn = QPushButton("AST 扫描")
@@ -727,7 +736,7 @@ class MainWindow(QMainWindow):
info_lay = QFormLayout()
info_lay.setContentsMargins(8, 8, 8, 8)
info_lay.setSpacing(5)
- self.ver_ver = QLineEdit("1.0.0")
+ self.ver_ver = QLineEdit("1.0.1")
self.ver_comp = QLineEdit("Developer Studio")
self.ver_desc = QLineEdit("Windows Executable Application")
info_lay.addRow("版本序列:", self.ver_ver)
@@ -890,7 +899,8 @@ class MainWindow(QMainWindow):
'version_file': str(version_file) if version_file else None,
'ver_comp': self.ver_comp.text() if engine != "cx_Freeze" else "",
'ver_desc': self.ver_desc.text() if engine != "cx_Freeze" else "",
- 'ver_ver': self.ver_ver.text() if engine != "cx_Freeze" else ""
+ 'ver_ver': self.ver_ver.text() if engine != "cx_Freeze" else "",
+ 'pip_index_url': self.pip_source_edit.text().strip()
}
self.log.clear()
@@ -960,4 +970,4 @@ def main():
sys.exit(app.exec_())
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()