Beginning of gui

This commit is contained in:
dazedanon 2025-07-18 15:12:28 -05:00
parent 8606a5c7b8
commit b44633f13c
10 changed files with 2639 additions and 0 deletions

23
gui/__init__.py Normal file
View file

@ -0,0 +1,23 @@
"""
DazedMTLTool GUI Package
"""
__version__ = "1.0.0"
__author__ = "DazedTranslations"
# Package imports
from .main import DazedMTLGUI
from .config_tab import ConfigTab
from .rpgmaker_tab import RPGMakerTab
from .other_modules_tab import OtherModulesTab
from .log_viewer import LogViewer
from .file_manager import FileManager
__all__ = [
"DazedMTLGUI",
"ConfigTab",
"RPGMakerTab",
"OtherModulesTab",
"LogViewer",
"FileManager"
]

220
gui/config_integration.py Normal file
View file

@ -0,0 +1,220 @@
"""
Configuration Integration Helper
Updates the actual module files with GUI configuration settings
"""
import re
import os
from pathlib import Path
from typing import Dict, Any
class ConfigIntegration:
"""Helper class to integrate GUI configuration with module files."""
def __init__(self):
self.modules_dir = Path("modules")
def update_rpgmaker_config(self, config: Dict[str, Any]) -> bool:
"""Update rpgmakermvmz.py with configuration from GUI."""
module_path = self.modules_dir / "rpgmakermvmz.py"
if not module_path.exists():
raise FileNotFoundError(f"Module file not found: {module_path}")
try:
# Read the current module file
with open(module_path, 'r', encoding='utf-8') as f:
content = f.read()
# Create backup
backup_path = module_path.with_suffix('.py.backup')
with open(backup_path, 'w', encoding='utf-8') as f:
f.write(content)
# Update configuration values
updated_content = self._update_config_values(content, config)
# Write back to file
with open(module_path, 'w', encoding='utf-8') as f:
f.write(updated_content)
return True
except Exception as e:
# Restore from backup if something went wrong
if backup_path.exists():
with open(backup_path, 'r', encoding='utf-8') as f:
content = f.read()
with open(module_path, 'w', encoding='utf-8') as f:
f.write(content)
raise e
def _update_config_values(self, content: str, config: Dict[str, Any]) -> str:
"""Update configuration values in the module content."""
lines = content.split('\n')
updated_lines = []
# Track which configurations we found and updated
found_configs = set()
for line in lines:
updated_line = line
# Look for configuration assignments
for config_key, config_value in config.items():
# Match lines like: CONFIG_NAME = value # comment
pattern = rf'^({re.escape(config_key)})\s*=\s*.*?(#.*)?$'
match = re.match(pattern, line.strip())
if match:
# Preserve the comment if it exists
comment = match.group(2) if match.group(2) else ""
if comment:
comment = " " + comment
# Create the new line with proper formatting
updated_line = f"{config_key} = {config_value}{comment}"
found_configs.add(config_key)
break
updated_lines.append(updated_line)
# Check if all configurations were found
missing_configs = set(config.keys()) - found_configs
if missing_configs:
print(f"Warning: Could not find these configurations in module: {missing_configs}")
return '\n'.join(updated_lines)
def update_env_file(self, config: Dict[str, Any], env_path: Path = None) -> bool:
"""Update .env file with configuration."""
if env_path is None:
env_path = Path(".env")
try:
from dotenv import set_key
for key, value in config.items():
set_key(env_path, key, str(value))
return True
except Exception as e:
print(f"Error updating .env file: {e}")
return False
def read_current_config(self, module_path: Path = None) -> Dict[str, Any]:
"""Read current configuration from module file."""
if module_path is None:
module_path = self.modules_dir / "rpgmakermvmz.py"
if not module_path.exists():
return {}
config = {}
try:
with open(module_path, 'r', encoding='utf-8') as f:
content = f.read()
# Find configuration lines
config_patterns = [
r'^(FIRSTLINESPEAKERS|FACENAME101|NAMES|BRFLAG|FIXTEXTWRAP|IGNORETLTEXT)\s*=\s*(True|False)',
r'^(CODE\d+)\s*=\s*(True|False)'
]
for line in content.split('\n'):
line = line.strip()
for pattern in config_patterns:
match = re.match(pattern, line)
if match:
key = match.group(1)
value = match.group(2) == 'True'
config[key] = value
except Exception as e:
print(f"Error reading configuration from {module_path}: {e}")
return config
def validate_module_syntax(self, module_path: Path = None) -> bool:
"""Validate that the module file has correct Python syntax."""
if module_path is None:
module_path = self.modules_dir / "rpgmakermvmz.py"
try:
with open(module_path, 'r', encoding='utf-8') as f:
content = f.read()
# Try to compile the code
compile(content, str(module_path), 'exec')
return True
except SyntaxError as e:
print(f"Syntax error in {module_path}: {e}")
return False
except Exception as e:
print(f"Error validating {module_path}: {e}")
return False
def create_config_template(self) -> Dict[str, Any]:
"""Create a template configuration with default values."""
return {
# General config
"FIRSTLINESPEAKERS": False,
"FACENAME101": False,
"NAMES": False,
"BRFLAG": False,
"FIXTEXTWRAP": True,
"IGNORETLTEXT": False,
# Main dialogue codes
"CODE401": True,
"CODE405": True,
"CODE102": True,
# Optional codes
"CODE101": False,
"CODE408": False,
# Variable codes
"CODE122": False,
# Other codes
"CODE355655": False,
"CODE357": False,
"CODE657": False,
"CODE356": False,
"CODE320": False,
"CODE324": False,
"CODE111": False,
"CODE108": False
}
def get_config_descriptions(self) -> Dict[str, str]:
"""Get descriptions for all configuration options."""
return {
"FIRSTLINESPEAKERS": "If 1st line of 401 is a speaker, set to True",
"FACENAME101": "Find Speakers in 101 Codes based on Face Name",
"NAMES": "Output a list of all the character names found",
"BRFLAG": "If the game uses <br> instead of newlines",
"FIXTEXTWRAP": "Overwrites textwrap for better formatting",
"IGNORETLTEXT": "Ignores all translated text",
"CODE401": "Show Text - Main dialogue content",
"CODE405": "Show Text (Scrolling) - Longer dialogue",
"CODE102": "Show Choices - Player choice options",
"CODE101": "Character Names - Turn on when names exist in 101",
"CODE408": "Comments - WARNING: Can inflate costs significantly",
"CODE122": "Control Variables - Text stored in variables",
"CODE355655": "Scripts - Text within script commands",
"CODE357": "Picture Text - Text displayed on pictures",
"CODE657": "Picture Text Extended - Extended picture text",
"CODE356": "Plugin Commands - Plugin command parameters",
"CODE320": "Change Name Input - Name input prompts",
"CODE324": "Change Nickname - Nickname changes",
"CODE111": "Conditional Branch - Conditional text",
"CODE108": "Comments - Comment blocks"
}

503
gui/config_tab.py Normal file
View file

