fix: resolve Tcl/Tk init.tcl not found error when running from explorer
- Move _configure_tk_runtime() before tkinter import to set TCL_LIBRARY early - Add PyInstaller 6.x _internal directory support for Tcl/Tk paths - Switch ControlPanel.spec to onedir mode for reliable Tcl/Tk bundling Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
parent
87c8f8a8aa
commit
204678026e
47
ControlPanel.spec
Normal file
47
ControlPanel.spec
Normal file
@ -0,0 +1,47 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['D:\\App\\python\\YantaiVisionX\\src\\ui\\control_panel.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('C:\\Users\\sladr\\anaconda3\\envs\\YantaiVisionX\\Library\\lib\\tcl8.6', 'tcl/tcl8.6'), ('C:\\Users\\sladr\\anaconda3\\envs\\YantaiVisionX\\Library\\lib\\tk8.6', 'tcl/tk8.6')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='ControlPanel',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='ControlPanel',
|
||||
)
|
||||
47
ControlPanel_debug.spec
Normal file
47
ControlPanel_debug.spec
Normal file
@ -0,0 +1,47 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
# Debug version with onedir mode to inspect file structure
|
||||
|
||||
a = Analysis(
|
||||
['D:\\App\\python\\YantaiVisionX\\src\\ui\\control_panel.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('C:\\Users\\sladr\\anaconda3\\envs\\YantaiVisionX\\Library\\lib\\tcl8.6', 'tcl/tcl8.6'), ('C:\\Users\\sladr\\anaconda3\\envs\\YantaiVisionX\\Library\\lib\\tk8.6', 'tcl/tk8.6')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='ControlPanel_debug',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True, # Enable console to see errors
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='ControlPanel_debug',
|
||||
)
|
||||
@ -2,17 +2,96 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _configure_tk_runtime() -> None:
|
||||
"""Ensure Tcl/Tk resource paths are set when running from a frozen bundle."""
|
||||
|
||||
def _env_path_is_valid(var_name: str) -> bool:
|
||||
path = os.environ.get(var_name)
|
||||
return bool(path and Path(path).exists())
|
||||
|
||||
def _preferred_candidates(folder: str) -> list[Path]:
|
||||
candidates: list[Path] = []
|
||||
if getattr(sys, "frozen", False):
|
||||
base = Path(getattr(sys, "_MEIPASS", Path(__file__).parent))
|
||||
candidates.extend(
|
||||
[
|
||||
base / "_internal" / "tcl" / folder, # PyInstaller 6.x onedir
|
||||
base / "tcl" / folder,
|
||||
base / folder,
|
||||
]
|
||||
)
|
||||
special_map = {
|
||||
"tcl8.6": [base / "_internal" / "_tcl_data", base / "_tcl_data"],
|
||||
"tk8.6": [base / "_internal" / "_tk_data", base / "_tk_data"],
|
||||
}
|
||||
candidates.extend(special_map.get(folder, []))
|
||||
return candidates
|
||||
|
||||
def _common_roots() -> list[Path]:
|
||||
roots: list[Path] = []
|
||||
try:
|
||||
roots.append(Path(__file__).resolve().parents[2])
|
||||
except ValueError:
|
||||
pass
|
||||
roots.extend([Path(sys.base_prefix), Path(sys.prefix)])
|
||||
|
||||
for var_name in ("TCL_LIBRARY", "TK_LIBRARY"):
|
||||
env_value = os.environ.get(var_name)
|
||||
if env_value:
|
||||
roots.append(Path(env_value).parent)
|
||||
|
||||
deduped: list[Path] = []
|
||||
seen: set[Path] = set()
|
||||
for root in roots:
|
||||
try:
|
||||
resolved = root.resolve()
|
||||
except OSError:
|
||||
continue
|
||||
if resolved in seen:
|
||||
continue
|
||||
seen.add(resolved)
|
||||
deduped.append(resolved)
|
||||
return deduped
|
||||
|
||||
def _set_library(var_name: str, folder_name: str) -> None:
|
||||
forced = False
|
||||
if getattr(sys, "frozen", False):
|
||||
for candidate in _preferred_candidates(folder_name):
|
||||
if candidate.exists() and candidate.is_dir():
|
||||
os.environ[var_name] = str(candidate)
|
||||
forced = True
|
||||
break
|
||||
if forced or _env_path_is_valid(var_name):
|
||||
return
|
||||
|
||||
for root in _common_roots():
|
||||
for candidate in (root / "tcl" / folder_name, root / folder_name):
|
||||
if candidate.exists() and candidate.is_dir():
|
||||
os.environ[var_name] = str(candidate)
|
||||
return
|
||||
|
||||
_set_library("TCL_LIBRARY", "tcl8.6")
|
||||
_set_library("TK_LIBRARY", "tk8.6")
|
||||
|
||||
|
||||
# Configure Tcl/Tk paths BEFORE importing tkinter
|
||||
_configure_tk_runtime()
|
||||
|
||||
import queue
|
||||
import subprocess
|
||||
import threading
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from tkinter import filedialog, messagebox, ttk
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from main import YantaiVisionXSystem
|
||||
from src.utils.path_utils import get_app_root, resolve_app_path
|
||||
from tools.roi_calibration_tool import ROICalibrationTool
|
||||
from src.utils.path_utils import resolve_app_path, get_app_root
|
||||
|
||||
|
||||
class ControlPanelApp:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user