PyInstallerEx 0.1.9__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyinstallerex-0.1.9/src/PyInstallerEx.egg-info → pyinstallerex-0.2.3}/PKG-INFO +83 -7
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/README.md +81 -6
- pyinstallerex-0.2.3/bin/launcher_windows.exe +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/pyproject.toml +3 -2
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/setup.py +2 -1
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/__main__.py +6 -5
- pyinstallerex-0.2.3/src/PyInstallerEx/server.py +291 -0
- pyinstallerex-0.2.3/src/PyInstallerEx/web/favicon.ico +0 -0
- pyinstallerex-0.2.3/src/PyInstallerEx/web/index.html +664 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3/src/PyInstallerEx.egg-info}/PKG-INFO +83 -7
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx.egg-info/SOURCES.txt +3 -1
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx.egg-info/requires.txt +1 -0
- pyinstallerex-0.1.9/bin/launcher_windows.exe +0 -0
- pyinstallerex-0.1.9/src/PyInstallerEx/PyInstallerEx.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/LICENSE +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/setup.cfg +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/__init__.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/core/__init__.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/core/config.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/core/packager.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/utils/__init__.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/utils/compression.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/utils/file_utils.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/utils/icon_helper.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/utils/logging.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx/utils/system_utils.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx.egg-info/dependency_links.txt +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx.egg-info/entry_points.txt +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx.egg-info/not-zip-safe +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/src/PyInstallerEx.egg-info/top_level.txt +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/test/test_python27_compatibility.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/test/test_python3x_compatibility.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.3}/test/test_rust_launcher.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyInstallerEx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: 扩展PyInstaller 使其拥有单文件安装功能不用每次都解压执行
|
|
5
5
|
Home-page: https://gitee.com/iiixxxiii/py-installer-ex
|
|
6
6
|
Author: lixin
|
|
@@ -27,6 +27,7 @@ Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
Requires-Dist: PyInstaller>=3.2.1
|
|
30
|
+
Requires-Dist: pyquickwebgui
|
|
30
31
|
Dynamic: author
|
|
31
32
|
Dynamic: home-page
|
|
32
33
|
Dynamic: license-file
|
|
@@ -52,6 +53,10 @@ PyInstallerEx 是一个基于 PyInstaller 的增强版打包工具,能够将 P
|
|
|
52
53
|
4. **灵活解压模式**:支持解压到系统临时目录或 exe 所在目录(通过 `--extract-mode` 参数控制)
|
|
53
54
|
5. **跨平台支持**:Windows、Linux x86/ARM全平台覆盖
|
|
54
55
|
6. **配置灵活**:支持嵌入式配置读取,命令行参数覆盖配置文件
|
|
56
|
+
7. **命令行参数透传**:打包后的可执行文件支持将命令行参数传递给内部程序
|
|
57
|
+
8. **Web GUI 界面**:无参数启动时自动打开图形化界面,可视化配置打包参数
|
|
58
|
+
- **多语言支持**:支持中文/英文界面切换,设置持久化保存
|
|
59
|
+
- **文件选择对话框**:原生 tkinter 文件/文件夹选择器,支持初始路径记忆
|
|
55
60
|
|
|
56
61
|
## 🔧 环境要求
|
|
57
62
|
|
|
@@ -130,6 +135,26 @@ pip install pyinstallerex
|
|
|
130
135
|
|
|
131
136
|
### 基本用法
|
|
132
137
|
|
|
138
|
+
#### 启动 Web GUI(图形界面)
|
|
139
|
+
|
|
140
|
+
直接运行命令,不带任何参数,即可启动 Web GUI 界面:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# 以下三种方式均可
|
|
144
|
+
PyInstallerEx
|
|
145
|
+
pyinstallerex
|
|
146
|
+
python -m PyInstallerEx
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
启动后会自动打开浏览器窗口,支持:
|
|
150
|
+
- 选择脚本或文件夹打包模式
|
|
151
|
+
- 可视化配置输出名称、版本、引擎、图标等参数
|
|
152
|
+
- 实时查看构建日志和结果
|
|
153
|
+
|
|
154
|
+
#### 命令行模式
|
|
155
|
+
|
|
156
|
+
带参数时使用命令行模式:
|
|
157
|
+
|
|
133
158
|
```bash
|
|
134
159
|
# 使用 pyinstallerex 命令
|
|
135
160
|
pyinstallerex your_script.py
|
|
@@ -176,6 +201,7 @@ test\test_script.exe # Windows
|
|
|
176
201
|
| 04 | `04 - hello_world - PyOxidizer` | 使用 PyOxidizer 引擎打包 | `--engine-mode pyoxidizer` |
|
|
177
202
|
| 05 | `05 - hello_world - icon` | 自定义 exe 图标(`.ico` 格式) | `--icon "exe.ico"` |
|
|
178
203
|
| 06 | `06 - hello_world - folder` | 文件夹直接打包,跳过引擎构建,将指定文件夹封装为单文件 | `--folder "hello_folder" --run "hello.exe"` |
|
|
204
|
+
| 07 | `07 - hello_world - parameter` | 命令行参数透传,支持 `--add1` 和 `--add2` 参数进行加法运算 | 无额外打包参数,运行时传参 |
|
|
179
205
|
|
|
180
206
|
> 每个示例均提供 `package.bat`(Windows)和 `package.sh`(Linux/Mac)两套构建脚本。
|
|
181
207
|
|
|
@@ -225,6 +251,32 @@ python -m PyInstallerEx your_script.py --engine-mode pyoxidizer
|
|
|
225
251
|
- **Nuitka**:需要安装 Nuitka (`pip install nuitka`) 和 C 编译器(如 Visual Studio Build Tools)
|
|
226
252
|
- **PyOxidizer**:需要单独安装 PyOxidizer,详见 [PyOxidizer 文档](https://pyoxidizer.readthedocs.io/)
|
|
227
253
|
|
|
254
|
+
### 命令行参数透传
|
|
255
|
+
|
|
256
|
+
打包后的可执行文件支持将命令行参数透传给内部程序,无需额外配置:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# 打包程序
|
|
260
|
+
python -m PyInstallerEx hello.py -n hello_param
|
|
261
|
+
|
|
262
|
+
# 运行并传递参数
|
|
263
|
+
hello_param.exe --add1 10 --add2 20
|
|
264
|
+
# 输出:Calculation: 10.0 + 20.0 = 30.0
|
|
265
|
+
|
|
266
|
+
# 查看帮助
|
|
267
|
+
hello_param.exe --help
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**工作原理**:
|
|
271
|
+
- Rust 启动器在运行内部程序时,自动收集并传递所有命令行参数
|
|
272
|
+
- 支持任意参数格式,完全兼容 `argparse` 等参数解析库
|
|
273
|
+
- 参数透传对所有打包模式(脚本模式、文件夹模式)均有效
|
|
274
|
+
|
|
275
|
+
**使用场景**:
|
|
276
|
+
- 需要接收用户输入的命令行工具
|
|
277
|
+
- 支持配置参数的自动化脚本
|
|
278
|
+
- 需要灵活控制运行时行为的工具程序
|
|
279
|
+
|
|
228
280
|
### 文件夹直接打包模式
|
|
229
281
|
|
|
230
282
|
使用 `--folder` 参数跳过 PyInstaller/Nuitka 等引擎打包流程,直接将指定文件夹压缩并附加到 Rust 启动器尾部,生成最终可执行文件。
|
|
@@ -327,7 +379,11 @@ py-installer-ex/
|
|
|
327
379
|
│ ├── PyInstallerEx/ # Python 主程序
|
|
328
380
|
│ │ ├── __init__.py
|
|
329
381
|
│ │ ├── __main__.py # CLI入口点
|
|
382
|
+
│ │ ├── server.py # Web GUI 后端服务
|
|
330
383
|
│ │ ├── PyInstallerEx.py # 主模块
|
|
384
|
+
│ │ ├── web/
|
|
385
|
+
│ │ │ ├── index.html # Web GUI 前端页面
|
|
386
|
+
│ │ │ └── favicon.ico # 页面图标
|
|
331
387
|
│ │ ├── core/
|
|
332
388
|
│ │ │ ├── config.py # 配置管理
|
|
333
389
|
│ │ │ └── packager.py # 打包逻辑
|
|
@@ -433,6 +489,7 @@ script\install.bat
|
|
|
433
489
|
- ✅ **智能安装**:首次运行自动提取,后续运行复用已安装版本
|
|
434
490
|
- ✅ **配置解析**:从可执行文件中读取和处理配置信息
|
|
435
491
|
- ✅ **临时目录管理**:自动管理临时文件和清理
|
|
492
|
+
- ✅ **命令行参数透传**:自动将启动器的命令行参数传递给内部程序
|
|
436
493
|
|
|
437
494
|
### 3. 文件夹直接打包
|
|
438
495
|
- ✅ **`--folder` 参数**:跳过引擎构建,直接将指定文件夹压缩打包
|
|
@@ -449,20 +506,39 @@ script\install.bat
|
|
|
449
506
|
|
|
450
507
|
- **Python 2.7+ / 3.x**: 主要开发语言
|
|
451
508
|
- **PyInstaller >= 3.2.1**: 应用程序打包基础
|
|
509
|
+
- **pyquickwebgui**: Web GUI 图形界面框架
|
|
452
510
|
- **Rust (edition 2021)**: 启动器开发语言(serde/serde_json、md5、flate2)
|
|
453
511
|
- **JSON**: 配置文件格式
|
|
454
512
|
- **ZIP**: 应用程序压缩格式
|
|
455
513
|
|
|
456
|
-
##
|
|
514
|
+
## 未来扩展方向
|
|
457
515
|
|
|
458
|
-
1.
|
|
459
|
-
2.
|
|
460
|
-
3.
|
|
461
|
-
4.
|
|
462
|
-
5.
|
|
516
|
+
1. ~~**GUI界面**~~:✅ 已实现 Web GUI 图形化界面(基于 pyquickwebgui)
|
|
517
|
+
2. ~~**多语言支持**~~:✅ 已实现中英文双语界面,localStorage + 配置文件双持久化
|
|
518
|
+
3. **数字签名**:支持代码签名和安全验证
|
|
519
|
+
4. **增量更新**:实现应用程序的增量更新功能
|
|
520
|
+
5. **更多平台**:支持macOS等其他操作系统
|
|
521
|
+
6. **插件系统**:允许扩展自定义打包逻辑
|
|
463
522
|
|
|
464
523
|
## 更新日志
|
|
465
524
|
|
|
525
|
+
- 2026-07-11 V 0.2.3
|
|
526
|
+
- 新增 **命令行参数透传**:Rust 启动器自动将命令行参数传递给内部程序
|
|
527
|
+
- 新增 **示例 07**:`07 - hello_world - parameter` 演示参数透传功能(`--add1`、`--add2`)
|
|
528
|
+
- 优化 `package.sh` 脚本:添加错误处理、路径优化、环境检查
|
|
529
|
+
|
|
530
|
+
- 2026-07-07 V 0.2.2
|
|
531
|
+
- 新增 **Web GUI 多语言支持**:支持中文/英文界面实时切换
|
|
532
|
+
- 新增 **语言设置持久化**:使用用户主目录下 `.pyinstallerex/ui_config.json` 配置文件保存语言偏好
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
- 2026-07-07 V 0.2.1
|
|
536
|
+
- 新增 **Web GUI 图形界面**:不带参数直接运行 `PyInstallerEx` 即可启动
|
|
537
|
+
- 基于 `pyquickwebgui` 实现,支持脚本模式/文件夹模式切换
|
|
538
|
+
- 可视化配置打包参数(引擎、图标、解压模式、输出目录等)
|
|
539
|
+
- 实时日志输出与构建状态轮询
|
|
540
|
+
- 新增 `server.py` 后端服务及 `web/` 前端页面
|
|
541
|
+
|
|
466
542
|
- 2026-07-03 V 0.1.10
|
|
467
543
|
- 新增 `--folder` 参数,支持文件夹直接打包模式:跳过引擎构建,直接将指定文件夹压缩并附加到启动器
|
|
468
544
|
- 新增 `--run` 参数,支持自定义解压后运行的主程序入口(配置 `main` 字段)
|
|
@@ -18,6 +18,10 @@ PyInstallerEx 是一个基于 PyInstaller 的增强版打包工具,能够将 P
|
|
|
18
18
|
4. **灵活解压模式**:支持解压到系统临时目录或 exe 所在目录(通过 `--extract-mode` 参数控制)
|
|
19
19
|
5. **跨平台支持**:Windows、Linux x86/ARM全平台覆盖
|
|
20
20
|
6. **配置灵活**:支持嵌入式配置读取,命令行参数覆盖配置文件
|
|
21
|
+
7. **命令行参数透传**:打包后的可执行文件支持将命令行参数传递给内部程序
|
|
22
|
+
8. **Web GUI 界面**:无参数启动时自动打开图形化界面,可视化配置打包参数
|
|
23
|
+
- **多语言支持**:支持中文/英文界面切换,设置持久化保存
|
|
24
|
+
- **文件选择对话框**:原生 tkinter 文件/文件夹选择器,支持初始路径记忆
|
|
21
25
|
|
|
22
26
|
## 🔧 环境要求
|
|
23
27
|
|
|
@@ -96,6 +100,26 @@ pip install pyinstallerex
|
|
|
96
100
|
|
|
97
101
|
### 基本用法
|
|
98
102
|
|
|
103
|
+
#### 启动 Web GUI(图形界面)
|
|
104
|
+
|
|
105
|
+
直接运行命令,不带任何参数,即可启动 Web GUI 界面:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# 以下三种方式均可
|
|
109
|
+
PyInstallerEx
|
|
110
|
+
pyinstallerex
|
|
111
|
+
python -m PyInstallerEx
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
启动后会自动打开浏览器窗口,支持:
|
|
115
|
+
- 选择脚本或文件夹打包模式
|
|
116
|
+
- 可视化配置输出名称、版本、引擎、图标等参数
|
|
117
|
+
- 实时查看构建日志和结果
|
|
118
|
+
|
|
119
|
+
#### 命令行模式
|
|
120
|
+
|
|
121
|
+
带参数时使用命令行模式:
|
|
122
|
+
|
|
99
123
|
```bash
|
|
100
124
|
# 使用 pyinstallerex 命令
|
|
101
125
|
pyinstallerex your_script.py
|
|
@@ -142,6 +166,7 @@ test\test_script.exe # Windows
|
|
|
142
166
|
| 04 | `04 - hello_world - PyOxidizer` | 使用 PyOxidizer 引擎打包 | `--engine-mode pyoxidizer` |
|
|
143
167
|
| 05 | `05 - hello_world - icon` | 自定义 exe 图标(`.ico` 格式) | `--icon "exe.ico"` |
|
|
144
168
|
| 06 | `06 - hello_world - folder` | 文件夹直接打包,跳过引擎构建,将指定文件夹封装为单文件 | `--folder "hello_folder" --run "hello.exe"` |
|
|
169
|
+
| 07 | `07 - hello_world - parameter` | 命令行参数透传,支持 `--add1` 和 `--add2` 参数进行加法运算 | 无额外打包参数,运行时传参 |
|
|
145
170
|
|
|
146
171
|
> 每个示例均提供 `package.bat`(Windows)和 `package.sh`(Linux/Mac)两套构建脚本。
|
|
147
172
|
|
|
@@ -191,6 +216,32 @@ python -m PyInstallerEx your_script.py --engine-mode pyoxidizer
|
|
|
191
216
|
- **Nuitka**:需要安装 Nuitka (`pip install nuitka`) 和 C 编译器(如 Visual Studio Build Tools)
|
|
192
217
|
- **PyOxidizer**:需要单独安装 PyOxidizer,详见 [PyOxidizer 文档](https://pyoxidizer.readthedocs.io/)
|
|
193
218
|
|
|
219
|
+
### 命令行参数透传
|
|
220
|
+
|
|
221
|
+
打包后的可执行文件支持将命令行参数透传给内部程序,无需额外配置:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# 打包程序
|
|
225
|
+
python -m PyInstallerEx hello.py -n hello_param
|
|
226
|
+
|
|
227
|
+
# 运行并传递参数
|
|
228
|
+
hello_param.exe --add1 10 --add2 20
|
|
229
|
+
# 输出:Calculation: 10.0 + 20.0 = 30.0
|
|
230
|
+
|
|
231
|
+
# 查看帮助
|
|
232
|
+
hello_param.exe --help
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**工作原理**:
|
|
236
|
+
- Rust 启动器在运行内部程序时,自动收集并传递所有命令行参数
|
|
237
|
+
- 支持任意参数格式,完全兼容 `argparse` 等参数解析库
|
|
238
|
+
- 参数透传对所有打包模式(脚本模式、文件夹模式)均有效
|
|
239
|
+
|
|
240
|
+
**使用场景**:
|
|
241
|
+
- 需要接收用户输入的命令行工具
|
|
242
|
+
- 支持配置参数的自动化脚本
|
|
243
|
+
- 需要灵活控制运行时行为的工具程序
|
|
244
|
+
|
|
194
245
|
### 文件夹直接打包模式
|
|
195
246
|
|
|
196
247
|
使用 `--folder` 参数跳过 PyInstaller/Nuitka 等引擎打包流程,直接将指定文件夹压缩并附加到 Rust 启动器尾部,生成最终可执行文件。
|
|
@@ -293,7 +344,11 @@ py-installer-ex/
|
|
|
293
344
|
│ ├── PyInstallerEx/ # Python 主程序
|
|
294
345
|
│ │ ├── __init__.py
|
|
295
346
|
│ │ ├── __main__.py # CLI入口点
|
|
347
|
+
│ │ ├── server.py # Web GUI 后端服务
|
|
296
348
|
│ │ ├── PyInstallerEx.py # 主模块
|
|
349
|
+
│ │ ├── web/
|
|
350
|
+
│ │ │ ├── index.html # Web GUI 前端页面
|
|
351
|
+
│ │ │ └── favicon.ico # 页面图标
|
|
297
352
|
│ │ ├── core/
|
|
298
353
|
│ │ │ ├── config.py # 配置管理
|
|
299
354
|
│ │ │ └── packager.py # 打包逻辑
|
|
@@ -399,6 +454,7 @@ script\install.bat
|
|
|
399
454
|
- ✅ **智能安装**:首次运行自动提取,后续运行复用已安装版本
|
|
400
455
|
- ✅ **配置解析**:从可执行文件中读取和处理配置信息
|
|
401
456
|
- ✅ **临时目录管理**:自动管理临时文件和清理
|
|
457
|
+
- ✅ **命令行参数透传**:自动将启动器的命令行参数传递给内部程序
|
|
402
458
|
|
|
403
459
|
### 3. 文件夹直接打包
|
|
404
460
|
- ✅ **`--folder` 参数**:跳过引擎构建,直接将指定文件夹压缩打包
|
|
@@ -415,20 +471,39 @@ script\install.bat
|
|
|
415
471
|
|
|
416
472
|
- **Python 2.7+ / 3.x**: 主要开发语言
|
|
417
473
|
- **PyInstaller >= 3.2.1**: 应用程序打包基础
|
|
474
|
+
- **pyquickwebgui**: Web GUI 图形界面框架
|
|
418
475
|
- **Rust (edition 2021)**: 启动器开发语言(serde/serde_json、md5、flate2)
|
|
419
476
|
- **JSON**: 配置文件格式
|
|
420
477
|
- **ZIP**: 应用程序压缩格式
|
|
421
478
|
|
|
422
|
-
##
|
|
479
|
+
## 未来扩展方向
|
|
423
480
|
|
|
424
|
-
1.
|
|
425
|
-
2.
|
|
426
|
-
3.
|
|
427
|
-
4.
|
|
428
|
-
5.
|
|
481
|
+
1. ~~**GUI界面**~~:✅ 已实现 Web GUI 图形化界面(基于 pyquickwebgui)
|
|
482
|
+
2. ~~**多语言支持**~~:✅ 已实现中英文双语界面,localStorage + 配置文件双持久化
|
|
483
|
+
3. **数字签名**:支持代码签名和安全验证
|
|
484
|
+
4. **增量更新**:实现应用程序的增量更新功能
|
|
485
|
+
5. **更多平台**:支持macOS等其他操作系统
|
|
486
|
+
6. **插件系统**:允许扩展自定义打包逻辑
|
|
429
487
|
|
|
430
488
|
## 更新日志
|
|
431
489
|
|
|
490
|
+
- 2026-07-11 V 0.2.3
|
|
491
|
+
- 新增 **命令行参数透传**:Rust 启动器自动将命令行参数传递给内部程序
|
|
492
|
+
- 新增 **示例 07**:`07 - hello_world - parameter` 演示参数透传功能(`--add1`、`--add2`)
|
|
493
|
+
- 优化 `package.sh` 脚本:添加错误处理、路径优化、环境检查
|
|
494
|
+
|
|
495
|
+
- 2026-07-07 V 0.2.2
|
|
496
|
+
- 新增 **Web GUI 多语言支持**:支持中文/英文界面实时切换
|
|
497
|
+
- 新增 **语言设置持久化**:使用用户主目录下 `.pyinstallerex/ui_config.json` 配置文件保存语言偏好
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
- 2026-07-07 V 0.2.1
|
|
501
|
+
- 新增 **Web GUI 图形界面**:不带参数直接运行 `PyInstallerEx` 即可启动
|
|
502
|
+
- 基于 `pyquickwebgui` 实现,支持脚本模式/文件夹模式切换
|
|
503
|
+
- 可视化配置打包参数(引擎、图标、解压模式、输出目录等)
|
|
504
|
+
- 实时日志输出与构建状态轮询
|
|
505
|
+
- 新增 `server.py` 后端服务及 `web/` 前端页面
|
|
506
|
+
|
|
432
507
|
- 2026-07-03 V 0.1.10
|
|
433
508
|
- 新增 `--folder` 参数,支持文件夹直接打包模式:跳过引擎构建,直接将指定文件夹压缩并附加到启动器
|
|
434
509
|
- 新增 `--run` 参数,支持自定义解压后运行的主程序入口(配置 `main` 字段)
|
|
Binary file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "PyInstallerEx"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "扩展PyInstaller 使其拥有单文件安装功能不用每次都解压执行"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|
@@ -32,6 +32,7 @@ classifiers = [
|
|
|
32
32
|
]
|
|
33
33
|
dependencies = [
|
|
34
34
|
"PyInstaller>=3.2.1",
|
|
35
|
+
"pyquickwebgui",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
[project.urls]
|
|
@@ -45,4 +46,4 @@ PyInstallerEx = "PyInstallerEx.__main__:main"
|
|
|
45
46
|
where = ["src"]
|
|
46
47
|
|
|
47
48
|
[tool.setuptools.package-data]
|
|
48
|
-
PyInstallerEx = ["../bin/*"]
|
|
49
|
+
PyInstallerEx = ["../bin/*", "web/*"]
|
|
@@ -25,7 +25,7 @@ setup(
|
|
|
25
25
|
packages=find_packages(where="src"), # 自动发现src目录下的子包
|
|
26
26
|
include_package_data=True, # 包含包数据文件
|
|
27
27
|
package_data={
|
|
28
|
-
"PyInstallerEx": ["../bin/*"], # 包含bin
|
|
28
|
+
"PyInstallerEx": ["../bin/*", "web/*"], # 包含bin目录中的所有文件及web前端文件
|
|
29
29
|
},
|
|
30
30
|
data_files=[
|
|
31
31
|
('bin', [
|
|
@@ -35,6 +35,7 @@ setup(
|
|
|
35
35
|
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", # 兼容 2.7 和 3.4+
|
|
36
36
|
install_requires=[ # 依赖列表
|
|
37
37
|
"PyInstaller>=3.2.1",
|
|
38
|
+
"pyquickwebgui",
|
|
38
39
|
# Windows平台可选依赖,用于设置exe图标(需要手动安装)
|
|
39
40
|
# pip install pywin32 或 pip install pefile
|
|
40
41
|
],
|
|
@@ -46,6 +46,12 @@ def main():
|
|
|
46
46
|
|
|
47
47
|
args = parser.parse_args()
|
|
48
48
|
|
|
49
|
+
# 无任何参数时启动 Web GUI 模式
|
|
50
|
+
if not args.script and not args.folder:
|
|
51
|
+
from .server import start_web_gui
|
|
52
|
+
start_web_gui()
|
|
53
|
+
return
|
|
54
|
+
|
|
49
55
|
# 设置日志
|
|
50
56
|
setup_logging(args.verbose)
|
|
51
57
|
|
|
@@ -55,11 +61,6 @@ def main():
|
|
|
55
61
|
print("[FAIL] Error: 'script' and '--folder' are mutually exclusive. Please specify only one.", file=sys.stderr)
|
|
56
62
|
sys.exit(1)
|
|
57
63
|
|
|
58
|
-
# 必须提供 script 或 --folder 之一
|
|
59
|
-
if not args.script and not args.folder:
|
|
60
|
-
print("[FAIL] Error: either 'script' or '--folder' must be specified.", file=sys.stderr)
|
|
61
|
-
sys.exit(1)
|
|
62
|
-
|
|
63
64
|
# --folder 模式校验:目录必须存在
|
|
64
65
|
if args.folder:
|
|
65
66
|
if not os.path.isdir(args.folder):
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
PyInstallerEx Web GUI 服务器
|
|
4
|
+
基于 pyquickwebgui 提供图形化打包界面
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import print_function
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import json
|
|
11
|
+
import threading
|
|
12
|
+
|
|
13
|
+
from pyquickwebgui import QuikeUI
|
|
14
|
+
|
|
15
|
+
from .core.packager import PackageBuilder
|
|
16
|
+
from .core.config import PackageConfig
|
|
17
|
+
from .utils.logging import setup_logging
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# 全局构建状态
|
|
21
|
+
_build_status = {
|
|
22
|
+
"running": False,
|
|
23
|
+
"progress": "",
|
|
24
|
+
"output": "",
|
|
25
|
+
"success": None,
|
|
26
|
+
"log": [],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# UI 配置文件路径(存储语言等设置)- 放在用户主目录下
|
|
30
|
+
_user_home = os.path.expanduser('~')
|
|
31
|
+
_ui_config_dir = os.path.join(_user_home, '.pyinstallerex')
|
|
32
|
+
if not os.path.exists(_ui_config_dir):
|
|
33
|
+
try:
|
|
34
|
+
os.makedirs(_ui_config_dir)
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
_ui_config_file = os.path.join(_ui_config_dir, 'ui_config.json')
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _load_ui_config():
|
|
41
|
+
"""加载 UI 配置(语言等)"""
|
|
42
|
+
try:
|
|
43
|
+
if os.path.exists(_ui_config_file):
|
|
44
|
+
with open(_ui_config_file, 'r', encoding='utf-8') as f:
|
|
45
|
+
return json.load(f)
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
return {"language": "en"}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _save_ui_config(config):
|
|
52
|
+
"""保存 UI 配置"""
|
|
53
|
+
try:
|
|
54
|
+
with open(_ui_config_file, 'w', encoding='utf-8') as f:
|
|
55
|
+
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
print("[PyInstallerEx] Failed to save UI config: {0}".format(e))
|
|
58
|
+
|
|
59
|
+
# 全局 QuikeUI 实例
|
|
60
|
+
_web_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "web")
|
|
61
|
+
qui = QuikeUI(
|
|
62
|
+
web_path=_web_dir,
|
|
63
|
+
index_html="index.html",
|
|
64
|
+
title="PyInstallerEx",
|
|
65
|
+
icon="favicon.ico",
|
|
66
|
+
width=900,
|
|
67
|
+
height=680,
|
|
68
|
+
debug=False,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _reset_status():
|
|
73
|
+
"""重置构建状态"""
|
|
74
|
+
_build_status["running"] = False
|
|
75
|
+
_build_status["progress"] = ""
|
|
76
|
+
_build_status["output"] = ""
|
|
77
|
+
_build_status["success"] = None
|
|
78
|
+
_build_status["log"] = []
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _append_log(msg):
|
|
82
|
+
"""追加日志"""
|
|
83
|
+
_build_status["log"].append(msg)
|
|
84
|
+
print(msg)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@qui.expose
|
|
88
|
+
def get_status():
|
|
89
|
+
"""获取当前构建状态"""
|
|
90
|
+
return dict(_build_status)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@qui.expose
|
|
94
|
+
def pick_file(initial_dir, file_types):
|
|
95
|
+
"""打开文件选择对话框
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
initial_dir: 初始目录
|
|
99
|
+
file_types: 文件类型过滤,如 '.py' 或 '.ico'
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
import tkinter as tk
|
|
103
|
+
from tkinter import filedialog
|
|
104
|
+
if initial_dir and os.path.isdir(initial_dir):
|
|
105
|
+
print("[PyInstallerEx] pick_file folder: {0}".format(initial_dir))
|
|
106
|
+
root = tk.Tk()
|
|
107
|
+
root.attributes("-topmost", True)
|
|
108
|
+
root.withdraw()
|
|
109
|
+
ft = [("{0} files".format(file_types), "*{0}".format(file_types)), ("All files", "*.*")]
|
|
110
|
+
result = filedialog.askopenfilename(initialdir=initial_dir, filetypes=ft)
|
|
111
|
+
print("[PyInstallerEx] pick_file result: {0}".format(repr(result)))
|
|
112
|
+
return result if result else ""
|
|
113
|
+
else:
|
|
114
|
+
return 'Not valid folder'
|
|
115
|
+
except Exception as e:
|
|
116
|
+
print("[PyInstallerEx] pick_file error: {0}".format(e))
|
|
117
|
+
return "Error: {0}".format(e)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@qui.expose
|
|
121
|
+
def pick_folder(initial_dir):
|
|
122
|
+
"""打开文件夹选择对话框
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
initial_dir: 初始目录
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
import tkinter as tk
|
|
129
|
+
from tkinter import filedialog
|
|
130
|
+
if initial_dir and os.path.isdir(initial_dir):
|
|
131
|
+
print("[PyInstallerEx] pick_folder folder: {0}".format(initial_dir))
|
|
132
|
+
root = tk.Tk()
|
|
133
|
+
root.attributes("-topmost", True)
|
|
134
|
+
root.withdraw()
|
|
135
|
+
result = filedialog.askdirectory(initialdir=initial_dir)
|
|
136
|
+
print("[PyInstallerEx] pick_folder result: {0}".format(repr(result)))
|
|
137
|
+
return result if result else ""
|
|
138
|
+
else:
|
|
139
|
+
return 'Not valid folder'
|
|
140
|
+
except Exception as e:
|
|
141
|
+
print("[PyInstallerEx] pick_folder error: {0}".format(e))
|
|
142
|
+
return "Error: {0}".format(e)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@qui.expose
|
|
146
|
+
def start_build(config_json):
|
|
147
|
+
"""
|
|
148
|
+
启动打包构建任务
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
config_json: JSON 字符串,包含打包配置
|
|
152
|
+
"""
|
|
153
|
+
if _build_status["running"]:
|
|
154
|
+
return {"success": False, "message": "A build task is already running"}
|
|
155
|
+
|
|
156
|
+
_reset_status()
|
|
157
|
+
_build_status["running"] = True
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
cfg = json.loads(config_json)
|
|
161
|
+
except Exception as e:
|
|
162
|
+
_build_status["running"] = False
|
|
163
|
+
return {"success": False, "message": "Invalid config JSON: {0}".format(e)}
|
|
164
|
+
|
|
165
|
+
def _run_build():
|
|
166
|
+
try:
|
|
167
|
+
_append_log("[INFO] Starting build...")
|
|
168
|
+
_build_status["progress"] = "Initializing..."
|
|
169
|
+
|
|
170
|
+
# 构建 PackageConfig
|
|
171
|
+
config = PackageConfig()
|
|
172
|
+
config.filename = cfg.get("filename", "{filename}")
|
|
173
|
+
config.version = cfg.get("version", "1.0.0")
|
|
174
|
+
config.description = cfg.get("description", "")
|
|
175
|
+
config.author = cfg.get("author", "Unknown")
|
|
176
|
+
config.extract_mode = cfg.get("extract_mode", "temp")
|
|
177
|
+
config.engine_mode = cfg.get("engine_mode", "pyinstaller")
|
|
178
|
+
config.main = cfg.get("main", None)
|
|
179
|
+
|
|
180
|
+
# 图标处理
|
|
181
|
+
icon_path = cfg.get("icon", None)
|
|
182
|
+
if icon_path and os.path.exists(icon_path):
|
|
183
|
+
config.icon = icon_path
|
|
184
|
+
else:
|
|
185
|
+
default_icon = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logo.ico")
|
|
186
|
+
if os.path.exists(default_icon):
|
|
187
|
+
config.icon = default_icon
|
|
188
|
+
else:
|
|
189
|
+
config.icon = None
|
|
190
|
+
|
|
191
|
+
# 设置日志级别
|
|
192
|
+
verbose = cfg.get("verbose", False)
|
|
193
|
+
setup_logging(verbose)
|
|
194
|
+
|
|
195
|
+
builder = PackageBuilder(config, verbose=verbose)
|
|
196
|
+
output_dir = cfg.get("output_dir", None) or None
|
|
197
|
+
|
|
198
|
+
# 文件夹模式 or 脚本模式
|
|
199
|
+
folder_path = cfg.get("folder", None)
|
|
200
|
+
script_path = cfg.get("script", None)
|
|
201
|
+
|
|
202
|
+
if folder_path:
|
|
203
|
+
_build_status["progress"] = "Packaging folder..."
|
|
204
|
+
if not os.path.isdir(folder_path):
|
|
205
|
+
raise ValueError("Folder not found: {0}".format(folder_path))
|
|
206
|
+
|
|
207
|
+
if cfg.get("name"):
|
|
208
|
+
config.filename = cfg["name"]
|
|
209
|
+
elif config.filename == "{filename}":
|
|
210
|
+
config.filename = os.path.basename(folder_path)
|
|
211
|
+
|
|
212
|
+
result_path = builder.build_folder(folder_path, output_dir)
|
|
213
|
+
elif script_path:
|
|
214
|
+
_build_status["progress"] = "Packaging script..."
|
|
215
|
+
if not os.path.isfile(script_path):
|
|
216
|
+
raise ValueError("Script not found: {0}".format(script_path))
|
|
217
|
+
|
|
218
|
+
if cfg.get("name"):
|
|
219
|
+
config.filename = cfg["name"]
|
|
220
|
+
elif config.filename == "{filename}":
|
|
221
|
+
config.filename = os.path.splitext(os.path.basename(script_path))[0]
|
|
222
|
+
|
|
223
|
+
result_path = builder.build(script_path, output_dir)
|
|
224
|
+
else:
|
|
225
|
+
raise ValueError("Either 'script' or 'folder' must be specified")
|
|
226
|
+
|
|
227
|
+
_build_status["progress"] = "Done"
|
|
228
|
+
_build_status["output"] = result_path
|
|
229
|
+
_build_status["success"] = True
|
|
230
|
+
_append_log("[OK] Build completed: {0}".format(result_path))
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
_build_status["progress"] = "Failed"
|
|
234
|
+
_build_status["success"] = False
|
|
235
|
+
_build_status["output"] = str(e)
|
|
236
|
+
_append_log("[FAIL] Build failed: {0}".format(e))
|
|
237
|
+
finally:
|
|
238
|
+
_build_status["running"] = False
|
|
239
|
+
|
|
240
|
+
# 在后台线程执行构建
|
|
241
|
+
t = threading.Thread(target=_run_build)
|
|
242
|
+
t.daemon = True
|
|
243
|
+
t.start()
|
|
244
|
+
|
|
245
|
+
return {"success": True, "message": "Build started"}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@qui.expose
|
|
249
|
+
def get_default_config():
|
|
250
|
+
"""获取默认配置模板"""
|
|
251
|
+
default_icon = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logo.ico")
|
|
252
|
+
ui_config = _load_ui_config()
|
|
253
|
+
return {
|
|
254
|
+
"filename": "{filename}",
|
|
255
|
+
"version": "1.0.0",
|
|
256
|
+
"description": "Application packaged with PyInstallerEx",
|
|
257
|
+
"author": "Unknown",
|
|
258
|
+
"extract_mode": "temp",
|
|
259
|
+
"engine_mode": "pyinstaller",
|
|
260
|
+
"icon": default_icon if os.path.exists(default_icon) else "",
|
|
261
|
+
"output_dir": "",
|
|
262
|
+
"verbose": False,
|
|
263
|
+
"language": ui_config.get("language", "en"),
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@qui.expose
|
|
268
|
+
def set_language(lang):
|
|
269
|
+
"""设置界面语言并保存到配置文件
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
lang: 'en' or 'cn'
|
|
273
|
+
"""
|
|
274
|
+
if lang not in ('en', 'cn'):
|
|
275
|
+
return {"success": False, "message": "Invalid language: {0}".format(lang)}
|
|
276
|
+
|
|
277
|
+
ui_config = _load_ui_config()
|
|
278
|
+
ui_config["language"] = lang
|
|
279
|
+
_save_ui_config(ui_config)
|
|
280
|
+
print("[PyInstallerEx] Language saved: {0}".format(lang))
|
|
281
|
+
return {"success": True, "language": lang}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def start_web_gui():
|
|
285
|
+
"""启动 Web GUI"""
|
|
286
|
+
print("[PyInstallerEx] Starting Web GUI...")
|
|
287
|
+
qui.run()
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
if __name__ == "__main__":
|
|
291
|
+
start_web_gui()
|
|
Binary file
|