AutoPercenty3/main.py

240 lines
8.4 KiB
Python

import sys
import ctypes
import os
import time
import logging
import atexit
from PySide6.QtWidgets import QApplication, QMessageBox
from PySide6.QtGui import QIcon
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 updateManager.__version__ import __file_log_level__, __gui_log_level__, __version__
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 set_dpi_awareness():
"""애플리케이션 DPI 설정"""
try:
ctypes.windll.shcore.SetProcessDpiAwareness(1) # 프로세스 DPI 인식 설정
except Exception:
pass # Windows가 아니거나 실패할 경우 무시
# COM 초기화·해제 헬퍼
def initialize_com():
"""Win32 OLE(COM) 라이브러리 초기화"""
ctypes.windll.ole32.OleInitialize(None)
def uninitialize_com():
"""Win32 OLE(COM) 라이브러리 해제"""
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:
import win32event
import win32api
import winerror
mutex = win32event.CreateMutex(None, False, "Edit_PartTimer3Mutex")
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
app = QApplication([])
QMessageBox.warning(None, "중복 실행 Mutex", "이미 프로그램이 실행 중입니다!")
sys.exit(0)
except ImportError:
pass
if is_already_running():
# 이미 실행 중인 경우 메시지 출력 후 종료
app = QApplication([])
logger.log("이미 프로그램이 실행 중입니다! - 중복실행", level=logging.INFO)
QMessageBox.warning(None, "중복 실행 LoHost", "이미 프로그램이 실행 중입니다!")
sys.exit(0)
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([])
# 애플리케이션 아이콘 설정
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)
mainWindow.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()