TransWorker/modules/settings_manager.py

260 lines
11 KiB
Python

from PySide6.QtCore import QSettings
import logging
class SettingsManager:
"""
사용자 설정값(토글, 스핀, 텍스트 등)을 저장/불러오고,
각종 위젯에 따라 UI 상태까지 자동 적용하는 매니저 클래스입니다.
"""
def __init__(self, logger=None, organization="WhenRideMycar", application="EditPartTimer3"):
"""
QSettings 기반 설정 매니저 초기화
:param logger: logging.Logger 또는 print 대체용 함수
:param organization: QSettings 구분용 회사명
:param application: QSettings 구분용 앱명
"""
self.logger = logger or logging.getLogger(__name__)
self.settings = QSettings(organization, application)
self.widget_map = {} # 위젯명 → 저장키 매핑
self.toggle_dependencies = {} # 토글명 → 종속 위젯 매핑
def _log(self, message, level=logging.INFO):
"""
커스텀/표준 로거 모두 지원하는 내부 로깅 함수
"""
if hasattr(self.logger, "log") and "level" in self.logger.log.__code__.co_varnames:
# 커스텀 로거: self.logger.log(msg, level=logging.INFO)
self.logger.log(message, level=level)
else:
# 표준 로거
if level == logging.DEBUG:
self.logger.debug(message)
elif level == logging.INFO:
self.logger.info(message)
elif level == logging.WARNING:
self.logger.warning(message)
elif level == logging.ERROR:
self.logger.error(message)
elif level == logging.CRITICAL:
self.logger.critical(message)
else:
self.logger.info(message)
def bind_widgets(self, widget_map, toggle_dependencies):
"""
위젯-키, 토글-종속 딕셔너리를 등록합니다.
:param widget_map: { "widget명": "저장키" }
:param toggle_dependencies: { "toggle명": { "dependents": [...], "visible": [...] } }
"""
self.widget_map = widget_map
self.toggle_dependencies = toggle_dependencies
def save_settings(self, widget_obj):
"""
현재 UI에 연결된 각종 위젯의 값을 QSettings에 저장합니다.
:param widget_obj: 실제 위젯 객체(self 등)
config 예시:
{
'discord_notify_toggle': {
'dependents': ['webhook_input'],
'visible': ['webhook_input'],
},
'ocr_toggle': {
'dependents': ['unwanted_words_button']
},
...
}
"""
for widget_name, key in self.widget_map.items():
widget = getattr(widget_obj, widget_name, None)
if widget is None:
self._log(f"[SettingsManager] '{widget_name}' 위젯을 찾을 수 없습니다.", logging.WARNING)
continue
try:
# 체크박스, 토글, 커스텀토글 등
if hasattr(widget, 'isChecked'):
value = bool(widget.isChecked())
self.logger.log(f"[SettingsManager] bool 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG)
self.settings.setValue(key, value)
# SpinBox/DoubleSpinBox 등
elif hasattr(widget, 'value'):
self.settings.setValue(key, widget.value())
self.logger.log(f"[SettingsManager] int/float 타입 저장: {key} 값 저장: {widget.value()}", level=logging.DEBUG)
# QLineEdit 등 (단일줄 텍스트)
elif hasattr(widget, 'text'):
self.settings.setValue(key, widget.text())
self.logger.log(f"[SettingsManager] str 타입 저장: {key} 값 저장: {widget.text()}", level=logging.DEBUG)
# QTextEdit 등 (여러줄 텍스트)
elif hasattr(widget, 'toPlainText'):
self.settings.setValue(key, widget.toPlainText())
self.logger.log(f"[SettingsManager] str 타입 저장: {key} 값 저장: {widget.toPlainText()}", level=logging.DEBUG)
else:
self._log(f"[SettingsManager] '{widget_name}'의 값을 저장하는 방법을 알 수 없습니다.", logging.WARNING)
except Exception as e:
self._log(f"[SettingsManager] '{widget_name}' 저장 중 오류: {e}", logging.ERROR, exc_info=True)
def save_value(self, key, value):
"""특정 키로 단일 값을 설정에 저장합니다. 타입별로 정확히 저장."""
try:
# bool
if isinstance(value, bool):
self.settings.setValue(key, value)
self.logger.log(f"[SettingsManager] bool 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG)
# int/float
elif isinstance(value, (int, float)):
self.settings.setValue(key, value)
self.logger.log(f"[SettingsManager] int/float 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG)
# 그 외(특히 str)
else:
self.settings.setValue(key, str(value))
self.logger.log(f"[SettingsManager] str 타입 저장: {key} 값 저장: {value}", level=logging.DEBUG)
self.settings.sync()
self._log(f"[SettingsManager] {key} 값을 저장했습니다: {value}")
except Exception as e:
self._log(f"[SettingsManager] {key} 값 저장 중 오류: {e}", level=logging.WARNING)
# 기타 값 직접 접근용
def get_value(self, key, default=None):
return self.settings.value(key, default)
def load_settings(self, widget_obj):
"""
QSettings에서 저장된 값을 각 위젯에 복원합니다.
:param widget_obj: 실제 위젯 객체(self 등)
"""
for widget_name, key in self.widget_map.items():
widget = getattr(widget_obj, widget_name, None)
if widget is None:
self._log(f"[SettingsManager] '{widget_name}' 위젯을 찾을 수 없습니다.", logging.WARNING)
continue
val = self.settings.value(key, None)
if val is None:
continue # 미저장 값
try:
# 체크박스/토글 등 (bool 변환)
if hasattr(widget, 'setChecked'):
# QSettings는 bool을 str로 저장할 수 있어 안전하게 변환
if isinstance(val, bool):
widget.setChecked(val)
elif isinstance(val, (int, float)):
widget.setChecked(bool(val))
elif isinstance(val, str):
widget.setChecked(val.lower() in ['true', '1', 'yes'])
# SpinBox류
elif hasattr(widget, 'setValue'):
if isinstance(val, (int, float)):
widget.setValue(val)
elif isinstance(val, str):
if '.' in val:
widget.setValue(float(val))
else:
widget.setValue(int(val))
# QLineEdit 등
elif hasattr(widget, 'setText'):
widget.setText(str(val))
# QTextEdit 등
elif hasattr(widget, 'setPlainText'):
widget.setPlainText(str(val))
else:
self._log(f"[SettingsManager] '{widget_name}'에 값을 복원할 수 없습니다.", logging.WARNING)
except Exception as e:
self._log(f"[SettingsManager] '{widget_name}' 불러오기 오류: {e}", logging.ERROR, exc_info=True)
def reset_settings(self):
"""
전체 QSettings 값을 초기화(삭제)합니다.
"""
try:
self.settings.clear()
self._log("[SettingsManager] 모든 설정이 초기화되었습니다.", logging.INFO)
except Exception as e:
self._log(f"[SettingsManager] 설정 초기화 실패: {e}", logging.ERROR, exc_info=True)
def remove_setting(self, key):
"""
특정 키의 설정만 삭제합니다.
:param key: 삭제할 설정 키(str)
"""
try:
self.settings.remove(key)
self._log(f"[SettingsManager] '{key}' 설정이 삭제되었습니다.", logging.INFO)
except Exception as e:
self._log(f"[SettingsManager] '{key}' 삭제 실패: {e}", logging.ERROR, exc_info=True)
def debug_print(self):
"""
QSettings에 저장된 모든 값을 로그로 출력합니다.
"""
try:
self.settings.sync()
all_keys = self.settings.allKeys()
self.logger.info("[SettingsManager] 저장된 모든 설정값:")
for key in all_keys:
value = self.settings.value(key)
self.logger.info(f" {key}: {value}")
except Exception as e:
self._log(f"[SettingsManager] 설정값 출력 실패: {e}", logging.ERROR, exc_info=True)
def apply_settings_to_ui(self, widget_obj):
"""
종속 딕셔너리(toggle_dependencies)에 따라,
토글/체크박스 등 상태에 따라 dependents/visible 위젯을 자동으로 활성/비활성, 표시/숨김 처리합니다.
:param widget_obj: 실제 위젯 객체(self 등)
"""
for toggle_name, deps in self.toggle_dependencies.items():
toggle_widget = getattr(widget_obj, toggle_name, None)
if toggle_widget is None or not hasattr(toggle_widget, "isChecked"):
continue
checked = toggle_widget.isChecked()
# 종속 위젯 enable/disable
for dep in deps.get("dependents", []):
dep_widget = getattr(widget_obj, dep, None)
if dep_widget and hasattr(dep_widget, "setEnabled"):
try:
dep_widget.setEnabled(checked)
except Exception as e:
self._log(f"[SettingsManager] {dep} setEnabled 실패: {e}", logging.ERROR, exc_info=True)
# 종속 위젯 visible
for vis in deps.get("visible", []):
vis_widget = getattr(widget_obj, vis, None)
if vis_widget and hasattr(vis_widget, "setVisible"):
try:
vis_widget.setVisible(checked)
except Exception as e:
self._log(f"[SettingsManager] {vis} setVisible 실패: {e}", logging.ERROR, exc_info=True)
# (필요 시) 확장: 토글에 따라 특정 값을 리셋, 콜백 트리거 등
def save_user_info(self, user_info: dict):
for key, value in user_info.items():
self.settings.setValue(f"user/{key}", value)
self.settings.sync()
def load_user_info(self) -> dict:
info = {}
for key in ["email", "password", "id", "membership_level", "name"]:
info[key] = self.settings.value(f"user/{key}", "")
return info