2 Commits

Author SHA1 Message Date
Claude
3c17405a6b Strip astral-plane emojis from Tk button labels
Python 3.7's bundled Tcl/Tk on Windows is UCS-2 only and refuses
characters above U+FFFF. The button labels contained game-controller,
desktop, folder and refresh emojis (U+1F3AE, U+1F5A5, U+1F4C2, U+1F501),
so App.__init__ raised TclError and gui.main caught it, exited 1, and
the user saw 'nothing happened'.

Replace with plain Korean text. Per CLAUDE.md these emojis should not
have been added in the first place.
2026-05-16 03:04:13 +09:00
Claude
915b5c9f45 Diagnostic v2: log on module import, dual log paths, ship debug.bat
v0.3.3 wrapper only logged once it reached _main(). User reports CMD
window flashes shut and no log file created — meaning Python likely
never reached our code. Two fixes:

1. Move first log write to module top (before any project import) and
   write to BOTH the exe directory AND %LOCALAPPDATA%/sephiria_inv/.
   Either log existing proves Python booted; neither existing means
   PyInstaller bootloader itself failed.

2. Add run-debug.bat that runs the exe with stdout/stderr captured to
   sephiria_inv_console.log and pauses, so the window does not close
   before the user can read it.
2026-05-16 02:58:23 +09:00
3 changed files with 90 additions and 32 deletions

22
run-debug.bat Normal file
View File

@@ -0,0 +1,22 @@
@echo off
REM Run the exe with output captured. Window does not auto-close.
setlocal
cd /d "%~dp0"
echo Running sephiria_inv.exe ...
echo.
sephiria_inv.exe > sephiria_inv_console.log 2>&1
set EC=%errorlevel%
echo.
echo === sephiria_inv.exe exited with code %EC% ===
echo.
echo --- sephiria_inv_console.log ---
if exist sephiria_inv_console.log type sephiria_inv_console.log
echo --- end console.log ---
echo.
echo --- sephiria_inv_startup.log ---
if exist sephiria_inv_startup.log type sephiria_inv_startup.log
echo --- end startup.log ---
echo.
echo Press any key to close this window.
pause > nul
endlocal

90
run.py
View File