@ -0,0 +1,503 @@
"""
Configuration Tab - Handles environment variables and global settings
"""
import os
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLineEdit,
QSpinBox, QDoubleSpinBox, QComboBox, QPushButton, QGroupBox,
QLabel, QFileDialog, QMessageBox, QScrollArea, QTextEdit,
QCheckBox, QApplication
)
from PyQt5.QtCore import Qt, pyqtSignal
from dotenv import load_dotenv, set_key
class ConfigTab(QWidget):
"""Configuration tab for managing environment variables and global settings."""
config_changed = pyqtSignal()
def __init__(self):
super().__init__()
self.env_file_path = Path(".env")
self.init_ui()
self.load_from_env()
def init_ui(self):
"""Initialize the user interface."""
main_layout = QVBoxLayout()
# Create scroll area
scroll_area = QScrollArea()
scroll_content = QWidget()
scroll_layout = QVBoxLayout()
# API Configuration Group
api_group = self.create_api_group()
scroll_layout.addWidget(api_group)
# Translation Settings Group
translation_group = self.create_translation_group()
scroll_layout.addWidget(translation_group)
# Performance Settings Group
performance_group = self.create_performance_group()
scroll_layout.addWidget(performance_group)
# Text Formatting Group
formatting_group = self.create_formatting_group()
scroll_layout.addWidget(formatting_group)
# Custom API Settings Group
custom_api_group = self.create_custom_api_group()
scroll_layout.addWidget(custom_api_group)
# UI Settings Group
ui_group = self.create_ui_settings_group()
scroll_layout.addWidget(ui_group)
# Buttons
button_layout = QHBoxLayout()
self.save_button = QPushButton("Save Configuration")
self.save_button.clicked.connect(self.save_to_env)
self.load_button = QPushButton("Load from File")
self.load_button.clicked.connect(self.load_from_file_dialog)
self.reset_button = QPushButton("Reset to Defaults")
self.reset_button.clicked.connect(self.reset_to_defaults)
button_layout.addWidget(self.save_button)
button_layout.addWidget(self.load_button)
button_layout.addWidget(self.reset_button)
button_layout.addStretch()
scroll_layout.addLayout(button_layout)
scroll_content.setLayout(scroll_layout)
scroll_area.setWidget(scroll_content)
scroll_area.setWidgetResizable(True)
main_layout.addWidget(scroll_area)
self.setLayout(main_layout)
def create_api_group(self):
"""Create API configuration group."""
group = QGroupBox("API Configuration")
layout = QFormLayout()
# API URL
self.api_url_edit = QLineEdit()
self.api_url_edit.setPlaceholderText("Leave blank for OpenAI API")
layout.addRow("API URL:", self.api_url_edit)
# API Key
self.api_key_edit = QLineEdit()
self.api_key_edit.setEchoMode(QLineEdit.Password)
self.api_key_edit.setPlaceholderText("Enter your API key")
layout.addRow("API Key:", self.api_key_edit)
# Organization
self.organization_edit = QLineEdit()
self.organization_edit.setPlaceholderText("Organization key")
layout.addRow("Organization:", self.organization_edit)
# Model
self.model_combo = QComboBox()
self.model_combo.setEditable(True)
self.model_combo.addItems([
"gpt-3.5-turbo",
"gpt-3.5-turbo-1106",
"gpt-4",
"gpt-4-1106-preview",
"gpt-4-turbo",
"claude-3-sonnet-20240229",
"claude-3-opus-20240229"
])
layout.addRow("Model:", self.model_combo)
group.setLayout(layout)
return group
def create_translation_group(self):
"""Create translation settings group."""
group = QGroupBox("Translation Settings")
layout = QFormLayout()
# Language
self.language_combo = QComboBox()
self.language_combo.addItems([
"English", "Spanish", "French", "German", "Italian",
"Portuguese", "Russian", "Chinese", "Korean", "Japanese"
])
layout.addRow("Target Language:", self.language_combo)
# Timeout
self.timeout_spin = QSpinBox()
self.timeout_spin.setRange(30, 300)
self.timeout_spin.setValue(120)
self.timeout_spin.setSuffix(" seconds")
layout.addRow("Timeout:", self.timeout_spin)
group.setLayout(layout)
return group
def create_performance_group(self):
"""Create performance settings group."""
group = QGroupBox("Performance Settings")
layout = QFormLayout()
# File Threads
self.file_threads_spin = QSpinBox()
self.file_threads_spin.setRange(1, 10)
self.file_threads_spin.setValue(1)
layout.addRow("File Threads:", self.file_threads_spin)
# Threads per File
self.threads_spin = QSpinBox()
self.threads_spin.setRange(1, 20)
self.threads_spin.setValue(1)
layout.addRow("Threads per File:", self.threads_spin)
# Batch Size
self.batch_size_spin = QSpinBox()
self.batch_size_spin.setRange(1, 100)
self.batch_size_spin.setValue(10)
layout.addRow("Batch Size:", self.batch_size_spin)
# Frequency Penalty
self.frequency_penalty_spin = QDoubleSpinBox()
self.frequency_penalty_spin.setRange(0.0, 2.0)
self.frequency_penalty_spin.setSingleStep(0.1)
self.frequency_penalty_spin.setValue(0.2)
layout.addRow("Frequency Penalty:", self.frequency_penalty_spin)
group.setLayout(layout)
return group
def create_formatting_group(self):
"""Create text formatting settings group."""
group = QGroupBox("Text Formatting")
layout = QFormLayout()
# Dialogue Width
self.width_spin = QSpinBox()
self.width_spin.setRange(20, 200)
self.width_spin.setValue(60)
self.width_spin.setSuffix(" characters")
layout.addRow("Dialogue Width:", self.width_spin)
# List Width
self.list_width_spin = QSpinBox()
self.list_width_spin.setRange(20, 200)
self.list_width_spin.setValue(100)
self.list_width_spin.setSuffix(" characters")
layout.addRow("List Width:", self.list_width_spin)
# Note Width
self.note_width_spin = QSpinBox()
self.note_width_spin.setRange(20, 200)
self.note_width_spin.setValue(75)
self.note_width_spin.setSuffix(" characters")
layout.addRow("Note Width:", self.note_width_spin)
group.setLayout(layout)
return group
def create_custom_api_group(self):
"""Create custom API settings group."""
group = QGroupBox("Custom API Pricing")
layout = QFormLayout()
# Input Cost
self.input_cost_spin = QDoubleSpinBox()
self.input_cost_spin.setRange(0.0, 1.0)
self.input_cost_spin.setDecimals(4)
self.input_cost_spin.setSingleStep(0.0001)
self.input_cost_spin.setValue(0.002)
self.input_cost_spin.setSuffix(" per 1K tokens")
layout.addRow("Input Cost:", self.input_cost_spin)
# Output Cost
self.output_cost_spin = QDoubleSpinBox()
self.output_cost_spin.setRange(0.0, 1.0)
self.output_cost_spin.setDecimals(4)
self.output_cost_spin.setSingleStep(0.0001)
self.output_cost_spin.setValue(0.002)
self.output_cost_spin.setSuffix(" per 1K tokens")
layout.addRow("Output Cost:", self.output_cost_spin)
group.setLayout(layout)
return group
def create_ui_settings_group(self):
"""Create UI settings group."""
group = QGroupBox("User Interface Settings")
layout = QFormLayout()
# Font Scale
self.font_scale_spin = QDoubleSpinBox()
self.font_scale_spin.setRange(0.5, 3.0)
self.font_scale_spin.setSingleStep(0.1)
self.font_scale_spin.setValue(1.0)
self.font_scale_spin.setDecimals(1)
self.font_scale_spin.setSuffix("x")
self.font_scale_spin.setToolTip("Scale factor for all fonts in the GUI (1.0 = normal, 1.5 = 150% larger)")
self.font_scale_spin.valueChanged.connect(self.on_font_scale_changed)
# Font scale presets
font_layout = QHBoxLayout()
font_layout.addWidget(self.font_scale_spin)
# Quick preset buttons
small_btn = QPushButton("Small")
small_btn.clicked.connect(lambda: self.font_scale_spin.setValue(0.8))
small_btn.setMaximumWidth(60)
normal_btn = QPushButton("Normal")
normal_btn.clicked.connect(lambda: self.font_scale_spin.setValue(1.0))
normal_btn.setMaximumWidth(60)
large_btn = QPushButton("Large")
large_btn.clicked.connect(lambda: self.font_scale_spin.setValue(1.5))
large_btn.setMaximumWidth(60)
xlarge_btn = QPushButton("X-Large")
xlarge_btn.clicked.connect(lambda: self.font_scale_spin.setValue(2.0))
xlarge_btn.setMaximumWidth(60)
font_layout.addWidget(small_btn)
font_layout.addWidget(normal_btn)
font_layout.addWidget(large_btn)
font_layout.addWidget(xlarge_btn)
layout.addRow("Font Scale:", font_layout)
# DPI Awareness
self.dpi_aware_cb = QCheckBox("High DPI Scaling")
self.dpi_aware_cb.setToolTip("Enable automatic scaling for high DPI displays")
self.dpi_aware_cb.setChecked(True)
layout.addRow("", self.dpi_aware_cb)
# Theme selection
self.theme_combo = QComboBox()
self.theme_combo.addItems(["Dark", "Light", "System"])
self.theme_combo.setCurrentText("Dark")
layout.addRow("Theme:", self.theme_combo)
# Show font tip
self.show_font_tip_cb = QCheckBox("Show font size tip on startup")
self.show_font_tip_cb.setToolTip("Display helpful font sizing information when the GUI starts")
self.show_font_tip_cb.setChecked(True)
layout.addRow("", self.show_font_tip_cb)
group.setLayout(layout)
return group
def on_font_scale_changed(self, value):
"""Handle font scale change."""
self.config_changed.emit()
# Apply font scaling immediately
self.apply_font_scaling(value)
def apply_font_scaling(self, scale_factor):
"""Apply font scaling to the entire application."""
app = QApplication.instance()
if app:
# Get the default font
font = app.font()
base_size = 9 # Base font size
new_size = int(base_size * scale_factor)
font.setPointSize(new_size)
app.setFont(font)
# Emit signal to notify other components
self.config_changed.emit()
def load_from_env(self):
"""Load configuration from .env file."""
if self.env_file_path.exists():
load_dotenv(self.env_file_path)
# Load API settings
self.api_url_edit.setText(os.getenv("api", ""))
self.api_key_edit.setText(os.getenv("key", ""))
self.organization_edit.setText(os.getenv("organization", ""))
self.model_combo.setCurrentText(os.getenv("model", "gpt-4"))
# Load translation settings
self.language_combo.setCurrentText(os.getenv("language", "English"))
self.timeout_spin.setValue(int(os.getenv("timeout", "120")))
# Load performance settings
self.file_threads_spin.setValue(int(os.getenv("fileThreads", "1")))
self.threads_spin.setValue(int(os.getenv("threads", "1")))
self.batch_size_spin.setValue(int(os.getenv("batchsize", "10")))
self.frequency_penalty_spin.setValue(float(os.getenv("frequency_penalty", "0.2")))
# Load formatting settings
self.width_spin.setValue(int(os.getenv("width", "60")))
self.list_width_spin.setValue(int(os.getenv("listWidth", "100")))
self.note_width_spin.setValue(int(os.getenv("noteWidth", "75")))
# Load custom API settings
self.input_cost_spin.setValue(float(os.getenv("input_cost", "0.002")))
self.output_cost_spin.setValue(float(os.getenv("output_cost", "0.002")))
# Load UI settings
self.font_scale_spin.setValue(float(os.getenv("font_scale", "1.0")))
self.dpi_aware_cb.setChecked(os.getenv("dpi_aware", "true").lower() == "true")
self.theme_combo.setCurrentText(os.getenv("theme", "Dark"))
self.show_font_tip_cb.setChecked(os.getenv("show_font_tip", "true").lower() == "true")
# Apply font scaling
self.apply_font_scaling(self.font_scale_spin.value())
def save_to_env(self):
"""Save configuration to .env file."""
try:
# Ensure .env file exists
if not self.env_file_path.exists():
self.env_file_path.touch()
# Save API settings
set_key(self.env_file_path, "api", self.api_url_edit.text())
set_key(self.env_file_path, "key", self.api_key_edit.text())
set_key(self.env_file_path, "organization", self.organization_edit.text())
set_key(self.env_file_path, "model", self.model_combo.currentText())
# Save translation settings
set_key(self.env_file_path, "language", self.language_combo.currentText())
set_key(self.env_file_path, "timeout", str(self.timeout_spin.value()))
# Save performance settings
set_key(self.env_file_path, "fileThreads", str(self.file_threads_spin.value()))
set_key(self.env_file_path, "threads", str(self.threads_spin.value()))
set_key(self.env_file_path, "batchsize", str(self.batch_size_spin.value()))
set_key(self.env_file_path, "frequency_penalty", str(self.frequency_penalty_spin.value()))
# Save formatting settings
set_key(self.env_file_path, "width", str(self.width_spin.value()))
set_key(self.env_file_path, "listWidth", str(self.list_width_spin.value()))
set_key(self.env_file_path, "noteWidth", str(self.note_width_spin.value()))
# Save custom API settings
set_key(self.env_file_path, "input_cost", str(self.input_cost_spin.value()))
set_key(self.env_file_path, "output_cost", str(self.output_cost_spin.value()))
# Save UI settings
set_key(self.env_file_path, "font_scale", str(self.font_scale_spin.value()))
set_key(self.env_file_path, "dpi_aware", str(self.dpi_aware_cb.isChecked()).lower())
set_key(self.env_file_path, "theme", self.theme_combo.currentText())
set_key(self.env_file_path, "show_font_tip", str(self.show_font_tip_cb.isChecked()).lower())
QMessageBox.information(self, "Success", "Configuration saved successfully!")
self.config_changed.emit()
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to save configuration:\n{str(e)}")
def load_from_file_dialog(self):
"""Load configuration from a file via dialog."""
file_path, _ = QFileDialog.getOpenFileName(
self, "Load Configuration", "", "Environment Files (*.env);;All Files (*)"
)
if file_path:
self.load_from_file(file_path)
def load_from_file(self, file_path):
"""Load configuration from a specific file."""
try:
load_dotenv(file_path, override=True)
self.load_from_env()
QMessageBox.information(self, "Success", f"Configuration loaded from {Path(file_path).name}")
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to load configuration:\n{str(e)}")
def reset_to_defaults(self):
"""Reset all settings to default values."""
# API settings
self.api_url_edit.clear()
self.api_key_edit.clear()
self.organization_edit.clear()
self.model_combo.setCurrentText("gpt-4")
# Translation settings
self.language_combo.setCurrentText("English")
self.timeout_spin.setValue(120)
# Performance settings
self.file_threads_spin.setValue(1)
self.threads_spin.setValue(1)
self.batch_size_spin.setValue(10)
self.frequency_penalty_spin.setValue(0.2)
# Formatting settings
self.width_spin.setValue(60)
self.list_width_spin.setValue(100)
self.note_width_spin.setValue(75)
# Custom API settings
self.input_cost_spin.setValue(0.002)
self.output_cost_spin.setValue(0.002)
# UI settings
self.font_scale_spin.setValue(1.0)
self.dpi_aware_cb.setChecked(True)
self.theme_combo.setCurrentText("Dark")
self.show_font_tip_cb.setChecked(True)
# Apply font scaling
self.apply_font_scaling(1.0)
def get_config(self):
"""Get current configuration as dictionary."""
return {
"api": self.api_url_edit.text(),
"key": self.api_key_edit.text(),
"organization": self.organization_edit.text(),
"model": self.model_combo.currentText(),
"language": self.language_combo.currentText(),
"timeout": self.timeout_spin.value(),
"fileThreads": self.file_threads_spin.value(),
"threads": self.threads_spin.value(),
"batchsize": self.batch_size_spin.value(),
"frequency_penalty": self.frequency_penalty_spin.value(),
"width": self.width_spin.value(),
"listWidth": self.list_width_spin.value(),
"noteWidth": self.note_width_spin.value(),
"input_cost": self.input_cost_spin.value(),
"output_cost": self.output_cost_spin.value(),
"font_scale": self.font_scale_spin.value(),
"dpi_aware": self.dpi_aware_cb.isChecked(),
"theme": self.theme_combo.currentText(),
"show_font_tip": self.show_font_tip_cb.isChecked()
}
def validate(self):
"""Validate the current configuration."""
errors = []
# Check required fields
if not self.api_key_edit.text().strip():
errors.append("API Key is required")
if not self.model_combo.currentText().strip():
errors.append("Model is required")
# Check numeric ranges
if self.timeout_spin.value() < 30:
errors.append("Timeout should be at least 30 seconds")
if self.threads_spin.value() > 10 and "gpt-4" in self.model_combo.currentText().lower():
errors.append("Too many threads for GPT-4 - recommended: 1-2")
if errors:
QMessageBox.warning(self, "Validation Errors", "\n".join(errors))
return False
return True

