Skip to content

Commit 707d4d3

Browse files
authored
Merge pull request #2 from muziing/packaging_task
Add `PackagingTask`
2 parents c307bb6 + 4775bb7 commit 707d4d3

6 files changed

Lines changed: 344 additions & 37 deletions

File tree

src/py2exe_gui/Core/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .packaging import Packaging
2+
from .packaging_task import PackagingTask
3+
from .validators import FilePathValidator, InterpreterValidator
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from pathlib import Path
2+
from typing import Optional
3+
4+
from PySide6 import QtCore
5+
6+
from .validators import FilePathValidator, InterpreterValidator
7+
8+
9+
class PackagingTask(QtCore.QObject):
10+
"""
11+
打包任务类,存储每个打包任务的详细信息
12+
"""
13+
14+
# 自定义信号
15+
option_set = QtCore.Signal(tuple) # 用户输入选项通过了验证,已设置为打包选项
16+
option_error = QtCore.Signal(str) # 用户输入选项有误,需要进一步处理
17+
ready_to_pack = QtCore.Signal(bool) # 是否已经可以运行该打包任务
18+
19+
def __init__(self, parent: Optional[QtCore.QObject] = None) -> None:
20+
"""
21+
:param parent: 父控件对象
22+
"""
23+
24+
super(PackagingTask, self).__init__(parent)
25+
26+
self.script_path: Optional[Path] = None
27+
self.icon_path: Optional[Path] = None
28+
self.out_name: Optional[str] = None
29+
30+
def handle_option(self, option: tuple[str, str]):
31+
"""
32+
处理用户在界面选择的打包选项,进行有效性验证并保存 \n
33+
:param option: 选项
34+
"""
35+
36+
arg_key, arg_value = option
37+
38+
# 进行有效性验证,有效则保存并发射option_set信号,无效则发射option_error信号
39+
if arg_key == "script_path":
40+
script_path = Path(arg_value)
41+
if FilePathValidator.validate_script(script_path):
42+
self.script_path = script_path
43+
self.ready_to_pack.emit(True)
44+
self.option_set.emit(option)
45+
self.out_name = script_path.stem # 输出名默认与脚本名相同
46+
self.option_set.emit(("out_name", self.out_name))
47+
else:
48+
self.ready_to_pack.emit(False)
49+
self.option_error.emit(arg_key)
50+
# self.option_error.emit(arg_key) # 测试用!
51+
52+
elif arg_key == "icon_path":
53+
icon_path = Path(arg_value)
54+
if FilePathValidator.validate_icon(icon_path):
55+
self.icon_path = icon_path
56+
self.option_set.emit(option)
57+
else:
58+
self.option_error.emit(arg_key)
59+
# self.option_error.emit(arg_key) # 测试用!
60+
61+
elif arg_key == "out_name":
62+
self.out_name = arg_value
63+
self.option_set.emit(option)
64+
65+
else:
66+
self.option_set.emit(option)
67+
68+
def write_to_file(self):
69+
"""
70+
将打包任务保存至文件
71+
"""
72+
73+
pass

src/py2exe_gui/Core/validators.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import os
2+
import subprocess
3+
from pathlib import Path
4+
from typing import Union
5+
6+
7+
class FilePathValidator:
8+
"""
9+
根据给定的路径验证文件的有效性
10+
"""
11+
12+
@classmethod
13+
def validate_script(cls, script_path: Union[str, Path]) -> bool:
14+
"""
15+
验证脚本路径是否有效 \n
16+
:param script_path: 脚本路径
17+
"""
18+
19+
path = Path(script_path)
20+
# TODO 完善验证方法
21+
if path.exists() and path.is_file():
22+
return True
23+
else:
24+
return False
25+
26+
@classmethod
27+
def validate_icon(cls, icon_path: Union[str, Path]) -> bool:
28+
"""
29+
验证图标路径是否有效 \n
30+
:param icon_path: 图标路径
31+
"""
32+
33+
path = Path(icon_path)
34+
# TODO 完善验证方法
35+
if path.exists() and path.is_file():
36+
return True
37+
else:
38+
return False
39+
40+
41+
class InterpreterValidator:
42+
"""
43+
验证给定的可执行文件是否为有效的Python解释器,并获取该解释器相关信息
44+
"""
45+
46+
def __init__(self, path: Union[str, os.PathLike[str]]) -> None:
47+
"""
48+
:param path: 可执行文件路径
49+
"""
50+
51+
self._itp_path: Path = Path(path)
52+
self._itp_validated: bool = False
53+
self.validate_itp()
54+
55+
def validate_itp(self) -> bool:
56+
"""
57+
:return: 解释器是否有效
58+
"""
59+
60+
if (
61+
self._itp_path.exists() # 该路径存在
62+
and self._itp_path.is_file() # 为文件
63+
and self._itp_path.name.startswith("python") # 文件名以python起始
64+
):
65+
try:
66+
# 尝试将该文件作为Python解释器运行
67+
subprocess_args = [
68+
str(self._itp_path.resolve()),
69+
"-c",
70+
"import sys",
71+
]
72+
result = subprocess.run(
73+
args=subprocess_args,
74+
timeout=300,
75+
)
76+
if result.returncode == 0:
77+
self._itp_validated = True
78+
return True
79+
else:
80+
self._itp_validated = False
81+
return False
82+
except OSError:
83+
self._itp_validated = False
84+
return False
85+
else:
86+
self._itp_validated = False
87+
return False
88+
89+
def itp_info(self):
90+
"""
91+
返回解释器的相关信息 \n
92+
"""
93+
94+
# FIXME 完善此方法
95+
if self._itp_validated:
96+
pass
97+
98+
def module_installed(self, module: str) -> bool:
99+
"""
100+
验证该解释器环境中是否已安装某个模块 \n
101+
:param module: 模块名,要求为import语句中使用的名称
102+
:return: 未安装该模块或解释器无效时返回False
103+
"""
104+
105+
if self._itp_validated:
106+
subprocess_arg_list = [
107+
str(self._itp_path.resolve()),
108+
"-c",
109+
f"import {module}",
110+
]
111+
result = subprocess.run(
112+
args=subprocess_arg_list,
113+
stdout=subprocess.PIPE,
114+
stderr=subprocess.PIPE,
115+
timeout=300,
116+
)
117+
if result.returncode != 0 and b"ModuleNotFoundError" in result.stderr:
118+
return False
119+
else:
120+
return True
121+
else:
122+
return False
123+
124+
@classmethod
125+
def validate(cls, path: Union[str, os.PathLike[str]]) -> bool:
126+
"""
127+
验证path是否指向有效的Python解释器 \n
128+
:return: 是否有效
129+
"""
130+
131+
return InterpreterValidator(path).validate_itp()
132+
133+
134+
if __name__ == "__main__":
135+
# print(InterpreterValidator.validate("/usr/bin/python"))
136+
iv = InterpreterValidator("/usr/bin/python")
137+
print(iv.module_installed("PySide6"))
138+
print(iv.module_installed("black"))

src/py2exe_gui/Widgets/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .arguments_browser import ArgumentsBrowser
2+
from .center_widget import CenterWidget
3+
from .dialog_widgets import *
4+
from .main_window import MainWindow

0 commit comments

Comments
 (0)