feat: add screenshot hotkey manager for manual game capture
- Add modules/screenshot_hotkey.py with global hotkey listener using pynput - Default hotkeys: F12=Full, Shift+F12=Region, Ctrl+F12=Loot, Alt+F12=HUD - Integrate into main_window.py with Settings dialog - Hotkeys work globally (even when game is focused) - Add Tools → Screenshot Hotkeys menu (Ctrl+Shift+S) - Shows status message when screenshot is captured
This commit is contained in:
parent
1ff64ac53f
commit
286d3b4f5c
|
|
@ -0,0 +1,471 @@
|
||||||
|
"""
|
||||||
|
Lemontropia Suite - Screenshot Hotkey Manager
|
||||||
|
Global hotkey listener for manual screenshot capture.
|
||||||
|
Uses pynput for global keyboard shortcuts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional, Callable, Dict, List
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pynput import keyboard
|
||||||
|
PYNPUT_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
PYNPUT_AVAILABLE = False
|
||||||
|
keyboard = None
|
||||||
|
|
||||||
|
from PyQt6.QtCore import QObject, pyqtSignal, QThread
|
||||||
|
|
||||||
|
from .auto_screenshot import AutoScreenshot
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ScreenshotEvent:
|
||||||
|
"""Screenshot capture event data."""
|
||||||
|
filepath: Path
|
||||||
|
timestamp: datetime
|
||||||
|
triggered_by: str # 'hotkey' or 'auto'
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class ScreenshotHotkeyManager(QObject):
|
||||||
|
"""
|
||||||
|
Global hotkey manager for manual screenshots.
|
||||||
|
Runs in background thread to capture hotkeys even when app not focused.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Signals for Qt integration
|
||||||
|
screenshot_captured = pyqtSignal(str) # filepath
|
||||||
|
hotkey_pressed = pyqtSignal(str) # hotkey name
|
||||||
|
|
||||||
|
# Default hotkeys
|
||||||
|
DEFAULT_HOTKEYS = {
|
||||||
|
'screenshot_full': 'f12', # Full screen
|
||||||
|
'screenshot_region': 'shift+f12', # Region selection
|
||||||
|
'screenshot_loot': 'ctrl+f12', # Loot window area
|
||||||
|
'screenshot_hud': 'alt+f12', # HUD area
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, screenshot_manager: Optional[AutoScreenshot] = None,
|
||||||
|
parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.screenshot_manager = screenshot_manager or AutoScreenshot()
|
||||||
|
self.hotkeys: Dict[str, str] = self.DEFAULT_HOTKEYS.copy()
|
||||||
|
self.enabled = True
|
||||||
|
self.listening = False
|
||||||
|
|
||||||
|
# Callbacks for custom actions
|
||||||
|
self.on_screenshot: Optional[Callable[[ScreenshotEvent], None]] = None
|
||||||
|
|
||||||
|
# Screenshot history
|
||||||
|
self.recent_screenshots: List[ScreenshotEvent] = []
|
||||||
|
self.max_history = 50
|
||||||
|
|
||||||
|
# Hotkey listener thread
|
||||||
|
self._listener_thread: Optional[threading.Thread] = None
|
||||||
|
self._listener: Optional[keyboard.Listener] = None
|
||||||
|
self._stop_event = threading.Event()
|
||||||
|
|
||||||
|
if not PYNPUT_AVAILABLE:
|
||||||
|
logger.warning("pynput not installed. Hotkey screenshots disabled.")
|
||||||
|
logger.info("Install with: pip install pynput")
|
||||||
|
|
||||||
|
def is_available(self) -> bool:
|
||||||
|
"""Check if hotkey manager is available."""
|
||||||
|
return PYNPUT_AVAILABLE
|
||||||
|
|
||||||
|
def start_listening(self):
|
||||||
|
"""Start global hotkey listener in background thread."""
|
||||||
|
if not PYNPUT_AVAILABLE:
|
||||||
|
logger.error("Cannot start hotkey listener: pynput not available")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.listening:
|
||||||
|
logger.warning("Hotkey listener already running")
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._stop_event.clear()
|
||||||
|
self._listener_thread = threading.Thread(
|
||||||
|
target=self._listen_for_hotkeys,
|
||||||
|
daemon=True
|
||||||
|
)
|
||||||
|
self._listener_thread.start()
|
||||||
|
self.listening = True
|
||||||
|
logger.info("Screenshot hotkey listener started")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to start hotkey listener: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def stop_listening(self):
|
||||||
|
"""Stop global hotkey listener."""
|
||||||
|
if not self.listening:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._stop_event.set()
|
||||||
|
|
||||||
|
if self._listener:
|
||||||
|
try:
|
||||||
|
self._listener.stop()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self._listener_thread:
|
||||||
|
self._listener_thread.join(timeout=1.0)
|
||||||
|
|
||||||
|
self.listening = False
|
||||||
|
logger.info("Screenshot hotkey listener stopped")
|
||||||
|
|
||||||
|
def _listen_for_hotkeys(self):
|
||||||
|
"""Background thread: listen for global hotkeys."""
|
||||||
|
try:
|
||||||
|
# Create hotkey combinations
|
||||||
|
hotkey_combinations = {}
|
||||||
|
|
||||||
|
for action, combo in self.hotkeys.items():
|
||||||
|
try:
|
||||||
|
hk = keyboard.HotKey(
|
||||||
|
keyboard.HotKey.parse(combo),
|
||||||
|
lambda a=action: self._on_hotkey(a)
|
||||||
|
)
|
||||||
|
hotkey_combinations[action] = hk
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to parse hotkey '{combo}': {e}")
|
||||||
|
|
||||||
|
def on_press(key):
|
||||||
|
for hk in hotkey_combinations.values():
|
||||||
|
hk.press(key)
|
||||||
|
|
||||||
|
def on_release(key):
|
||||||
|
for hk in hotkey_combinations.values():
|
||||||
|
hk.release(key)
|
||||||
|
|
||||||
|
self._listener = keyboard.Listener(
|
||||||
|
on_press=on_press,
|
||||||
|
on_release=on_release
|
||||||
|
)
|
||||||
|
|
||||||
|
self._listener.start()
|
||||||
|
self._listener.join()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Hotkey listener error: {e}")
|
||||||
|
|
||||||
|
def _on_hotkey(self, action: str):
|
||||||
|
"""Handle hotkey press."""
|
||||||
|
if not self.enabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"Hotkey triggered: {action}")
|
||||||
|
self.hotkey_pressed.emit(action)
|
||||||
|
|
||||||
|
if action == 'screenshot_full':
|
||||||
|
self._capture_full_screen()
|
||||||
|
elif action == 'screenshot_region':
|
||||||
|
self._capture_region()
|
||||||
|
elif action == 'screenshot_loot':
|
||||||
|
self._capture_loot_window()
|
||||||
|
elif action == 'screenshot_hud':
|
||||||
|
self._capture_hud()
|
||||||
|
|
||||||
|
def _capture_full_screen(self):
|
||||||
|
"""Capture full screen."""
|
||||||
|
filename = f"manual_full_{datetime.now():%Y%m%d_%H%M%S_%f}"[:-3] + ".png"
|
||||||
|
filepath = self.screenshot_manager.capture_full_screen(filename)
|
||||||
|
|
||||||
|
if filepath:
|
||||||
|
event = ScreenshotEvent(
|
||||||
|
filepath=filepath,
|
||||||
|
timestamp=datetime.now(),
|
||||||
|
triggered_by='hotkey',
|
||||||
|
description='Full screen capture (F12)'
|
||||||
|
)
|
||||||
|
self._add_to_history(event)
|
||||||
|
self.screenshot_captured.emit(str(filepath))
|
||||||
|
|
||||||
|
def _capture_region(self):
|
||||||
|
"""Capture region (would need region selector UI)."""
|
||||||
|
# For now, capture center of screen as a reasonable default
|
||||||
|
# In the future, this could open a region selection overlay
|
||||||
|
logger.info("Region capture: using center 800x600 region")
|
||||||
|
filename = f"manual_region_{datetime.now():%Y%m%d_%H%M%S_%f}"[:-3] + ".png"
|
||||||
|
|
||||||
|
# Get primary monitor dimensions
|
||||||
|
try:
|
||||||
|
import mss
|
||||||
|
with mss.mss() as sct:
|
||||||
|
monitor = sct.monitors[1]
|
||||||
|
screen_w = monitor['width']
|
||||||
|
screen_h = monitor['height']
|
||||||
|
|
||||||
|
# Capture center 800x600
|
||||||
|
x = (screen_w - 800) // 2
|
||||||
|
y = (screen_h - 600) // 2
|
||||||
|
|
||||||
|
filepath = self.screenshot_manager.capture_region(
|
||||||
|
x, y, 800, 600, filename
|
||||||
|
)
|
||||||
|
|
||||||
|
if filepath:
|
||||||
|
event = ScreenshotEvent(
|
||||||
|
filepath=filepath,
|
||||||
|
timestamp=datetime.now(),
|
||||||
|
triggered_by='hotkey',
|
||||||
|
description='Region capture (Shift+F12)'
|
||||||
|
)
|
||||||
|
self._add_to_history(event)
|
||||||
|
self.screenshot_captured.emit(str(filepath))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Region capture failed: {e}")
|
||||||
|
|
||||||
|
def _capture_loot_window(self):
|
||||||
|
"""Capture typical loot window area."""
|
||||||
|
filename = f"manual_loot_{datetime.now():%Y%m%d_%H%M%S_%f}"[:-3] + ".png"
|
||||||
|
|
||||||
|
# Typical loot window position (right side of screen)
|
||||||
|
try:
|
||||||
|
import mss
|
||||||
|
with mss.mss() as sct:
|
||||||
|
monitor = sct.monitors[1]
|
||||||
|
screen_w = monitor['width']
|
||||||
|
screen_h = monitor['height']
|
||||||
|
|
||||||
|
# Loot window: right side, ~300x400
|
||||||
|
x = screen_w - 350
|
||||||
|
y = screen_h // 2 - 200
|
||||||
|
w = 300
|
||||||
|
h = 400
|
||||||
|
|
||||||
|
filepath = self.screenshot_manager.capture_region(
|
||||||
|
max(0, x), max(0, y), w, h, filename
|
||||||
|
)
|
||||||
|
|
||||||
|
if filepath:
|
||||||
|
event = ScreenshotEvent(
|
||||||
|
filepath=filepath,
|
||||||
|
timestamp=datetime.now(),
|
||||||
|
triggered_by='hotkey',
|
||||||
|
description='Loot window capture (Ctrl+F12)'
|
||||||
|
)
|
||||||
|
self._add_to_history(event)
|
||||||
|
self.screenshot_captured.emit(str(filepath))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Loot capture failed: {e}")
|
||||||
|
|
||||||
|
def _capture_hud(self):
|
||||||
|
"""Capture HUD area."""
|
||||||
|
filename = f"manual_hud_{datetime.now():%Y%m%d_%H%M%S_%f}"[:-3] + ".png"
|
||||||
|
|
||||||
|
try:
|
||||||
|
import mss
|
||||||
|
with mss.mss() as sct:
|
||||||
|
monitor = sct.monitors[1]
|
||||||
|
screen_w = monitor['width']
|
||||||
|
screen_h = monitor['height']
|
||||||
|
|
||||||
|
# HUD: bottom center
|
||||||
|
w = 600
|
||||||
|
h = 150
|
||||||
|
x = (screen_w - w) // 2
|
||||||
|
y = screen_h - h - 50
|
||||||
|
|
||||||
|
filepath = self.screenshot_manager.capture_region(
|
||||||
|
max(0, x), max(0, y), w, h, filename
|
||||||
|
)
|
||||||
|
|
||||||
|
if filepath:
|
||||||
|
event = ScreenshotEvent(
|
||||||
|
filepath=filepath,
|
||||||
|
timestamp=datetime.now(),
|
||||||
|
triggered_by='hotkey',
|
||||||
|
description='HUD capture (Alt+F12)'
|
||||||
|
)
|
||||||
|
self._add_to_history(event)
|
||||||
|
self.screenshot_captured.emit(str(filepath))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"HUD capture failed: {e}")
|
||||||
|
|
||||||
|
def _add_to_history(self, event: ScreenshotEvent):
|
||||||
|
"""Add screenshot to history."""
|
||||||
|
self.recent_screenshots.insert(0, event)
|
||||||
|
|
||||||
|
# Trim history
|
||||||
|
if len(self.recent_screenshots) > self.max_history:
|
||||||
|
self.recent_screenshots = self.recent_screenshots[:self.max_history]
|
||||||
|
|
||||||
|
# Call custom callback if set
|
||||||
|
if self.on_screenshot:
|
||||||
|
try:
|
||||||
|
self.on_screenshot(event)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Screenshot callback error: {e}")
|
||||||
|
|
||||||
|
def get_recent_screenshots(self, n: int = 10) -> List[ScreenshotEvent]:
|
||||||
|
"""Get recent screenshots."""
|
||||||
|
return self.recent_screenshots[:n]
|
||||||
|
|
||||||
|
def set_hotkey(self, action: str, combination: str) -> bool:
|
||||||
|
"""
|
||||||
|
Change hotkey for an action.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
action: Action name ('screenshot_full', etc.)
|
||||||
|
combination: New hotkey combination (e.g., 'f10', 'ctrl+shift+s')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if successful
|
||||||
|
"""
|
||||||
|
if action not in self.hotkeys:
|
||||||
|
logger.error(f"Unknown action: {action}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Validate hotkey format
|
||||||
|
keyboard.HotKey.parse(combination)
|
||||||
|
|
||||||
|
# Restart listener with new hotkey
|
||||||
|
was_listening = self.listening
|
||||||
|
self.stop_listening()
|
||||||
|
|
||||||
|
self.hotkeys[action] = combination
|
||||||
|
logger.info(f"Hotkey for '{action}' set to '{combination}'")
|
||||||
|
|
||||||
|
if was_listening:
|
||||||
|
return self.start_listening()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Invalid hotkey combination '{combination}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_hotkey_help(self) -> str:
|
||||||
|
"""Get formatted hotkey help text."""
|
||||||
|
lines = ["📸 Screenshot Hotkeys:", ""]
|
||||||
|
|
||||||
|
descriptions = {
|
||||||
|
'screenshot_full': 'Full screen',
|
||||||
|
'screenshot_region': 'Center region (800x600)',
|
||||||
|
'screenshot_loot': 'Loot window area',
|
||||||
|
'screenshot_hud': 'HUD area',
|
||||||
|
}
|
||||||
|
|
||||||
|
for action, combo in self.hotkeys.items():
|
||||||
|
desc = descriptions.get(action, action)
|
||||||
|
lines.append(f" {combo.upper():<15} - {desc}")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
class ScreenshotHotkeyWidget:
|
||||||
|
"""
|
||||||
|
PyQt widget for configuring screenshot hotkeys.
|
||||||
|
Can be embedded in settings dialog.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hotkey_manager: ScreenshotHotkeyManager):
|
||||||
|
self.manager = hotkey_manager
|
||||||
|
|
||||||
|
def create_settings_widget(self):
|
||||||
|
"""Create settings widget for hotkey configuration."""
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||||
|
QLineEdit, QPushButton, QGroupBox, QFormLayout,
|
||||||
|
QMessageBox
|
||||||
|
)
|
||||||
|
|
||||||
|
widget = QWidget()
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Hotkey group
|
||||||
|
group = QGroupBox("Screenshot Hotkeys")
|
||||||
|
form = QFormLayout()
|
||||||
|
|
||||||
|
self.hotkey_inputs = {}
|
||||||
|
|
||||||
|
descriptions = {
|
||||||
|
'screenshot_full': 'Full Screen:',
|
||||||
|
'screenshot_region': 'Region (Center):',
|
||||||
|
'screenshot_loot': 'Loot Window:',
|
||||||
|
'screenshot_hud': 'HUD Area:',
|
||||||
|
}
|
||||||
|
|
||||||
|
for action, combo in self.manager.hotkeys.items():
|
||||||
|
row = QHBoxLayout()
|
||||||
|
|
||||||
|
line_edit = QLineEdit(combo)
|
||||||
|
line_edit.setMaximumWidth(150)
|
||||||
|
self.hotkey_inputs[action] = line_edit
|
||||||
|
|
||||||
|
row.addWidget(line_edit)
|
||||||
|
|
||||||
|
test_btn = QPushButton("Test")
|
||||||
|
test_btn.setMaximumWidth(60)
|
||||||
|
test_btn.clicked.connect(lambda checked, a=action: self._test_screenshot(a))
|
||||||
|
row.addWidget(test_btn)
|
||||||
|
|
||||||
|
row.addStretch()
|
||||||
|
|
||||||
|
form.addRow(descriptions.get(action, action), row)
|
||||||
|
|
||||||
|
group.setLayout(form)
|
||||||
|
layout.addWidget(group)
|
||||||
|
|
||||||
|
# Save button
|
||||||
|
save_btn = QPushButton("Save Hotkeys")
|
||||||
|
save_btn.clicked.connect(self._save_hotkeys)
|
||||||
|
layout.addWidget(save_btn)
|
||||||
|
|
||||||
|
# Help text
|
||||||
|
help_label = QLabel(self.manager.get_hotkey_help())
|
||||||
|
help_label.setWordWrap(True)
|
||||||
|
layout.addWidget(help_label)
|
||||||
|
|
||||||
|
layout.addStretch()
|
||||||
|
widget.setLayout(layout)
|
||||||
|
|
||||||
|
return widget
|
||||||
|
|
||||||
|
def _test_screenshot(self, action: str):
|
||||||
|
"""Test screenshot action."""
|
||||||
|
self.manager._on_hotkey(action)
|
||||||
|
|
||||||
|
def _save_hotkeys(self):
|
||||||
|
"""Save hotkey changes."""
|
||||||
|
for action, line_edit in self.hotkey_inputs.items():
|
||||||
|
combo = line_edit.text().strip().lower()
|
||||||
|
if combo:
|
||||||
|
self.manager.set_hotkey(action, combo)
|
||||||
|
|
||||||
|
|
||||||
|
# Convenience function
|
||||||
|
def create_screenshot_hotkeys(screenshot_manager: Optional[AutoScreenshot] = None,
|
||||||
|
start_listening: bool = True) -> Optional[ScreenshotHotkeyManager]:
|
||||||
|
"""
|
||||||
|
Create and start screenshot hotkey manager.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
screenshot_manager: Existing AutoScreenshot instance
|
||||||
|
start_listening: Start listening immediately
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ScreenshotHotkeyManager or None if not available
|
||||||
|
"""
|
||||||
|
if not PYNPUT_AVAILABLE:
|
||||||
|
logger.error("pynput not installed. Run: pip install pynput")
|
||||||
|
return None
|
||||||
|
|
||||||
|
manager = ScreenshotHotkeyManager(screenshot_manager)
|
||||||
|
|
||||||
|
if start_listening:
|
||||||
|
manager.start_listening()
|
||||||
|
|
||||||
|
return manager
|
||||||
|
|
@ -120,6 +120,18 @@ from ui.hud_overlay_clean import HUDOverlay
|
||||||
from ui.session_history import SessionHistoryDialog
|
from ui.session_history import SessionHistoryDialog
|
||||||
from ui.gallery_dialog import GalleryDialog, ScreenshotCapture
|
from ui.gallery_dialog import GalleryDialog, ScreenshotCapture
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Screenshot Hotkey Integration
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
try:
|
||||||
|
from modules.screenshot_hotkey import ScreenshotHotkeyManager, ScreenshotHotkeyWidget
|
||||||
|
SCREENSHOT_HOTKEYS_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
SCREENSHOT_HOTKEYS_AVAILABLE = False
|
||||||
|
ScreenshotHotkeyManager = None
|
||||||
|
ScreenshotHotkeyWidget = None
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Core Integration
|
# Core Integration
|
||||||
|
|
@ -398,6 +410,10 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
# Screenshot capture
|
# Screenshot capture
|
||||||
self._screenshot_capture = ScreenshotCapture(self.db)
|
self._screenshot_capture = ScreenshotCapture(self.db)
|
||||||
|
|
||||||
|
# Screenshot hotkey manager (global hotkeys for manual capture)
|
||||||
|
self._screenshot_hotkeys = None
|
||||||
|
self._init_screenshot_hotkeys()
|
||||||
|
|
||||||
# Setup UI
|
# Setup UI
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
|
|
@ -812,6 +828,14 @@ class MainWindow(QMainWindow):
|
||||||
vision_test_action.triggered.connect(self.on_vision_test)
|
vision_test_action.triggered.connect(self.on_vision_test)
|
||||||
vision_menu.addAction(vision_test_action)
|
vision_menu.addAction(vision_test_action)
|
||||||
|
|
||||||
|
tools_menu.addSeparator()
|
||||||
|
|
||||||
|
# Screenshot hotkey settings
|
||||||
|
screenshot_hotkeys_action = QAction("📸 Screenshot &Hotkeys", self)
|
||||||
|
screenshot_hotkeys_action.setShortcut("Ctrl+Shift+S")
|
||||||
|
screenshot_hotkeys_action.triggered.connect(self._show_screenshot_hotkey_settings)
|
||||||
|
tools_menu.addAction(screenshot_hotkeys_action)
|
||||||
|
|
||||||
# View menu
|
# View menu
|
||||||
view_menu = menubar.addMenu("&View")
|
view_menu = menubar.addMenu("&View")
|
||||||
|
|
||||||
|
|
@ -1867,6 +1891,83 @@ class MainWindow(QMainWindow):
|
||||||
# Settings Management
|
# Settings Management
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
|
|
||||||
|
def _init_screenshot_hotkeys(self):
|
||||||
|
"""Initialize global screenshot hotkey manager."""
|
||||||
|
if not SCREENSHOT_HOTKEYS_AVAILABLE:
|
||||||
|
logger.warning("Screenshot hotkeys not available (pynput not installed)")
|
||||||
|
self.log_info("Hotkeys", "Screenshot hotkeys disabled - install pynput")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
from modules.auto_screenshot import AutoScreenshot
|
||||||
|
|
||||||
|
# Create screenshot manager
|
||||||
|
auto_screenshot = AutoScreenshot(self._screenshot_capture.screenshot_dir)
|
||||||
|
|
||||||
|
# Create hotkey manager
|
||||||
|
self._screenshot_hotkeys = ScreenshotHotkeyManager(auto_screenshot)
|
||||||
|
|
||||||
|
# Connect signals
|
||||||
|
self._screenshot_hotkeys.screenshot_captured.connect(self._on_screenshot_captured)
|
||||||
|
self._screenshot_hotkeys.hotkey_pressed.connect(self._on_hotkey_pressed)
|
||||||
|
|
||||||
|
# Start listening
|
||||||
|
if self._screenshot_hotkeys.start_listening():
|
||||||
|
help_text = self._screenshot_hotkeys.get_hotkey_help()
|
||||||
|
self.log_info("Hotkeys", "Screenshot hotkeys active")
|
||||||
|
self.log_info("Hotkeys", "F12=Full, Shift+F12=Region, Ctrl+F12=Loot, Alt+F12=HUD")
|
||||||
|
else:
|
||||||
|
self.log_warning("Hotkeys", "Failed to start screenshot hotkeys")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to initialize screenshot hotkeys: {e}")
|
||||||
|
self._screenshot_hotkeys = None
|
||||||
|
|
||||||
|
def _on_screenshot_captured(self, filepath: str):
|
||||||
|
"""Handle screenshot captured signal."""
|
||||||
|
self.log_info("Screenshot", f"Saved: {filepath}")
|
||||||
|
|
||||||
|
# Show notification
|
||||||
|
self.statusBar().showMessage(f"📸 Screenshot saved: {filepath}", 3000)
|
||||||
|
|
||||||
|
# Refresh gallery if open
|
||||||
|
# (Gallery dialog would need to be updated to refresh)
|
||||||
|
|
||||||
|
def _on_hotkey_pressed(self, action: str):
|
||||||
|
"""Handle hotkey pressed signal."""
|
||||||
|
logger.debug(f"Screenshot hotkey pressed: {action}")
|
||||||
|
|
||||||
|
def _show_screenshot_hotkey_settings(self):
|
||||||
|
"""Show screenshot hotkey settings dialog."""
|
||||||
|
if not SCREENSHOT_HOTKEYS_AVAILABLE or not self._screenshot_hotkeys:
|
||||||
|
QMessageBox.information(
|
||||||
|
self,
|
||||||
|
"Screenshot Hotkeys",
|
||||||
|
"Screenshot hotkeys are not available.\n\n"
|
||||||
|
"Install pynput to enable:\n"
|
||||||
|
" pip install pynput\n\n"
|
||||||
|
"Default hotkeys:\n"
|
||||||
|
" F12 - Full screen\n"
|
||||||
|
" Shift+F12 - Center region\n"
|
||||||
|
" Ctrl+F12 - Loot window\n"
|
||||||
|
" Alt+F12 - HUD area"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import QDialog, QVBoxLayout
|
||||||
|
|
||||||
|
dialog = QDialog(self)
|
||||||
|
dialog.setWindowTitle("Screenshot Hotkey Settings")
|
||||||
|
dialog.setMinimumWidth(400)
|
||||||
|
|
||||||
|
layout = QVBoxLayout(dialog)
|
||||||
|
|
||||||
|
# Create settings widget
|
||||||
|
widget = ScreenshotHotkeyWidget(self._screenshot_hotkeys)
|
||||||
|
layout.addWidget(widget.create_settings_widget())
|
||||||
|
|
||||||
|
dialog.exec()
|
||||||
|
|
||||||
def _load_settings(self):
|
def _load_settings(self):
|
||||||
"""Load persistent settings from QSettings."""
|
"""Load persistent settings from QSettings."""
|
||||||
settings = QSettings("Lemontropia", "Suite")
|
settings = QSettings("Lemontropia", "Suite")
|
||||||
|
|
@ -1900,6 +2001,14 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""Handle window close event."""
|
"""Handle window close event."""
|
||||||
|
# Stop screenshot hotkey listener
|
||||||
|
if self._screenshot_hotkeys:
|
||||||
|
try:
|
||||||
|
self._screenshot_hotkeys.stop_listening()
|
||||||
|
logger.info("Screenshot hotkeys stopped")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error stopping screenshot hotkeys: {e}")
|
||||||
|
|
||||||
if self.session_state == SessionState.RUNNING:
|
if self.session_state == SessionState.RUNNING:
|
||||||
reply = QMessageBox.question(
|
reply = QMessageBox.question(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue