736 lines
28 KiB
Python
736 lines
28 KiB
Python
"""
|
|
EU-Utility - Auto-Screenshot Plugin
|
|
|
|
Automatically capture screenshots on Global, HOF, or other
|
|
significant game events. Perfect for sharing achievements!
|
|
"""
|
|
|
|
import os
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional, Any
|
|
from dataclasses import dataclass, field
|
|
|
|
from PyQt6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
|
QCheckBox, QSpinBox, QGroupBox, QFileDialog, QLineEdit,
|
|
QComboBox, QListWidget, QListWidgetItem, QTabWidget
|
|
)
|
|
from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QObject
|
|
from PyQt6.QtGui import QColor
|
|
|
|
from plugins.base_plugin import BasePlugin
|
|
from core.event_bus import GlobalEvent, LootEvent, SkillGainEvent
|
|
|
|
|
|
@dataclass
|
|
class ScreenshotRule:
|
|
"""Rule for when to take a screenshot."""
|
|
event_type: str # 'global', 'hof', 'ath', 'skill', 'loot_value', 'custom'
|
|
min_value: float = 0.0 # For value-based triggers (PED, skill gain, etc.)
|
|
enabled: bool = True
|
|
play_sound: bool = True
|
|
show_notification: bool = True
|
|
custom_pattern: str = "" # For custom log patterns
|
|
|
|
|
|
class AutoScreenshotSignals(QObject):
|
|
"""Signals for thread-safe UI updates."""
|
|
screenshot_taken = pyqtSignal(str) # Path to screenshot
|
|
event_detected = pyqtSignal(str) # Event description
|
|
|
|
|
|
class AutoScreenshotPlugin(BasePlugin):
|
|
"""
|
|
Plugin for automatic screenshots on game events.
|
|
|
|
Features:
|
|
- Screenshot on Global/HOF/ATH
|
|
- Screenshot on rare loot
|
|
- Screenshot on significant skill gains
|
|
- Configurable capture delay
|
|
- Custom filename patterns
|
|
- Notification and sound options
|
|
"""
|
|
|
|
name = "Auto-Screenshot"
|
|
version = "1.0.0"
|
|
author = "EU-Utility"
|
|
description = "Capture screenshots on Global/HOF and other events"
|
|
icon = "📸"
|
|
hotkey = "ctrl+shift+c"
|
|
|
|
def __init__(self, overlay_window, config):
|
|
super().__init__(overlay_window, config)
|
|
|
|
# Settings
|
|
self.enabled = self.get_config('enabled', True)
|
|
self.capture_delay_ms = self.get_config('capture_delay_ms', 500)
|
|
self.save_directory = self.get_config('save_directory',
|
|
str(Path.home() / "Documents" / "Entropia Universe" / "Screenshots" / "Globals"))
|
|
|
|
# Filename pattern
|
|
self.filename_pattern = self.get_config('filename_pattern',
|
|
"{event_type}_{timestamp}_{player}")
|
|
|
|
# Screenshot rules
|
|
self.rules: Dict[str, ScreenshotRule] = {}
|
|
self._load_rules()
|
|
|
|
# Statistics
|
|
self.screenshots_taken = 0
|
|
self.events_captured: List[Dict] = []
|
|
|
|
# UI references
|
|
self._ui = None
|
|
self.events_list = None
|
|
self.status_label = None
|
|
self.dir_label = None
|
|
|
|
# Signals
|
|
self.signals = AutoScreenshotSignals()
|
|
self.signals.screenshot_taken.connect(self._on_screenshot_saved)
|
|
self.signals.event_detected.connect(self._on_event_logged)
|
|
|
|
# Event subscriptions
|
|
self._subscriptions: List[str] = []
|
|
|
|
# Pending screenshot timer
|
|
self._pending_screenshot = None
|
|
self._pending_timer = None
|
|
|
|
def initialize(self) -> None:
|
|
"""Initialize plugin."""
|
|
self.log_info("Initializing Auto-Screenshot")
|
|
|
|
# Ensure save directory exists
|
|
Path(self.save_directory).mkdir(parents=True, exist_ok=True)
|
|
|
|
# Subscribe to events
|
|
if self.enabled:
|
|
self._subscribe_to_events()
|
|
|
|
self.log_info(f"Auto-Screenshot initialized (enabled={self.enabled})")
|
|
|
|
def _subscribe_to_events(self) -> None:
|
|
"""Subscribe to game events."""
|
|
# Global/HOF events
|
|
self._subscriptions.append(
|
|
self.subscribe_typed(GlobalEvent, self._on_global_event)
|
|
)
|
|
|
|
# Loot events (for high-value loot)
|
|
self._subscriptions.append(
|
|
self.subscribe_typed(LootEvent, self._on_loot_event)
|
|
)
|
|
|
|
# Skill gain events
|
|
self._subscriptions.append(
|
|
self.subscribe_typed(SkillGainEvent, self._on_skill_event)
|
|
)
|
|
|
|
def get_ui(self) -> QWidget:
|
|
"""Return the plugin's UI widget."""
|
|
if self._ui is None:
|
|
self._ui = self._create_ui()
|
|
return self._ui
|
|
|
|
def _create_ui(self) -> QWidget:
|
|
"""Create the plugin UI."""
|
|
widget = QWidget()
|
|
layout = QVBoxLayout(widget)
|
|
layout.setSpacing(12)
|
|
layout.setContentsMargins(16, 16, 16, 16)
|
|
|
|
# Header
|
|
header = QLabel("📸 Auto-Screenshot")
|
|
header.setStyleSheet("""
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: white;
|
|
padding-bottom: 8px;
|
|
""")
|
|
layout.addWidget(header)
|
|
|
|
# Status
|
|
self.status_label = QLabel(f"Status: {'Enabled' if self.enabled else 'Disabled'} | Captured: {self.screenshots_taken}")
|
|
self.status_label.setStyleSheet("color: rgba(255, 255, 255, 150);")
|
|
layout.addWidget(self.status_label)
|
|
|
|
# Create tabs
|
|
tabs = QTabWidget()
|
|
tabs.setStyleSheet("""
|
|
QTabWidget::pane {
|
|
background-color: rgba(30, 35, 45, 150);
|
|
border: 1px solid rgba(100, 150, 200, 50);
|
|
border-radius: 8px;
|
|
}
|
|
QTabBar::tab {
|
|
background-color: rgba(50, 60, 75, 200);
|
|
color: white;
|
|
padding: 8px 16px;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
}
|
|
QTabBar::tab:selected {
|
|
background-color: rgba(100, 150, 200, 200);
|
|
}
|
|
""")
|
|
|
|
# === Rules Tab ===
|
|
rules_widget = QWidget()
|
|
rules_layout = QVBoxLayout(rules_widget)
|
|
rules_layout.setContentsMargins(12, 12, 12, 12)
|
|
|
|
rules_header = QLabel("Capture Rules")
|
|
rules_header.setStyleSheet("font-weight: bold; color: white;")
|
|
rules_layout.addWidget(rules_header)
|
|
|
|
# Global checkbox
|
|
self.global_checkbox = QCheckBox("Capture on Global (any value)")
|
|
self.global_checkbox.setChecked(self.rules.get('global', ScreenshotRule('global')).enabled)
|
|
self.global_checkbox.setStyleSheet("color: white;")
|
|
self.global_checkbox.stateChanged.connect(lambda: self._update_rule('global', self.global_checkbox.isChecked()))
|
|
rules_layout.addWidget(self.global_checkbox)
|
|
|
|
# HOF checkbox
|
|
self.hof_checkbox = QCheckBox("Capture on HOF (50+ PED)")
|
|
self.hof_checkbox.setChecked(self.rules.get('hof', ScreenshotRule('hof', min_value=50)).enabled)
|
|
self.hof_checkbox.setStyleSheet("color: white;")
|
|
self.hof_checkbox.stateChanged.connect(lambda: self._update_rule('hof', self.hof_checkbox.isChecked()))
|
|
rules_layout.addWidget(self.hof_checkbox)
|
|
|
|
# ATH checkbox
|
|
self.ath_checkbox = QCheckBox("Capture on ATH (All-Time High)")
|
|
self.ath_checkbox.setChecked(self.rules.get('ath', ScreenshotRule('ath')).enabled)
|
|
self.ath_checkbox.setStyleSheet("color: white;")
|
|
self.ath_checkbox.stateChanged.connect(lambda: self._update_rule('ath', self.ath_checkbox.isChecked()))
|
|
rules_layout.addWidget(self.ath_checkbox)
|
|
|
|
# Discovery checkbox
|
|
self.discovery_checkbox = QCheckBox("Capture on Discovery")
|
|
self.discovery_checkbox.setChecked(self.rules.get('discovery', ScreenshotRule('discovery')).enabled)
|
|
self.discovery_checkbox.setStyleSheet("color: white;")
|
|
self.discovery_checkbox.stateChanged.connect(lambda: self._update_rule('discovery', self.discovery_checkbox.isChecked()))
|
|
rules_layout.addWidget(self.discovery_checkbox)
|
|
|
|
# High value loot
|
|
loot_layout = QHBoxLayout()
|
|
self.loot_checkbox = QCheckBox("Capture on loot above")
|
|
self.loot_checkbox.setChecked(self.rules.get('loot_value', ScreenshotRule('loot_value', min_value=100)).enabled)
|
|
self.loot_checkbox.setStyleSheet("color: white;")
|
|
self.loot_checkbox.stateChanged.connect(lambda: self._update_rule('loot_value', self.loot_checkbox.isChecked()))
|
|
loot_layout.addWidget(self.loot_checkbox)
|
|
|
|
self.loot_spin = QSpinBox()
|
|
self.loot_spin.setRange(1, 10000)
|
|
self.loot_spin.setValue(int(self.rules.get('loot_value', ScreenshotRule('loot_value', min_value=100)).min_value))
|
|
self.loot_spin.setSuffix(" PED")
|
|
self.loot_spin.setStyleSheet(self._spinbox_style())
|
|
loot_layout.addWidget(self.loot_spin)
|
|
loot_layout.addStretch()
|
|
rules_layout.addLayout(loot_layout)
|
|
|
|
# Skill gain
|
|
skill_layout = QHBoxLayout()
|
|
self.skill_checkbox = QCheckBox("Capture on skill gain above")
|
|
self.skill_checkbox.setChecked(self.rules.get('skill', ScreenshotRule('skill', min_value=0.1)).enabled)
|
|
self.skill_checkbox.setStyleSheet("color: white;")
|
|
self.skill_checkbox.stateChanged.connect(lambda: self._update_rule('skill', self.skill_checkbox.isChecked()))
|
|
skill_layout.addWidget(self.skill_checkbox)
|
|
|
|
self.skill_spin = QSpinBox()
|
|
self.skill_spin.setRange(1, 1000)
|
|
self.skill_spin.setValue(int(self.rules.get('skill', ScreenshotRule('skill', min_value=0.1)).min_value * 100))
|
|
self.skill_spin.setSuffix(" points (x0.01)")
|
|
self.skill_spin.setStyleSheet(self._spinbox_style())
|
|
skill_layout.addWidget(self.skill_spin)
|
|
skill_layout.addStretch()
|
|
rules_layout.addLayout(skill_layout)
|
|
|
|
# Sound/notification options
|
|
options_group = QGroupBox("Notification Options")
|
|
options_layout = QVBoxLayout(options_group)
|
|
|
|
self.sound_checkbox = QCheckBox("Play sound on capture")
|
|
self.sound_checkbox.setChecked(self.get_config('play_sound', True))
|
|
self.sound_checkbox.setStyleSheet("color: white;")
|
|
options_layout.addWidget(self.sound_checkbox)
|
|
|
|
self.notification_checkbox = QCheckBox("Show notification on capture")
|
|
self.notification_checkbox.setChecked(self.get_config('show_notification', True))
|
|
self.notification_checkbox.setStyleSheet("color: white;")
|
|
options_layout.addWidget(self.notification_checkbox)
|
|
|
|
rules_layout.addWidget(options_group)
|
|
rules_layout.addStretch()
|
|
|
|
tabs.addTab(rules_widget, "📋 Rules")
|
|
|
|
# === History Tab ===
|
|
history_widget = QWidget()
|
|
history_layout = QVBoxLayout(history_widget)
|
|
history_layout.setContentsMargins(12, 12, 12, 12)
|
|
|
|
history_header = QLabel("Recent Captures")
|
|
history_header.setStyleSheet("font-weight: bold; color: white;")
|
|
history_layout.addWidget(history_header)
|
|
|
|
self.events_list = QListWidget()
|
|
self.events_list.setStyleSheet("""
|
|
QListWidget {
|
|
background-color: rgba(30, 35, 45, 150);
|
|
border: 1px solid rgba(100, 150, 200, 50);
|
|
border-radius: 8px;
|
|
color: white;
|
|
}
|
|
QListWidget::item {
|
|
padding: 8px;
|
|
border-bottom: 1px solid rgba(100, 150, 200, 30);
|
|
}
|
|
QListWidget::item:selected {
|
|
background-color: rgba(100, 150, 200, 100);
|
|
}
|
|
""")
|
|
history_layout.addWidget(self.events_list)
|
|
|
|
# History buttons
|
|
history_btn_layout = QHBoxLayout()
|
|
|
|
open_folder_btn = QPushButton("📁 Open Folder")
|
|
open_folder_btn.setStyleSheet(self._button_style("#2196f3"))
|
|
open_folder_btn.clicked.connect(self._open_screenshots_folder)
|
|
history_btn_layout.addWidget(open_folder_btn)
|
|
|
|
clear_history_btn = QPushButton("🧹 Clear History")
|
|
clear_history_btn.setStyleSheet(self._button_style())
|
|
clear_history_btn.clicked.connect(self._clear_history)
|
|
history_btn_layout.addWidget(clear_history_btn)
|
|
|
|
history_btn_layout.addStretch()
|
|
history_layout.addLayout(history_btn_layout)
|
|
|
|
tabs.addTab(history_widget, "📜 History")
|
|
|
|
# === Settings Tab ===
|
|
settings_widget = QWidget()
|
|
settings_layout = QVBoxLayout(settings_widget)
|
|
settings_layout.setContentsMargins(12, 12, 12, 12)
|
|
|
|
settings_header = QLabel("Capture Settings")
|
|
settings_header.setStyleSheet("font-weight: bold; color: white;")
|
|
settings_layout.addWidget(settings_header)
|
|
|
|
# Master enable
|
|
self.enable_checkbox = QCheckBox("Enable Auto-Screenshot")
|
|
self.enable_checkbox.setChecked(self.enabled)
|
|
self.enable_checkbox.setStyleSheet("color: #4caf50; font-weight: bold;")
|
|
self.enable_checkbox.stateChanged.connect(self._toggle_enabled)
|
|
settings_layout.addWidget(self.enable_checkbox)
|
|
|
|
# Capture delay
|
|
delay_layout = QHBoxLayout()
|
|
delay_label = QLabel("Capture Delay:")
|
|
delay_label.setStyleSheet("color: white;")
|
|
delay_layout.addWidget(delay_label)
|
|
|
|
self.delay_spin = QSpinBox()
|
|
self.delay_spin.setRange(0, 5000)
|
|
self.delay_spin.setValue(self.capture_delay_ms)
|
|
self.delay_spin.setSuffix(" ms")
|
|
self.delay_spin.setSingleStep(100)
|
|
self.delay_spin.setStyleSheet(self._spinbox_style())
|
|
delay_layout.addWidget(self.delay_spin)
|
|
|
|
delay_info = QLabel("(time to hide overlay)")
|
|
delay_info.setStyleSheet("color: rgba(255, 255, 255, 100); font-size: 11px;")
|
|
delay_layout.addWidget(delay_info)
|
|
delay_layout.addStretch()
|
|
settings_layout.addLayout(delay_layout)
|
|
|
|
# Save directory
|
|
dir_group = QGroupBox("Save Location")
|
|
dir_layout = QVBoxLayout(dir_group)
|
|
|
|
dir_row = QHBoxLayout()
|
|
self.dir_label = QLabel(self.save_directory)
|
|
self.dir_label.setStyleSheet("color: white;")
|
|
self.dir_label.setWordWrap(True)
|
|
dir_row.addWidget(self.dir_label, 1)
|
|
|
|
change_dir_btn = QPushButton("📁 Change")
|
|
change_dir_btn.setStyleSheet(self._button_style())
|
|
change_dir_btn.clicked.connect(self._change_save_directory)
|
|
dir_row.addWidget(change_dir_btn)
|
|
|
|
dir_layout.addLayout(dir_row)
|
|
settings_layout.addWidget(dir_group)
|
|
|
|
# Filename pattern
|
|
pattern_group = QGroupBox("Filename Pattern")
|
|
pattern_layout = QVBoxLayout(pattern_group)
|
|
|
|
pattern_info = QLabel("Available variables: {timestamp}, {event_type}, {player}, {value}")
|
|
pattern_info.setStyleSheet("color: rgba(255, 255, 255, 100); font-size: 11px;")
|
|
pattern_layout.addWidget(pattern_info)
|
|
|
|
self.pattern_input = QLineEdit(self.filename_pattern)
|
|
self.pattern_input.setStyleSheet(self._input_style())
|
|
pattern_layout.addWidget(self.pattern_input)
|
|
|
|
settings_layout.addWidget(pattern_group)
|
|
settings_layout.addStretch()
|
|
|
|
tabs.addTab(settings_widget, "⚙️ Settings")
|
|
|
|
layout.addWidget(tabs)
|
|
|
|
# Save button
|
|
save_btn = QPushButton("💾 Save Settings")
|
|
save_btn.setStyleSheet(self._button_style("#4caf50"))
|
|
save_btn.clicked.connect(self._save_settings)
|
|
layout.addWidget(save_btn)
|
|
|
|
# Test button
|
|
test_btn = QPushButton("📸 Test Screenshot")
|
|
test_btn.setStyleSheet(self._button_style("#ff9800"))
|
|
test_btn.clicked.connect(lambda: self._take_screenshot("test", "TestUser", 0))
|
|
layout.addWidget(test_btn)
|
|
|
|
return widget
|
|
|
|
def _button_style(self, color: str = "#607d8b") -> str:
|
|
"""Generate button stylesheet."""
|
|
return f"""
|
|
QPushButton {{
|
|
background-color: {color};
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 10px 16px;
|
|
font-weight: bold;
|
|
}}
|
|
QPushButton:hover {{
|
|
background-color: {color}dd;
|
|
}}
|
|
QPushButton:pressed {{
|
|
background-color: {color}aa;
|
|
}}
|
|
"""
|
|
|
|
def _input_style(self) -> str:
|
|
"""Generate input stylesheet."""
|
|
return """
|
|
QLineEdit {
|
|
background-color: rgba(50, 60, 75, 200);
|
|
color: white;
|
|
border: 1px solid rgba(100, 150, 200, 100);
|
|
border-radius: 8px;
|
|
padding: 8px 12px;
|
|
}
|
|
"""
|
|
|
|
def _spinbox_style(self) -> str:
|
|
"""Generate spinbox stylesheet."""
|
|
return """
|
|
QSpinBox {
|
|
background-color: rgba(50, 60, 75, 200);
|
|
color: white;
|
|
border: 1px solid rgba(100, 150, 200, 100);
|
|
border-radius: 4px;
|
|
padding: 4px;
|
|
}
|
|
"""
|
|
|
|
def _on_global_event(self, event: GlobalEvent) -> None:
|
|
"""Handle global/HOF events."""
|
|
if not self.enabled:
|
|
return
|
|
|
|
event_type = event.achievement_type.lower()
|
|
|
|
# Check if we should capture
|
|
if event_type == 'global' and self.rules.get('global', ScreenshotRule('global')).enabled:
|
|
self._schedule_screenshot('global', event.player_name, event.value)
|
|
|
|
elif event_type in ['hof', 'hall of fame'] and self.rules.get('hof', ScreenshotRule('hof')).enabled:
|
|
if event.value >= self.rules.get('hof', ScreenshotRule('hof', min_value=50)).min_value:
|
|
self._schedule_screenshot('hof', event.player_name, event.value)
|
|
|
|
elif event_type in ['ath', 'all time high'] and self.rules.get('ath', ScreenshotRule('ath')).enabled:
|
|
self._schedule_screenshot('ath', event.player_name, event.value)
|
|
|
|
elif event_type == 'discovery' and self.rules.get('discovery', ScreenshotRule('discovery')).enabled:
|
|
self._schedule_screenshot('discovery', event.player_name, event.value)
|
|
|
|
def _on_loot_event(self, event: LootEvent) -> None:
|
|
"""Handle loot events."""
|
|
if not self.enabled:
|
|
return
|
|
|
|
rule = self.rules.get('loot_value', ScreenshotRule('loot_value', min_value=100))
|
|
if rule.enabled and event.total_tt_value >= rule.min_value:
|
|
self._schedule_screenshot('loot', 'player', event.total_tt_value)
|
|
|
|
def _on_skill_event(self, event: SkillGainEvent) -> None:
|
|
"""Handle skill gain events."""
|
|
if not self.enabled:
|
|
return
|
|
|
|
rule = self.rules.get('skill', ScreenshotRule('skill', min_value=0.1))
|
|
if rule.enabled and event.gain_amount >= rule.min_value:
|
|
self._schedule_screenshot('skill', 'player', event.gain_amount)
|
|
|
|
def _schedule_screenshot(self, event_type: str, player_name: str, value: float) -> None:
|
|
"""Schedule a screenshot with optional delay."""
|
|
if self._pending_timer:
|
|
self._pending_timer.stop()
|
|
|
|
self._pending_screenshot = (event_type, player_name, value)
|
|
|
|
if self.capture_delay_ms > 0:
|
|
self._pending_timer = QTimer()
|
|
self._pending_timer.timeout.connect(lambda: self._execute_screenshot())
|
|
self._pending_timer.setSingleShot(True)
|
|
self._pending_timer.start(self.capture_delay_ms)
|
|
else:
|
|
self._execute_screenshot()
|
|
|
|
def _execute_screenshot(self) -> None:
|
|
"""Execute the pending screenshot."""
|
|
if not self._pending_screenshot:
|
|
return
|
|
|
|
event_type, player_name, value = self._pending_screenshot
|
|
self._pending_screenshot = None
|
|
|
|
self._take_screenshot(event_type, player_name, value)
|
|
|
|
def _take_screenshot(self, event_type: str, player_name: str, value: float) -> None:
|
|
"""Take and save a screenshot."""
|
|
try:
|
|
# Generate filename
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
filename = self.filename_pattern.format(
|
|
timestamp=timestamp,
|
|
event_type=event_type.upper(),
|
|
player=player_name,
|
|
value=f"{value:.0f}"
|
|
)
|
|
filename = f"{filename}.png"
|
|
|
|
# Clean filename
|
|
filename = "".join(c for c in filename if c.isalnum() or c in "._- ")
|
|
|
|
filepath = Path(self.save_directory) / filename
|
|
|
|
# Capture screenshot
|
|
screenshot = self.capture_screen(full_screen=True)
|
|
|
|
# Save
|
|
screenshot.save(str(filepath), 'PNG')
|
|
|
|
self.screenshots_taken += 1
|
|
|
|
# Log event
|
|
event_data = {
|
|
'timestamp': datetime.now().isoformat(),
|
|
'event_type': event_type,
|
|
'player': player_name,
|
|
'value': value,
|
|
'filepath': str(filepath)
|
|
}
|
|
self.events_captured.append(event_data)
|
|
|
|
# Trim history
|
|
if len(self.events_captured) > 100:
|
|
self.events_captured = self.events_captured[-100:]
|
|
|
|
# Emit signals
|
|
self.signals.screenshot_taken.emit(str(filepath))
|
|
self.signals.event_detected.emit(f"{event_type.upper()}: {player_name} - {value:.0f} PED")
|
|
|
|
# Play sound if enabled
|
|
if self.sound_checkbox.isChecked():
|
|
self.play_sound('global' if event_type in ['global', 'hof', 'ath'] else 'skill_gain')
|
|
|
|
# Show notification if enabled
|
|
if self.notification_checkbox.isChecked():
|
|
self.notify(
|
|
f"📸 {event_type.upper()} Captured!",
|
|
f"Saved to {filename}",
|
|
notification_type='success'
|
|
)
|
|
|
|
self.log_info(f"Screenshot saved: {filepath}")
|
|
|
|
except Exception as e:
|
|
self.log_error(f"Screenshot failed: {e}")
|
|
self.notify_error("Screenshot Failed", str(e))
|
|
|
|
def _on_screenshot_saved(self, filepath: str) -> None:
|
|
"""Handle screenshot saved event."""
|
|
self.status_label.setText(f"Status: {'Enabled' if self.enabled else 'Disabled'} | Captured: {self.screenshots_taken}")
|
|
|
|
def _on_event_logged(self, description: str) -> None:
|
|
"""Handle event logged."""
|
|
if self.events_list:
|
|
item = QListWidgetItem(f"[{datetime.now().strftime('%H:%M:%S')}] {description}")
|
|
item.setForeground(QColor("white"))
|
|
self.events_list.insertItem(0, item)
|
|
|
|
# Trim list
|
|
while self.events_list.count() > 50:
|
|
self.events_list.takeItem(self.events_list.count() - 1)
|
|
|
|
def _update_rule(self, rule_name: str, enabled: bool) -> None:
|
|
"""Update a rule's enabled state."""
|
|
if rule_name not in self.rules:
|
|
# Create default rule
|
|
defaults = {
|
|
'global': ScreenshotRule('global'),
|
|
'hof': ScreenshotRule('hof', min_value=50),
|
|
'ath': ScreenshotRule('ath'),
|
|
'discovery': ScreenshotRule('discovery'),
|
|
'loot_value': ScreenshotRule('loot_value', min_value=100),
|
|
'skill': ScreenshotRule('skill', min_value=0.1)
|
|
}
|
|
self.rules[rule_name] = defaults.get(rule_name, ScreenshotRule(rule_name))
|
|
|
|
self.rules[rule_name].enabled = enabled
|
|
self._save_rules()
|
|
|
|
def _toggle_enabled(self, state) -> None:
|
|
"""Toggle plugin enabled state."""
|
|
self.enabled = state == Qt.CheckState.Checked.value
|
|
|
|
if self.enabled:
|
|
self._subscribe_to_events()
|
|
self.status_label.setText(f"Status: Enabled | Captured: {self.screenshots_taken}")
|
|
self.status_label.setStyleSheet("color: #4caf50;")
|
|
else:
|
|
# Unsubscribe
|
|
for sub_id in self._subscriptions:
|
|
self.unsubscribe_typed(sub_id)
|
|
self._subscriptions.clear()
|
|
self.status_label.setText(f"Status: Disabled | Captured: {self.screenshots_taken}")
|
|
self.status_label.setStyleSheet("color: #f44336;")
|
|
|
|
def _change_save_directory(self) -> None:
|
|
"""Change the save directory."""
|
|
new_dir = QFileDialog.getExistingDirectory(
|
|
self._ui,
|
|
"Select Screenshot Directory",
|
|
self.save_directory
|
|
)
|
|
|
|
if new_dir:
|
|
self.save_directory = new_dir
|
|
Path(self.save_directory).mkdir(parents=True, exist_ok=True)
|
|
if self.dir_label:
|
|
self.dir_label.setText(new_dir)
|
|
|
|
def _open_screenshots_folder(self) -> None:
|
|
"""Open the screenshots folder."""
|
|
import subprocess
|
|
import platform
|
|
|
|
try:
|
|
if platform.system() == 'Windows':
|
|
subprocess.run(['explorer', self.save_directory], check=True)
|
|
elif platform.system() == 'Darwin':
|
|
subprocess.run(['open', self.save_directory], check=True)
|
|
else:
|
|
subprocess.run(['xdg-open', self.save_directory], check=True)
|
|
except Exception as e:
|
|
self.log_error(f"Failed to open folder: {e}")
|
|
|
|
def _clear_history(self) -> None:
|
|
"""Clear the events history."""
|
|
self.events_captured.clear()
|
|
if self.events_list:
|
|
self.events_list.clear()
|
|
|
|
def _save_settings(self) -> None:
|
|
"""Save all settings."""
|
|
self.capture_delay_ms = self.delay_spin.value()
|
|
self.filename_pattern = self.pattern_input.text()
|
|
|
|
# Update rules with values
|
|
if 'loot_value' in self.rules:
|
|
self.rules['loot_value'].min_value = self.loot_spin.value()
|
|
if 'skill' in self.rules:
|
|
self.rules['skill'].min_value = self.skill_spin.value() / 100
|
|
|
|
self.set_config('enabled', self.enabled)
|
|
self.set_config('capture_delay_ms', self.capture_delay_ms)
|
|
self.set_config('save_directory', self.save_directory)
|
|
self.set_config('filename_pattern', self.filename_pattern)
|
|
self.set_config('play_sound', self.sound_checkbox.isChecked())
|
|
self.set_config('show_notification', self.notification_checkbox.isChecked())
|
|
|
|
self._save_rules()
|
|
|
|
self.notify_success("Settings Saved", "Auto-screenshot settings updated")
|
|
|
|
def _save_rules(self) -> None:
|
|
"""Save rules to storage."""
|
|
rules_data = {
|
|
name: {
|
|
'event_type': rule.event_type,
|
|
'min_value': rule.min_value,
|
|
'enabled': rule.enabled,
|
|
'play_sound': rule.play_sound,
|
|
'show_notification': rule.show_notification
|
|
}
|
|
for name, rule in self.rules.items()
|
|
}
|
|
self.save_data('rules', rules_data)
|
|
self.save_data('events_captured', self.events_captured)
|
|
|
|
def _load_rules(self) -> None:
|
|
"""Load rules from storage."""
|
|
rules_data = self.load_data('rules', {})
|
|
for name, data in rules_data.items():
|
|
self.rules[name] = ScreenshotRule(
|
|
event_type=data['event_type'],
|
|
min_value=data.get('min_value', 0),
|
|
enabled=data.get('enabled', True),
|
|
play_sound=data.get('play_sound', True),
|
|
show_notification=data.get('show_notification', True)
|
|
)
|
|
|
|
# Set defaults if not loaded
|
|
defaults = {
|
|
'global': ScreenshotRule('global'),
|
|
'hof': ScreenshotRule('hof', min_value=50),
|
|
'ath': ScreenshotRule('ath'),
|
|
'discovery': ScreenshotRule('discovery'),
|
|
'loot_value': ScreenshotRule('loot_value', min_value=100),
|
|
'skill': ScreenshotRule('skill', min_value=0.1)
|
|
}
|
|
|
|
for name, rule in defaults.items():
|
|
if name not in self.rules:
|
|
self.rules[name] = rule
|
|
|
|
self.events_captured = self.load_data('events_captured', [])
|
|
self.screenshots_taken = len(self.events_captured)
|
|
|
|
def on_hotkey(self) -> None:
|
|
"""Handle hotkey press - take manual screenshot."""
|
|
self._take_screenshot('manual', 'player', 0)
|
|
|
|
def shutdown(self) -> None:
|
|
"""Cleanup on shutdown."""
|
|
self._save_settings()
|
|
|
|
if self._pending_timer:
|
|
self._pending_timer.stop()
|
|
|
|
# Unsubscribe
|
|
for sub_id in self._subscriptions:
|
|
self.unsubscribe_typed(sub_id)
|
|
|
|
super().shutdown()
|