260 lines
11 KiB
Python
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
|
|
|