3 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
Claude
a70499edfa Wrap startup in try/except with file log + Tk messagebox
--noconsole exes silently exit on import-time errors, so when v0.3.2
crashed during startup the user just saw nothing happen. This wrapper:
- Catches BaseException at module-import and main() level
- Writes traceback to %LOCALAPPDATA%/sephiria_inv/startup.log
- Pops a Tk messagebox-equivalent window with the traceback
- Falls back gracefully if Tk itself is unavailable

Also build with --console (no --noconsole) so prints/tracebacks are
visible in real time. Once we know what is failing we can re-enable
windowed mode.
2026-05-16 02:38:51 +09:00
3 changed files with 140 additions and 8 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

114
run.py
View File

@@ -1,6 +1,116 @@
"""PyInstaller entry shim.""" """PyInstaller entry shim with aggressive startup diagnostics.
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
import os
import sys
import traceback
from datetime import datetime
def _candidate_log_paths():
paths = []
# 1. Next to the exe (most discoverable for the user)
try:
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
# 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:
try:
import tkinter as tk
from tkinter import scrolledtext
root = tk.Tk()
root.title(title)
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:
pass
def _main() -> int:
try:
from sephiria_inv.__main__ import main from sephiria_inv.__main__ import main
except BaseException:
tb = traceback.format_exc()
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\nLogs: {logs}\n\n{tb}",
)
return 11
try:
return int(main() or 0)
except SystemExit:
raise
except BaseException:
tb = traceback.format_exc()
logs = _write_log("RUNTIME FAIL\n" + tb)
_show_error(
"sephiria_inv: runtime error",
f"Crashed during main()\n\nLogs: {logs}\n\n{tb}",
)
return 12
if __name__ == "__main__": if __name__ == "__main__":
raise SystemExit(main()) raise SystemExit(_main())

View File

@@ -409,11 +409,11 @@ class ScreenshotFrame(ttk.Frame):
ctl = ttk.Frame(self) ctl = ttk.Frame(self)
ctl.pack(fill="x") ctl.pack(fill="x")
ttk.Button(ctl, text="🎮 게임 창 선택…", command=self._pick_window).pack(side="left") 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._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._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._reselect_bbox).pack(side="left", padx=4)
ttk.Button(ctl, text="이 구성으로 계산", command=self._confirm).pack(side="right") ttk.Button(ctl, text="이 구성으로 계산", command=self._confirm).pack(side="right")
self.status = ttk.Label( self.status = ttk.Label(
self, self,