""" Gear Selector Dialog for Lemontropia Suite Uses Entropia Nexus API to search and select real gear. """ from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QComboBox, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QTabWidget, QGroupBox, QFormLayout, QMessageBox, QHeaderView ) from PyQt6.QtCore import Qt, pyqtSignal, QThread from decimal import Decimal from typing import Optional, List from core.nexus_api import EntropiaNexusAPI, WeaponStats, ArmorStats, FinderStats class WeaponLoaderThread(QThread): """Thread to load weapons from API without blocking UI.""" weapons_loaded = pyqtSignal(list) error_occurred = pyqtSignal(str) def run(self): try: api = EntropiaNexusAPI() weapons = api.get_all_weapons() self.weapons_loaded.emit(weapons) except Exception as e: self.error_occurred.emit(str(e)) class ArmorLoaderThread(QThread): """Thread to load armors from API.""" armors_loaded = pyqtSignal(list) error_occurred = pyqtSignal(str) def run(self): try: api = EntropiaNexusAPI() armors = api.get_all_armors() self.armors_loaded.emit(armors) except Exception as e: self.error_occurred.emit(str(e)) class FinderLoaderThread(QThread): """Thread to load finders from API.""" finders_loaded = pyqtSignal(list) error_occurred = pyqtSignal(str) def run(self): try: api = EntropiaNexusAPI() finders = api.get_all_finders() self.finders_loaded.emit(finders) except Exception as e: self.error_occurred.emit(str(e)) class GearSelectorDialog(QDialog): """Dialog for selecting gear from Entropia Nexus.""" gear_selected = pyqtSignal(str, str, dict) # type, name, stats def __init__(self, gear_type: str = "weapon", parent=None): super().__init__(parent) self.gear_type = gear_type self.selected_gear = None self.items: List = [] self.api = EntropiaNexusAPI() self.setWindowTitle(f"Select {gear_type.title()} - Entropia Nexus") self.setMinimumSize(800, 600) self.setup_ui() self.load_data_async() def setup_ui(self): layout = QVBoxLayout(self) layout.setSpacing(10) # Status label self.status_label = QLabel(f"Loading {self.gear_type}s from Entropia Nexus...") layout.addWidget(self.status_label) # Search box search_layout = QHBoxLayout() search_layout.addWidget(QLabel("Search:")) self.search_input = QLineEdit() self.search_input.setPlaceholderText(f"Search {self.gear_type}s...") self.search_input.returnPressed.connect(self.on_search) self.search_input.setEnabled(False) search_layout.addWidget(self.search_input) self.search_btn = QPushButton("Search") self.search_btn.clicked.connect(self.on_search) self.search_btn.setEnabled(False) search_layout.addWidget(self.search_btn) layout.addLayout(search_layout) # Results list self.results_tree = QTreeWidget() self.setup_columns() self.results_tree.setAlternatingRowColors(True) self.results_tree.setSelectionMode(QTreeWidget.SelectionMode.SingleSelection) self.results_tree.itemSelectionChanged.connect(self.on_selection_changed) self.results_tree.itemDoubleClicked.connect(self.on_item_double_clicked) layout.addWidget(self.results_tree) # Stats preview self.stats_group = QGroupBox("Stats Preview") self.stats_layout = QFormLayout(self.stats_group) self.stats_layout.addRow("Select an item to view stats", QLabel("")) layout.addWidget(self.stats_group) # Buttons button_box = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel ) button_box.accepted.connect(self.on_accept) button_box.rejected.connect(self.reject) self.ok_btn = button_box.button(QDialogButtonBox.StandardButton.Ok) self.ok_btn.setEnabled(False) self.ok_btn.setText(f"Select {self.gear_type.title()}") layout.addWidget(button_box) def setup_columns(self): """Setup tree columns based on gear type.""" if self.gear_type == "weapon": self.results_tree.setHeaderLabels(["Name", "Type", "Damage", "DPP", "Cost/h", "Eff%"]) header = self.results_tree.header() header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeMode.Fixed) header.setSectionResizeMode(3, QHeaderView.ResizeMode.Fixed) header.setSectionResizeMode(4, QHeaderView.ResizeMode.Fixed) header.setSectionResizeMode(5, QHeaderView.ResizeMode.Fixed) header.resizeSection(1, 100) header.resizeSection(2, 60) header.resizeSection(3, 60) header.resizeSection(4, 70) header.resizeSection(5, 50) elif self.gear_type == "armor": self.results_tree.setHeaderLabels(["Name", "Protection", "Durability"]) header = self.results_tree.header() header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeMode.Fixed) header.resizeSection(1, 100) header.resizeSection(2, 100) elif self.gear_type == "finder": self.results_tree.setHeaderLabels(["Name", "Type", "Depth", "Radius", "Decay"]) header = self.results_tree.header() header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) for i in range(1, 5): header.setSectionResizeMode(i, QHeaderView.ResizeMode.Fixed) header.resizeSection(1, 100) header.resizeSection(2, 80) header.resizeSection(3, 80) header.resizeSection(4, 80) def load_data_async(self): """Load data in background thread.""" if self.gear_type == "weapon": self.loader_thread = WeaponLoaderThread() self.loader_thread.weapons_loaded.connect(self.on_data_loaded) self.loader_thread.error_occurred.connect(self.on_load_error) elif self.gear_type == "armor": self.loader_thread = ArmorLoaderThread() self.loader_thread.armors_loaded.connect(self.on_data_loaded) self.loader_thread.error_occurred.connect(self.on_load_error) elif self.gear_type == "finder": self.loader_thread = FinderLoaderThread() self.loader_thread.finders_loaded.connect(self.on_data_loaded) self.loader_thread.error_occurred.connect(self.on_load_error) else: return self.loader_thread.start() def on_data_loaded(self, items): """Handle loaded data.""" self.items = items self.status_label.setText(f"Loaded {len(items)} {self.gear_type}s from Entropia Nexus") self.search_input.setEnabled(True) self.search_btn.setEnabled(True) self.populate_tree(items[:100]) def on_load_error(self, error): """Handle load error.""" self.status_label.setText(f"Error: {error}") def populate_tree(self, items): """Populate tree with items.""" self.results_tree.clear() if self.gear_type == "weapon": for w in items: item = QTreeWidgetItem([ w.name, f"{w.type} {w.category}", str(w.total_damage), f"{w.dpp:.2f}", f"{w.cost_per_hour:.0f}", f"{w.efficiency:.0f}" if w.efficiency else "-" ]) item.setData(0, Qt.ItemDataRole.UserRole, w) self.results_tree.addTopLevelItem(item) elif self.gear_type == "armor": for a in items: item = QTreeWidgetItem([ a.name, str(a.total_protection), str(a.durability) ]) item.setData(0, Qt.ItemDataRole.UserRole, a) self.results_tree.addTopLevelItem(item) elif self.gear_type == "finder": for f in items: item = QTreeWidgetItem([ f.name, f.type, str(f.depth), str(f.radius), str(f.decay) ]) item.setData(0, Qt.ItemDataRole.UserRole, f) self.results_tree.addTopLevelItem(item) def on_search(self): """Search items.""" query = self.search_input.text().strip().lower() if not query: self.populate_tree(self.items[:100]) return if self.gear_type == "weapon": results = self.api.search_weapons(query) else: results = [i for i in self.items if query in i.name.lower()] self.populate_tree(results) self.status_label.setText(f"Found {len(results)} {self.gear_type}s matching '{query}'") def on_selection_changed(self): """Handle selection change.""" selected = self.results_tree.selectedItems() if selected: item = selected[0].data(0, Qt.ItemDataRole.UserRole) self.selected_gear = item self.ok_btn.setEnabled(True) self.update_stats_preview(item) else: self.selected_gear = None self.ok_btn.setEnabled(False) def update_stats_preview(self, item): """Update stats preview.""" while self.stats_layout.rowCount() > 0: self.stats_layout.removeRow(0) if self.gear_type == "weapon" and isinstance(item, WeaponStats): self.stats_layout.addRow("Name:", QLabel(item.name)) self.stats_layout.addRow("Type:", QLabel(f"{item.type} {item.category}")) self.stats_layout.addRow("Damage:", QLabel(str(item.total_damage))) self.stats_layout.addRow("DPP:", QLabel(f"{item.dpp:.3f}")) self.stats_layout.addRow("Decay:", QLabel(f"{item.decay} PEC" if item.decay else "-")) self.stats_layout.addRow("Ammo:", QLabel(f"{item.ammo_burn} per shot" if item.ammo_burn else "-")) self.stats_layout.addRow("Cost/hour:", QLabel(f"{item.cost_per_hour:.2f} PED")) elif self.gear_type == "armor" and isinstance(item, ArmorStats): self.stats_layout.addRow("Name:", QLabel(item.name)) self.stats_layout.addRow("Total Protection:", QLabel(str(item.total_protection))) self.stats_layout.addRow("Durability:", QLabel(str(item.durability))) elif self.gear_type == "finder" and isinstance(item, FinderStats): self.stats_layout.addRow("Name:", QLabel(item.name)) self.stats_layout.addRow("Depth:", QLabel(f"{item.depth}m")) self.stats_layout.addRow("Radius:", QLabel(f"{item.radius}m")) self.stats_layout.addRow("Decay:", QLabel(f"{item.decay} PEC")) def on_item_double_clicked(self, item, column): """Handle double click.""" self.on_accept() def on_accept(self): """Handle OK button.""" if self.selected_gear: if self.gear_type == "weapon": w = self.selected_gear self.gear_selected.emit("weapon", w.name, { 'id': w.id, 'item_id': w.item_id, 'dpp': float(w.dpp), 'cost_per_hour': float(w.cost_per_hour), 'total_damage': float(w.total_damage), }) elif self.gear_type == "armor": a = self.selected_gear self.gear_selected.emit("armor", a.name, { 'id': a.id, 'item_id': a.item_id, 'protection': float(a.total_protection), }) elif self.gear_type == "finder": f = self.selected_gear self.gear_selected.emit("finder", f.name, { 'id': f.id, 'item_id': f.item_id, 'depth': float(f.depth), 'radius': float(f.radius), }) self.accept() if __name__ == "__main__": import sys from PyQt6.QtWidgets import QApplication app = QApplication(sys.argv) dialog = GearSelectorDialog("weapon") if dialog.exec() == QDialog.DialogCode.Accepted: print("Selected:", dialog.selected_gear.name if dialog.selected_gear else None) sys.exit(0)