# 엔트리포인트 — 스플래시 화면 표시 후 백그라운드 초기화, GUI 실행 import multiprocessing import sys from logger import setup_logging, teardown_logging from PyQt5.QtWidgets import QApplication from PyQt5.QtGui import QIcon from utils.path_helper import get_path from gui.main_window import MainWindow from gui.splash_screen import SplashScreen, InitWorker ICON_PATH = get_path("assets", "images", "ant_logo.png") def _apply_windows_taskbar_icon(app_id: str = "ant.vision.inspection.1.0"): if sys.platform != "win32": return try: import ctypes ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id) except Exception as e: print(f"[main] AppUserModelID 설정 실패: {e}") def main(): # setup_logging() must be called inside main(), not at module level. # On Windows, multiprocessing spawns subprocesses that re-import __main__, # and module-level setup_logging() would corrupt the log file and replace # builtins.print in every worker process. setup_logging() _apply_windows_taskbar_icon() app = QApplication(sys.argv) icon = QIcon(ICON_PATH) app.setWindowIcon(icon) app.setStyleSheet(_DARK_STYLE) splash = SplashScreen() splash.show() QApplication.processEvents() worker = InitWorker() worker.progress.connect(splash.update_progress) def on_finished(results): window = MainWindow( results["insight"], results["basler"], results["config"], plc_client=results.get("plc"), ) window.setWindowIcon(icon) window.show() splash.close() def cleanup(): results["insight"].disconnect() results["basler"].disconnect() if results["db"].is_connected(): results["db"].disconnect() plc = results.get("plc") if plc and plc.is_connected(): plc.disconnect() app.aboutToQuit.connect(cleanup) worker.finished.connect(on_finished) worker.start() try: ret = app.exec_() finally: teardown_logging() sys.exit(ret) _DARK_STYLE = """ QWidget { background-color: #1a1a1a; color: #ffffff; font-size: 14px; } QGroupBox { background-color: #222222; border: 1px solid #444444; border-radius: 6px; margin-top: 14px; padding: 10px 8px 8px 8px; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 4px; color: #aaaaaa; } QPushButton { background-color: #2e2e2e; color: #ffffff; border: 1px solid #555555; border-radius: 4px; min-height: 56px; padding: 0 20px; font-size: 14px; } QPushButton:hover { background-color: #3a3a3a; } QPushButton:pressed { background-color: #1e1e1e; } QPushButton:checked { background-color: #0055cc; border-color: #0077ff; } QPushButton:disabled { color: #666666; } QLineEdit, QSpinBox, QDoubleSpinBox { background-color: #2a2a2a; color: #ffffff; border: 1px solid #555555; border-radius: 4px; padding: 6px 8px; min-height: 38px; } QLabel { color: #ffffff; } QListWidget { background-color: #222222; color: #ffffff; border: 1px solid #444444; outline: none; } QListWidget::item:selected { background-color: #0055cc; } QProgressBar { background-color: #2a2a2a; border: 1px solid #555555; border-radius: 4px; text-align: center; min-height: 22px; } QProgressBar::chunk { background-color: #0055cc; border-radius: 4px; } QCheckBox { min-height: 38px; } QCheckBox::indicator { width: 22px; height: 22px; } QScrollBar:vertical { background: #2a2a2a; width: 10px; border-radius: 5px; } QScrollBar::handle:vertical { background: #555555; border-radius: 5px; min-height: 30px; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0; } """ if __name__ == "__main__": multiprocessing.freeze_support() main()