PyInstallerEx 0.1.9__tar.gz → 0.2.2__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.2}/PKG-INFO +49 -7
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/README.md +47 -6
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/pyproject.toml +3 -2
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/setup.py +2 -1
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/__main__.py +6 -5
- pyinstallerex-0.2.2/src/PyInstallerEx/server.py +291 -0
- pyinstallerex-0.2.2/src/PyInstallerEx/web/favicon.ico +0 -0
- pyinstallerex-0.2.2/src/PyInstallerEx/web/index.html +664 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2/src/PyInstallerEx.egg-info}/PKG-INFO +49 -7
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx.egg-info/SOURCES.txt +3 -1
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx.egg-info/requires.txt +1 -0
- pyinstallerex-0.1.9/src/PyInstallerEx/PyInstallerEx.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/LICENSE +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/bin/launcher_windows.exe +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/setup.cfg +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/__init__.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/core/__init__.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/core/config.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/core/packager.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/utils/__init__.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/utils/compression.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/utils/file_utils.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/utils/icon_helper.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/utils/logging.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx/utils/system_utils.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx.egg-info/dependency_links.txt +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx.egg-info/entry_points.txt +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx.egg-info/not-zip-safe +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/src/PyInstallerEx.egg-info/top_level.txt +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/test/test_python27_compatibility.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/test/test_python3x_compatibility.py +0 -0
- {pyinstallerex-0.1.9 → pyinstallerex-0.2.2}/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.2
|
|
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,9 @@ PyInstallerEx 是一个基于 PyInstaller 的增强版打包工具,能够将 P
|
|
|
52
53
|
4. **灵活解压模式**:支持解压到系统临时目录或 exe 所在目录(通过 `--extract-mode` 参数控制)
|
|
53
54
|
5. **跨平台支持**:Windows、Linux x86/ARM全平台覆盖
|
|
54
55
|
6. **配置灵活**:支持嵌入式配置读取,命令行参数覆盖配置文件
|
|
56
|
+
7. **Web GUI 界面**:无参数启动时自动打开图形化界面,可视化配置打包参数
|
|
57
|
+
- **多语言支持**:支持中文/英文界面切换,设置持久化保存
|
|
58
|
+
- **文件选择对话框**:原生 tkinter 文件/文件夹选择器,支持初始路径记忆
|
|
55
59
|
|
|
56
60
|
## 🔧 环境要求
|
|
57
61
|
|
|
@@ -130,6 +134,26 @@ pip install pyinstallerex
|
|
|
130
134
|
|
|
131
135
|
### 基本用法
|
|
132
136
|
|
|
137
|
+
#### 启动 Web GUI(图形界面)
|
|
138
|
+
|
|
139
|
+
直接运行命令,不带任何参数,即可启动 Web GUI 界面:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# 以下三种方式均可
|
|
143
|
+
PyInstallerEx
|
|
144
|
+
pyinstallerex
|
|
145
|
+
python -m PyInstallerEx
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
启动后会自动打开浏览器窗口,支持:
|
|
149
|
+
- 选择脚本或文件夹打包模式
|
|
150
|
+
- 可视化配置输出名称、版本、引擎、图标等参数
|
|
151
|
+
- 实时查看构建日志和结果
|
|
152
|
+
|
|
153
|
+
#### 命令行模式
|
|
154
|
+
|
|
155
|
+
带参数时使用命令行模式:
|
|
156
|
+
|
|
133
157
|
```bash
|
|
134
158
|
# 使用 pyinstallerex 命令
|
|
135
159
|
pyinstallerex your_script.py
|
|
@@ -327,7 +351,11 @@ py-installer-ex/
|
|
|
327
351
|
│ ├── PyInstallerEx/ # Python 主程序
|
|
328
352
|
│ │ ├── __init__.py
|
|
329
353
|
│ │ ├── __main__.py # CLI入口点
|
|
354
|
+
│ │ ├── server.py # Web GUI 后端服务
|
|
330
355
|
│ │ ├── PyInstallerEx.py # 主模块
|
|
356
|
+
│ │ ├── web/
|
|
357
|
+
│ │ │ ├── index.html # Web GUI 前端页面
|
|
358
|
+
│ │ │ └── favicon.ico # 页面图标
|
|
331
359
|
│ │ ├── core/
|
|
332
360
|
│ │ │ ├── config.py # 配置管理
|
|
333
361
|
│ │ │ └── packager.py # 打包逻辑
|
|
@@ -449,20 +477,34 @@ script\install.bat
|
|
|
449
477
|
|
|
450
478
|
- **Python 2.7+ / 3.x**: 主要开发语言
|
|
451
479
|
- **PyInstaller >= 3.2.1**: 应用程序打包基础
|
|
480
|
+
- **pyquickwebgui**: Web GUI 图形界面框架
|
|
452
481
|
- **Rust (edition 2021)**: 启动器开发语言(serde/serde_json、md5、flate2)
|
|
453
482
|
- **JSON**: 配置文件格式
|
|
454
483
|
- **ZIP**: 应用程序压缩格式
|
|
455
484
|
|
|
456
|
-
##
|
|
485
|
+
## 未来扩展方向
|
|
457
486
|
|
|
458
|
-
1.
|
|
459
|
-
2.
|
|
460
|
-
3.
|
|
461
|
-
4.
|
|
462
|
-
5.
|
|
487
|
+
1. ~~**GUI界面**~~:✅ 已实现 Web GUI 图形化界面(基于 pyquickwebgui)
|
|
488
|
+
2. ~~**多语言支持**~~:✅ 已实现中英文双语界面,localStorage + 配置文件双持久化
|
|
489
|
+
3. **数字签名**:支持代码签名和安全验证
|
|
490
|
+
4. **增量更新**:实现应用程序的增量更新功能
|
|
491
|
+
5. **更多平台**:支持macOS等其他操作系统
|
|
492
|
+
6. **插件系统**:允许扩展自定义打包逻辑
|
|
463
493
|
|
|
464
494
|
## 更新日志
|
|
465
495
|
|
|
496
|
+
- 2026-07-07 V 0.2.2
|
|
497
|
+
- 新增 **Web GUI 多语言支持**:支持中文/英文界面实时切换
|
|
498
|
+
- 新增 **语言设置持久化**:使用用户主目录下 `.pyinstallerex/ui_config.json` 配置文件保存语言偏好
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
- 2026-07-07 V 0.2.1
|
|
502
|
+
- 新增 **Web GUI 图形界面**:不带参数直接运行 `PyInstallerEx` 即可启动
|
|
503
|
+
- 基于 `pyquickwebgui` 实现,支持脚本模式/文件夹模式切换
|
|
504
|
+
- 可视化配置打包参数(引擎、图标、解压模式、输出目录等)
|
|
505
|
+
- 实时日志输出与构建状态轮询
|
|
506
|
+
- 新增 `server.py` 后端服务及 `web/` 前端页面
|
|
507
|
+
|
|
466
508
|
- 2026-07-03 V 0.1.10
|
|
467
509
|
- 新增 `--folder` 参数,支持文件夹直接打包模式:跳过引擎构建,直接将指定文件夹压缩并附加到启动器
|
|
468
510
|
- 新增 `--run` 参数,支持自定义解压后运行的主程序入口(配置 `main` 字段)
|
|
@@ -18,6 +18,9 @@ PyInstallerEx 是一个基于 PyInstaller 的增强版打包工具,能够将 P
|
|
|
18
18
|
4. **灵活解压模式**:支持解压到系统临时目录或 exe 所在目录(通过 `--extract-mode` 参数控制)
|
|
19
19
|
5. **跨平台支持**:Windows、Linux x86/ARM全平台覆盖
|
|
20
20
|
6. **配置灵活**:支持嵌入式配置读取,命令行参数覆盖配置文件
|
|
21
|
+
7. **Web GUI 界面**:无参数启动时自动打开图形化界面,可视化配置打包参数
|
|
22
|
+
- **多语言支持**:支持中文/英文界面切换,设置持久化保存
|
|
23
|
+
- **文件选择对话框**:原生 tkinter 文件/文件夹选择器,支持初始路径记忆
|
|
21
24
|
|
|
22
25
|
## 🔧 环境要求
|
|
23
26
|
|
|
@@ -96,6 +99,26 @@ pip install pyinstallerex
|
|
|
96
99
|
|
|
97
100
|
### 基本用法
|
|
98
101
|
|
|
102
|
+
#### 启动 Web GUI(图形界面)
|
|
103
|
+
|
|
104
|
+
直接运行命令,不带任何参数,即可启动 Web GUI 界面:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# 以下三种方式均可
|
|
108
|
+
PyInstallerEx
|
|
109
|
+
pyinstallerex
|
|
110
|
+
python -m PyInstallerEx
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
启动后会自动打开浏览器窗口,支持:
|
|
114
|
+
- 选择脚本或文件夹打包模式
|
|
115
|
+
- 可视化配置输出名称、版本、引擎、图标等参数
|
|
116
|
+
- 实时查看构建日志和结果
|
|
117
|
+
|
|
118
|
+
#### 命令行模式
|
|
119
|
+
|
|
120
|
+
带参数时使用命令行模式:
|
|
121
|
+
|
|
99
122
|
```bash
|
|
100
123
|
# 使用 pyinstallerex 命令
|
|
101
124
|
pyinstallerex your_script.py
|
|
@@ -293,7 +316,11 @@ py-installer-ex/
|
|
|
293
316
|
│ ├── PyInstallerEx/ # Python 主程序
|
|
294
317
|
│ │ ├── __init__.py
|
|
295
318
|
│ │ ├── __main__.py # CLI入口点
|
|
319
|
+
│ │ ├── server.py # Web GUI 后端服务
|
|
296
320
|
│ │ ├── PyInstallerEx.py # 主模块
|
|
321
|
+
│ │ ├── web/
|
|
322
|
+
│ │ │ ├── index.html # Web GUI 前端页面
|
|
323
|
+
│ │ │ └── favicon.ico # 页面图标
|
|
297
324
|
│ │ ├── core/
|
|
298
325
|
│ │ │ ├── config.py # 配置管理
|
|
299
326
|
│ │ │ └── packager.py # 打包逻辑
|
|
@@ -415,20 +442,34 @@ script\install.bat
|
|
|
415
442
|
|
|
416
443
|
- **Python 2.7+ / 3.x**: 主要开发语言
|
|
417
444
|
- **PyInstaller >= 3.2.1**: 应用程序打包基础
|
|
445
|
+
- **pyquickwebgui**: Web GUI 图形界面框架
|
|
418
446
|
- **Rust (edition 2021)**: 启动器开发语言(serde/serde_json、md5、flate2)
|
|
419
447
|
- **JSON**: 配置文件格式
|
|
420
448
|
- **ZIP**: 应用程序压缩格式
|
|
421
449
|
|
|
422
|
-
##
|
|
450
|
+
## 未来扩展方向
|
|
423
451
|
|
|
424
|
-
1.
|
|
425
|
-
2.
|
|
426
|
-
3.
|
|
427
|
-
4.
|
|
428
|
-
5.
|
|
452
|
+
1. ~~**GUI界面**~~:✅ 已实现 Web GUI 图形化界面(基于 pyquickwebgui)
|
|
453
|
+
2. ~~**多语言支持**~~:✅ 已实现中英文双语界面,localStorage + 配置文件双持久化
|
|
454
|
+
3. **数字签名**:支持代码签名和安全验证
|
|
455
|
+
4. **增量更新**:实现应用程序的增量更新功能
|
|
456
|
+
5. **更多平台**:支持macOS等其他操作系统
|
|
457
|
+
6. **插件系统**:允许扩展自定义打包逻辑
|
|
429
458
|
|
|
430
459
|
## 更新日志
|
|
431
460
|
|
|
461
|
+
- 2026-07-07 V 0.2.2
|
|
462
|
+
- 新增 **Web GUI 多语言支持**:支持中文/英文界面实时切换
|
|
463
|
+
- 新增 **语言设置持久化**:使用用户主目录下 `.pyinstallerex/ui_config.json` 配置文件保存语言偏好
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
- 2026-07-07 V 0.2.1
|
|
467
|
+
- 新增 **Web GUI 图形界面**:不带参数直接运行 `PyInstallerEx` 即可启动
|
|
468
|
+
- 基于 `pyquickwebgui` 实现,支持脚本模式/文件夹模式切换
|
|
469
|
+
- 可视化配置打包参数(引擎、图标、解压模式、输出目录等)
|
|
470
|
+
- 实时日志输出与构建状态轮询
|
|
471
|
+
- 新增 `server.py` 后端服务及 `web/` 前端页面
|
|
472
|
+
|
|
432
473
|
- 2026-07-03 V 0.1.10
|
|
433
474
|
- 新增 `--folder` 参数,支持文件夹直接打包模式:跳过引擎构建,直接将指定文件夹压缩并附加到启动器
|
|
434
475
|
- 新增 `--run` 参数,支持自定义解压后运行的主程序入口(配置 `main` 字段)
|
|
@@ -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.2"
|
|
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
|