405
gui/file_manager.py Normal file
View file

@ -0,0 +1,405 @@
"""
File Manager - Handle input and output files
"""
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QTreeWidget, QTreeWidgetItem,
QPushButton, QLabel, QFileDialog, QMessageBox, QGroupBox,
QSplitter, QTextEdit, QProgressBar, QCheckBox
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import json
class FileManager(QWidget):
"""Widget for managing input and output files."""
files_changed = pyqtSignal()
def __init__(self):
super().__init__()
self.input_files = []
self.output_directory = Path("translated")
self.init_ui()
self.refresh_files()
def init_ui(self):
"""Initialize the user interface."""
layout = QVBoxLayout()
# Create splitter for input and output sections
splitter = QSplitter(Qt.Horizontal)
# Input files section
input_group = self.create_input_section()
splitter.addWidget(input_group)
# Output files section
output_group = self.create_output_section()
splitter.addWidget(output_group)
splitter.setSizes([400, 400])
layout.addWidget(splitter)
# Progress section
progress_group = self.create_progress_section()
layout.addWidget(progress_group)
self.setLayout(layout)
def create_input_section(self):
"""Create the input files section."""
group = QGroupBox("Input Files")
layout = QVBoxLayout()
# Buttons
button_layout = QHBoxLayout()
self.add_files_button = QPushButton("Add Files")
self.add_files_button.clicked.connect(self.add_input_files)
self.add_folder_button = QPushButton("Add Folder")
self.add_folder_button.clicked.connect(self.add_input_folder)
self.remove_files_button = QPushButton("Remove Selected")
self.remove_files_button.clicked.connect(self.remove_selected_files)
self.clear_files_button = QPushButton("Clear All")
self.clear_files_button.clicked.connect(self.clear_input_files)
button_layout.addWidget(self.add_files_button)
button_layout.addWidget(self.add_folder_button)
button_layout.addWidget(self.remove_files_button)
button_layout.addWidget(self.clear_files_button)
layout.addLayout(button_layout)
# File tree
self.input_tree = QTreeWidget()
self.input_tree.setHeaderLabels(["File", "Size", "Type"])
self.input_tree.itemSelectionChanged.connect(self.on_input_selection_changed)
layout.addWidget(self.input_tree)
# File info
self.input_info = QTextEdit()
self.input_info.setMaximumHeight(100)
self.input_info.setReadOnly(True)
layout.addWidget(self.input_info)
group.setLayout(layout)
return group
def create_output_section(self):
"""Create the output files section."""
group = QGroupBox("Output Files")
layout = QVBoxLayout()
# Output directory selection
dir_layout = QHBoxLayout()
self.output_dir_label = QLabel(str(self.output_directory))
self.output_dir_label.setStyleSheet("border: 1px solid #555; padding: 5px;")
self.browse_output_button = QPushButton("Browse")
self.browse_output_button.clicked.connect(self.browse_output_directory)
dir_layout.addWidget(QLabel("Output Directory:"))
dir_layout.addWidget(self.output_dir_label)
dir_layout.addWidget(self.browse_output_button)
layout.addLayout(dir_layout)
# Output file tree
self.output_tree = QTreeWidget()
self.output_tree.setHeaderLabels(["File", "Size", "Modified"])
self.output_tree.itemSelectionChanged.connect(self.on_output_selection_changed)
layout.addWidget(self.output_tree)
# Output file preview
self.output_preview = QTextEdit()
self.output_preview.setMaximumHeight(100)
self.output_preview.setReadOnly(True)
layout.addWidget(self.output_preview)
# Output buttons
output_button_layout = QHBoxLayout()
self.open_output_button = QPushButton("Open in Editor")
self.open_output_button.clicked.connect(self.open_output_file)
self.delete_output_button = QPushButton("Delete Selected")
self.delete_output_button.clicked.connect(self.delete_output_file)
output_button_layout.addWidget(self.open_output_button)
output_button_layout.addWidget(self.delete_output_button)
output_button_layout.addStretch()
layout.addLayout(output_button_layout)
group.setLayout(layout)
return group
def create_progress_section(self):
"""Create the progress monitoring section."""
group = QGroupBox("Translation Progress")
layout = QVBoxLayout()
# Progress bar
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)
# Status and options
status_layout = QHBoxLayout()
self.status_label = QLabel("Ready")
self.backup_cb = QCheckBox("Create Backups")
self.backup_cb.setChecked(True)
status_layout.addWidget(self.status_label)
status_layout.addStretch()
status_layout.addWidget(self.backup_cb)
layout.addLayout(status_layout)
group.setLayout(layout)
return group
def add_input_files(self):
"""Add input files via dialog."""
files, _ = QFileDialog.getOpenFileNames(
self,
"Select Input Files",
"",
"JSON Files (*.json);;All Files (*)"
)
for file_path in files:
if file_path not in self.input_files:
self.input_files.append(file_path)
self.refresh_input_files()
def add_input_folder(self):
"""Add all files from a folder."""
folder = QFileDialog.getExistingDirectory(self, "Select Input Folder")
if folder:
folder_path = Path(folder)
for file_path in folder_path.rglob("*.json"):
file_str = str(file_path)
if file_str not in self.input_files:
self.input_files.append(file_str)
self.refresh_input_files()
def remove_selected_files(self):
"""Remove selected input files."""
selected_items = self.input_tree.selectedItems()
for item in selected_items:
file_path = item.text(0)
if file_path in self.input_files:
self.input_files.remove(file_path)
self.refresh_input_files()
def clear_input_files(self):
"""Clear all input files."""
self.input_files.clear()
self.refresh_input_files()
def refresh_input_files(self):
"""Refresh the input files display."""
self.input_tree.clear()
for file_path in self.input_files:
path_obj = Path(file_path)
if path_obj.exists():
size = path_obj.stat().st_size
file_type = path_obj.suffix
item = QTreeWidgetItem([
path_obj.name,
f"{size:,} bytes",
file_type
])
item.setToolTip(0, str(path_obj))
self.input_tree.addTopLevelItem(item)
else:
# File doesn't exist - show in red
item = QTreeWidgetItem([
f"{path_obj.name} (Missing)",
"0 bytes",
"N/A"
])
item.setBackground(0, Qt.red)
self.input_tree.addTopLevelItem(item)
# Update info
self.update_input_info()
def refresh_output_files(self):
"""Refresh the output files display."""
self.output_tree.clear()
if self.output_directory.exists():
for file_path in self.output_directory.iterdir():
if file_path.is_file():
size = file_path.stat().st_size
modified = file_path.stat().st_mtime
import datetime
modified_str = datetime.datetime.fromtimestamp(modified).strftime("%Y-%m-%d %H:%M")
item = QTreeWidgetItem([
file_path.name,
f"{size:,} bytes",
modified_str
])
item.setToolTip(0, str(file_path))
self.output_tree.addTopLevelItem(item)
def browse_output_directory(self):
"""Browse for output directory."""
directory = QFileDialog.getExistingDirectory(
self, "Select Output Directory", str(self.output_directory)
)
if directory:
self.output_directory = Path(directory)
self.output_dir_label.setText(str(self.output_directory))
self.refresh_output_files()
def refresh_files(self):
"""Refresh both input and output file displays."""
self.refresh_input_files()
self.refresh_output_files()
def on_input_selection_changed(self):
"""Handle input file selection change."""
selected_items = self.input_tree.selectedItems()
if selected_items:
item = selected_items[0]
file_name = item.text(0)
# Find the full path
full_path = None
for file_path in self.input_files:
if Path(file_path).name == file_name:
full_path = file_path
break
if full_path and Path(full_path).exists():
try:
with open(full_path, 'r', encoding='utf-8') as f:
content = f.read()
# Show preview of file structure
if full_path.endswith('.json'):
data = json.loads(content)
if isinstance(data, dict):
keys = list(data.keys())[:10] # Show first 10 keys
preview = f"JSON Object with {len(data)} keys:\n"
preview += "\n".join(keys)
if len(data) > 10:
preview += f"\n... and {len(data) - 10} more"
elif isinstance(data, list):
preview = f"JSON Array with {len(data)} items"
else:
preview = f"JSON: {type(data).__name__}"
else:
preview = content[:500] + "..." if len(content) > 500 else content
self.input_info.setPlainText(preview)
except Exception as e:
self.input_info.setPlainText(f"Error reading file: {str(e)}")
def on_output_selection_changed(self):
"""Handle output file selection change."""
selected_items = self.output_tree.selectedItems()
if selected_items:
item = selected_items[0]
file_name = item.text(0)
file_path = self.output_directory / file_name
if file_path.exists():
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
preview = content[:500] + "..." if len(content) > 500 else content
self.output_preview.setPlainText(preview)
except Exception as e:
self.output_preview.setPlainText(f"Error reading file: {str(e)}")
def open_output_file(self):
"""Open selected output file in external editor."""
selected_items = self.output_tree.selectedItems()
if selected_items:
item = selected_items[0]
file_name = item.text(0)
file_path = self.output_directory / file_name
if file_path.exists():
import subprocess
try:
subprocess.run(['notepad.exe', str(file_path)], check=True)
except subprocess.CalledProcessError:
QMessageBox.warning(self, "Warning", "Could not open file in external editor")
def delete_output_file(self):
"""Delete selected output file."""
selected_items = self.output_tree.selectedItems()
if selected_items:
item = selected_items[0]
file_name = item.text(0)
file_path = self.output_directory / file_name
reply = QMessageBox.question(
self,
"Confirm Deletion",
f"Are you sure you want to delete '{file_name}'?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes and file_path.exists():
try:
file_path.unlink()
self.refresh_output_files()
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to delete file:\n{str(e)}")
def update_input_info(self):
"""Update the input files information."""
total_files = len(self.input_files)
total_size = 0
for file_path in self.input_files:
path_obj = Path(file_path)
if path_obj.exists():
total_size += path_obj.stat().st_size
info = f"Total Files: {total_files}\nTotal Size: {total_size:,} bytes"
# Count by file type
extensions = {}
for file_path in self.input_files:
ext = Path(file_path).suffix
extensions[ext] = extensions.get(ext, 0) + 1
if extensions:
info += "\n\nFile Types:"
for ext, count in sorted(extensions.items()):
info += f"\n{ext or 'No extension'}: {count}"
self.input_info.setPlainText(info)
def get_input_files(self):
"""Get list of input files."""
return self.input_files.copy()
def get_output_directory(self):
"""Get output directory path."""
return self.output_directory

149
gui/log_viewer.py Normal file
View file

@ -0,0 +1,149 @@
"""
Log Viewer - Real-time log display and monitoring
"""
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QPushButton,
QLabel, QCheckBox, QComboBox, QGroupBox, QSplitter
)
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
from PyQt5.QtGui import QTextCursor, QFont
from pathlib import Path
import datetime
class LogViewer(QWidget):
"""Widget for viewing translation logs and monitoring progress."""
log_updated = pyqtSignal(str)
def __init__(self):
super().__init__()
self.log_files = {
"Translation History": "log/translationHistory.txt",
"Mismatch History": "log/mismatchHistory.txt"
}
self.auto_refresh_timer = QTimer()
self.auto_refresh_timer.timeout.connect(self.refresh_logs)
self.init_ui()
def init_ui(self):
"""Initialize the user interface."""
layout = QVBoxLayout()
# Control panel
control_group = QGroupBox("Log Controls")
control_layout = QHBoxLayout()
# Log file selector
self.log_selector = QComboBox()
self.log_selector.addItems(list(self.log_files.keys()))
self.log_selector.currentTextChanged.connect(self.load_selected_log)
control_layout.addWidget(QLabel("Log File:"))
control_layout.addWidget(self.log_selector)
# Auto-refresh checkbox
self.auto_refresh_cb = QCheckBox("Auto Refresh")
self.auto_refresh_cb.toggled.connect(self.toggle_auto_refresh)
control_layout.addWidget(self.auto_refresh_cb)
# Manual refresh button
self.refresh_button = QPushButton("Refresh")
self.refresh_button.clicked.connect(self.refresh_logs)
control_layout.addWidget(self.refresh_button)
# Clear button
self.clear_button = QPushButton("Clear")
self.clear_button.clicked.connect(self.clear_log)
control_layout.addWidget(self.clear_button)
control_layout.addStretch()
control_group.setLayout(control_layout)
layout.addWidget(control_group)
# Log display area
self.log_display = QTextEdit()
self.log_display.setReadOnly(True)
self.log_display.setFont(QFont("Consolas", 10))
self.log_display.setStyleSheet("""
QTextEdit {
background-color: #1e1e1e;
color: #ffffff;
border: 1px solid #555555;
}
""")
layout.addWidget(self.log_display)
# Status bar
self.status_label = QLabel("Ready")
self.status_label.setStyleSheet("color: #cccccc; padding: 5px;")
layout.addWidget(self.status_label)
self.setLayout(layout)
# Initial load
self.load_selected_log()
def toggle_auto_refresh(self, enabled):
"""Toggle auto-refresh functionality."""
if enabled:
self.auto_refresh_timer.start(2000) # Refresh every 2 seconds
self.status_label.setText("Auto-refresh enabled")
else:
self.auto_refresh_timer.stop()
self.status_label.setText("Auto-refresh disabled")
def load_selected_log(self):
"""Load the currently selected log file."""
selected_log = self.log_selector.currentText()
if selected_log in self.log_files:
self.load_log_file(self.log_files[selected_log])
def load_log_file(self, file_path):
"""Load content from a specific log file."""
try:
log_path = Path(file_path)
if log_path.exists():
with open(log_path, 'r', encoding='utf-8') as f:
content = f.read()
self.log_display.setPlainText(content)
# Scroll to bottom
cursor = self.log_display.textCursor()
cursor.movePosition(QTextCursor.End)
self.log_display.setTextCursor(cursor)
# Update status
file_size = log_path.stat().st_size
self.status_label.setText(f"Loaded {log_path.name} ({file_size} bytes)")
else:
self.log_display.setPlainText(f"Log file not found: {file_path}")
self.status_label.setText("Log file not found")
except Exception as e:
self.log_display.setPlainText(f"Error loading log file: {str(e)}")
self.status_label.setText(f"Error: {str(e)}")
def refresh_logs(self):
"""Refresh the current log display."""
self.load_selected_log()
def clear_log(self):
"""Clear the log display."""
self.log_display.clear()
self.status_label.setText("Log cleared")
def append_log_message(self, message):
"""Append a message to the log display."""
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
formatted_message = f"[{timestamp}] {message}"
self.log_display.append(formatted_message)
# Scroll to bottom
cursor = self.log_display.textCursor()
cursor.movePosition(QTextCursor.End)
self.log_display.setTextCursor(cursor)
def get_current_log_content(self):
"""Get the current log content."""
return self.log_display.toPlainText()

