Lemontropia-Suite/ui/armor_selector.py

210 lines
8.0 KiB
Python

"""
Searchable Armor Selector for Lemontropia Suite
Browse and search armors from Entropia Nexus API
"""
from decimal import Decimal
from PyQt6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton,
QTreeWidget, QTreeWidgetItem, QHeaderView, QLabel, QDialogButtonBox,
QProgressBar, QGroupBox, QFormLayout
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from typing import Optional, List
from core.nexus_full_api import get_nexus_api, NexusArmor
class ArmorLoaderThread(QThread):
"""Background thread for loading armors from API."""
armors_loaded = pyqtSignal(list)
error_occurred = pyqtSignal(str)
def run(self):
try:
api = get_nexus_api()
armors = api.get_all_armors()
self.armors_loaded.emit(armors)
except Exception as e:
self.error_occurred.emit(str(e))
class ArmorSelectorDialog(QDialog):
"""Dialog for selecting armors from Entropia Nexus API with search."""
armor_selected = pyqtSignal(NexusArmor)
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Select Armor - Entropia Nexus")
self.setMinimumSize(1000, 700)
self.all_armors: List[NexusArmor] = []
self.selected_armor: Optional[NexusArmor] = None
self._setup_ui()
self._load_data()
def _setup_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(10)
# Status and search
self.status_label = QLabel("Loading armors from Entropia Nexus...")
layout.addWidget(self.status_label)
self.progress = QProgressBar()
self.progress.setRange(0, 0) # Indeterminate
layout.addWidget(self.progress)
# Search
search_layout = QHBoxLayout()
search_layout.addWidget(QLabel("Search:"))
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("Type to search armors...")
self.search_input.textChanged.connect(self._on_search)
search_layout.addWidget(self.search_input)
self.clear_btn = QPushButton("Clear")
self.clear_btn.clicked.connect(self._clear_search)
search_layout.addWidget(self.clear_btn)
layout.addLayout(search_layout)
# Results tree
self.results_tree = QTreeWidget()
self.results_tree.setHeaderLabels([
"Name", "Type", "Durability",
"Impact", "Cut", "Stab", "Burn", "Cold", "Acid", "Electric",
"Total Prot"
])
header = self.results_tree.header()
header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
header.setStretchLastSection(False)
self.results_tree.itemSelectionChanged.connect(self._on_selection_changed)
self.results_tree.itemDoubleClicked.connect(self._on_double_click)
layout.addWidget(self.results_tree)
# Preview panel
self.preview_group = QGroupBox("Armor Preview")
preview_layout = QFormLayout(self.preview_group)
self.preview_name = QLabel("-")
self.preview_type = QLabel("-")
self.preview_durability = QLabel("-")
self.preview_protection = QLabel("-")
preview_layout.addRow("Name:", self.preview_name)
preview_layout.addRow("Type:", self.preview_type)
preview_layout.addRow("Durability:", self.preview_durability)
preview_layout.addRow("Protection:", self.preview_protection)
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.setEnabled(False)
layout.addWidget(buttons)
def _load_data(self):
"""Load armors in background thread."""
self.loader = ArmorLoaderThread()
self.loader.armors_loaded.connect(self._on_armors_loaded)
self.loader.error_occurred.connect(self._on_load_error)
self.loader.start()
def _on_armors_loaded(self, armors: List[NexusArmor]):
"""Handle loaded armors."""
self.all_armors = armors
self.status_label.setText(f"Loaded {len(armors)} armors from Entropia Nexus")
self.progress.setRange(0, 100)
self.progress.setValue(100)
self._populate_results(armors)
def _on_load_error(self, error: str):
"""Handle load error."""
self.status_label.setText(f"Error loading armors: {error}")
self.progress.setRange(0, 100)
self.progress.setValue(0)
def _populate_results(self, armors: List[NexusArmor]):
"""Populate results tree."""
self.results_tree.clear()
for armor in armors:
item = QTreeWidgetItem()
item.setText(0, armor.name)
item.setText(1, armor.type)
item.setText(2, str(armor.durability))
item.setText(3, str(armor.protection_impact))
item.setText(4, str(armor.protection_cut))
item.setText(5, str(armor.protection_stab))
item.setText(6, str(armor.protection_burn))
item.setText(7, str(armor.protection_cold))
item.setText(8, str(armor.protection_acid))
item.setText(9, str(armor.protection_electric))
total = armor.protection_impact + armor.protection_cut + armor.protection_stab
item.setText(10, str(total))
item.setData(0, Qt.ItemDataRole.UserRole, armor)
self.results_tree.addTopLevelItem(item)
def _on_search(self, text: str):
"""Handle search text change."""
if not text:
self._populate_results(self.all_armors)
return
query = text.lower()
filtered = [a for a in self.all_armors if query in a.name.lower()]
self._populate_results(filtered)
self.status_label.setText(f"Found {len(filtered)} armors matching '{text}'")
def _clear_search(self):
"""Clear search."""
self.search_input.clear()
self._populate_results(self.all_armors)
self.status_label.setText(f"Showing all {len(self.all_armors)} armors")
def _on_selection_changed(self):
"""Handle selection change."""
items = self.results_tree.selectedItems()
if items:
self.selected_armor = items[0].data(0, Qt.ItemDataRole.UserRole)
self.ok_button.setEnabled(True)
self._update_preview(self.selected_armor)
else:
self.selected_armor = None
self.ok_button.setEnabled(False)
def _update_preview(self, armor: NexusArmor):
"""Update preview panel."""
self.preview_name.setText(armor.name)
self.preview_type.setText(armor.type)
self.preview_durability.setText(f"{armor.durability} (~{Decimal('20') / (Decimal('1') - Decimal(armor.durability) / Decimal('100000')):.2f} hp/pec)")
prot_parts = []
if armor.protection_impact > 0:
prot_parts.append(f"Imp:{armor.protection_impact}")
if armor.protection_cut > 0:
prot_parts.append(f"Cut:{armor.protection_cut}")
if armor.protection_stab > 0:
prot_parts.append(f"Stab:{armor.protection_stab}")
if armor.protection_burn > 0:
prot_parts.append(f"Burn:{armor.protection_burn}")
if armor.protection_cold > 0:
prot_parts.append(f"Cold:{armor.protection_cold}")
self.preview_protection.setText(", ".join(prot_parts) if prot_parts else "None")
def _on_double_click(self, item, column):
"""Handle double click."""
self._on_accept()
def _on_accept(self):
"""Handle OK button."""
if self.selected_armor:
self.armor_selected.emit(self.selected_armor)
self.accept()