""" Lemontropia Suite - Icon Browser UI GUI for browsing and exporting item icons. """ from pathlib import Path from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QListWidget, QListWidgetItem, QFileDialog, QMessageBox, QLineEdit, QComboBox, QProgressBar ) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QPixmap, QIcon from modules.icon_manager import IconManager class IconDownloadWorker(QThread): """Background worker for downloading icons.""" progress = pyqtSignal(int) finished_item = pyqtSignal(str, bool) finished_all = pyqtSignal() def __init__(self, icon_manager: IconManager, item_names: list, size: str): super().__init__() self.icon_manager = icon_manager self.item_names = item_names self.size = size def run(self): for i, item_name in enumerate(self.item_names): path = self.icon_manager.get_icon(item_name, self.size) self.progress.emit(int((i + 1) / len(self.item_names) * 100)) self.finished_item.emit(item_name, path is not None) self.finished_all.emit() class IconBrowserDialog(QDialog): """ Dialog for browsing, previewing, and exporting item icons. """ def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Icon Browser") self.setMinimumSize(600, 500) self.icon_manager = IconManager() self.current_preview = None self._setup_ui() self._load_cached_icons() def _setup_ui(self): layout = QVBoxLayout(self) # Search bar search_layout = QHBoxLayout() search_layout.addWidget(QLabel("Search:")) self.search_edit = QLineEdit() self.search_edit.setPlaceholderText("Enter item name...") self.search_edit.returnPressed.connect(self._on_search) search_layout.addWidget(self.search_edit) self.size_combo = QComboBox() self.size_combo.addItems(['small (32x32)', 'medium (64x64)', 'large (128x128)']) search_layout.addWidget(self.size_combo) self.search_btn = QPushButton("Download") self.search_btn.clicked.connect(self._on_search) search_layout.addWidget(self.search_btn) layout.addLayout(search_layout) # Progress bar (hidden initially) self.progress = QProgressBar() self.progress.setVisible(False) layout.addWidget(self.progress) # Main content content = QHBoxLayout() # Icon list self.icon_list = QListWidget() self.icon_list.itemClicked.connect(self._on_item_selected) content.addWidget(self.icon_list, 1) # Preview panel preview_panel = QVBoxLayout() self.preview_label = QLabel("No icon selected") self.preview_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.preview_label.setMinimumSize(200, 200) self.preview_label.setStyleSheet("background-color: #1a1a1a; border: 1px solid #333;") preview_panel.addWidget(self.preview_label) self.item_name_label = QLabel("") self.item_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter) preview_panel.addWidget(self.item_name_label) # Export buttons export_layout = QVBoxLayout() self.export_single_btn = QPushButton("Export This Icon") self.export_single_btn.clicked.connect(self._export_single) self.export_single_btn.setEnabled(False) export_layout.addWidget(self.export_single_btn) self.export_all_btn = QPushButton("Export All Icons") self.export_all_btn.clicked.connect(self._export_all) export_layout.addWidget(self.export_all_btn) self.clear_cache_btn = QPushButton("Clear Cache") self.clear_cache_btn.clicked.connect(self._clear_cache) export_layout.addWidget(self.clear_cache_btn) preview_panel.addLayout(export_layout) preview_panel.addStretch() content.addLayout(preview_panel, 1) layout.addLayout(content, 1) # Status bar self.status_label = QLabel("Ready") layout.addWidget(self.status_label) # Close button btn_layout = QHBoxLayout() btn_layout.addStretch() close_btn = QPushButton("Close") close_btn.clicked.connect(self.accept) btn_layout.addWidget(close_btn) layout.addLayout(btn_layout) def _load_cached_icons(self): """Load list of cached icons.""" self.icon_list.clear() cache_dir = self.icon_manager.wiki.cache_dir if cache_dir.exists(): for icon_file in sorted(cache_dir.glob("*.png")): item_name = icon_file.stem.split('_')[0] # Remove size suffix item = QListWidgetItem(item_name) item.setData(Qt.ItemDataRole.UserRole, str(icon_file)) self.icon_list.addItem(item) stats = self.icon_manager.get_cache_stats() self.status_label.setText(f"Cached icons: {stats['cached_icons']}") def _on_search(self): """Search and download icon.""" item_name = self.search_edit.text().strip() if not item_name: return size_text = self.size_combo.currentText() size = size_text.split(' ')[0] # 'small', 'medium', 'large' self.status_label.setText(f"Downloading: {item_name}...") # Download in background path = self.icon_manager.get_icon(item_name, size) if path: self.status_label.setText(f"Downloaded: {item_name}") self._load_cached_icons() # Refresh list # Select the new item for i in range(self.icon_list.count()): if item_name.lower() in self.icon_list.item(i).text().lower(): self.icon_list.setCurrentRow(i) self._on_item_selected(self.icon_list.item(i)) break else: QMessageBox.warning(self, "Not Found", f"Icon not found for: {item_name}\n\n" "The item may not exist on EntropiaWiki.") self.status_label.setText("Icon not found") def _on_item_selected(self, item: QListWidgetItem): """Preview selected icon.""" icon_path = item.data(Qt.ItemDataRole.UserRole) item_name = item.text() if icon_path and Path(icon_path).exists(): pixmap = QPixmap(icon_path) # Scale to fit preview while maintaining aspect ratio scaled = pixmap.scaled( 180, 180, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) self.preview_label.setPixmap(scaled) self.current_preview = icon_path self.item_name_label.setText(item_name) self.export_single_btn.setEnabled(True) def _export_single(self): """Export currently selected icon.""" if not self.current_preview: return item_name = self.item_name_label.text() filepath, _ = QFileDialog.getSaveFileName( self, "Export Icon", f"{item_name}.png", "PNG Images (*.png)"") if filepath: import shutil shutil.copy2(self.current_preview, filepath) QMessageBox.information(self, "Exported", f"Icon exported to:\n{filepath}") def _export_all(self): """Export all cached icons.""" export_dir = QFileDialog.getExistingDirectory(self, "Select Export Directory") if export_dir: results = self.icon_manager.batch_export_icons( [self.icon_list.item(i).text() for i in range(self.icon_list.count())], Path(export_dir) ) success_count = sum(1 for _, success in results if success) QMessageBox.information(self, "Export Complete", f"Exported {success_count}/{len(results)} icons") def _clear_cache(self): """Clear icon cache.""" reply = QMessageBox.question(self, "Clear Cache", "Delete all cached icons?\nThey will be re-downloaded when needed.", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: self.icon_manager.clear_cache() self._load_cached_icons() self.preview_label.setText("No icon selected") self.item_name_label.setText("") self.export_single_btn.setEnabled(False) class PriceTrackerDialog(QDialog): """ Dialog for managing item prices and markups. """ def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Price Tracker") self.setMinimumSize(500, 400) from modules.market_prices import ManualPriceTracker self.price_tracker = ManualPriceTracker() self._setup_ui() self._load_prices() def _setup_ui(self): layout = QVBoxLayout(self) # Price list layout.addWidget(QLabel("Tracked Item Prices:")) self.price_list = QListWidget() self.price_list.itemClicked.connect(self._on_item_selected) layout.addWidget(self.price_list) # Add/Edit panel form = QHBoxLayout() form.addWidget(QLabel("Item:")) self.item_edit = QLineEdit() form.addWidget(self.item_edit) form.addWidget(QLabel("MU %:")) self.mu_edit = QLineEdit() self.mu_edit.setMaximumWidth(60) form.addWidget(self.mu_edit) form.addWidget(QLabel("TT:")) self.tt_edit = QLineEdit() self.tt_edit.setMaximumWidth(80) form.addWidget(self.tt_edit) self.add_btn = QPushButton("Add/Update") self.add_btn.clicked.connect(self._add_price) form.addWidget(self.add_btn) layout.addLayout(form) # Buttons btn_layout = QHBoxLayout() btn_layout.addStretch() delete_btn = QPushButton("Delete Selected") delete_btn.clicked.connect(self._delete_selected) btn_layout.addWidget(delete_btn) close_btn = QPushButton("Close") close_btn.clicked.connect(self.accept) btn_layout.addWidget(close_btn) layout.addLayout(btn_layout) def _load_prices(self): """Load prices into list.""" self.price_list.clear() for item_name, price in self.price_tracker.prices.items(): display = f"{item_name} - {price.markup}% (MU: {price.mu_value:.2f} PED)" item = QListWidgetItem(display) item.setData(Qt.ItemDataRole.UserRole, item_name) self.price_list.addItem(item) def _on_item_selected(self, item: QListWidgetItem): """Fill form with selected item.""" item_name = item.data(Qt.ItemDataRole.UserRole) price = self.price_tracker.get_price(item_name) if price: self.item_edit.setText(item_name) self.mu_edit.setText(str(price.markup)) self.tt_edit.setText(str(price.tt_value)) def _add_price(self): """Add or update price.""" item_name = self.item_edit.text().strip() try: markup = float(self.mu_edit.text()) tt = float(self.tt_edit.text()) except ValueError: QMessageBox.warning(self, "Error", "Invalid numbers") return from decimal import Decimal self.price_tracker.set_price(item_name, Decimal(str(markup)), Decimal(str(tt))) self._load_prices() # Clear form self.item_edit.clear() self.mu_edit.clear() self.tt_edit.clear() def _delete_selected(self): """Delete selected price.""" item = self.price_list.currentItem() if item: item_name = item.data(Qt.ItemDataRole.UserRole) if item_name in self.price_tracker.prices: del self.price_tracker.prices[item_name] self.price_tracker._save_prices() self._load_prices() # Export dialogs __all__ = ['IconBrowserDialog', 'PriceTrackerDialog']