import multiprocessing as mp import sys import ctypes import os import time import logging import atexit from PySide6.QtWidgets import QApplication, QMessageBox from PySide6.QtGui import QIcon, QKeySequence, QShortcut from sleep_control import prevent_sleep_mode, restore_sleep_mode from login_dialog import LoginDialog from mainUI_SP import MAIN_GUI from limited_gui import shuffleMAIN_GUI from src.modules.settings_manager import SettingsManager from loggerModule import Logger from src.loggerModules.loggerModule_structlog import StructlogImprovedLogger from updateManager.__version__ import __file_log_level__, __gui_log_level__, __version__ from src.ui.global_style import apply_global_theme, toggle_global_theme import socket # Windows DPI Awareness Constants DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4 # 윈도우 플랫폼 여부 확인 IS_WIN = sys.platform.startswith('win') def is_already_running(port=56387): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.bind(("127.0.0.1", port)) return False # 정상 바인딩 = 최초 실행 except OSError: return True # 이미 바인딩 되어 있음 = 실행 중 def run_single_instance_check(logger): # 포트 / Mutex 두 방식 모두 여기로 이동 if is_already_running(): app = QApplication([]) logger.log("이미 프로그램이 실행 중입니다! - 중복실행", level=logging.INFO) QMessageBox.warning(None, "중복 실행", "이미 프로그램이 실행 중입니다!") sys.exit(0) # Mutex 방식 (Windows에서만) if IS_WIN: try: import win32event, win32api, winerror mutex = win32event.CreateMutex(None, False, "Edit_PartTimer3Mutex") if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS: app = QApplication([]) QMessageBox.warning(None, "중복 실행", "이미 프로그램이 실행 중입니다!") sys.exit(0) except ImportError: logger.log("win32 모듈을 찾을 수 없습니다. Mutex 사용 불가", level=logging.WARNING) def set_dpi_awareness(): """애플리케이션 DPI 설정 (Windows에서만)""" if IS_WIN: try: ctypes.windll.shcore.SetProcessDpiAwareness(1) # 프로세스 DPI 인식 설정 except Exception: pass # 실패할 경우 무시 # COM 초기화·해제 헬퍼 (Windows에서만) def initialize_com(): """Win32 OLE(COM) 라이브러리 초기화""" if IS_WIN: ctypes.windll.ole32.OleInitialize(None) def uninitialize_com(): """Win32 OLE(COM) 라이브러리 해제""" if IS_WIN: ctypes.windll.ole32.OleUninitialize() def get_application_path(): """ 애플리케이션 실행 경로를 반환합니다. 개발 환경과 패키지 배포 환경 모두에서 작동합니다. """ if getattr(sys, 'frozen', False): # 패키징된 경우 base_dir = os.path.dirname(sys.executable) internal_dir = os.path.join(base_dir, 'lib', 'src') # lib 디렉토리 포함 if os.path.exists(internal_dir): # lib 디렉토리가 존재하면 base_dir로 설정 return internal_dir else: # 일반 Python 실행 환경 base_dir = os.path.dirname(os.path.abspath(__file__)) debug_dir = os.path.join(base_dir, 'src') # lib 디렉토리 포함 return debug_dir def setup_logging(): """ 로그 디렉토리와 파일을 설정하고 경로를 반환합니다. """ base_path = get_application_path() logs_dir = os.path.join(base_path, "logs") # 로그 디렉토리 생성 (존재하지 않는 경우) try: if not os.path.exists(logs_dir): os.makedirs(logs_dir) print(f"로그 디렉토리 생성됨: {logs_dir}") except Exception as e: print(f"로그 디렉토리 생성 실패: {e}") logs_dir = base_path # 실패 시 기본 경로 사용 log_file_path = os.path.join(logs_dir, "Edit_PartTimer3.log") # # 로그 파일 경로 출력 (디버깅용) # print(f"로그 파일 경로: {log_file_path}") return { "logs_dir": logs_dir, "log_file_path": log_file_path } def main(): # 로그 설정 log_paths = setup_logging() # 로거 초기화 - 경로 정보 활용 logger = Logger( log_file=log_paths["log_file_path"], logger_name="EP3_Logger", file_log_level=__file_log_level__ ) # try: # if mp.current_process().name == "MainProcess": # run_single_instance_check(logger) # except ImportError: # pass logger.log("===== 프로그램 시작 =====", level=logging.INFO) logger.log(f"로그 파일 경로: {log_paths['log_file_path']}", level=logging.INFO) settings_manager = SettingsManager(logger=logger, organization="WhenRideMycar", application="EditPartTimer3") # DPI 관련 경고 제거 (Qt 기본값 설정) set_dpi_awareness() # ▶ COM 초기화 (한 번만 실행) initialize_com() """프로그램 시작점""" app = QApplication([]) # 전역 라이트 테마 강제 적용 (OS 다크/라이트 설정 무시) apply_global_theme(app) # 애플리케이션 아이콘 설정 try: app_path = get_application_path() icon_path = os.path.join(app_path, "Edit_PartTimer3.ico") if os.path.exists(icon_path): app.setWindowIcon(QIcon(icon_path)) logger.log(f"애플리케이션 아이콘 설정: {icon_path}", level=logging.INFO) else: logger.log(f"아이콘 파일을 찾을 수 없습니다: {icon_path}", level=logging.WARNING) except Exception as e: logger.log(f"아이콘 설정 실패: {e}", level=logging.ERROR) # 슬립 방지 활성화 prevent_sleep_mode() # 업데이트 여부 체크 changelog = '' # 로그인 다이얼로그 실행 (모달) login_dialog = LoginDialog(logger, settings_manager) if login_dialog.exec() == LoginDialog.Accepted: user_info = login_dialog.get_user_info() supabase_manager = login_dialog.get_supabase_manager() system_info = login_dialog.get_system_info() # run_type = login_dialog.get_run_type() # <- 추가 update_selection_data = login_dialog.get_update_selection_data() # 업데이트 선택 정보 추가 # logger.log(f"run_type: {run_type}", level=logging.INFO) logger.log(f"update_selection: {update_selection_data}", level=logging.DEBUG) # run_type 값에 따라 분기 # if run_type == "편집알바생": # 알바생소집: 기존 메인UI 실행 mainWindow = MAIN_GUI( logger=logger, user_info=user_info, supabase_manager=supabase_manager, settings_manager=settings_manager, system_info=system_info, update_log=changelog, app=app, version=__version__, log_paths=log_paths ) logger.log("편집알바생 실행", level=logging.INFO) # if run_type == "업로드알바생": # mainWindow = shuffleMAIN_GUI( # logger=logger, # user_info=user_info, # supabase_manager=supabase_manager, # settings_manager=settings_manager, # system_info=system_info, # update_log=changelog, # app=app, # version=__version__, # log_paths=log_paths # ) # logger.log("업로드알바생 실행", level=logging.INFO) else: sys.exit("로그인 실패 또는 취소되었습니다.") # 윈도우에도 아이콘 설정 try: app_path = get_application_path() icon_path = os.path.join(app_path, "Edit_PartTimer3.ico") if os.path.exists(icon_path): app_icon = QIcon(icon_path) mainWindow.setWindowIcon(app_icon) logger.log("메인 윈도우에 아이콘 설정 완료", level=logging.INFO) except Exception as e: logger.log(f"메인 윈도우 아이콘 설정 실패: {e}", level=logging.ERROR) # 애플리케이션 종료 시 세션 정리 코드 추가 def cleanup_on_exit(): # 프로그램 종료 시 COM 해제 atexit.register(uninitialize_com) # 세션 종료 처리 try: logger.log(f"프로그램 종료: 현재 세션 종료 중 ...", level=logging.INFO) # 세션 종료 supabase_manager.close_session() # # 저장된 세션 ID 제거 # settings_manager.remove_session_id() logger.log("세션이 성공적으로 종료되었습니다.", level=logging.INFO) except Exception as e: logger.log(f"세션 종료 오류: {e}", level=logging.ERROR) # 슬립 모드 복원 restore_sleep_mode() # 애플리케이션 종료 이벤트 연결 app.aboutToQuit.connect(cleanup_on_exit) # 전역 테마 토글 단축키 (Ctrl+Alt+T) try: theme_shortcut = QShortcut(QKeySequence("Ctrl+Alt+T"), mainWindow) theme_shortcut.setContext(3) # Qt.ApplicationShortcut theme_shortcut.activated.connect(lambda: toggle_global_theme(app)) except Exception: pass mainWindow.show() sys.exit(app.exec()) if __name__ == "__main__": import multiprocessing as mp mp.freeze_support() # ← 추가 main()