@@ -1,9 +1,13 @@
"""PyInstaller entry shim.
"""PyInstaller entry shim with aggressive startup diagnostics.
Wrapped in a top-level try/except so any startup failure is written to
%LOCALAPPDATA%\\sephiria_inv\\startup.log and surfaced to the user via a
Tk messagebox when possible. Without this wrapper, --noconsole builds
exit silently on import-time errors and the user sees "nothing happens".
Writes a log entry the moment this module is loaded (before any project
imports). Logs to two locations:
1. Next to the exe (sys.executable directory) -- usually the user's
Downloads folder; trivially findable.
2. %LOCALAPPDATA%/sephiria_inv/startup.log
Either log existing tells us Python started. Neither existing means the
PyInstaller bootloader died before Python ran (DLL/runtime issue).
"""
from __future__ import annotations
@@ -14,24 +18,57 @@ import traceback
from datetime import datetime
def _log_path() -> str:
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
folder = os.path.join(base, "sephiria_inv")
def _candidate_log_paths():
paths = []
# 1. Next to the exe (most discoverable for the user)
try:
os.makedirs(folder, exist_ok=True)
except OSError:
folder = os.path.dirname(os.path.abspath(sys.argv[0])) or "."
return os.path.join(folder, "startup.log")
def _write_log(msg: str) -> str:
path = _log_path()
try:
with open(path, "a", encoding="utf-8") as fh:
fh.write(f"\n=== {datetime.now().isoformat()} ===\n{msg}\n")
except OSError:
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
if exe_dir:
paths.append(os.path.join(exe_dir, "sephiria_inv_startup.log"))
except Exception:
pass
return path
# 2. LOCALAPPDATA / home
try:
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
folder = os.path.join(base, "sephiria_inv")
os.makedirs(folder, exist_ok=True)
paths.append(os.path.join(folder, "startup.log"))
except Exception:
pass
return paths
def _write_log(msg: str) -> list:
written = []
stamp = datetime.now().isoformat()
for path in _candidate_log_paths():
try:
with open(path, "a", encoding="utf-8") as fh:
fh.write(f"\n=== {stamp} ===\n{msg}\n")
written.append(path)
except Exception:
pass
return written
def _env_dump() -> str:
lines = [
f"python: {sys.version}",
f"executable: {sys.executable}",
f"argv: {sys.argv}",
f"frozen: {getattr(sys, 'frozen', False)}",
f"_MEIPASS: {getattr(sys, '_MEIPASS', '<none>')}",
f"cwd: {os.getcwd()}",
f"PATH head: {os.environ.get('PATH', '')[:200]}",
f"LOCALAPPDATA: {os.environ.get('LOCALAPPDATA', '<none>')}",
]
return "\n".join(lines)
# === MODULE-IMPORT BREADCRUMB ===
# This runs the moment Python loads the entry script. If this never appears
# in either log, the PyInstaller bootloader failed before Python started.
_BOOT_LOGS = _write_log("BOOT: run.py loaded\n" + _env_dump())
def _show_error(title: str, body: str) -> None:
@@ -40,14 +77,13 @@ def _show_error(title: str, body: str) -> None:
from tkinter import scrolledtext
root = tk.Tk()
root.title(title)
root.geometry("760x420")
root.geometry("780x460")
txt = scrolledtext.ScrolledText(root, wrap="word")
txt.pack(fill="both", expand=True)
txt.insert("1.0", body)
tk.Button(root, text="Close", command=root.destroy).pack(pady=4)
root.mainloop()
except Exception:
# Tk itself is broken; nothing we can show.
pass
@@ -56,10 +92,10 @@ def _main() -> int:
from sephiria_inv.__main__ import main
except BaseException:
tb = traceback.format_exc()
log = _write_log("IMPORT FAIL\n" + tb)
logs = _write_log("IMPORT FAIL\n" + tb + "\n--env--\n" + _env_dump())
_show_error(
"sephiria_inv: import failed",
f"Failed to import sephiria_inv.__main__\n\nLog: {log}\n\n{tb}",
f"Failed to import sephiria_inv.__main__\n\nLogs: {logs}\n\n{tb}",
)
return 11
try:
@@ -68,10 +104,10 @@ def _main() -> int:
raise
except BaseException:
tb = traceback.format_exc()
log = _write_log("RUNTIME FAIL\n" + tb)
logs = _write_log("RUNTIME FAIL\n" + tb)
_show_error(
"sephiria_inv: runtime error",
f"Crashed during main()\n\nLog: {log}\n\n{tb}",
f"Crashed during main()\n\nLogs: {logs}\n\n{tb}",
)
return 12

View File

@@ -409,11 +409,11 @@ class ScreenshotFrame(ttk.Frame):
ctl = ttk.Frame(self)
ctl.pack(fill="x")
ttk.Button(ctl, text="🎮 게임 창 선택…", command=self._pick_window).pack(side="left")
ttk.Button(ctl, text="🖥 전체 화면 캡처", command=self._capture_screen).pack(side="left", padx=4)
ttk.Button(ctl, text="📂 파일 열기…", command=self._open_file).pack(side="left", padx=4)
ttk.Button(ctl, text="🔁 영역 재지정", command=self._reselect_bbox).pack(side="left", padx=4)
ttk.Button(ctl, text="이 구성으로 계산", command=self._confirm).pack(side="right")
ttk.Button(ctl, text="게임 창 선택…", command=self._pick_window).pack(side="left")
ttk.Button(ctl, text="전체 화면 캡처", command=self._capture_screen).pack(side="left", padx=4)
ttk.Button(ctl, text="파일 열기…", command=self._open_file).pack(side="left", padx=4)
ttk.Button(ctl, text="영역 재지정", command=self._reselect_bbox).pack(side="left", padx=4)
ttk.Button(ctl, text="이 구성으로 계산", command=self._confirm).pack(side="right")
self.status = ttk.Label(
self,