Browse Source

bug修复

master
dongchangxi 3 weeks ago
parent
commit
df797bc906
  1. 7
      script/factory_sliceing_v2/build_exe.spec
  2. 154
      script/factory_sliceing_v2/utils/funcs.py

7
script/factory_sliceing_v2/build_exe.spec

@ -6,7 +6,10 @@ a = Analysis( @@ -6,7 +6,10 @@ a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
datas=[
# 将 small_machine_transform.py 作为数据文件包含,以便 Blender 可以调用
('utils/small_machine_transform.py', 'utils'),
],
hiddenimports=[
'redis',
'oss2',
@ -39,7 +42,7 @@ exe = EXE( @@ -39,7 +42,7 @@ exe = EXE(
[],
name='factory_sliceing',
debug=False,
bootloader_ignore_signals=False,
bootloader_ignore_signals=False, # 允许信号传播,使 Ctrl+C 可以中断
strip=False,
upx=True,
upx_exclude=[],

154
script/factory_sliceing_v2/utils/funcs.py

@ -6,6 +6,8 @@ import shutil @@ -6,6 +6,8 @@ import shutil
import json
import shlex
import subprocess
import sys
import signal
from concurrent.futures import ThreadPoolExecutor, as_completed
from .oss_redis import ossClient
from .logs import log
@ -21,6 +23,75 @@ elif ENV == 'prod': @@ -21,6 +23,75 @@ elif ENV == 'prod':
url = 'https://mp.api.suwa3d.com'
def get_resource_path(relative_path):
"""
获取资源文件的绝对路径兼容 PyInstaller 打包后的环境
参数:
relative_path: 相对于脚本目录的相对路径
返回:
资源文件的绝对路径
"""
try:
# PyInstaller 创建临时文件夹,并将路径存储在 _MEIPASS 中
base_path = sys._MEIPASS
except AttributeError:
# 如果不是打包环境,使用脚本所在目录
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, relative_path)
def get_transform_script_path():
"""
获取 small_machine_transform.py 脚本的路径
在打包成exe后如果脚本不在可访问位置则将其提取到临时目录
返回:
transform_script_path: 脚本的绝对路径
"""
# 首先尝试从资源路径获取
if getattr(sys, 'frozen', False):
# 打包后的环境
# 尝试从资源路径获取
resource_path = get_resource_path('utils/small_machine_transform.py')
# 如果资源路径存在,直接使用
if os.path.exists(resource_path):
return resource_path
# 如果不存在,尝试提取到临时目录
import tempfile
temp_dir = tempfile.gettempdir()
extract_path = os.path.join(temp_dir, 'factory_sliceing_utils', 'small_machine_transform.py')
# 确保目录存在
os.makedirs(os.path.dirname(extract_path), exist_ok=True)
# 如果提取路径不存在,尝试从资源中复制
if not os.path.exists(extract_path):
# 尝试从多个可能的资源路径查找
possible_paths = [
get_resource_path('small_machine_transform.py'),
get_resource_path('utils/small_machine_transform.py'),
]
for src_path in possible_paths:
if os.path.exists(src_path):
shutil.copy2(src_path, extract_path)
log(f"已将 small_machine_transform.py 提取到: {extract_path}")
return extract_path
# 如果提取路径存在,直接使用
if os.path.exists(extract_path):
return extract_path
# 如果都找不到,返回资源路径(即使不存在,让调用者处理错误)
return resource_path
else:
# 开发环境,直接使用相对路径
script_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(script_dir, 'small_machine_transform.py')
# 根据打印ID 获取下载目录
def getDownloadDirByPrintId(printIds):
# 调用接口获取下载的路径
@ -317,9 +388,8 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine @@ -317,9 +388,8 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
# 通过blender 调用执行 python 文件
blender_bin_path = findBpyModule()
# 获取 small_machine_transform.py 的绝对路径
script_dir = os.path.dirname(os.path.abspath(__file__))
transform_script_path = os.path.join(script_dir, 'small_machine_transform.py')
# 获取 small_machine_transform.py 的绝对路径(兼容打包环境)
transform_script_path = get_transform_script_path()
# 将 homo_matrix 转换为 JSON 字符串
homo_matrix_json = json.dumps(homo_matrix)
@ -358,18 +428,86 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine @@ -358,18 +428,86 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
try:
log(f"开始执行 Blender 转换命令...")
# 使用 subprocess 执行命令,捕获输出和错误
# 使用 subprocess.Popen 以便更好地控制进程和信号处理
# 在 Windows 上,Blender 输出可能是 UTF-8,需要指定编码并处理错误
result = subprocess.run(
# 准备进程启动参数
startupinfo = None
preexec_fn = None
if platform.system() == 'Windows':
# Windows 上,不设置 CREATE_NEW_PROCESS_GROUP,以便 Ctrl+C 可以传播
import subprocess as sp
startupinfo = sp.STARTUPINFO()
startupinfo.dwFlags |= sp.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = sp.SW_HIDE
else:
# Unix 系统,设置 preexec_fn 以确保信号处理正确
preexec_fn = os.setsid
process = subprocess.Popen(
cmd,
capture_output=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding='utf-8', # 指定 UTF-8 编码
errors='replace', # 遇到无法解码的字符时替换为占位符,而不是抛出异常
timeout=300, # 5分钟超时
check=False # 不自动抛出异常,手动检查返回码
startupinfo=startupinfo,
preexec_fn=preexec_fn
)
try:
# 等待进程完成,捕获输出
stdout, stderr = process.communicate(timeout=300) # 5分钟超时
returncode = process.returncode
except subprocess.TimeoutExpired:
# 超时,终止进程
log(f"Blender 命令执行超时,正在终止进程...")
if platform.system() == 'Windows':
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
else:
# Unix 系统,发送 SIGTERM 到进程组
try:
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
process.wait(timeout=5)
except (subprocess.TimeoutExpired, ProcessLookupError, OSError):
try:
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
except (ProcessLookupError, OSError):
pass
raise
except KeyboardInterrupt:
# 收到中断信号,终止进程
log(f"收到中断信号,正在终止 Blender 进程...")
if platform.system() == 'Windows':
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
else:
# Unix 系统,发送 SIGTERM 到进程组
try:
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
process.wait(timeout=5)
except (subprocess.TimeoutExpired, ProcessLookupError, OSError):
try:
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
except (ProcessLookupError, OSError):
pass
raise # 重新抛出,让调用者处理
# 创建结果对象(模拟 subprocess.run 的返回值)
class Result:
def __init__(self, returncode, stdout, stderr):
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
result = Result(returncode, stdout, stderr)
log(f"Blender 命令执行完成, 返回码: {result.returncode}")
# 无论成功失败都输出详细信息

Loading…
Cancel
Save