#!/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, QSettings from PyQt5.QtGui import QIcon, QFont, QPixmap, QScreen # Import configuration widgets from gui.config_tab import ConfigTab from gui.engine_config_tab import EngineConfigTab from gui.log_viewer import LogViewer from gui.file_manager import FileManager from gui.translation_tab import TranslationTab class DazedMTLGUI(QMainWindow): """Main GUI window for the DazedMTLTool.""" def __init__(self): super().__init__() self.settings = QSettings("DazedTranslations", "DazedMTLTool") self.init_ui() self.setup_status_bar() self.setup_font_scaling() self.restore_window_state() def restore_window_state(self): """Restore window geometry and state from settings.""" try: # Restore window geometry geometry = self.settings.value("geometry") if geometry: self.restoreGeometry(geometry) # Restore window state (maximized, etc.) window_state = self.settings.value("windowState") if window_state: self.restoreState(window_state) except Exception as e: print(f"Warning: Could not restore window state: {e}") def save_window_state(self): """Save window geometry and state to settings.""" try: self.settings.setValue("geometry", self.saveGeometry()) self.settings.setValue("windowState", self.saveState()) except Exception as e: print(f"Warning: Could not save window state: {e}") def closeEvent(self, event): """Handle application close event.""" self.save_window_state() event.accept() 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") # Get screen geometry and set window size more responsively screen = QApplication.primaryScreen() screen_geometry = screen.availableGeometry() # Set window size to 80% of screen size, with reasonable minimums window_width = max(1200, int(screen_geometry.width() * 0.8)) window_height = max(800, int(screen_geometry.height() * 0.8)) # Center the window on screen x = (screen_geometry.width() - window_width) // 2 y = (screen_geometry.height() - window_height) // 2 self.setGeometry(x, y, window_width, window_height) # Set minimum size to prevent the window from becoming too small self.setMinimumSize(1000, 600) # 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) # Set proportional sizes instead of fixed pixel sizes main_splitter.setStretchFactor(0, 2) # Main content gets 2/3 main_splitter.setStretchFactor(1, 1) # Log viewer gets 1/3 # 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") # Translation Execution Tab (includes file management) self.translation_tab = TranslationTab(self) try: self.translation_tab.engine_changed.connect(self.show_engine_tab) except Exception: pass self.tab_widget.addTab(self.translation_tab, "Translation") # Unified Engine Configuration Tab self.engine_config_tab = EngineConfigTab() self.tab_widget.addTab(self.engine_config_tab, "Engine Config") # Default to MV/MZ config on first load try: self.show_engine_tab("mvmz") except Exception: pass def show_engine_tab(self, engine: str): """Select engine config tab and display appropriate engine sub-widget.""" try: # Switch to Engine Config tab for i in range(self.tab_widget.count()): if self.tab_widget.tabText(i) == "Engine Config": self.tab_widget.setCurrentIndex(i) break self.engine_config_tab.show_engine(engine) except Exception: pass 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", """
A visual interface for the DazedMTLTool translation system.
This tool helps translate visual novels, RPG games, and other text-based content using AI translation services.
Features: