fix: 多项稳定性修复与打包优化

- PyInstaller: runtime hook 预加载 osgeo DLL 避免 Qt5 符号冲突;
  spec 纳入 osgeo .py 文件 + 运行时 DLL 依赖 (ffi/lzma/bz2/expat/sqlite3);
  移除 DLL 双副本防止 segfault
- GDAL 环境: 新增 _MEIPASS/osgeo/data/gdal 路径搜索,改善打包后 GDAL_DATA 检测
- 预览生成器: 掩膜与底图同分辨率时跳过 Warp 加速读取
- 专题图: ConvexHull 坐标中心化修复 UTM 大坐标精度退化;
  边界采样兜底防止外扩点为空;
  TIFF 已含 NaN 掩膜时跳过矢量擦除;
  形态学闭运算填充掩膜小孔洞
- NDWI: int16→float32 防止减法溢出
- 面板注册表: 步骤模块分组重构 (模块一/二/三/四重划分)
This commit is contained in:
duxin
2026-07-26 20:35:50 +08:00
parent 0bd54d5fdd
commit b609a64811
7 changed files with 177 additions and 46 deletions

View File

@ -60,6 +60,16 @@ def get_data_files():
if os.path.exists(rthook_script):
datas.append((rthook_script, "scripts"))
# 1-6. osgeo Python 模块(__init__.py, gdal.py, ogr.py 等)
# ★ 关键:PyInstaller 会把 .py 文件打入 PYZ (嵌入 EXE),但 .pyd 文件
# 作为 binaries 被放到 _internal/osgeo/ 物理目录下。当物理 osgeo/
# 目录存在但没有 __init__.py 时,Python 无法将其识别为 package,
# 导致 "import osgeo" 失败 → "GDAL 未安装"。
# 解决方案:将 osgeo 的所有 .py 文件显式复制到物理 osgeo/ 目录。
if os.path.isdir(CONDA_OSGEO_DLLS):
for py_file in glob.glob(os.path.join(CONDA_OSGEO_DLLS, "*.py")):
datas.append((py_file, "osgeo"))
return datas
datas = get_data_files()
@ -90,13 +100,26 @@ def get_binaries():
if os.path.exists(dll_path):
binaries.append((dll_path, "."))
# 2-2. 搜集底层 GDAL/GEOS/PROJ 核心框架与二进制扩展
if os.path.isdir(CONDA_OSGEO_DLLS):
for dll_name in ["gdal.dll", "geos.dll", "geos_c.dll", "proj_9.dll"]:
dll_path = os.path.join(CONDA_OSGEO_DLLS, dll_name)
# 2-1b. Python C 扩展的运行时依赖 DLL(_ctypes → ffi.dll, _lzma → liblzma.dll 等)
# 这些 DLL 位于 Conda 环境的 Library/bin,PyInstaller 分析时无法自动发现
CONDA_LIBRARY_BIN = os.path.join(CONDA_ENV_DLLS, "Library", "bin")
if os.path.isdir(CONDA_LIBRARY_BIN):
for dll_name in [
"ffi.dll", # _ctypes.pyd 依赖
"liblzma.dll", # _lzma.pyd 依赖
"libbz2.dll", # _bz2.pyd 依赖
"libexpat.dll", # pyexpat.pyd 依赖
"sqlite3.dll", # _sqlite3.pyd 依赖
]:
dll_path = os.path.join(CONDA_LIBRARY_BIN, dll_name)
if os.path.exists(dll_path):
binaries.append((dll_path, "."))
# 2-2. 搜集底层 GDAL/GEOS/PROJ 核心框架与二进制扩展
# ★ 注意:gdal.dll / geos.dll / geos_c.dll / proj_9.dll 不再显式添加,
# PyInstaller 会自动将它们收集到 osgeo/ 目录(随 .pyd 依赖解析)。
# 显式添加到根目录会导致 DLL 双副本冲突 → segfault。
if os.path.isdir(CONDA_OSGEO_DLLS):
# 映射 Python 3.12 对应的底层二进制接口 (C-Extension)
for pyd_name in [
"_gdal.cp312-win_amd64.pyd",
@ -185,6 +208,10 @@ a.binaries = [
if not x[0].lower().startswith(('msvcp140', 'vcruntime140'))
]
# ★ 注意:.libs 目录中的 PROJ/GEOS/GDAL DLL 副本无需删除。
# rthook_add_dll_dirs.py 运行时钩子会在 PyQt5 之前预加载 osgeo/ 的所有 DLL,
# 避免了 DLL 加载顺序导致的 segfault。各包的 .libs 副本可以安全共存。
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
# 生成物理启动核心
@ -198,7 +225,7 @@ exe = EXE(
bootloader_ignore_signals=False,
strip=False,
upx=False, # 禁用 UPX 暴力压缩,保护二进制 C++ 区段完整性
console=False, # ⚠️ 维持开启控制台,拒绝绝对静默崩溃
console=False, # GUI 应用,不显示控制台
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,