# Description: Loadout selection dialog for hunting sessions # Allows choosing a loadout when starting a new session # Standards: Python 3.11+, PyQt6, type hints from decimal import Decimal from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QListWidget, QListWidgetItem, QDialogButtonBox, QGroupBox, QFormLayout, QMessageBox ) from PyQt6.QtCore import Qt, pyqtSignal from typing import Optional, Dict, Any, List from core.loadout_db import LoadoutDatabase from core.database import DatabaseManager class LoadoutSelectionDialog(QDialog): """ Dialog for selecting a loadout when starting a hunting session. Shows saved loadouts with their per-action cost metrics. """ loadout_selected = pyqtSignal(int, str) # loadout_id, loadout_name def __init__(self, parent=None, db_manager: Optional[DatabaseManager] = None): """ Initialize loadout selection dialog. Args: parent: Parent widget db_manager: Optional database manager """ super().__init__(parent) self.setWindowTitle("Select Loadout - Hunting Session") self.setMinimumSize(500, 400) self.db = db_manager or DatabaseManager() self.loadout_db = LoadoutDatabase(self.db) self.selected_loadout_id: Optional[int] = None self.selected_loadout_name: Optional[str] = None self._setup_ui() self._load_loadouts() def _setup_ui(self): """Setup the dialog UI.""" layout = QVBoxLayout(self) layout.setSpacing(10) # Header header = QLabel("⚙️ Select Your Hunting Loadout") header.setStyleSheet("font-size: 16px; font-weight: bold; color: #FFD700;") layout.addWidget(header) description = QLabel( "Choose a loadout to track your costs accurately during this hunting session.\n" "Costs will be calculated based on your configured gear." ) description.setStyleSheet("color: #888888;") description.setWordWrap(True) layout.addWidget(description) # Loadout list self.loadout_list = QListWidget() self.loadout_list.setAlternatingRowColors(True) self.loadout_list.itemSelectionChanged.connect(self._on_selection_changed) self.loadout_list.itemDoubleClicked.connect(self._on_double_click) layout.addWidget(self.loadout_list) # Preview panel self.preview_group = QGroupBox("Loadout Details") preview_layout = QFormLayout(self.preview_group) self.preview_weapon = QLabel("-") self.preview_armor = QLabel("-") self.preview_healing = QLabel("-") self.preview_cost_shot = QLabel("-") self.preview_cost_hit = QLabel("-") self.preview_cost_heal = QLabel("-") preview_layout.addRow("🗡️ Weapon:", self.preview_weapon) preview_layout.addRow("🛡️ Armor:", self.preview_armor) preview_layout.addRow("💚 Healing:", self.preview_healing) preview_layout.addRow("💰 Cost/Shot:", self.preview_cost_shot) preview_layout.addRow("💰 Cost/Hit:", self.preview_cost_hit) preview_layout.addRow("💰 Cost/Heal:", self.preview_cost_heal) layout.addWidget(self.preview_group) # Buttons buttons = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel ) buttons.accepted.connect(self._on_accept) buttons.rejected.connect(self.reject) self.ok_button = buttons.button(QDialogButtonBox.StandardButton.Ok) self.ok_button.setText("Start Session") self.ok_button.setEnabled(False) # Add "No Loadout" option button no_loadout_btn = QPushButton("Skip (No Loadout)") no_loadout_btn.setToolTip("Start session without cost tracking") no_loadout_btn.clicked.connect(self._on_no_loadout) btn_layout = QHBoxLayout() btn_layout.addWidget(no_loadout_btn) btn_layout.addStretch() btn_layout.addWidget(buttons) layout.addLayout(btn_layout) def _load_loadouts(self): """Load saved loadouts from database.""" loadouts = self.loadout_db.list_loadouts() if not loadouts: # No loadouts saved item = QListWidgetItem("No loadouts saved yet") item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsSelectable) item.setForeground(Qt.GlobalColor.gray) self.loadout_list.addItem(item) return for loadout in loadouts: item = QListWidgetItem() item.setText(loadout['name']) # Build tooltip with gear info tooltip = f"Weapon: {loadout['weapon_name'] or 'None'}\n" tooltip += f"Armor: {loadout['armor_name'] or 'None'}\n" tooltip += f"Healing: {loadout['healing_tool_name'] or 'None'}\n" tooltip += f"Cost/Shot: {loadout['cost_per_shot_ped']:.4f} PED\n" tooltip += f"Cost/Hit: {loadout['cost_per_hit_ped']:.4f} PED\n" tooltip += f"Cost/Heal: {loadout['cost_per_heal_ped']:.4f} PED" item.setToolTip(tooltip) # Store loadout ID item.setData(Qt.ItemDataRole.UserRole, loadout['id']) # Highlight active loadout if loadout.get('is_active'): item.setText(f"⭐ {loadout['name']} (Active)") font = item.font() font.setBold(True) item.setFont(font) self.loadout_list.addItem(item) def _on_selection_changed(self): """Handle loadout selection change.""" items = self.loadout_list.selectedItems() if not items: self.ok_button.setEnabled(False) self._clear_preview() return item = items[0] loadout_id = item.data(Qt.ItemDataRole.UserRole) if loadout_id is None: self.ok_button.setEnabled(False) return self.selected_loadout_id = loadout_id self.selected_loadout_name = item.text().replace("⭐ ", "").replace(" (Active)", "") # Load and display preview loadout = self.loadout_db.get_loadout(self.selected_loadout_name) if loadout: self._update_preview(loadout) self.ok_button.setEnabled(True) def _update_preview(self, loadout: Dict[str, Any]): """Update preview panel with loadout details.""" self.preview_weapon.setText(loadout.get('weapon_name') or "None") self.preview_armor.setText(loadout.get('armor_name') or "None") self.preview_healing.setText(loadout.get('healing_tool_name') or "None") # Format costs cost_shot = Decimal(str(loadout.get('cost_per_shot_ped', 0))) cost_hit = Decimal(str(loadout.get('cost_per_hit_ped', 0))) cost_heal = Decimal(str(loadout.get('cost_per_heal_ped', 0))) self.preview_cost_shot.setText(f"{cost_shot:.4f} PED") self.preview_cost_hit.setText(f"{cost_hit:.4f} PED") self.preview_cost_heal.setText(f"{cost_heal:.4f} PED") # Color code based on cost if cost_shot > Decimal("0.5"): self.preview_cost_shot.setStyleSheet("color: #FF7F7F;") else: self.preview_cost_shot.setStyleSheet("color: #7FFF7F;") def _clear_preview(self): """Clear preview panel.""" self.preview_weapon.setText("-") self.preview_armor.setText("-") self.preview_healing.setText("-") self.preview_cost_shot.setText("-") self.preview_cost_hit.setText("-") self.preview_cost_heal.setText("-") def _on_double_click(self, item: QListWidgetItem): """Handle double click on loadout.""" if item.data(Qt.ItemDataRole.UserRole): self._on_accept() def _on_accept(self): """Handle OK button - start session with selected loadout.""" if self.selected_loadout_id: self.loadout_selected.emit(self.selected_loadout_id, self.selected_loadout_name or "") self.accept() def _on_no_loadout(self): """Handle skip button - start session without loadout.""" reply = QMessageBox.question( self, "No Loadout Selected", "Start session without cost tracking?\n\n" "You won't be able to track accurate weapon/armor/healing costs.", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: self.loadout_selected.emit(0, "No Loadout") self.accept() # Main entry for testing if __name__ == "__main__": import sys import logging from PyQt6.QtWidgets import QApplication logging.basicConfig(level=logging.INFO) app = QApplication(sys.argv) app.setStyle('Fusion') dialog = LoadoutSelectionDialog() # Connect signal for testing dialog.loadout_selected.connect( lambda id, name: print(f"Selected loadout: {name} (ID: {id})") ) if dialog.exec() == QDialog.DialogCode.Accepted: print("Session starting!") sys.exit(0)