406 lines
14 KiB
Python
406 lines
14 KiB
Python
"""
|
|
Lemontropia Suite - TGA Icon Converter Dialog
|
|
UI for converting Entropia Universe cached .tga icons to PNG.
|
|
"""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from PyQt6.QtWidgets import (
|
|
QDialog, QVBoxLayout, QHBoxLayout, QGridLayout,
|
|
QLabel, QPushButton, QListWidget, QListWidgetItem,
|
|
QGroupBox, QProgressBar, QTextEdit, QFileDialog,
|
|
QMessageBox, QSpinBox, QCheckBox, QComboBox
|
|
)
|
|
from PyQt6.QtCore import Qt, QThread, pyqtSignal
|
|
from PyQt6.QtGui import QPixmap
|
|
|
|
from modules.tga_converter import TGAConverter
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TGAConvertWorker(QThread):
|
|
"""Background worker for TGA conversion."""
|
|
|
|
progress_update = pyqtSignal(str)
|
|
file_converted = pyqtSignal(str, str) # tga_name, output_path
|
|
conversion_complete = pyqtSignal(int, int) # success_count, total_count
|
|
conversion_error = pyqtSignal(str)
|
|
|
|
def __init__(self, converter: TGAConverter, cache_path: Optional[Path] = None,
|
|
output_format: str = 'png'):
|
|
super().__init__()
|
|
self.converter = converter
|
|
self.cache_path = cache_path
|
|
self.output_format = output_format
|
|
self._is_running = True
|
|
|
|
def run(self):
|
|
"""Run the conversion."""
|
|
try:
|
|
# Use provided cache path or find it
|
|
if self.cache_path and self.cache_path.exists():
|
|
cache_path = self.cache_path
|
|
self.progress_update.emit(f"Using cache folder: {cache_path}")
|
|
else:
|
|
self.progress_update.emit("Finding cache folder...")
|
|
cache_path = self.converter.find_cache_folder()
|
|
|
|
if not cache_path:
|
|
self.conversion_error.emit("Cache folder not found")
|
|
return
|
|
|
|
# Get list of TGA files
|
|
tga_files = list(cache_path.glob("*.tga"))
|
|
total = len(tga_files)
|
|
success = 0
|
|
|
|
self.progress_update.emit(f"Found {total} TGA files to convert to {self.output_format.upper()}")
|
|
|
|
for i, tga_path in enumerate(tga_files):
|
|
if not self._is_running:
|
|
break
|
|
|
|
self.progress_update.emit(f"[{i+1}/{total}] Converting: {tga_path.name}")
|
|
|
|
output_path = self.converter.convert_tga_to_png(tga_path, output_format=self.output_format)
|
|
if output_path:
|
|
success += 1
|
|
self.file_converted.emit(tga_path.name, str(output_path))
|
|
|
|
self.conversion_complete.emit(success, total)
|
|
|
|
except Exception as e:
|
|
self.conversion_error.emit(str(e))
|
|
|
|
def stop(self):
|
|
"""Stop the conversion."""
|
|
self._is_running = False
|
|
|
|
|
|
class TGAConverterDialog(QDialog):
|
|
"""
|
|
Dialog for converting Entropia Universe TGA icons to PNG.
|
|
"""
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle("🔧 TGA Icon Converter")
|
|
self.setMinimumSize(700, 600)
|
|
self.resize(800, 700)
|
|
|
|
self.converter = TGAConverter()
|
|
self.convert_worker: Optional[TGAConvertWorker] = None
|
|
self.converted_files = []
|
|
|
|
self._setup_ui()
|
|
self._apply_dark_theme()
|
|
|
|
# Auto-scan on open
|
|
self._scan_cache()
|
|
|
|
def _setup_ui(self):
|
|
"""Setup the dialog UI."""
|
|
layout = QVBoxLayout(self)
|
|
layout.setContentsMargins(15, 15, 15, 15)
|
|
layout.setSpacing(10)
|
|
|
|
# Header
|
|
header = QLabel("🔧 TGA Icon Converter")
|
|
header.setStyleSheet("font-size: 18px; font-weight: bold; color: #4caf50;")
|
|
layout.addWidget(header)
|
|
|
|
desc = QLabel(
|
|
"Convert Entropia Universe cached .tga icon files to PNG or WebP format.\n"
|
|
"The game stores item icons in the cache folder as .tga files.\n"
|
|
"WebP format is recommended for web uploads (smaller file size, better quality)."
|
|
)
|
|
desc.setStyleSheet("color: #888; padding: 5px;")
|
|
desc.setWordWrap(True)
|
|
layout.addWidget(desc)
|
|
|
|
# Cache folder info
|
|
cache_group = QGroupBox("📁 Cache Folder")
|
|
cache_layout = QVBoxLayout(cache_group)
|
|
|
|
self.cache_path_label = QLabel("Searching...")
|
|
self.cache_path_label.setStyleSheet("font-family: Consolas; color: #888;")
|
|
cache_layout.addWidget(self.cache_path_label)
|
|
|
|
cache_buttons = QHBoxLayout()
|
|
|
|
self.scan_btn = QPushButton("🔍 Scan Cache")
|
|
self.scan_btn.clicked.connect(self._scan_cache)
|
|
cache_buttons.addWidget(self.scan_btn)
|
|
|
|
browse_cache_btn = QPushButton("Browse...")
|
|
browse_cache_btn.clicked.connect(self._browse_cache_folder)
|
|
cache_buttons.addWidget(browse_cache_btn)
|
|
|
|
cache_buttons.addStretch()
|
|
cache_layout.addLayout(cache_buttons)
|
|
|
|
layout.addWidget(cache_group)
|
|
|
|
# TGA Files list
|
|
files_group = QGroupBox("📄 TGA Files Found")
|
|
files_layout = QVBoxLayout(files_group)
|
|
|
|
self.files_count_label = QLabel("No files found")
|
|
files_layout.addWidget(self.files_count_label)
|
|
|
|
self.files_list = QListWidget()
|
|
self.files_list.setMaximumHeight(150)
|
|
files_layout.addWidget(self.files_list)
|
|
|
|
layout.addWidget(files_group)
|
|
|
|
# Output settings
|
|
output_group = QGroupBox("💾 Output Settings")
|
|
output_layout = QHBoxLayout(output_group)
|
|
|
|
output_layout.addWidget(QLabel("Output folder:"))
|
|
|
|
self.output_path_label = QLabel(str(self.converter.output_dir))
|
|
self.output_path_label.setStyleSheet("font-family: Consolas; color: #888;")
|
|
output_layout.addWidget(self.output_path_label, 1)
|
|
|
|
output_browse = QPushButton("Browse...")
|
|
output_browse.clicked.connect(self._browse_output_folder)
|
|
output_layout.addWidget(output_browse)
|
|
|
|
output_layout.addSpacing(20)
|
|
|
|
# Format selector
|
|
output_layout.addWidget(QLabel("Format:"))
|
|
self.format_combo = QComboBox()
|
|
self.format_combo.addItem("PNG", "png")
|
|
self.format_combo.addItem("WebP (better for web)", "webp")
|
|
self.format_combo.currentIndexChanged.connect(self._on_format_changed)
|
|
output_layout.addWidget(self.format_combo)
|
|
|
|
layout.addWidget(output_group)
|
|
|
|
# Convert button
|
|
self.convert_btn = QPushButton("🚀 Convert All to PNG")
|
|
self.convert_btn.setMinimumHeight(50)
|
|
self.convert_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #0d47a1;
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
}
|
|
QPushButton:hover { background-color: #1565c0; }
|
|
QPushButton:disabled { background-color: #333; }
|
|
""")
|
|
self.convert_btn.clicked.connect(self._start_conversion)
|
|
layout.addWidget(self.convert_btn)
|
|
|
|
# Progress
|
|
self.progress_bar = QProgressBar()
|
|
self.progress_bar.setRange(0, 0) # Indeterminate
|
|
self.progress_bar.setVisible(False)
|
|
layout.addWidget(self.progress_bar)
|
|
|
|
self.status_label = QLabel("Ready")
|
|
self.status_label.setStyleSheet("color: #888; padding: 5px;")
|
|
layout.addWidget(self.status_label)
|
|
|
|
# Results
|
|
results_group = QGroupBox("✅ Converted Files")
|
|
results_layout = QVBoxLayout(results_group)
|
|
|
|
self.results_list = QListWidget()
|
|
results_layout.addWidget(self.results_list)
|
|
|
|
results_buttons = QHBoxLayout()
|
|
|
|
open_folder_btn = QPushButton("📂 Open Output Folder")
|
|
open_folder_btn.clicked.connect(self._open_output_folder)
|
|
results_buttons.addWidget(open_folder_btn)
|
|
|
|
results_buttons.addStretch()
|
|
results_layout.addLayout(results_buttons)
|
|
|
|
layout.addWidget(results_group)
|
|
|
|
# Close button
|
|
close_btn = QPushButton("Close")
|
|
close_btn.clicked.connect(self.accept)
|
|
layout.addWidget(close_btn)
|
|
|
|
def _scan_cache(self):
|
|
"""Scan for TGA files in cache."""
|
|
self.files_list.clear()
|
|
self.files_count_label.setText("Scanning...")
|
|
|
|
# Use manually set path if available, otherwise find it
|
|
if self.converter._cache_path and self.converter._cache_path.exists():
|
|
cache_path = self.converter._cache_path
|
|
else:
|
|
cache_path = self.converter.find_cache_folder()
|
|
|
|
if cache_path:
|
|
self.cache_path_label.setText(str(cache_path))
|
|
|
|
tga_files = list(cache_path.glob("*.tga"))
|
|
self.files_count_label.setText(f"Found {len(tga_files)} TGA files")
|
|
|
|
for tga_file in tga_files:
|
|
item = QListWidgetItem(tga_file.name)
|
|
# Try to get TGA info
|
|
tga_info = self.converter.read_tga_header(tga_file)
|
|
if tga_info:
|
|
item.setToolTip(f"{tga_info.width}x{tga_info.height}, {tga_info.pixel_depth}bpp")
|
|
self.files_list.addItem(item)
|
|
|
|
self.convert_btn.setEnabled(len(tga_files) > 0)
|
|
else:
|
|
self.cache_path_label.setText("❌ Not found")
|
|
self.files_count_label.setText("Cache folder not found")
|
|
self.convert_btn.setEnabled(False)
|
|
|
|
def _browse_cache_folder(self):
|
|
"""Browse for cache folder."""
|
|
dir_path = QFileDialog.getExistingDirectory(
|
|
self,
|
|
"Select Entropia Universe Cache Folder",
|
|
str(Path.home() / "Documents" / "Entropia Universe"),
|
|
QFileDialog.Option.ShowDirsOnly
|
|
)
|
|
|
|
if dir_path:
|
|
self.converter._cache_path = Path(dir_path)
|
|
self._scan_cache()
|
|
|
|
def _browse_output_folder(self):
|
|
"""Browse for output folder."""
|
|
dir_path = QFileDialog.getExistingDirectory(
|
|
self,
|
|
"Select Output Folder",
|
|
str(self.converter.output_dir),
|
|
QFileDialog.Option.ShowDirsOnly
|
|
)
|
|
|
|
if dir_path:
|
|
self.converter.output_dir = Path(dir_path)
|
|
self.output_path_label.setText(dir_path)
|
|
|
|
def _on_format_changed(self):
|
|
"""Handle output format change."""
|
|
output_format = self.format_combo.currentData()
|
|
if output_format == 'webp':
|
|
self.convert_btn.setText("🚀 Convert All to WebP")
|
|
else:
|
|
self.convert_btn.setText("🚀 Convert All to PNG")
|
|
|
|
def _start_conversion(self):
|
|
"""Start TGA to PNG conversion."""
|
|
if self.convert_worker and self.convert_worker.isRunning():
|
|
QMessageBox.warning(self, "Busy", "Conversion already in progress")
|
|
return
|
|
|
|
self.convert_btn.setEnabled(False)
|
|
self.progress_bar.setVisible(True)
|
|
self.results_list.clear()
|
|
self.converted_files = []
|
|
|
|
# Start worker with the selected cache path and format
|
|
cache_path = self.converter._cache_path if self.converter._cache_path else None
|
|
output_format = self.format_combo.currentData()
|
|
self.convert_worker = TGAConvertWorker(self.converter, cache_path, output_format)
|
|
self.convert_worker.progress_update.connect(self._on_progress)
|
|
self.convert_worker.file_converted.connect(self._on_file_converted)
|
|
self.convert_worker.conversion_complete.connect(self._on_conversion_complete)
|
|
self.convert_worker.conversion_error.connect(self._on_conversion_error)
|
|
self.convert_worker.start()
|
|
|
|
def _on_progress(self, message: str):
|
|
"""Handle progress update."""
|
|
self.status_label.setText(message)
|
|
|
|
def _on_file_converted(self, tga_name: str, png_path: str):
|
|
"""Handle file converted."""
|
|
self.converted_files.append(png_path)
|
|
item = QListWidgetItem(f"✅ {tga_name} → {Path(png_path).name}")
|
|
self.results_list.addItem(item)
|
|
|
|
def _on_conversion_complete(self, success: int, total: int):
|
|
"""Handle conversion complete."""
|
|
self.progress_bar.setVisible(False)
|
|
self.convert_btn.setEnabled(True)
|
|
self.status_label.setText(f"Complete: {success}/{total} files converted")
|
|
|
|
QMessageBox.information(
|
|
self,
|
|
"Conversion Complete",
|
|
f"Successfully converted {success} of {total} files\n\n"
|
|
f"Output: {self.converter.output_dir}"
|
|
)
|
|
|
|
def _on_conversion_error(self, error: str):
|
|
"""Handle conversion error."""
|
|
self.progress_bar.setVisible(False)
|
|
self.convert_btn.setEnabled(True)
|
|
self.status_label.setText(f"Error: {error}")
|
|
|
|
QMessageBox.critical(self, "Error", error)
|
|
|
|
def _open_output_folder(self):
|
|
"""Open output folder in file explorer."""
|
|
import os
|
|
import platform
|
|
|
|
output_path = str(self.converter.output_dir)
|
|
|
|
if platform.system() == "Windows":
|
|
os.startfile(output_path)
|
|
elif platform.system() == "Darwin": # macOS
|
|
import subprocess
|
|
subprocess.run(["open", output_path])
|
|
else: # Linux
|
|
import subprocess
|
|
subprocess.run(["xdg-open", output_path])
|
|
|
|
def closeEvent(self, event):
|
|
"""Handle dialog close."""
|
|
if self.convert_worker and self.convert_worker.isRunning():
|
|
self.convert_worker.stop()
|
|
self.convert_worker.wait(1000)
|
|
event.accept()
|
|
|
|
def _apply_dark_theme(self):
|
|
"""Apply dark theme."""
|
|
self.setStyleSheet("""
|
|
QDialog {
|
|
background-color: #1e1e1e;
|
|
color: #e0e0e0;
|
|
}
|
|
QGroupBox {
|
|
font-weight: bold;
|
|
border: 1px solid #444;
|
|
border-radius: 6px;
|
|
margin-top: 10px;
|
|
padding-top: 10px;
|
|
}
|
|
QListWidget {
|
|
background-color: #252525;
|
|
border: 1px solid #444;
|
|
color: #e0e0e0;
|
|
}
|
|
QPushButton {
|
|
background-color: #0d47a1;
|
|
border: 1px solid #1565c0;
|
|
border-radius: 4px;
|
|
padding: 6px 12px;
|
|
color: white;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #1565c0;
|
|
}
|
|
QLabel {
|
|
color: #e0e0e0;
|
|
}
|
|
""") |