623
gui/main.py Normal file
View file

@ -0,0 +1,623 @@
#!/usr/bin/env python3
"""
DazedMTLTool GUI - Main Application
A PyQt-based graphical user interface for the DazedMTLTool translation system.
"""
import sys
import os
import json
from pathlib import Path
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QTabWidget, QVBoxLayout, QHBoxLayout,
QWidget, QPushButton, QLabel, QFileDialog, QMessageBox, QProgressBar,
QTextEdit, QSplitter, QGroupBox, QStatusBar
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QIcon, QFont, QPixmap
# Import configuration widgets
from gui.config_tab import ConfigTab
from gui.rpgmaker_tab import RPGMakerTab
from gui.other_modules_tab import OtherModulesTab
from gui.log_viewer import LogViewer
from gui.file_manager import FileManager
class DazedMTLGUI(QMainWindow):
"""Main GUI window for the DazedMTLTool."""
def __init__(self):
super().__init__()
self.init_ui()
self.setup_status_bar()
self.setup_font_scaling()
def setup_font_scaling(self):
"""Set up font scaling based on configuration."""
try:
from dotenv import load_dotenv
import os
# Load environment variables
load_dotenv()
# Get font scale setting
font_scale = float(os.getenv("font_scale", "1.0"))
# Apply font scaling
self.apply_font_scaling(font_scale)
except Exception as e:
print(f"Warning: Could not apply font scaling: {e}")
def apply_font_scaling(self, scale_factor):
"""Apply font scaling to the entire application."""
try:
app = QApplication.instance()
if app:
# Get the default font
font = app.font()
base_size = 9 # Base font size
new_size = int(base_size * scale_factor)
font.setPointSize(new_size)
app.setFont(font)
# Also update window title to show scaling
if scale_factor != 1.0:
self.setWindowTitle(f"DazedMTLTool - Visual Translation Interface (Font: {scale_factor:.1f}x)")
else:
self.setWindowTitle("DazedMTLTool - Visual Translation Interface")
except Exception as e:
print(f"Warning: Could not apply font scaling: {e}")
def init_ui(self):
"""Initialize the user interface."""
self.setWindowTitle("DazedMTLTool - Visual Translation Interface")
self.setGeometry(100, 100, 1200, 800)
# Set window icon if available
icon_path = Path("screens/tool.png")
if icon_path.exists():
self.setWindowIcon(QIcon(str(icon_path)))
# Create central widget and main layout
central_widget = QWidget()
self.setCentralWidget(central_widget)
# Create main splitter
main_splitter = QSplitter(Qt.Horizontal)
# Create tab widget
self.tab_widget = QTabWidget()
self.tab_widget.setTabPosition(QTabWidget.North)
# Add tabs
self.setup_tabs()
# Create log viewer
self.log_viewer = LogViewer()
# Add widgets to splitter
main_splitter.addWidget(self.tab_widget)
main_splitter.addWidget(self.log_viewer)
main_splitter.setSizes([800, 400])
# Set main layout
layout = QVBoxLayout()
layout.addWidget(main_splitter)
central_widget.setLayout(layout)
# Create menu bar
self.create_menu_bar()
def setup_tabs(self):
"""Set up all the tabs in the interface."""
# Configuration Tab
self.config_tab = ConfigTab()
self.config_tab.config_changed.connect(self.on_config_changed)
self.tab_widget.addTab(self.config_tab, "Configuration")
# RPG Maker MV/MZ Tab
self.rpgmaker_tab = RPGMakerTab()
self.tab_widget.addTab(self.rpgmaker_tab, "RPG Maker MV/MZ")
# Other Modules Tab
self.other_modules_tab = OtherModulesTab()
self.tab_widget.addTab(self.other_modules_tab, "Other Modules")
# File Manager Tab
self.file_manager = FileManager()
self.tab_widget.addTab(self.file_manager, "File Manager")
def on_config_changed(self):
"""Handle configuration changes."""
# This will be called when font scale or other settings change
try:
config = self.config_tab.get_config()
font_scale = config.get("font_scale", 1.0)
self.apply_font_scaling(font_scale)
except Exception as e:
print(f"Warning: Could not apply configuration changes: {e}")
def set_font_scale(self, scale_factor):
"""Set the font scale and update the configuration."""
try:
# Apply the scaling immediately
self.apply_font_scaling(scale_factor)
# Update the configuration tab
self.config_tab.font_scale_spin.setValue(scale_factor)
# Save to .env file
self.config_tab.save_to_env()
self.status_label.setText(f"Font scale set to {scale_factor:.1f}x")
except Exception as e:
QMessageBox.warning(self, "Warning", f"Failed to set font scale: {str(e)}")
def create_menu_bar(self):
"""Create the menu bar."""
menubar = self.menuBar()
# File menu
file_menu = menubar.addMenu('File')
# Load project action
load_action = file_menu.addAction('Load Project')
load_action.triggered.connect(self.load_project)
# Save project action
save_action = file_menu.addAction('Save Project')
save_action.triggered.connect(self.save_project)
file_menu.addSeparator()
# Exit action
exit_action = file_menu.addAction('Exit')
exit_action.triggered.connect(self.close)
# Tools menu
tools_menu = menubar.addMenu('Tools')
# Font Size submenu
font_menu = tools_menu.addMenu('Font Size')
# Font size actions
small_font_action = font_menu.addAction('Small (0.8x)')
small_font_action.triggered.connect(lambda: self.set_font_scale(0.8))
normal_font_action = font_menu.addAction('Normal (1.0x)')
normal_font_action.triggered.connect(lambda: self.set_font_scale(1.0))
large_font_action = font_menu.addAction('Large (1.5x)')
large_font_action.triggered.connect(lambda: self.set_font_scale(1.5))
xlarge_font_action = font_menu.addAction('Extra Large (2.0x)')
xlarge_font_action.triggered.connect(lambda: self.set_font_scale(2.0))
tools_menu.addSeparator()
# Reset to defaults
reset_action = tools_menu.addAction('Reset to Defaults')
reset_action.triggered.connect(self.reset_to_defaults)
# Validate configuration
validate_action = tools_menu.addAction('Validate Configuration')
validate_action.triggered.connect(self.validate_configuration)
# Help menu
help_menu = menubar.addMenu('Help')
# About action
about_action = help_menu.addAction('About')
about_action.triggered.connect(self.show_about)
def setup_status_bar(self):
"""Set up the status bar."""
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
# Add status labels
self.status_label = QLabel("Ready")
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
self.status_bar.addWidget(self.status_label)
self.status_bar.addPermanentWidget(self.progress_bar)
def load_project(self):
"""Load a project configuration."""
file_path, _ = QFileDialog.getOpenFileName(
self,
"Load Project Configuration",
"",
"JSON Files (*.json);;All Files (*)"
)
if file_path:
try:
# Load configuration from file
self.config_tab.load_from_file(file_path)
self.rpgmaker_tab.load_from_file(file_path)
self.status_label.setText(f"Loaded project: {Path(file_path).name}")
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to load project:\n{str(e)}")
def save_project(self):
"""Save the current project configuration."""
file_path, _ = QFileDialog.getSaveFileName(
self,
"Save Project Configuration",
"",
"JSON Files (*.json);;All Files (*)"
)
if file_path:
try:
# Save configuration to file
config_data = {}
config_data.update(self.config_tab.get_config())
config_data.update(self.rpgmaker_tab.get_config())
import json
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(config_data, f, indent=2, ensure_ascii=False)
self.status_label.setText(f"Saved project: {Path(file_path).name}")
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to save project:\n{str(e)}")
def reset_to_defaults(self):
"""Reset all configurations to default values."""
reply = QMessageBox.question(
self,
"Reset to Defaults",
"Are you sure you want to reset all settings to their default values?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
self.config_tab.reset_to_defaults()
self.rpgmaker_tab.reset_to_defaults()
self.status_label.setText("Reset to defaults")
def validate_configuration(self):
"""Validate the current configuration."""
try:
# Validate configuration settings
config_valid = self.config_tab.validate()
rpgmaker_valid = self.rpgmaker_tab.validate()
if config_valid and rpgmaker_valid:
QMessageBox.information(self, "Validation", "Configuration is valid!")
self.status_label.setText("Configuration validated")
else:
QMessageBox.warning(self, "Validation", "Configuration has issues. Check the log for details.")
except Exception as e:
QMessageBox.critical(self, "Validation Error", f"Failed to validate configuration:\n{str(e)}")
def show_about(self):
"""Show the about dialog."""
QMessageBox.about(
self,
"About DazedMTLTool GUI",
"""
<h3>DazedMTLTool GUI</h3>
<p>A visual interface for the DazedMTLTool translation system.</p>
<p>This tool helps translate visual novels, RPG games, and other text-based content using AI translation services.</p>
<p><b>Features:</b></p>
<ul>
<li>Visual configuration management</li>
<li>Module-specific settings</li>
<li>Real-time translation monitoring</li>
<li>File management and organization</li>
</ul>
"""
)
def update_status(self, message):
"""Update the status bar message."""
self.status_label.setText(message)
def show_progress(self, show=True):
"""Show or hide the progress bar."""
self.progress_bar.setVisible(show)
def set_progress(self, value):
"""Set the progress bar value (0-100)."""
self.progress_bar.setValue(value)
def main():
"""Main entry point for the GUI application."""
try:
# Enable high DPI scaling before creating QApplication
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
app = QApplication(sys.argv)
except Exception as e:
print(f"Failed to create QApplication: {e}")
print("Make sure PyQt5 is properly installed:")
print(" pip install PyQt5>=5.15.0")
return 1
# Set application properties
app.setApplicationName("DazedMTLTool")
app.setApplicationVersion("1.0")
app.setOrganizationName("DazedTranslations")
# Apply dark theme (optional)
app.setStyleSheet("""
QMainWindow {
background-color: #2b2b2b;
color: #ffffff;
}
QWidget {
background-color: #2b2b2b;
color: #ffffff;
}
QTabWidget::pane {
border: 1px solid #555555;
background-color: #3c3c3c;
}
QTabBar::tab {
background-color: #555555;
color: #ffffff;
padding: 8px 12px;
margin-right: 2px;
border: 1px solid #666666;
}
QTabBar::tab:selected {
background-color: #007acc;
color: #ffffff;
}
QTabBar::tab:hover {
background-color: #666666;
color: #ffffff;
}
QGroupBox {
font-weight: bold;
border: 2px solid #555555;
border-radius: 5px;
margin: 10px 0px;
padding-top: 10px;
color: #ffffff;
background-color: #3c3c3c;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #007acc;
background-color: transparent;
}
QPushButton {
background-color: #0078d4;
color: white;
border: none;
padding: 6px 12px;
border-radius: 3px;
font-weight: bold;
}
QPushButton:hover {
background-color: #106ebe;
}
QPushButton:pressed {
background-color: #005a9e;
}
QCheckBox {
color: #ffffff;
spacing: 5px;
background-color: transparent;
}
QCheckBox::indicator {
width: 16px;
height: 16px;
}
QCheckBox::indicator:unchecked {
background-color: #404040;
border: 1px solid #555555;
}
QCheckBox::indicator:checked {
background-color: #0078d4;
border: 1px solid #0078d4;
}
QLabel {
color: #ffffff;
background-color: transparent;
}
QLineEdit {
background-color: #404040;
color: #ffffff;
border: 1px solid #555555;
padding: 4px;
border-radius: 2px;
}
QLineEdit:focus {
border: 2px solid #007acc;
}
QSpinBox, QDoubleSpinBox {
background-color: #404040;
color: #ffffff;
border: 1px solid #555555;
padding: 4px;
border-radius: 2px;
}
QSpinBox:focus, QDoubleSpinBox:focus {
border: 2px solid #007acc;
}
QComboBox {
background-color: #404040;
color: #ffffff;
border: 1px solid #555555;
padding: 4px;
border-radius: 2px;
}
QComboBox:focus {
border: 2px solid #007acc;
}
QComboBox::drop-down {
border: none;
background-color: #555555;
}
QComboBox::down-arrow {
image: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #ffffff;
}
QComboBox QAbstractItemView {
background-color: #404040;
color: #ffffff;
selection-background-color: #007acc;
border: 1px solid #555555;
}
QTextEdit {
background-color: #1e1e1e;
color: #ffffff;
border: 1px solid #555555;
selection-background-color: #007acc;
}
QScrollArea {
background-color: #2b2b2b;
border: none;
}
QScrollBar:vertical {
background-color: #404040;
width: 12px;
border-radius: 6px;
}
QScrollBar::handle:vertical {
background-color: #666666;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background-color: #007acc;
}
QScrollBar:horizontal {
background-color: #404040;
height: 12px;
border-radius: 6px;
}
QScrollBar::handle:horizontal {
background-color: #666666;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:horizontal:hover {
background-color: #007acc;
}
QTreeWidget {
background-color: #1e1e1e;
color: #ffffff;
border: 1px solid #555555;
selection-background-color: #007acc;
}
QTreeWidget::item {
padding: 4px;
border-bottom: 1px solid #333333;
}
QTreeWidget::item:selected {
background-color: #007acc;
color: #ffffff;
}
QTreeWidget::item:hover {
background-color: #404040;
}
QHeaderView::section {
background-color: #555555;
color: #ffffff;
padding: 6px;
border: 1px solid #666666;
}
QStatusBar {
background-color: #2b2b2b;
color: #ffffff;
border-top: 1px solid #555555;
}
QProgressBar {
background-color: #404040;
border: 1px solid #555555;
border-radius: 3px;
text-align: center;
color: #ffffff;
}
QProgressBar::chunk {
background-color: #007acc;
border-radius: 2px;
}
QMenuBar {
background-color: #2b2b2b;
color: #ffffff;
border-bottom: 1px solid #555555;
}
QMenuBar::item {
padding: 6px 12px;
background-color: transparent;
}
QMenuBar::item:selected {
background-color: #007acc;
}
QMenu {
background-color: #404040;
color: #ffffff;
border: 1px solid #555555;
}
QMenu::item {
padding: 6px 12px;
}
QMenu::item:selected {
background-color: #007acc;
}
QFrame[frameShape="4"] {
color: #555555;
}
QFrame[frameShape="5"] {
color: #555555;
}
""")
try:
# Create and show the main window
window = DazedMTLGUI()
window.show()
# Show font adjustment tip on first run (optional)
try:
from dotenv import load_dotenv
load_dotenv()
show_font_tip = os.getenv("show_font_tip", "true").lower() == "true"
if show_font_tip:
QMessageBox.information(
window,
"Font Size Adjustment",
"💡 Font too small?\n\n"
"• Use Tools → Font Size menu for quick adjustment\n"
"• Or go to Configuration tab → UI Settings\n"
"• Try 'Large (1.5x)' or 'Extra Large (2.0x)' for high DPI displays\n\n"
"This tip can be disabled in the Configuration tab."
)
# Set flag to not show again
from dotenv import set_key
set_key(".env", "show_font_tip", "false")
except Exception:
pass # Ignore if .env operations fail
# Start the application
return app.exec_()
except Exception as e:
print(f"Error starting GUI: {e}")
return 1
if __name__ == "__main__":
sys.exit(main())

182
gui/other_modules_tab.py Normal file
View file

@ -0,0 +1,182 @@
"""
Other Modules Tab - Configuration for other translation modules
"""
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QPushButton,
QGroupBox, QLabel, QScrollArea, QListWidget, QListWidgetItem,
QMessageBox, QTextEdit, QFrame
)
from PyQt5.QtCore import Qt, pyqtSignal
class OtherModulesTab(QWidget):
"""Tab for configuring other translation modules."""
config_changed = pyqtSignal()
def __init__(self):
super().__init__()
self.modules_info = self.get_modules_info()
self.init_ui()
def init_ui(self):
"""Initialize the user interface."""
main_layout = QVBoxLayout()
# Create scroll area
scroll_area = QScrollArea()
scroll_content = QWidget()
scroll_layout = QVBoxLayout()
# Title
title_label = QLabel("Other Translation Modules")
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;")
scroll_layout.addWidget(title_label)
# Description
description_label = QLabel(
"Configure settings for other game engines and file formats supported by DazedMTLTool."
)
description_label.setWordWrap(True)
description_label.setStyleSheet("color: #cccccc; margin-bottom: 10px;")
scroll_layout.addWidget(description_label)
# Create module groups
for category, modules in self.modules_info.items():
group = self.create_module_group(category, modules)
scroll_layout.addWidget(group)
# Buttons
button_layout = QHBoxLayout()
self.refresh_button = QPushButton("Refresh Modules")
self.refresh_button.clicked.connect(self.refresh_modules)
self.enable_all_button = QPushButton("Enable All")
self.enable_all_button.clicked.connect(self.enable_all_modules)
self.disable_all_button = QPushButton("Disable All")
self.disable_all_button.clicked.connect(self.disable_all_modules)
button_layout.addWidget(self.refresh_button)
button_layout.addWidget(self.enable_all_button)
button_layout.addWidget(self.disable_all_button)
button_layout.addStretch()
scroll_layout.addLayout(button_layout)
scroll_content.setLayout(scroll_layout)
scroll_area.setWidget(scroll_content)
scroll_area.setWidgetResizable(True)
main_layout.addWidget(scroll_area)
self.setLayout(main_layout)
def get_modules_info(self):
"""Get information about available modules."""
return {
"Visual Novel Engines": {
"kirikiri": "Kirikiri engine games (.ks, .tjs files)",
"renpy": "Ren'Py visual novels (.rpy files)",
"tyrano": "TyranoBuilder games",
"nscript": "NScripter games",
"lune": "Lune engine games"
},
"RPG Engines": {
"rpgmakerace": "RPG Maker Ace (legacy)",
"rpgmakerplugin": "RPG Maker plugins",
"unity": "Unity engine games",
"wolf": "Wolf RPG Editor games",
"wolf2": "Wolf RPG Editor 2 games"
},
"File Formats": {
"json": "Generic JSON files",
"csv": "CSV/Excel files",
"text": "Plain text files",
"regex": "Custom regex patterns",
"images": "Image text extraction"
}
}
def create_module_group(self, category, modules):
"""Create a group for a category of modules."""
group = QGroupBox(category)
layout = QVBoxLayout()
for module_name, description in modules.items():
# Check if module file exists
module_path = Path(f"modules/{module_name}.py")
exists = module_path.exists()
checkbox = QCheckBox(f"{module_name.upper()}")
checkbox.setEnabled(exists)
checkbox.setToolTip(description)
if not exists:
checkbox.setText(f"{module_name.upper()} (Not Found)")
checkbox.setStyleSheet("color: #ff6b6b;")
# Add description label
desc_label = QLabel(description)
desc_label.setStyleSheet("color: #aaaaaa; font-size: 11px; margin-left: 20px;")
desc_label.setWordWrap(True)
layout.addWidget(checkbox)
layout.addWidget(desc_label)
# Store reference for later use
setattr(self, f"{module_name}_cb", checkbox)
group.setLayout(layout)
return group
def refresh_modules(self):
"""Refresh the modules list."""
# Clear current layout and rebuild
self.modules_info = self.get_modules_info()
# Find all Python files in modules directory
modules_dir = Path("modules")
if modules_dir.exists():
found_modules = [f.stem for f in modules_dir.glob("*.py") if f.stem != "__init__"]
info_text = f"Found {len(found_modules)} module files:\n"
info_text += ", ".join(sorted(found_modules))
QMessageBox.information(self, "Modules Refreshed", info_text)
else:
QMessageBox.warning(self, "Warning", "Modules directory not found!")
def enable_all_modules(self):
"""Enable all available modules."""
for category, modules in self.modules_info.items():
for module_name in modules.keys():
checkbox = getattr(self, f"{module_name}_cb", None)
if checkbox and checkbox.isEnabled():
checkbox.setChecked(True)
def disable_all_modules(self):
"""Disable all modules."""
for category, modules in self.modules_info.items():
for module_name in modules.keys():
checkbox = getattr(self, f"{module_name}_cb", None)
if checkbox:
checkbox.setChecked(False)
def get_enabled_modules(self):
"""Get list of enabled modules."""
enabled = []
for category, modules in self.modules_info.items():
for module_name in modules.keys():
checkbox = getattr(self, f"{module_name}_cb", None)
if checkbox and checkbox.isChecked():
enabled.append(module_name)
return enabled
def get_config(self):
"""Get current configuration."""
return {
"enabled_modules": self.get_enabled_modules()
}

