361 lines
12 KiB
Python
361 lines
12 KiB
Python
"""
|
|
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']
|