684 lines
26 KiB
Python
684 lines
26 KiB
Python
"""
|
||
Lemontropia Suite - Comprehensive Settings Dialog
|
||
Unified settings for Player, Screenshot Hotkeys, Computer Vision, and General preferences.
|
||
"""
|
||
|
||
import logging
|
||
from pathlib import Path
|
||
from typing import Optional, Dict, Any
|
||
|
||
from PyQt6.QtWidgets import (
|
||
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
|
||
QLabel, QLineEdit, QPushButton, QComboBox,
|
||
QCheckBox, QGroupBox, QTabWidget, QDialogButtonBox,
|
||
QMessageBox, QFileDialog, QWidget, QGridLayout,
|
||
QSpinBox, QDoubleSpinBox, QFrame
|
||
)
|
||
from PyQt6.QtCore import Qt, QSettings
|
||
from PyQt6.QtGui import QKeySequence
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class SettingsDialog(QDialog):
|
||
"""
|
||
Comprehensive settings dialog with tabbed interface.
|
||
|
||
Tabs:
|
||
- General: Player name, log path, activity defaults
|
||
- Screenshot Hotkeys: Configure F12 and other hotkeys
|
||
- Computer Vision: OCR backend selection, GPU settings
|
||
- Advanced: Performance, logging, database options
|
||
"""
|
||
|
||
def __init__(self, parent=None, db=None):
|
||
super().__init__(parent)
|
||
self.setWindowTitle("Lemontropia Suite - Settings")
|
||
self.setMinimumSize(600, 500)
|
||
self.resize(700, 550)
|
||
|
||
self.db = db
|
||
self._settings = QSettings("Lemontropia", "Suite")
|
||
|
||
# Load current values
|
||
self._load_current_values()
|
||
|
||
self._setup_ui()
|
||
self._apply_dark_theme()
|
||
|
||
def _load_current_values(self):
|
||
"""Load current settings values."""
|
||
# General
|
||
self._player_name = self._settings.value("player/name", "", type=str)
|
||
self._log_path = self._settings.value("log/path", "", type=str)
|
||
self._auto_detect_log = self._settings.value("log/auto_detect", True, type=bool)
|
||
self._default_activity = self._settings.value("activity/default", "hunting", type=str)
|
||
|
||
# Screenshot hotkeys
|
||
self._hotkey_full = self._settings.value("hotkey/screenshot_full", "F12", type=str)
|
||
self._hotkey_region = self._settings.value("hotkey/screenshot_region", "Shift+F12", type=str)
|
||
self._hotkey_loot = self._settings.value("hotkey/screenshot_loot", "Ctrl+F12", type=str)
|
||
self._hotkey_hud = self._settings.value("hotkey/screenshot_hud", "Alt+F12", type=str)
|
||
|
||
# Computer Vision
|
||
self._cv_backend = self._settings.value("cv/backend", "auto", type=str)
|
||
self._cv_use_gpu = self._settings.value("cv/use_gpu", True, type=bool)
|
||
self._cv_confidence = self._settings.value("cv/confidence", 0.5, type=float)
|
||
|
||
def _setup_ui(self):
|
||
"""Setup the dialog UI with tabs."""
|
||
layout = QVBoxLayout(self)
|
||
layout.setContentsMargins(15, 15, 15, 15)
|
||
layout.setSpacing(10)
|
||
|
||
# Title
|
||
title = QLabel("⚙️ Settings")
|
||
title.setStyleSheet("font-size: 18px; font-weight: bold; color: #4caf50;")
|
||
layout.addWidget(title)
|
||
|
||
# Tab widget
|
||
self.tabs = QTabWidget()
|
||
layout.addWidget(self.tabs)
|
||
|
||
# Create tabs
|
||
self.tabs.addTab(self._create_general_tab(), "📋 General")
|
||
self.tabs.addTab(self._create_hotkeys_tab(), "📸 Screenshot Hotkeys")
|
||
self.tabs.addTab(self._create_vision_tab(), "👁️ Computer Vision")
|
||
self.tabs.addTab(self._create_advanced_tab(), "🔧 Advanced")
|
||
|
||
# Button box
|
||
button_box = QDialogButtonBox(
|
||
QDialogButtonBox.StandardButton.Save |
|
||
QDialogButtonBox.StandardButton.Cancel |
|
||
QDialogButtonBox.StandardButton.Reset
|
||
)
|
||
button_box.accepted.connect(self._on_save)
|
||
button_box.rejected.connect(self.reject)
|
||
button_box.button(QDialogButtonBox.StandardButton.Reset).clicked.connect(self._on_reset)
|
||
layout.addWidget(button_box)
|
||
|
||
def _create_general_tab(self) -> QWidget:
|
||
"""Create General settings tab."""
|
||
tab = QWidget()
|
||
layout = QVBoxLayout(tab)
|
||
layout.setSpacing(15)
|
||
|
||
# Player Settings
|
||
player_group = QGroupBox("🎮 Player Settings")
|
||
player_form = QFormLayout(player_group)
|
||
|
||
self.player_name_edit = QLineEdit(self._player_name)
|
||
self.player_name_edit.setPlaceholderText("Your avatar name in Entropia Universe")
|
||
player_form.addRow("Avatar Name:", self.player_name_edit)
|
||
|
||
player_help = QLabel("This name is used to identify your globals and HoFs in the log.")
|
||
player_help.setStyleSheet("color: #888; font-size: 11px;")
|
||
player_help.setWordWrap(True)
|
||
player_form.addRow(player_help)
|
||
|
||
layout.addWidget(player_group)
|
||
|
||
# Log File Settings
|
||
log_group = QGroupBox("📄 Log File Settings")
|
||
log_layout = QVBoxLayout(log_group)
|
||
|
||
log_form = QFormLayout()
|
||
|
||
log_path_layout = QHBoxLayout()
|
||
self.log_path_edit = QLineEdit(self._log_path)
|
||
self.log_path_edit.setPlaceholderText(r"C:\Users\...\Documents\Entropia Universe\chat.log")
|
||
log_path_layout.addWidget(self.log_path_edit)
|
||
|
||
browse_btn = QPushButton("Browse...")
|
||
browse_btn.clicked.connect(self._browse_log_path)
|
||
log_path_layout.addWidget(browse_btn)
|
||
|
||
log_form.addRow("Chat Log Path:", log_path_layout)
|
||
|
||
self.auto_detect_check = QCheckBox("Auto-detect log path on startup")
|
||
self.auto_detect_check.setChecked(self._auto_detect_log)
|
||
log_form.addRow(self.auto_detect_check)
|
||
|
||
log_layout.addLayout(log_form)
|
||
|
||
# Quick paths
|
||
quick_paths_layout = QHBoxLayout()
|
||
quick_paths_layout.addWidget(QLabel("Quick select:"))
|
||
|
||
default_path_btn = QPushButton("Default Location")
|
||
default_path_btn.clicked.connect(self._set_default_log_path)
|
||
quick_paths_layout.addWidget(default_path_btn)
|
||
|
||
quick_paths_layout.addStretch()
|
||
log_layout.addLayout(quick_paths_layout)
|
||
|
||
layout.addWidget(log_group)
|
||
|
||
# Default Activity
|
||
activity_group = QGroupBox("🎯 Default Activity")
|
||
activity_form = QFormLayout(activity_group)
|
||
|
||
self.default_activity_combo = QComboBox()
|
||
activities = [
|
||
("hunting", "🎯 Hunting"),
|
||
("mining", "⛏️ Mining"),
|
||
("crafting", "⚒️ Crafting")
|
||
]
|
||
for value, display in activities:
|
||
self.default_activity_combo.addItem(display, value)
|
||
if value == self._default_activity:
|
||
self.default_activity_combo.setCurrentIndex(self.default_activity_combo.count() - 1)
|
||
|
||
activity_form.addRow("Default Activity:", self.default_activity_combo)
|
||
|
||
layout.addWidget(activity_group)
|
||
|
||
layout.addStretch()
|
||
return tab
|
||
|
||
def _create_hotkeys_tab(self) -> QWidget:
|
||
"""Create Screenshot Hotkeys tab."""
|
||
tab = QWidget()
|
||
layout = QVBoxLayout(tab)
|
||
layout.setSpacing(15)
|
||
|
||
# Info header
|
||
info = QLabel("📸 Configure screenshot hotkeys. Hotkeys work when the app is focused.")
|
||
info.setStyleSheet("color: #888; padding: 5px;")
|
||
info.setWordWrap(True)
|
||
layout.addWidget(info)
|
||
|
||
# Status
|
||
status_group = QGroupBox("Status")
|
||
status_layout = QVBoxLayout(status_group)
|
||
|
||
try:
|
||
import keyboard
|
||
self.hotkey_status = QLabel("✅ Global hotkeys available (keyboard library installed)")
|
||
self.hotkey_status.setStyleSheet("color: #4caf50;")
|
||
except ImportError:
|
||
self.hotkey_status = QLabel("ℹ️ Qt shortcuts only (install 'keyboard' library for global hotkeys)\npip install keyboard")
|
||
self.hotkey_status.setStyleSheet("color: #ff9800;")
|
||
self.hotkey_status.setWordWrap(True)
|
||
|
||
status_layout.addWidget(self.hotkey_status)
|
||
layout.addWidget(status_group)
|
||
|
||
# Hotkey configuration
|
||
hotkey_group = QGroupBox("Hotkey Configuration")
|
||
hotkey_form = QFormLayout(hotkey_group)
|
||
|
||
# Full screen
|
||
full_layout = QHBoxLayout()
|
||
self.hotkey_full_edit = QLineEdit(self._hotkey_full)
|
||
full_layout.addWidget(self.hotkey_full_edit)
|
||
full_test = QPushButton("Test")
|
||
full_test.clicked.connect(lambda: self._test_hotkey("full"))
|
||
full_layout.addWidget(full_test)
|
||
hotkey_form.addRow("Full Screen:", full_layout)
|
||
|
||
# Region
|
||
region_layout = QHBoxLayout()
|
||
self.hotkey_region_edit = QLineEdit(self._hotkey_region)
|
||
region_layout.addWidget(self.hotkey_region_edit)
|
||
region_test = QPushButton("Test")
|
||
region_test.clicked.connect(lambda: self._test_hotkey("region"))
|
||
region_layout.addWidget(region_test)
|
||
hotkey_form.addRow("Center Region (800x600):", region_layout)
|
||
|
||
# Loot
|
||
loot_layout = QHBoxLayout()
|
||
self.hotkey_loot_edit = QLineEdit(self._hotkey_loot)
|
||
loot_layout.addWidget(self.hotkey_loot_edit)
|
||
loot_test = QPushButton("Test")
|
||
loot_test.clicked.connect(lambda: self._test_hotkey("loot"))
|
||
loot_layout.addWidget(loot_test)
|
||
hotkey_form.addRow("Loot Window:", loot_layout)
|
||
|
||
# HUD
|
||
hud_layout = QHBoxLayout()
|
||
self.hotkey_hud_edit = QLineEdit(self._hotkey_hud)
|
||
hud_layout.addWidget(self.hotkey_hud_edit)
|
||
hud_test = QPushButton("Test")
|
||
hud_test.clicked.connect(lambda: self._test_hotkey("hud"))
|
||
hud_layout.addWidget(hud_test)
|
||
hotkey_form.addRow("HUD Area:", hud_layout)
|
||
|
||
layout.addWidget(hotkey_group)
|
||
|
||
# Help text
|
||
help_group = QGroupBox("Help")
|
||
help_layout = QVBoxLayout(help_group)
|
||
|
||
help_text = QLabel(
|
||
"Format examples:\n"
|
||
" F12, Ctrl+F12, Shift+F12, Alt+F12\n"
|
||
" Ctrl+Shift+S, Alt+Tab (don't use system shortcuts)\n\n"
|
||
"Note: Global hotkeys require the 'keyboard' library and may need admin privileges.\n"
|
||
"Qt shortcuts (app focused only) work without additional libraries."
|
||
)
|
||
help_text.setStyleSheet("color: #888; font-family: monospace;")
|
||
help_layout.addWidget(help_text)
|
||
|
||
layout.addWidget(help_group)
|
||
layout.addStretch()
|
||
|
||
return tab
|
||
|
||
def _create_vision_tab(self) -> QWidget:
|
||
"""Create Computer Vision tab."""
|
||
tab = QWidget()
|
||
layout = QVBoxLayout(tab)
|
||
layout.setSpacing(15)
|
||
|
||
# Info header
|
||
info = QLabel("👁️ Computer Vision settings for automatic loot detection and OCR.")
|
||
info.setStyleSheet("color: #888; padding: 5px;")
|
||
info.setWordWrap(True)
|
||
layout.addWidget(info)
|
||
|
||
# OCR Backend Selection
|
||
backend_group = QGroupBox("OCR Backend")
|
||
backend_layout = QFormLayout(backend_group)
|
||
|
||
self.cv_backend_combo = QComboBox()
|
||
backends = [
|
||
("auto", "🤖 Auto-detect (recommended)"),
|
||
("opencv", "⚡ OpenCV EAST (fastest, no extra dependencies)"),
|
||
("easyocr", "📖 EasyOCR (good accuracy, lighter than Paddle)"),
|
||
("tesseract", "🔍 Tesseract (traditional, stable)"),
|
||
("paddle", "🧠 PaddleOCR (best accuracy, requires PyTorch)")
|
||
]
|
||
|
||
for value, display in backends:
|
||
self.cv_backend_combo.addItem(display, value)
|
||
if value == self._cv_backend:
|
||
self.cv_backend_combo.setCurrentIndex(self.cv_backend_combo.count() - 1)
|
||
|
||
self.cv_backend_combo.currentIndexChanged.connect(self._on_backend_changed)
|
||
backend_layout.addRow("OCR Backend:", self.cv_backend_combo)
|
||
|
||
# Backend status
|
||
self.backend_status = QLabel()
|
||
self._update_backend_status()
|
||
backend_layout.addRow(self.backend_status)
|
||
|
||
layout.addWidget(backend_group)
|
||
|
||
# GPU Settings
|
||
gpu_group = QGroupBox("GPU Acceleration")
|
||
gpu_layout = QFormLayout(gpu_group)
|
||
|
||
self.cv_use_gpu_check = QCheckBox("Use GPU acceleration if available")
|
||
self.cv_use_gpu_check.setChecked(self._cv_use_gpu)
|
||
self.cv_use_gpu_check.setToolTip("Faster processing but requires compatible GPU")
|
||
gpu_layout.addRow(self.cv_use_gpu_check)
|
||
|
||
# GPU Info
|
||
self.gpu_info = QLabel()
|
||
self._update_gpu_info()
|
||
gpu_layout.addRow(self.gpu_info)
|
||
|
||
layout.addWidget(gpu_group)
|
||
|
||
# Detection Settings
|
||
detection_group = QGroupBox("Detection Settings")
|
||
detection_layout = QFormLayout(detection_group)
|
||
|
||
self.cv_confidence_spin = QDoubleSpinBox()
|
||
self.cv_confidence_spin.setRange(0.1, 1.0)
|
||
self.cv_confidence_spin.setSingleStep(0.05)
|
||
self.cv_confidence_spin.setValue(self._cv_confidence)
|
||
self.cv_confidence_spin.setDecimals(2)
|
||
detection_layout.addRow("Confidence Threshold:", self.cv_confidence_spin)
|
||
|
||
confidence_help = QLabel("Lower = more sensitive (may detect non-text)\nHigher = stricter (may miss some text)")
|
||
confidence_help.setStyleSheet("color: #888; font-size: 11px;")
|
||
detection_layout.addRow(confidence_help)
|
||
|
||
layout.addWidget(detection_group)
|
||
|
||
# Test buttons
|
||
test_group = QGroupBox("Test Computer Vision")
|
||
test_layout = QHBoxLayout(test_group)
|
||
|
||
test_ocr_btn = QPushButton("📝 Test OCR")
|
||
test_ocr_btn.clicked.connect(self._test_ocr)
|
||
test_layout.addWidget(test_ocr_btn)
|
||
|
||
test_icon_btn = QPushButton("🎯 Test Icon Detection")
|
||
test_icon_btn.clicked.connect(self._test_icon_detection)
|
||
test_layout.addWidget(test_icon_btn)
|
||
|
||
calibrate_btn = QPushButton("📐 Calibrate")
|
||
calibrate_btn.clicked.connect(self._calibrate_vision)
|
||
test_layout.addWidget(calibrate_btn)
|
||
|
||
layout.addWidget(test_group)
|
||
layout.addStretch()
|
||
|
||
return tab
|
||
|
||
def _create_advanced_tab(self) -> QWidget:
|
||
"""Create Advanced settings tab."""
|
||
tab = QWidget()
|
||
layout = QVBoxLayout(tab)
|
||
layout.setSpacing(15)
|
||
|
||
# Performance
|
||
perf_group = QGroupBox("Performance")
|
||
perf_layout = QFormLayout(perf_group)
|
||
|
||
self.fps_limit_spin = QSpinBox()
|
||
self.fps_limit_spin.setRange(1, 144)
|
||
self.fps_limit_spin.setValue(60)
|
||
self.fps_limit_spin.setSuffix(" FPS")
|
||
perf_layout.addRow("Target FPS:", self.fps_limit_spin)
|
||
|
||
layout.addWidget(perf_group)
|
||
|
||
# Database
|
||
db_group = QGroupBox("Database")
|
||
db_layout = QVBoxLayout(db_group)
|
||
|
||
db_info = QLabel(f"Database location:\n{self.db.db_path if self.db else 'Not connected'}")
|
||
db_info.setStyleSheet("color: #888; font-family: monospace; font-size: 11px;")
|
||
db_info.setWordWrap(True)
|
||
db_layout.addWidget(db_info)
|
||
|
||
db_buttons = QHBoxLayout()
|
||
|
||
backup_btn = QPushButton("💾 Backup Database")
|
||
backup_btn.clicked.connect(self._backup_database)
|
||
db_buttons.addWidget(backup_btn)
|
||
|
||
export_btn = QPushButton("📤 Export Data")
|
||
export_btn.clicked.connect(self._export_data)
|
||
db_buttons.addWidget(export_btn)
|
||
|
||
db_buttons.addStretch()
|
||
db_layout.addLayout(db_buttons)
|
||
|
||
layout.addWidget(db_group)
|
||
|
||
# Logging
|
||
log_group = QGroupBox("Logging")
|
||
log_layout = QFormLayout(log_group)
|
||
|
||
self.log_level_combo = QComboBox()
|
||
log_levels = ["DEBUG", "INFO", "WARNING", "ERROR"]
|
||
for level in log_levels:
|
||
self.log_level_combo.addItem(level)
|
||
self.log_level_combo.setCurrentText("INFO")
|
||
log_layout.addRow("Log Level:", self.log_level_combo)
|
||
|
||
layout.addWidget(log_group)
|
||
|
||
layout.addStretch()
|
||
return tab
|
||
|
||
def _on_backend_changed(self):
|
||
"""Handle OCR backend selection change."""
|
||
self._update_backend_status()
|
||
|
||
def _update_backend_status(self):
|
||
"""Update backend status label."""
|
||
backend = self.cv_backend_combo.currentData()
|
||
|
||
status_text = ""
|
||
if backend == "auto":
|
||
status_text = "Will try: OpenCV → EasyOCR → Tesseract → PaddleOCR"
|
||
elif backend == "opencv":
|
||
status_text = "✅ Always available - uses OpenCV DNN (EAST model)"
|
||
elif backend == "easyocr":
|
||
try:
|
||
import easyocr
|
||
status_text = "✅ EasyOCR installed and ready"
|
||
except ImportError:
|
||
status_text = "❌ EasyOCR not installed: pip install easyocr"
|
||
elif backend == "tesseract":
|
||
try:
|
||
import pytesseract
|
||
status_text = "✅ Tesseract Python module installed"
|
||
except ImportError:
|
||
status_text = "❌ pytesseract not installed: pip install pytesseract"
|
||
elif backend == "paddle":
|
||
try:
|
||
from paddleocr import PaddleOCR
|
||
status_text = "✅ PaddleOCR installed"
|
||
except ImportError:
|
||
status_text = "❌ PaddleOCR not installed: pip install paddlepaddle paddleocr"
|
||
|
||
self.backend_status.setText(status_text)
|
||
self.backend_status.setStyleSheet(
|
||
"color: #4caf50;" if status_text.startswith("✅") else
|
||
"color: #f44336;" if status_text.startswith("❌") else "color: #888;"
|
||
)
|
||
|
||
def _update_gpu_info(self):
|
||
"""Update GPU info label."""
|
||
info_parts = []
|
||
|
||
# Check CUDA
|
||
try:
|
||
import cv2
|
||
if cv2.cuda.getCudaEnabledDeviceCount() > 0:
|
||
info_parts.append("✅ OpenCV CUDA")
|
||
else:
|
||
info_parts.append("❌ OpenCV CUDA")
|
||
except:
|
||
info_parts.append("❌ OpenCV CUDA")
|
||
|
||
# Check PyTorch CUDA
|
||
try:
|
||
import torch
|
||
if torch.cuda.is_available():
|
||
info_parts.append(f"✅ PyTorch CUDA ({torch.cuda.get_device_name(0)})")
|
||
else:
|
||
info_parts.append("❌ PyTorch CUDA")
|
||
except:
|
||
info_parts.append("❌ PyTorch CUDA")
|
||
|
||
self.gpu_info.setText(" | ".join(info_parts))
|
||
|
||
def _browse_log_path(self):
|
||
"""Browse for log file."""
|
||
path, _ = QFileDialog.getOpenFileName(
|
||
self,
|
||
"Select Entropia Universe chat.log",
|
||
"",
|
||
"Log Files (*.log);;All Files (*)"
|
||
)
|
||
if path:
|
||
self.log_path_edit.setText(path)
|
||
|
||
def _set_default_log_path(self):
|
||
"""Set default log path."""
|
||
default_path = Path.home() / "Documents" / "Entropia Universe" / "chat.log"
|
||
self.log_path_edit.setText(str(default_path))
|
||
|
||
def _test_hotkey(self, hotkey_type: str):
|
||
"""Test a screenshot hotkey."""
|
||
try:
|
||
from modules.auto_screenshot import AutoScreenshot
|
||
screenshots_dir = Path(__file__).parent.parent / "data" / "screenshots"
|
||
ss = AutoScreenshot(screenshots_dir)
|
||
|
||
filename = f"test_{hotkey_type}_{datetime.now():%Y%m%d_%H%M%S}.png"
|
||
|
||
if hotkey_type == "full":
|
||
filepath = ss.capture_full_screen(filename)
|
||
elif hotkey_type == "region":
|
||
import mss
|
||
with mss.mss() as sct:
|
||
monitor = sct.monitors[1]
|
||
x = (monitor['width'] - 800) // 2
|
||
y = (monitor['height'] - 600) // 2
|
||
filepath = ss.capture_region(x, y, 800, 600, filename)
|
||
elif hotkey_type == "loot":
|
||
import mss
|
||
with mss.mss() as sct:
|
||
monitor = sct.monitors[1]
|
||
x = monitor['width'] - 350
|
||
y = monitor['height'] // 2 - 200
|
||
filepath = ss.capture_region(x, y, 300, 400, filename)
|
||
elif hotkey_type == "hud":
|
||
import mss
|
||
with mss.mss() as sct:
|
||
monitor = sct.monitors[1]
|
||
w, h = 600, 150
|
||
x = (monitor['width'] - w) // 2
|
||
y = monitor['height'] - h - 50
|
||
filepath = ss.capture_region(x, y, w, h, filename)
|
||
else:
|
||
filepath = None
|
||
|
||
if filepath:
|
||
QMessageBox.information(self, "Screenshot Taken", f"Saved to:\n{filepath}")
|
||
else:
|
||
QMessageBox.warning(self, "Error", "Failed to capture screenshot")
|
||
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "Error", f"Screenshot failed:\n{e}")
|
||
|
||
def _test_ocr(self):
|
||
"""Test OCR functionality."""
|
||
QMessageBox.information(self, "OCR Test", "OCR test will be implemented in the Vision Test dialog.")
|
||
# TODO: Open vision test dialog
|
||
|
||
def _test_icon_detection(self):
|
||
"""Test icon detection."""
|
||
QMessageBox.information(self, "Icon Detection", "Icon detection test will be implemented in the Vision Test dialog.")
|
||
# TODO: Open vision test dialog
|
||
|
||
def _calibrate_vision(self):
|
||
"""Open vision calibration."""
|
||
QMessageBox.information(self, "Calibration", "Vision calibration will be implemented in the Calibration dialog.")
|
||
# TODO: Open calibration dialog
|
||
|
||
def _backup_database(self):
|
||
"""Backup the database."""
|
||
if not self.db:
|
||
QMessageBox.warning(self, "Error", "Database not connected")
|
||
return
|
||
|
||
try:
|
||
import shutil
|
||
from datetime import datetime
|
||
|
||
backup_path = self.db.db_path.parent / f"lemontropia_backup_{datetime.now():%Y%m%d_%H%M%S}.db"
|
||
shutil.copy2(self.db.db_path, backup_path)
|
||
|
||
QMessageBox.information(self, "Backup Complete", f"Database backed up to:\n{backup_path}")
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "Backup Failed", str(e))
|
||
|
||
def _export_data(self):
|
||
"""Export data to CSV/JSON."""
|
||
QMessageBox.information(self, "Export", "Export functionality coming soon!")
|
||
|
||
def _on_save(self):
|
||
"""Save all settings."""
|
||
try:
|
||
# General
|
||
self._settings.setValue("player/name", self.player_name_edit.text().strip())
|
||
self._settings.setValue("log/path", self.log_path_edit.text().strip())
|
||
self._settings.setValue("log/auto_detect", self.auto_detect_check.isChecked())
|
||
self._settings.setValue("activity/default", self.default_activity_combo.currentData())
|
||
|
||
# Hotkeys
|
||
self._settings.setValue("hotkey/screenshot_full", self.hotkey_full_edit.text().strip())
|
||
self._settings.setValue("hotkey/screenshot_region", self.hotkey_region_edit.text().strip())
|
||
self._settings.setValue("hotkey/screenshot_loot", self.hotkey_loot_edit.text().strip())
|
||
self._settings.setValue("hotkey/screenshot_hud", self.hotkey_hud_edit.text().strip())
|
||
|
||
# Computer Vision
|
||
self._settings.setValue("cv/backend", self.cv_backend_combo.currentData())
|
||
self._settings.setValue("cv/use_gpu", self.cv_use_gpu_check.isChecked())
|
||
self._settings.setValue("cv/confidence", self.cv_confidence_spin.value())
|
||
|
||
# Advanced
|
||
self._settings.setValue("performance/fps_limit", self.fps_limit_spin.value())
|
||
self._settings.setValue("logging/level", self.log_level_combo.currentText())
|
||
|
||
self._settings.sync()
|
||
|
||
QMessageBox.information(self, "Settings Saved", "All settings have been saved successfully!")
|
||
self.accept()
|
||
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "Error", f"Failed to save settings:\n{e}")
|
||
|
||
def _on_reset(self):
|
||
"""Reset settings to defaults."""
|
||
reply = QMessageBox.question(
|
||
self,
|
||
"Reset Settings",
|
||
"Are you sure you want to reset all settings to defaults?",
|
||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
||
)
|
||
|
||
if reply == QMessageBox.StandardButton.Yes:
|
||
# Clear all settings
|
||
self._settings.clear()
|
||
self._settings.sync()
|
||
|
||
QMessageBox.information(self, "Settings Reset", "Settings have been reset. Please restart the application.")
|
||
self.reject()
|
||
|
||
def _apply_dark_theme(self):
|
||
"""Apply dark theme to the dialog."""
|
||
self.setStyleSheet("""
|
||
QDialog {
|
||
background-color: #1e1e1e;
|
||
color: #e0e0e0;
|
||
}
|
||
QTabWidget::pane {
|
||
background-color: #252525;
|
||
border: 1px solid #444;
|
||
border-radius: 4px;
|
||
}
|
||
QTabBar::tab {
|
||
background-color: #2d2d2d;
|
||
padding: 8px 16px;
|
||
border: 1px solid #444;
|
||
border-bottom: none;
|
||
border-top-left-radius: 4px;
|
||
border-top-right-radius: 4px;
|
||
}
|
||
QTabBar::tab:selected {
|
||
background-color: #0d47a1;
|
||
}
|
||
QGroupBox {
|
||
font-weight: bold;
|
||
border: 1px solid #444;
|
||
border-radius: 6px;
|
||
margin-top: 10px;
|
||
padding-top: 10px;
|
||
}
|
||
QGroupBox::title {
|
||
subcontrol-origin: margin;
|
||
left: 10px;
|
||
padding: 0 5px;
|
||
}
|
||
QLineEdit, QComboBox, QSpinBox, QDoubleSpinBox {
|
||
background-color: #252525;
|
||
border: 1px solid #444;
|
||
border-radius: 4px;
|
||
padding: 6px;
|
||
color: #e0e0e0;
|
||
}
|
||
QPushButton {
|
||
background-color: #0d47a1;
|
||
border: 1px solid #1565c0;
|
||
border-radius: 4px;
|
||
padding: 6px 12px;
|
||
color: white;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #1565c0;
|
||
}
|
||
QLabel {
|
||
color: #e0e0e0;
|
||
}
|
||
""") |