473
gui/rpgmaker_tab.py Normal file
View file

@ -0,0 +1,473 @@
"""
RPG Maker MV/MZ Tab - Configuration for RPG Maker specific settings
"""
import json
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QCheckBox,
QPushButton, QGroupBox, QLabel, QMessageBox, QScrollArea,
QTextEdit, QSpinBox, QFrame
)
from PyQt5.QtCore import Qt, pyqtSignal
try:
from .config_integration import ConfigIntegration
except ImportError:
# Fallback if config_integration is not available
ConfigIntegration = None
class RPGMakerTab(QWidget):
"""RPG Maker MV/MZ configuration tab."""
config_changed = pyqtSignal()
def __init__(self):
super().__init__()
self.config_integration = ConfigIntegration() if ConfigIntegration else None
self.init_ui()
self.reset_to_defaults()
def init_ui(self):
"""Initialize the user interface."""
main_layout = QVBoxLayout()
# Create scroll area
scroll_area = QScrollArea()
scroll_content = QWidget()
scroll_layout = QVBoxLayout()
# Title and description
title_label = QLabel("RPG Maker MV/MZ Translation Settings")
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;")
scroll_layout.addWidget(title_label)
description_label = QLabel(
"Configure which RPG Maker event codes to translate and additional options. "
"Each code represents a specific type of game content."
)
description_label.setWordWrap(True)
description_label.setStyleSheet("color: #cccccc; margin-bottom: 10px;")
scroll_layout.addWidget(description_label)
# General Config Group
general_group = self.create_general_config_group()
scroll_layout.addWidget(general_group)
# Main Dialogue Codes Group
dialogue_group = self.create_dialogue_codes_group()
scroll_layout.addWidget(dialogue_group)
# Optional Codes Group
optional_group = self.create_optional_codes_group()
scroll_layout.addWidget(optional_group)
# Variable Codes Group
variable_group = self.create_variable_codes_group()
scroll_layout.addWidget(variable_group)
# Other Codes Group
other_group = self.create_other_codes_group()
scroll_layout.addWidget(other_group)
# Buttons
button_layout = QHBoxLayout()
self.reset_button = QPushButton("Reset to Defaults")
self.reset_button.clicked.connect(self.reset_to_defaults)
self.validate_button = QPushButton("Validate Settings")
self.validate_button.clicked.connect(self.validate_and_show_report)
self.apply_button = QPushButton("Apply to Module")
self.apply_button.clicked.connect(self.apply_to_module)
self.apply_button.setToolTip("Apply current settings to the rpgmakermvmz.py module file")
button_layout.addWidget(self.reset_button)
button_layout.addWidget(self.validate_button)
button_layout.addWidget(self.apply_button)
button_layout.addStretch()
scroll_layout.addLayout(button_layout)
# Add separator
separator = QFrame()
separator.setFrameShape(QFrame.HLine)
separator.setFrameShadow(QFrame.Sunken)
scroll_layout.addWidget(separator)
# Add note about performance
note_text = QTextEdit()
note_text.setMaximumHeight(100)
note_text.setReadOnly(True)
note_text.setHtml("""
<b>Performance Notes:</b><br>
Enable only the codes you need to reduce translation time and costs<br>
CODE408 can significantly increase costs as it translates comments<br>
Main dialogue codes (401, 405, 102) are typically required for story content<br>
Test with a small file first to verify settings work correctly
""")
scroll_layout.addWidget(note_text)
scroll_content.setLayout(scroll_layout)
scroll_area.setWidget(scroll_content)
scroll_area.setWidgetResizable(True)
main_layout.addWidget(scroll_area)
self.setLayout(main_layout)
def create_general_config_group(self):
"""Create general configuration group."""
group = QGroupBox("General Configuration")
layout = QVBoxLayout()
# FIRSTLINESPEAKERS
self.first_line_speakers_cb = QCheckBox("First Line Speakers")
self.first_line_speakers_cb.setToolTip(
"If the first line of event code 401 contains a speaker name, enable this option"
)
layout.addWidget(self.first_line_speakers_cb)
# FACENAME101
self.facename_101_cb = QCheckBox("Find Speakers in 101 Codes based on Face Name")
self.facename_101_cb.setToolTip(
"Use character face names to identify speakers in event code 101"
)
layout.addWidget(self.facename_101_cb)
# NAMES
self.names_cb = QCheckBox("Output Character Names List")
self.names_cb.setToolTip(
"Generate a list of all character names found during translation"
)
layout.addWidget(self.names_cb)
# BRFLAG
self.br_flag_cb = QCheckBox("Use <br> Tags")
self.br_flag_cb.setToolTip(
"Use <br> HTML tags instead of newlines for line breaks"
)
layout.addWidget(self.br_flag_cb)
# FIXTEXTWRAP
self.fix_textwrap_cb = QCheckBox("Fix Text Wrapping")
self.fix_textwrap_cb.setToolTip(
"Automatically fix text wrapping for better display"
)
layout.addWidget(self.fix_textwrap_cb)
# IGNORETLTEXT
self.ignore_tl_text_cb = QCheckBox("Ignore Already Translated Text")
self.ignore_tl_text_cb.setToolTip(
"Skip text that appears to already be translated"
)
layout.addWidget(self.ignore_tl_text_cb)
group.setLayout(layout)
return group
def create_dialogue_codes_group(self):
"""Create dialogue codes group."""
group = QGroupBox("Main Dialogue Codes (Recommended)")
layout = QVBoxLayout()
# CODE401
self.code_401_cb = QCheckBox("CODE 401 - Show Text")
self.code_401_cb.setToolTip(
"Translate dialogue text displayed in message windows. Essential for story content."
)
layout.addWidget(self.code_401_cb)
# CODE405
self.code_405_cb = QCheckBox("CODE 405 - Show Text (Scrolling)")
self.code_405_cb.setToolTip(
"Translate scrolling text messages. Used for longer dialogue passages."
)
layout.addWidget(self.code_405_cb)
# CODE102
self.code_102_cb = QCheckBox("CODE 102 - Show Choices")
self.code_102_cb.setToolTip(
"Translate choice options presented to the player."
)
layout.addWidget(self.code_102_cb)
group.setLayout(layout)
return group
def create_optional_codes_group(self):
"""Create optional codes group."""
group = QGroupBox("Optional Codes")
layout = QVBoxLayout()
# CODE101
self.code_101_cb = QCheckBox("CODE 101 - Character Names")
self.code_101_cb.setToolTip(
"Translate character names. Enable only when names are stored in code 101."
)
layout.addWidget(self.code_101_cb)
# CODE408
self.code_408_cb = QCheckBox("CODE 408 - Comments (Warning: High Cost)")
self.code_408_cb.setToolTip(
"Translate comment text. WARNING: This can significantly increase translation costs!"
)
self.code_408_cb.setStyleSheet("QCheckBox { color: #ff6b6b; }")
layout.addWidget(self.code_408_cb)
group.setLayout(layout)
return group
def create_variable_codes_group(self):
"""Create variable codes group."""
group = QGroupBox("Variable Codes")
layout = QVBoxLayout()
# CODE122
self.code_122_cb = QCheckBox("CODE 122 - Control Variables")
self.code_122_cb.setToolTip(
"Translate text stored in game variables."
)
layout.addWidget(self.code_122_cb)
group.setLayout(layout)
return group
def create_other_codes_group(self):
"""Create other codes group."""
group = QGroupBox("Other Event Codes")
layout = QVBoxLayout()
codes_info = [
("355655", "Scripts", "Translate text within script commands"),
("357", "Picture Text", "Translate text displayed on pictures"),
("657", "Picture Text Extended", "Extended picture text translation"),
("356", "Plugin Commands", "Translate plugin command parameters"),
("320", "Change Name Input", "Translate name input prompts"),
("324", "Change Nickname", "Translate nickname changes"),
("111", "Conditional Branch", "Translate conditional text"),
("108", "Comments", "Translate comment blocks")
]
self.other_code_checkboxes = {}
for code, name, tooltip in codes_info:
cb = QCheckBox(f"CODE {code} - {name}")
cb.setToolTip(tooltip)
layout.addWidget(cb)
self.other_code_checkboxes[code] = cb
group.setLayout(layout)
return group
def reset_to_defaults(self):
"""Reset all settings to default values."""
# General config defaults
self.first_line_speakers_cb.setChecked(False)
self.facename_101_cb.setChecked(False)
self.names_cb.setChecked(False)
self.br_flag_cb.setChecked(False)
self.fix_textwrap_cb.setChecked(True)
self.ignore_tl_text_cb.setChecked(False)
# Main dialogue codes (enabled by default)
self.code_401_cb.setChecked(True)
self.code_405_cb.setChecked(True)
self.code_102_cb.setChecked(True)
# Optional codes (disabled by default)
self.code_101_cb.setChecked(False)
self.code_408_cb.setChecked(False)
# Variable codes (disabled by default)
self.code_122_cb.setChecked(False)
# Other codes (all disabled by default)
for cb in self.other_code_checkboxes.values():
cb.setChecked(False)
def get_config(self):
"""Get current configuration as dictionary."""
config = {
# General settings
"FIRSTLINESPEAKERS": self.first_line_speakers_cb.isChecked(),
"FACENAME101": self.facename_101_cb.isChecked(),
"NAMES": self.names_cb.isChecked(),
"BRFLAG": self.br_flag_cb.isChecked(),
"FIXTEXTWRAP": self.fix_textwrap_cb.isChecked(),
"IGNORETLTEXT": self.ignore_tl_text_cb.isChecked(),
# Main dialogue codes
"CODE401": self.code_401_cb.isChecked(),
"CODE405": self.code_405_cb.isChecked(),
"CODE102": self.code_102_cb.isChecked(),
# Optional codes
"CODE101": self.code_101_cb.isChecked(),
"CODE408": self.code_408_cb.isChecked(),
# Variable codes
"CODE122": self.code_122_cb.isChecked(),
}
# Other codes
for code, cb in self.other_code_checkboxes.items():
config[f"CODE{code}"] = cb.isChecked()
return config
def set_config(self, config):
"""Set configuration from dictionary."""
# General settings
self.first_line_speakers_cb.setChecked(config.get("FIRSTLINESPEAKERS", False))
self.facename_101_cb.setChecked(config.get("FACENAME101", False))
self.names_cb.setChecked(config.get("NAMES", False))
self.br_flag_cb.setChecked(config.get("BRFLAG", False))
self.fix_textwrap_cb.setChecked(config.get("FIXTEXTWRAP", True))
self.ignore_tl_text_cb.setChecked(config.get("IGNORETLTEXT", False))
# Main dialogue codes
self.code_401_cb.setChecked(config.get("CODE401", True))
self.code_405_cb.setChecked(config.get("CODE405", True))
self.code_102_cb.setChecked(config.get("CODE102", True))
# Optional codes
self.code_101_cb.setChecked(config.get("CODE101", False))
self.code_408_cb.setChecked(config.get("CODE408", False))
# Variable codes
self.code_122_cb.setChecked(config.get("CODE122", False))
# Other codes
for code, cb in self.other_code_checkboxes.items():
cb.setChecked(config.get(f"CODE{code}", False))
def load_from_file(self, file_path):
"""Load configuration from file."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
config = json.load(f)
self.set_config(config)
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to load RPG Maker config:\n{str(e)}")
def validate(self):
"""Validate current configuration."""
warnings = []
errors = []
# Check if any main dialogue codes are enabled
main_codes_enabled = (
self.code_401_cb.isChecked() or
self.code_405_cb.isChecked() or
self.code_102_cb.isChecked()
)
if not main_codes_enabled:
warnings.append("No main dialogue codes are enabled. You may not get any translated content.")
# Check for high-cost options
if self.code_408_cb.isChecked():
warnings.append("CODE 408 (Comments) is enabled. This can significantly increase translation costs!")
# Check for conflicting options
if self.br_flag_cb.isChecked() and self.fix_textwrap_cb.isChecked():
warnings.append("Both BR tags and text wrapping are enabled. This might cause formatting issues.")
return len(errors) == 0, warnings, errors
def validate_and_show_report(self):
"""Validate and show a detailed report."""
is_valid, warnings, errors = self.validate()
report = []
if errors:
report.append("<b>Errors:</b>")
for error in errors:
report.append(f"{error}")
report.append("")
if warnings:
report.append("<b>Warnings:</b>")
for warning in warnings:
report.append(f"{warning}")
report.append("")
# Add enabled codes summary
enabled_codes = []
config = self.get_config()
for key, value in config.items():
if key.startswith("CODE") and value:
enabled_codes.append(key)
if enabled_codes:
report.append("<b>Enabled Codes:</b>")
report.append(", ".join(enabled_codes))
else:
report.append("<b>No codes enabled!</b>")
if not report:
report.append("Configuration is valid with no issues.")
msg_box = QMessageBox(self)
msg_box.setWindowTitle("Validation Report")
msg_box.setTextFormat(Qt.RichText)
msg_box.setText("<br>".join(report))
if errors:
msg_box.setIcon(QMessageBox.Critical)
elif warnings:
msg_box.setIcon(QMessageBox.Warning)
else:
msg_box.setIcon(QMessageBox.Information)
msg_box.exec_()
return is_valid
def apply_to_module(self):
"""Apply current configuration to the rpgmakermvmz.py module."""
if not self.config_integration:
QMessageBox.warning(self, "Warning", "Configuration integration not available")
return
try:
config = self.get_config()
success = self.config_integration.update_rpgmaker_config(config)
if success:
QMessageBox.information(
self,
"Success",
"Configuration has been applied to modules/rpgmakermvmz.py\n\n"
"The module will now use these settings when running translations."
)
else:
QMessageBox.warning(self, "Warning", "Failed to apply configuration to module")
except FileNotFoundError:
QMessageBox.critical(
self,
"Error",
"modules/rpgmakermvmz.py not found!\n\n"
"Make sure you're running the GUI from the correct directory."
)
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to apply configuration:\n{str(e)}")
def load_from_module(self):
"""Load configuration from the actual module file."""
if not self.config_integration:
return
try:
config = self.config_integration.read_current_config()
if config:
self.set_config(config)
QMessageBox.information(self, "Success", "Configuration loaded from module file")
else:
QMessageBox.warning(self, "Warning", "No configuration found in module file")
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to load from module:\n{str(e)}")

View file

@ -6,3 +6,4 @@ ruamel.yaml==0.17.32
tiktoken==0.8.0
tqdm==4.65.0
pillow==10.4.0
PyQt5>=5.15.0

60
start_gui.py Normal file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
Launch script for DazedMTLTool GUI
"""
import sys
import os
from pathlib import Path
# Add the project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
def check_dependencies():
"""Check if required dependencies are installed."""
missing_deps = []
try:
import PyQt5
except ImportError:
missing_deps.append("PyQt5")
try:
from dotenv import load_dotenv
except ImportError:
missing_deps.append("python-dotenv")
if missing_deps:
print("Missing dependencies:")
for dep in missing_deps:
print(f" - {dep}")
print("\nPlease install them using:")
print(" pip install -r requirements_gui.txt")
return False
return True
def main():
"""Main entry point."""
print("DazedMTLTool GUI Launcher")
print("=" * 40)
# Check dependencies
if not check_dependencies():
sys.exit(1)
# Import and run GUI
try:
from gui.main import main as gui_main
gui_main()
except ImportError as e:
print(f"Error importing GUI modules: {e}")
print("Make sure all GUI files are in the 'gui' directory")
sys.exit(1)
except Exception as e:
print(f"Error starting GUI: {e}")
sys.exit(1)
if __name__ == "__main__":
main()