add version
This commit is contained in:
+113
-10
@@ -5,6 +5,8 @@ import json
|
||||
import time
|
||||
import traceback
|
||||
import subprocess
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
from pathlib import Path
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import logging
|
||||
@@ -14,6 +16,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
APP_NAME = "Watchdog GUI"
|
||||
APP_VERSION = "1.1.0"
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
CONFIG_PATH = BASE_DIR / "watchdog_config.json"
|
||||
LOG_DIR = BASE_DIR / "watchdog_logs"
|
||||
@@ -113,6 +117,68 @@ def save_config(cfg: dict):
|
||||
LOGGER.exception("설정 파일 저장 실패")
|
||||
|
||||
|
||||
# =========================
|
||||
# 버전 유틸
|
||||
# =========================
|
||||
def get_file_version(file_path: str) -> str:
|
||||
"""
|
||||
Windows EXE/DLL 파일 버전 읽기
|
||||
실패 시 '알 수 없음' 반환
|
||||
"""
|
||||
try:
|
||||
if not file_path or not os.path.exists(file_path):
|
||||
return "알 수 없음"
|
||||
|
||||
size = ctypes.windll.version.GetFileVersionInfoSizeW(file_path, None)
|
||||
if not size:
|
||||
return "알 수 없음"
|
||||
|
||||
res = ctypes.create_string_buffer(size)
|
||||
ctypes.windll.version.GetFileVersionInfoW(file_path, 0, size, res)
|
||||
|
||||
u_len = wintypes.UINT()
|
||||
u_ptr = ctypes.c_void_p()
|
||||
|
||||
ok = ctypes.windll.version.VerQueryValueW(
|
||||
res,
|
||||
"\\",
|
||||
ctypes.byref(u_ptr),
|
||||
ctypes.byref(u_len)
|
||||
)
|
||||
if not ok:
|
||||
return "알 수 없음"
|
||||
|
||||
class VS_FIXEDFILEINFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("dwSignature", wintypes.DWORD),
|
||||
("dwStrucVersion", wintypes.DWORD),
|
||||
("dwFileVersionMS", wintypes.DWORD),
|
||||
("dwFileVersionLS", wintypes.DWORD),
|
||||
("dwProductVersionMS", wintypes.DWORD),
|
||||
("dwProductVersionLS", wintypes.DWORD),
|
||||
("dwFileFlagsMask", wintypes.DWORD),
|
||||
("dwFileFlags", wintypes.DWORD),
|
||||
("dwFileOS", wintypes.DWORD),
|
||||
("dwFileType", wintypes.DWORD),
|
||||
("dwFileSubtype", wintypes.DWORD),
|
||||
("dwFileDateMS", wintypes.DWORD),
|
||||
("dwFileDateLS", wintypes.DWORD),
|
||||
]
|
||||
|
||||
ffi = ctypes.cast(u_ptr, ctypes.POINTER(VS_FIXEDFILEINFO)).contents
|
||||
|
||||
major = (ffi.dwFileVersionMS >> 16) & 0xFFFF
|
||||
minor = ffi.dwFileVersionMS & 0xFFFF
|
||||
build = (ffi.dwFileVersionLS >> 16) & 0xFFFF
|
||||
revision = ffi.dwFileVersionLS & 0xFFFF
|
||||
|
||||
return f"{major}.{minor}.{build}.{revision}"
|
||||
|
||||
except Exception:
|
||||
LOGGER.exception("파일 버전 읽기 실패")
|
||||
return "알 수 없음"
|
||||
|
||||
|
||||
# =========================
|
||||
# 감시 유틸
|
||||
# =========================
|
||||
@@ -184,9 +250,9 @@ def start_target_program(target_exe_path: str) -> bool:
|
||||
class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle(APP_NAME)
|
||||
self.resize(520, 250)
|
||||
self.setMinimumSize(500, 230)
|
||||
self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
|
||||
self.resize(560, 300)
|
||||
self.setMinimumSize(520, 270)
|
||||
|
||||
self.cfg = load_config()
|
||||
self.monitoring = False
|
||||
@@ -204,7 +270,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.timer.timeout.connect(self.check_target_once)
|
||||
|
||||
LOGGER.info("=" * 60)
|
||||
LOGGER.info("Watchdog GUI 시작")
|
||||
LOGGER.info(f"{APP_NAME} 시작 - 버전 {APP_VERSION}")
|
||||
LOGGER.info(f"로그 파일: {LOG_FILE}")
|
||||
LOGGER.info("=" * 60)
|
||||
|
||||
@@ -212,6 +278,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.start_monitoring()
|
||||
|
||||
self.update_target_status_label()
|
||||
self.update_version_labels()
|
||||
|
||||
if self.cfg.get("start_minimized_to_tray", False):
|
||||
QtCore.QTimer.singleShot(300, self.hide_to_tray)
|
||||
@@ -227,6 +294,20 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
main_layout.setContentsMargins(10, 10, 10, 10)
|
||||
main_layout.setSpacing(8)
|
||||
|
||||
# 버전 표시
|
||||
version_layout = QtWidgets.QGridLayout()
|
||||
version_layout.setHorizontalSpacing(8)
|
||||
version_layout.setVerticalSpacing(4)
|
||||
|
||||
self.lbl_watchdog_version = QtWidgets.QLabel(f"Watchdog 버전: {APP_VERSION}")
|
||||
self.lbl_watchdog_version.setStyleSheet("font-weight: bold; color: #1f1f1f;")
|
||||
|
||||
self.lbl_target_version = QtWidgets.QLabel("대상 버전: 알 수 없음")
|
||||
self.lbl_target_version.setStyleSheet("font-weight: bold; color: #1f1f1f;")
|
||||
|
||||
version_layout.addWidget(self.lbl_watchdog_version, 0, 0)
|
||||
version_layout.addWidget(self.lbl_target_version, 0, 1)
|
||||
|
||||
# 경로 행
|
||||
path_layout = QtWidgets.QHBoxLayout()
|
||||
path_layout.setSpacing(6)
|
||||
@@ -320,6 +401,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
log_layout.addWidget(self.text_log)
|
||||
log_layout.addWidget(self.btn_clear_log)
|
||||
|
||||
main_layout.addLayout(version_layout)
|
||||
main_layout.addLayout(path_layout)
|
||||
main_layout.addLayout(option_layout)
|
||||
main_layout.addLayout(top_btn_layout)
|
||||
@@ -334,7 +416,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.btn_open_log_file.clicked.connect(self.open_log_file)
|
||||
self.btn_clear_log.clicked.connect(self.text_log.clear)
|
||||
|
||||
self.edit_exe_path.editingFinished.connect(self.save_ui_to_config)
|
||||
self.edit_exe_path.editingFinished.connect(self.on_path_edited)
|
||||
self.spin_interval.valueChanged.connect(self.save_ui_to_config)
|
||||
self.spin_cooldown.valueChanged.connect(self.save_ui_to_config)
|
||||
self.chk_auto_start.stateChanged.connect(self.save_ui_to_config)
|
||||
@@ -361,9 +443,9 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.btn_toggle_log.setText("로그 숨기기" if show_log else "로그 보기")
|
||||
|
||||
if show_log:
|
||||
self.resize(560, 500)
|
||||
self.resize(600, 540)
|
||||
else:
|
||||
self.resize(520, 250)
|
||||
self.resize(560, 300)
|
||||
|
||||
def save_ui_to_config(self):
|
||||
self.cfg["target_exe_path"] = self.edit_exe_path.text().strip()
|
||||
@@ -375,6 +457,17 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.cfg["show_log_panel"] = bool(self.log_group.isVisible())
|
||||
save_config(self.cfg)
|
||||
|
||||
def on_path_edited(self):
|
||||
self.save_ui_to_config()
|
||||
self.update_target_status_label()
|
||||
self.update_version_labels()
|
||||
|
||||
def update_version_labels(self):
|
||||
exe_path = self.edit_exe_path.text().strip()
|
||||
target_ver = get_file_version(exe_path)
|
||||
self.lbl_watchdog_version.setText(f"Watchdog 버전: {APP_VERSION}")
|
||||
self.lbl_target_version.setText(f"대상 버전: {target_ver}")
|
||||
|
||||
# ---------------------
|
||||
# 트레이
|
||||
# ---------------------
|
||||
@@ -444,6 +537,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
if path:
|
||||
self.edit_exe_path.setText(path)
|
||||
self.save_ui_to_config()
|
||||
self.update_version_labels()
|
||||
LOGGER.info(f"감시 대상 선택: {path}")
|
||||
self.update_target_status_label()
|
||||
|
||||
@@ -453,9 +547,9 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.btn_toggle_log.setText("로그 숨기기" if visible else "로그 보기")
|
||||
|
||||
if visible:
|
||||
self.resize(max(self.width(), 560), 500)
|
||||
self.resize(max(self.width(), 600), 540)
|
||||
else:
|
||||
self.resize(max(520, self.width()), 250)
|
||||
self.resize(max(560, self.width()), 300)
|
||||
|
||||
self.save_ui_to_config()
|
||||
|
||||
@@ -493,6 +587,8 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
self.lbl_monitor.setText("감시 상태: 실행 중")
|
||||
self.lbl_monitor.setStyleSheet("font-weight: bold; color: #007a1f;")
|
||||
|
||||
self.update_version_labels()
|
||||
|
||||
LOGGER.info(f"감시 시작: {exe_path}")
|
||||
LOGGER.info(f"체크 주기: {self.spin_interval.value()}초 / 쿨다운: {self.spin_cooldown.value()}초")
|
||||
|
||||
@@ -532,16 +628,19 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
|
||||
if not exe_path:
|
||||
self.update_target_status_label(False)
|
||||
self.update_version_labels()
|
||||
LOGGER.warning("감시 대상 경로가 비어 있습니다.")
|
||||
return
|
||||
|
||||
if not os.path.exists(exe_path):
|
||||
self.update_target_status_label(False)
|
||||
self.update_version_labels()
|
||||
LOGGER.error(f"감시 대상 파일이 존재하지 않습니다: {exe_path}")
|
||||
return
|
||||
|
||||
running = is_process_running_by_path(exe_path)
|
||||
self.update_target_status_label(running)
|
||||
self.update_version_labels()
|
||||
|
||||
if running:
|
||||
return
|
||||
@@ -566,7 +665,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
QtWidgets.QSystemTrayIcon.Warning,
|
||||
2500
|
||||
)
|
||||
QtCore.QTimer.singleShot(1500, self.update_target_status_label)
|
||||
QtCore.QTimer.singleShot(1500, self._refresh_after_restart)
|
||||
else:
|
||||
self.update_target_status_label(False)
|
||||
|
||||
@@ -574,6 +673,10 @@ class WatchdogWindow(QtWidgets.QMainWindow):
|
||||
LOGGER.error("감시 루프 오류 발생")
|
||||
LOGGER.error(traceback.format_exc())
|
||||
|
||||
def _refresh_after_restart(self):
|
||||
self.update_target_status_label()
|
||||
self.update_version_labels()
|
||||
|
||||
# ---------------------
|
||||
# 종료 / 최소화
|
||||
# ---------------------
|
||||
|
||||
Reference in New Issue
Block a user