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.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
|
||||
|
|
@ -399,6 +411,10 @@ class MainWindow(QMainWindow):
|
|||
# Screenshot capture
|
||||
self._screenshot_capture = ScreenshotCapture(self.db)
|
||||
|
||||
# Screenshot hotkey manager (global hotkeys for manual capture)
|
||||
self._screenshot_hotkeys = None
|
||||
self._init_screenshot_hotkeys()
|
||||
|
||||
# Setup UI
|
||||
self.setup_ui()
|
||||
self.apply_dark_theme()
|
||||
|
|
@ -812,6 +828,14 @@ class MainWindow(QMainWindow):
|
|||
vision_test_action.triggered.connect(self.on_vision_test)
|
||||
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 = menubar.addMenu("&View")
|
||||
|
||||
|
|
@ -1867,6 +1891,83 @@ class MainWindow(QMainWindow):
|
|||
# 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):
|
||||
"""Load persistent settings from QSettings."""
|
||||
settings = QSettings("Lemontropia", "Suite")
|
||||
|
|
@ -1900,6 +2001,14 @@ class MainWindow(QMainWindow):
|
|||
|
||||
def closeEvent(self, 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:
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
|
|
|
|||
Loading…
Reference in New Issue