From 38a0e8f03a3d40ee48a899dd3d94c61b611735e5 Mon Sep 17 00:00:00 2001 From: dazedanon Date: Sat, 25 Oct 2025 16:34:21 -0500 Subject: [PATCH] more improvements --- gui/config_tab.py | 46 +++-- gui/rpgmaker_tab.py | 455 +++++++++++++++++++------------------------- gui/wolf_tab.py | 227 ++++++++++++++++------ 3 files changed, 393 insertions(+), 335 deletions(-) diff --git a/gui/config_tab.py b/gui/config_tab.py index 8f5ae79..003b697 100644 --- a/gui/config_tab.py +++ b/gui/config_tab.py @@ -130,18 +130,18 @@ class ConfigTab(QWidget): self.api_url_edit = QLineEdit() self.api_url_edit.setPlaceholderText("Leave blank for OpenAI API") - self.api_url_edit.setMaximumWidth(400) + self.api_url_edit.setFixedWidth(350) # Large api_form.addRow("API URL:", self.api_url_edit) self.api_key_edit = QLineEdit() self.api_key_edit.setEchoMode(QLineEdit.Password) self.api_key_edit.setPlaceholderText("Enter your API key") - self.api_key_edit.setMaximumWidth(400) + self.api_key_edit.setFixedWidth(350) # Large api_form.addRow("API Key:", self.api_key_edit) self.organization_edit = QLineEdit() self.organization_edit.setPlaceholderText("Optional organization key") - self.organization_edit.setMaximumWidth(400) + self.organization_edit.setFixedWidth(350) # Large api_form.addRow("Organization:", self.organization_edit) self.model_combo = QComboBox() @@ -151,7 +151,7 @@ class ConfigTab(QWidget): "deepseek-chat", "claude-3-sonnet-20240229", "gemini-2.0-flash", "gemini-2.5-flash", "gemini-2.5-pro" ]) - self.model_combo.setMaximumWidth(400) + self.model_combo.setFixedWidth(200) # Medium api_form.addRow("Model:", self.model_combo) left_column.addLayout(api_form) @@ -169,14 +169,15 @@ class ConfigTab(QWidget): "English", "Spanish", "French", "German", "Italian", "Portuguese", "Russian", "Chinese", "Korean", "Japanese" ]) - self.language_combo.setMaximumWidth(250) + self.language_combo.setFixedWidth(200) # Medium trans_form.addRow("Target Language:", self.language_combo) self.timeout_spin = QSpinBox() + self.timeout_spin.setButtonSymbols(QSpinBox.NoButtons) self.timeout_spin.setRange(30, 300) - self.timeout_spin.setValue(120) + self.timeout_spin.setValue(90) self.timeout_spin.setSuffix(" sec") - self.timeout_spin.setMaximumWidth(150) + self.timeout_spin.setFixedWidth(90) # Small trans_form.addRow("Timeout:", self.timeout_spin) left_column.addLayout(trans_form) @@ -190,28 +191,32 @@ class ConfigTab(QWidget): perf_form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) self.file_threads_spin = QSpinBox() + self.file_threads_spin.setButtonSymbols(QSpinBox.NoButtons) self.file_threads_spin.setRange(1, 10) self.file_threads_spin.setValue(1) - self.file_threads_spin.setMaximumWidth(150) + self.file_threads_spin.setFixedWidth(90) # Small perf_form.addRow("File Threads:", self.file_threads_spin) self.threads_spin = QSpinBox() + self.threads_spin.setButtonSymbols(QSpinBox.NoButtons) self.threads_spin.setRange(1, 20) self.threads_spin.setValue(1) - self.threads_spin.setMaximumWidth(150) + self.threads_spin.setFixedWidth(90) # Small perf_form.addRow("Threads per File:", self.threads_spin) self.batch_size_spin = QSpinBox() + self.batch_size_spin.setButtonSymbols(QSpinBox.NoButtons) self.batch_size_spin.setRange(1, 100) self.batch_size_spin.setValue(30) - self.batch_size_spin.setMaximumWidth(150) + self.batch_size_spin.setFixedWidth(90) # Small perf_form.addRow("Batch Size:", self.batch_size_spin) self.frequency_penalty_spin = QDoubleSpinBox() + self.frequency_penalty_spin.setButtonSymbols(QDoubleSpinBox.NoButtons) self.frequency_penalty_spin.setRange(0.0, 2.0) self.frequency_penalty_spin.setSingleStep(0.05) self.frequency_penalty_spin.setValue(0.05) - self.frequency_penalty_spin.setMaximumWidth(150) + self.frequency_penalty_spin.setFixedWidth(90) # Small perf_form.addRow("Frequency Penalty:", self.frequency_penalty_spin) left_column.addLayout(perf_form) @@ -229,24 +234,27 @@ class ConfigTab(QWidget): format_form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) self.width_spin = QSpinBox() + self.width_spin.setButtonSymbols(QSpinBox.NoButtons) self.width_spin.setRange(20, 200) self.width_spin.setValue(60) self.width_spin.setSuffix(" chars") - self.width_spin.setMaximumWidth(150) + self.width_spin.setFixedWidth(90) # Small format_form.addRow("Dialogue Width:", self.width_spin) self.list_width_spin = QSpinBox() + self.list_width_spin.setButtonSymbols(QSpinBox.NoButtons) self.list_width_spin.setRange(20, 200) self.list_width_spin.setValue(100) self.list_width_spin.setSuffix(" chars") - self.list_width_spin.setMaximumWidth(150) + self.list_width_spin.setFixedWidth(90) # Small format_form.addRow("List Width:", self.list_width_spin) self.note_width_spin = QSpinBox() + self.note_width_spin.setButtonSymbols(QSpinBox.NoButtons) self.note_width_spin.setRange(20, 200) self.note_width_spin.setValue(75) self.note_width_spin.setSuffix(" chars") - self.note_width_spin.setMaximumWidth(150) + self.note_width_spin.setFixedWidth(90) # Small format_form.addRow("Note Width:", self.note_width_spin) right_column.addLayout(format_form) @@ -266,21 +274,23 @@ class ConfigTab(QWidget): price_form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) self.input_cost_spin = QDoubleSpinBox() + self.input_cost_spin.setButtonSymbols(QDoubleSpinBox.NoButtons) self.input_cost_spin.setRange(0.0, 100.0) self.input_cost_spin.setDecimals(4) self.input_cost_spin.setSingleStep(0.1) self.input_cost_spin.setValue(2.0) self.input_cost_spin.setSuffix(" per 1M tokens") - self.input_cost_spin.setMaximumWidth(200) + self.input_cost_spin.setFixedWidth(200) # Medium price_form.addRow("Input Cost:", self.input_cost_spin) self.output_cost_spin = QDoubleSpinBox() + self.output_cost_spin.setButtonSymbols(QDoubleSpinBox.NoButtons) self.output_cost_spin.setRange(0.0, 100.0) self.output_cost_spin.setDecimals(4) self.output_cost_spin.setSingleStep(0.1) self.output_cost_spin.setValue(8.0) self.output_cost_spin.setSuffix(" per 1M tokens") - self.output_cost_spin.setMaximumWidth(200) + self.output_cost_spin.setFixedWidth(200) # Medium price_form.addRow("Output Cost:", self.output_cost_spin) right_column.addLayout(price_form) @@ -314,7 +324,7 @@ class ConfigTab(QWidget): # Load translation settings self.language_combo.setCurrentText(os.getenv("language", "English")) - self.timeout_spin.setValue(int(os.getenv("timeout", "120"))) + self.timeout_spin.setValue(int(os.getenv("timeout", "90"))) # Load performance settings self.file_threads_spin.setValue(int(os.getenv("fileThreads", "1"))) @@ -397,7 +407,7 @@ class ConfigTab(QWidget): # Translation settings self.language_combo.setCurrentText("English") - self.timeout_spin.setValue(120) + self.timeout_spin.setValue(90) # Performance settings self.file_threads_spin.setValue(1) diff --git a/gui/rpgmaker_tab.py b/gui/rpgmaker_tab.py index d39b3ca..e580054 100644 --- a/gui/rpgmaker_tab.py +++ b/gui/rpgmaker_tab.py @@ -7,7 +7,7 @@ from pathlib import Path from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QCheckBox, QPushButton, QGroupBox, QLabel, QMessageBox, QScrollArea, - QTextEdit, QSpinBox, QFrame + QTextEdit, QSpinBox, QFrame, QGridLayout ) from PyQt5.QtCore import Qt, pyqtSignal @@ -18,6 +18,21 @@ except ImportError: ConfigIntegration = None +def create_section_label(text): + """Create a section label for grouping checkboxes.""" + label = QLabel(text) + label.setStyleSheet(""" + QLabel { + font-size: 12px; + font-weight: bold; + color: #007acc; + padding: 5px 0px 3px 0px; + background-color: transparent; + } + """) + return label + + class RPGMakerTab(QWidget): """RPG Maker MV/MZ & Ace configuration tab. @@ -154,232 +169,148 @@ class RPGMakerTab(QWidget): self.reset_to_defaults() def init_ui(self): - """Initialize the user interface.""" + """Initialize the user interface with compact two-column layout.""" main_layout = QVBoxLayout() - - # Create scroll area - scroll_area = QScrollArea() - scroll_content = QWidget() - scroll_layout = QVBoxLayout() + main_layout.setContentsMargins(10, 10, 10, 10) + main_layout.setSpacing(10) # Title and description title = "RPG Maker MV/MZ" if self.engine != "ACE" else "RPG Maker Ace" title_label = QLabel(f"{title} Translation Settings") - title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;") - scroll_layout.addWidget(title_label) + title_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #007acc;") + main_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." + "Enable event codes to translate. Only enable what you need to reduce translation time and costs." ) description_label.setWordWrap(True) - description_label.setStyleSheet("color: #cccccc; margin-bottom: 10px;") - scroll_layout.addWidget(description_label) + description_label.setStyleSheet("color: #888888; font-size: 11px; margin-bottom: 5px;") + main_layout.addWidget(description_label) - # General Config Group - general_group = self.create_general_config_group() - scroll_layout.addWidget(general_group) + # Two-column layout for checkboxes + columns_layout = QHBoxLayout() + columns_layout.setSpacing(30) - # Main Dialogue Codes Group - dialogue_group = self.create_dialogue_codes_group() - scroll_layout.addWidget(dialogue_group) + # LEFT COLUMN + left_column = QVBoxLayout() + left_column.setSpacing(3) - # 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_with_message) - - self.validate_button = QPushButton("Validate Settings") - self.validate_button.clicked.connect(self.validate_and_show_report) - - button_layout.addWidget(self.reset_button) - button_layout.addWidget(self.validate_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(""" - Performance Notes:
- • Enable only the codes you need to reduce translation time and costs
- • CODE408 can significantly increase costs as it translates comments
- • Main dialogue codes (401, 405, 102) are typically required for story content
- • 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 + # General Configuration + left_column.addWidget(create_section_label("⚙️ General Options")) 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) + self.first_line_speakers_cb.setToolTip("Enable if first line of CODE401 contains speaker name") + left_column.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) + self.facename101_cb = QCheckBox("Face Name in CODE101") + self.facename101_cb.setToolTip("Enable to translate face names in CODE101") + left_column.addWidget(self.facename101_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) + self.names_cb = QCheckBox("Translate Names") + self.names_cb.setToolTip("Enable to translate character names") + left_column.addWidget(self.names_cb) - # BRFLAG - self.br_flag_cb = QCheckBox("Use
Tags") - self.br_flag_cb.setToolTip( - "Use
HTML tags instead of newlines for line breaks" - ) - layout.addWidget(self.br_flag_cb) + self.brflag_cb = QCheckBox("Line Break Flag") + self.brflag_cb.setToolTip("Enable line break handling") + left_column.addWidget(self.brflag_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) + self.fixtextwrap_cb = QCheckBox("Fix Text Wrap") + self.fixtextwrap_cb.setToolTip("Automatically fix text wrapping issues") + left_column.addWidget(self.fixtextwrap_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) + self.ignoretltext_cb = QCheckBox("Ignore TL Text") + self.ignoretltext_cb.setToolTip("Ignore already translated text") + left_column.addWidget(self.ignoretltext_cb) - group.setLayout(layout) - return group + left_column.addSpacing(8) - def create_dialogue_codes_group(self): - """Create dialogue codes group.""" - group = QGroupBox("Main Dialogue Codes (Recommended)") - layout = QVBoxLayout() + # Main Dialogue Codes + left_column.addWidget(create_section_label("💬 Main Dialogue (Recommended)")) + self.code401_cb = QCheckBox("CODE401 - Show Text") + left_column.addWidget(self.code401_cb) - # 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) + self.code405_cb = QCheckBox("CODE405 - Show Text (line 4+)") + left_column.addWidget(self.code405_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) + self.code102_cb = QCheckBox("CODE102 - Show Choices") + left_column.addWidget(self.code102_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) + left_column.addSpacing(8) - group.setLayout(layout) - return group + # Optional Dialogue Codes + left_column.addWidget(create_section_label("📝 Optional Dialogue")) + self.code101_cb = QCheckBox("CODE101 - Show Text (face)") + left_column.addWidget(self.code101_cb) - def create_optional_codes_group(self): - """Create optional codes group.""" - group = QGroupBox("Optional Codes") - layout = QVBoxLayout() + self.code408_cb = QCheckBox("CODE408 - Show Text (continuation)") + self.code408_cb.setToolTip("Can significantly increase costs") + left_column.addWidget(self.code408_cb) - # 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) + self.code122_cb = QCheckBox("CODE122 - Control Variables") + left_column.addWidget(self.code122_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) + left_column.addStretch() - group.setLayout(layout) - return group + # RIGHT COLUMN + right_column = QVBoxLayout() + right_column.setSpacing(3) - def create_variable_codes_group(self): - """Create variable codes group.""" - group = QGroupBox("Variable Codes") - layout = QVBoxLayout() + # Event Codes (compact grid layout) + right_column.addWidget(create_section_label("🎮 Event Codes")) - # 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) + # Create a grid for better space usage + event_grid = QGridLayout() + event_grid.setSpacing(2) + event_grid.setHorizontalSpacing(15) - 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") + # Event codes in a more compact format + event_codes = [ + ("CODE103", "Input Number"), ("CODE104", "Select Item"), + ("CODE111", "Conditional Branch"), ("CODE117", "Common Event"), + ("CODE119", "Set Movement"), ("CODE121", "Control Switches"), + ("CODE125", "Change Gold"), ("CODE126", "Change Items"), + ("CODE127", "Change Weapons"), ("CODE128", "Change Armors"), + ("CODE129", "Change Party"), ("CODE201", "Transfer Player"), + ("CODE202", "Set Vehicle Loc"), ("CODE203", "Set Event Loc"), + ("CODE211", "Change Transparency"), ("CODE212", "Show Animation"), + ("CODE213", "Show Balloon"), ("CODE214", "Erase Event"), + ("CODE221", "Fadeout Screen"), ("CODE222", "Fadein Screen"), + ("CODE231", "Show Picture"), ("CODE232", "Move Picture"), + ("CODE241", "Play BGM"), ("CODE242", "Fadeout BGM"), ] - self.other_code_checkboxes = {} + self.event_checkboxes = {} + for idx, (code, label) in enumerate(event_codes): + cb = QCheckBox(f"{code} - {label}") + cb.setStyleSheet("QCheckBox { font-size: 10px; }") + event_grid.addWidget(cb, idx // 2, idx % 2) + self.event_checkboxes[code] = cb - 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 + right_column.addLayout(event_grid) + right_column.addStretch() - group.setLayout(layout) - return group + # Add both columns + columns_layout.addLayout(left_column, 1) + columns_layout.addLayout(right_column, 1) + main_layout.addLayout(columns_layout) + + # Bottom buttons + button_layout = QHBoxLayout() + button_layout.setSpacing(10) + + self.reset_button = QPushButton("🔄 Reset to Defaults") + self.reset_button.clicked.connect(self.reset_to_defaults_with_message) + self.reset_button.setMaximumWidth(200) + + self.apply_button = QPushButton("✓ Apply Settings") + self.apply_button.clicked.connect(self.apply_to_module) + self.apply_button.setMaximumWidth(200) + + button_layout.addWidget(self.reset_button) + button_layout.addWidget(self.apply_button) + button_layout.addStretch() + + main_layout.addLayout(button_layout) + self.setLayout(main_layout) def reset_to_defaults(self): """Reset all settings to default values.""" @@ -388,31 +319,27 @@ class RPGMakerTab(QWidget): # Apply default values from the DEFAULT_CONFIG constant self.first_line_speakers_cb.setChecked(self.DEFAULT_CONFIG["FIRSTLINESPEAKERS"]) - self.facename_101_cb.setChecked(self.DEFAULT_CONFIG["FACENAME101"]) + self.facename101_cb.setChecked(self.DEFAULT_CONFIG["FACENAME101"]) self.names_cb.setChecked(self.DEFAULT_CONFIG["NAMES"]) - self.br_flag_cb.setChecked(self.DEFAULT_CONFIG["BRFLAG"]) - self.fix_textwrap_cb.setChecked(self.DEFAULT_CONFIG["FIXTEXTWRAP"]) - self.ignore_tl_text_cb.setChecked(self.DEFAULT_CONFIG["IGNORETLTEXT"]) + self.brflag_cb.setChecked(self.DEFAULT_CONFIG["BRFLAG"]) + self.fixtextwrap_cb.setChecked(self.DEFAULT_CONFIG["FIXTEXTWRAP"]) + self.ignoretltext_cb.setChecked(self.DEFAULT_CONFIG["IGNORETLTEXT"]) # Main dialogue codes - self.code_401_cb.setChecked(self.DEFAULT_CONFIG["CODE401"]) - self.code_405_cb.setChecked(self.DEFAULT_CONFIG["CODE405"]) - self.code_102_cb.setChecked(self.DEFAULT_CONFIG["CODE102"]) + self.code401_cb.setChecked(self.DEFAULT_CONFIG["CODE401"]) + self.code405_cb.setChecked(self.DEFAULT_CONFIG["CODE405"]) + self.code102_cb.setChecked(self.DEFAULT_CONFIG["CODE102"]) # Optional codes - self.code_101_cb.setChecked(self.DEFAULT_CONFIG["CODE101"]) - self.code_408_cb.setChecked(self.DEFAULT_CONFIG["CODE408"]) + self.code101_cb.setChecked(self.DEFAULT_CONFIG["CODE101"]) + self.code408_cb.setChecked(self.DEFAULT_CONFIG["CODE408"]) # Variable codes - self.code_122_cb.setChecked(self.DEFAULT_CONFIG["CODE122"]) + self.code122_cb.setChecked(self.DEFAULT_CONFIG["CODE122"]) - # Other codes - apply defaults from the constant - for code, cb in self.other_code_checkboxes.items(): - default_key = f"CODE{code}" - if default_key in self.DEFAULT_CONFIG: - cb.setChecked(self.DEFAULT_CONFIG[default_key]) - else: - cb.setChecked(False) # Fallback to False if not in defaults + # Event codes + for code, cb in self.event_checkboxes.items(): + cb.setChecked(self.DEFAULT_CONFIG.get(code, False)) # Reconnect signals and apply changes once self.connect_auto_apply() @@ -432,26 +359,26 @@ class RPGMakerTab(QWidget): try: # General settings checkboxes self.first_line_speakers_cb.stateChanged.disconnect() - self.facename_101_cb.stateChanged.disconnect() + self.facename101_cb.stateChanged.disconnect() self.names_cb.stateChanged.disconnect() - self.br_flag_cb.stateChanged.disconnect() - self.fix_textwrap_cb.stateChanged.disconnect() - self.ignore_tl_text_cb.stateChanged.disconnect() + self.brflag_cb.stateChanged.disconnect() + self.fixtextwrap_cb.stateChanged.disconnect() + self.ignoretltext_cb.stateChanged.disconnect() # Main dialogue codes - self.code_401_cb.stateChanged.disconnect() - self.code_405_cb.stateChanged.disconnect() - self.code_102_cb.stateChanged.disconnect() + self.code401_cb.stateChanged.disconnect() + self.code405_cb.stateChanged.disconnect() + self.code102_cb.stateChanged.disconnect() # Optional codes - self.code_101_cb.stateChanged.disconnect() - self.code_408_cb.stateChanged.disconnect() + self.code101_cb.stateChanged.disconnect() + self.code408_cb.stateChanged.disconnect() # Variable codes - self.code_122_cb.stateChanged.disconnect() + self.code122_cb.stateChanged.disconnect() - # Other codes - for cb in self.other_code_checkboxes.values(): + # Event codes + for cb in self.event_checkboxes.values(): cb.stateChanged.disconnect() except TypeError: # Ignore if signals are not connected @@ -461,26 +388,26 @@ class RPGMakerTab(QWidget): """Connect all checkboxes to auto-apply changes when modified.""" # General settings checkboxes self.first_line_speakers_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.facename_101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.facename101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) self.names_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.br_flag_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.fix_textwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.ignore_tl_text_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.brflag_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.fixtextwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.ignoretltext_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) # Main dialogue codes - self.code_401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.code_405_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.code_102_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.code401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.code405_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.code102_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) # Optional codes - self.code_101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - self.code_408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.code101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.code408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) # Variable codes - self.code_122_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) + self.code122_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) - # Other codes - for cb in self.other_code_checkboxes.values(): + # Event codes + for cb in self.event_checkboxes.values(): cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False)) def get_config(self): @@ -488,28 +415,28 @@ class RPGMakerTab(QWidget): config = { # General settings "FIRSTLINESPEAKERS": self.first_line_speakers_cb.isChecked(), - "FACENAME101": self.facename_101_cb.isChecked(), + "FACENAME101": self.facename101_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(), + "BRFLAG": self.brflag_cb.isChecked(), + "FIXTEXTWRAP": self.fixtextwrap_cb.isChecked(), + "IGNORETLTEXT": self.ignoretltext_cb.isChecked(), # Main dialogue codes - "CODE401": self.code_401_cb.isChecked(), - "CODE405": self.code_405_cb.isChecked(), - "CODE102": self.code_102_cb.isChecked(), + "CODE401": self.code401_cb.isChecked(), + "CODE405": self.code405_cb.isChecked(), + "CODE102": self.code102_cb.isChecked(), # Optional codes - "CODE101": self.code_101_cb.isChecked(), - "CODE408": self.code_408_cb.isChecked(), + "CODE101": self.code101_cb.isChecked(), + "CODE408": self.code408_cb.isChecked(), # Variable codes - "CODE122": self.code_122_cb.isChecked(), + "CODE122": self.code122_cb.isChecked(), } - # Other codes - for code, cb in self.other_code_checkboxes.items(): - config[f"CODE{code}"] = cb.isChecked() + # Event codes + for code, cb in self.event_checkboxes.items(): + config[code] = cb.isChecked() return config @@ -517,27 +444,27 @@ class RPGMakerTab(QWidget): """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.facename101_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)) + self.brflag_cb.setChecked(config.get("BRFLAG", False)) + self.fixtextwrap_cb.setChecked(config.get("FIXTEXTWRAP", True)) + self.ignoretltext_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)) + self.code401_cb.setChecked(config.get("CODE401", True)) + self.code405_cb.setChecked(config.get("CODE405", True)) + self.code102_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)) + self.code101_cb.setChecked(config.get("CODE101", False)) + self.code408_cb.setChecked(config.get("CODE408", False)) # Variable codes - self.code_122_cb.setChecked(config.get("CODE122", False)) + self.code122_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)) + # Event codes + for code, cb in self.event_checkboxes.items(): + cb.setChecked(config.get(code, False)) def load_from_file(self, file_path): """Load configuration from file.""" @@ -555,20 +482,20 @@ class RPGMakerTab(QWidget): # 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() + self.code401_cb.isChecked() or + self.code405_cb.isChecked() or + self.code102_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(): + if self.code408_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(): + if self.brflag_cb.isChecked() and self.fixtextwrap_cb.isChecked(): warnings.append("Both BR tags and text wrapping are enabled. This might cause formatting issues.") return len(errors) == 0, warnings, errors diff --git a/gui/wolf_tab.py b/gui/wolf_tab.py index f63f7b9..7950102 100644 --- a/gui/wolf_tab.py +++ b/gui/wolf_tab.py @@ -5,11 +5,17 @@ what gets translated. This can be expanded later. """ from pathlib import Path from PyQt5.QtWidgets import ( - QWidget, QVBoxLayout, QGroupBox, QCheckBox, QPushButton, QMessageBox, QLabel, QHBoxLayout, QScrollArea + QWidget, QVBoxLayout, QCheckBox, QPushButton, QMessageBox, QLabel, QHBoxLayout, QGridLayout ) from PyQt5.QtCore import pyqtSignal import re +def create_section_label(text): + """Create a styled section header label.""" + label = QLabel(text) + label.setStyleSheet("font-size: 12px; font-weight: bold; color: #007acc; padding: 2px 0px;") + return label + class WolfTab(QWidget): config_changed = pyqtSignal() @@ -45,67 +51,183 @@ class WolfTab(QWidget): self.reset_to_defaults() def init_ui(self): + """Initialize the user interface with compact two-column layout.""" main_layout = QVBoxLayout() - scroll = QScrollArea() - content = QWidget() - v = QVBoxLayout() + main_layout.setContentsMargins(10, 10, 10, 10) + main_layout.setSpacing(10) + + # Title and description + title = QLabel("Wolf RPG Editor Translation Settings") + title.setStyleSheet("font-size: 14px; font-weight: bold; color: #007acc;") + main_layout.addWidget(title) - title = QLabel("Wolf RPG Settings") - title.setStyleSheet("font-size:16px;font-weight:bold;color:#007acc") - v.addWidget(title) - desc = QLabel("Toggle which parts of Wolf RPG data are translated. This is a minimal first pass; more options can be added later.") - desc.setWordWrap(True) - desc.setStyleSheet("color:#cccccc") - v.addWidget(desc) - - # Groups - self.checkboxes = {} - self.add_group(v, "Dialogue / Choices", ["CODE101", "CODE102"]) - self.add_group(v, "Pictures / Variables", ["CODE150", "CODE122"]) - self.add_group(v, "Other Event Codes", ["CODE210", "CODE300", "CODE250"]) - self.add_group(v, "Database Sections", [ - "SCENARIOFLAG", "OPTIONSFLAG", "NPCFLAG", "DBNAMEFLAG", "DBVALUEFLAG", - "ITEMFLAG", "STATEFLAG", "ENEMYFLAG", "ARMORFLAG", "WEAPONFLAG", "SKILLFLAG" - ]) - - # Buttons - btn_row = QHBoxLayout() - self.reset_btn = QPushButton("Reset") - self.reset_btn.clicked.connect(self.reset_to_defaults) - self.apply_btn = QPushButton("Apply Now") - self.apply_btn.clicked.connect(self.apply_to_module) - btn_row.addWidget(self.reset_btn) - btn_row.addWidget(self.apply_btn) - btn_row.addStretch() - v.addLayout(btn_row) - - content.setLayout(v) - scroll.setWidget(content) - scroll.setWidgetResizable(True) - main_layout.addWidget(scroll) + description = QLabel( + "Enable translation options for Wolf RPG Editor projects. Only enable what you need." + ) + description.setWordWrap(True) + description.setStyleSheet("color: #888888; font-size: 11px; margin-bottom: 5px;") + main_layout.addWidget(description) + + # Two-column layout for checkboxes + columns_layout = QHBoxLayout() + columns_layout.setSpacing(30) + + # LEFT COLUMN + left_column = QVBoxLayout() + left_column.setSpacing(3) + + # Dialogue & Choices + left_column.addWidget(create_section_label("💬 Dialogue & Choices")) + self.code101_cb = QCheckBox("CODE101 - Show Text") + self.code101_cb.setToolTip("Enable translation of dialogue text") + left_column.addWidget(self.code101_cb) + + self.code102_cb = QCheckBox("CODE102 - Show Choices") + self.code102_cb.setToolTip("Enable translation of choice options") + left_column.addWidget(self.code102_cb) + + left_column.addSpacing(8) + + # Pictures & Variables + left_column.addWidget(create_section_label("🖼️ Pictures & Variables")) + self.code150_cb = QCheckBox("CODE150 - Show Picture") + self.code150_cb.setToolTip("Enable translation of picture-related text") + left_column.addWidget(self.code150_cb) + + self.code122_cb = QCheckBox("CODE122 - String Operations") + self.code122_cb.setToolTip("Enable translation of string variables") + left_column.addWidget(self.code122_cb) + + left_column.addSpacing(8) + + # Other Event Codes + left_column.addWidget(create_section_label("🎮 Other Event Codes")) + self.code210_cb = QCheckBox("CODE210 - Conditional Branch") + left_column.addWidget(self.code210_cb) + + self.code300_cb = QCheckBox("CODE300 - Set Variable") + left_column.addWidget(self.code300_cb) + + self.code250_cb = QCheckBox("CODE250 - Sound Effect") + left_column.addWidget(self.code250_cb) + + left_column.addStretch() + + # RIGHT COLUMN + right_column = QVBoxLayout() + right_column.setSpacing(3) + + # Database Sections + right_column.addWidget(create_section_label("📚 Database Sections")) + + # Database flags in compact 2-column grid + db_grid = QGridLayout() + db_grid.setSpacing(2) + db_grid.setHorizontalSpacing(10) + + database_flags = [ + ("SCENARIOFLAG", "Scenario Text"), + ("OPTIONSFLAG", "Options"), + ("NPCFLAG", "NPC Data"), + ("DBNAMEFLAG", "Database Names"), + ("DBVALUEFLAG", "Database Values"), + ("ITEMFLAG", "Items"), + ("STATEFLAG", "States"), + ("ENEMYFLAG", "Enemies"), + ("ARMORFLAG", "Armor"), + ("WEAPONFLAG", "Weapons"), + ("SKILLFLAG", "Skills"), + ] + + self.db_checkboxes = {} + for idx, (key, label) in enumerate(database_flags): + cb = QCheckBox(label) + cb.setStyleSheet("QCheckBox { font-size: 10px; }") + db_grid.addWidget(cb, idx // 2, idx % 2) + self.db_checkboxes[key] = cb + + right_column.addLayout(db_grid) + right_column.addStretch() + + # Add both columns + columns_layout.addLayout(left_column, 1) + columns_layout.addLayout(right_column, 1) + main_layout.addLayout(columns_layout) + + # Bottom buttons + button_layout = QHBoxLayout() + button_layout.setSpacing(10) + + self.reset_btn = QPushButton("🔄 Reset to Defaults") + self.reset_btn.clicked.connect(self.reset_to_defaults_with_message) + self.reset_btn.setMaximumWidth(200) + + self.apply_btn = QPushButton("✓ Apply Settings") + self.apply_btn.clicked.connect(lambda: self.apply_to_module(True)) + self.apply_btn.setMaximumWidth(200) + + button_layout.addWidget(self.reset_btn) + button_layout.addWidget(self.apply_btn) + button_layout.addStretch() + + main_layout.addLayout(button_layout) self.setLayout(main_layout) + + # Connect auto-apply + self.code101_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + self.code102_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + self.code150_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + self.code122_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + self.code210_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + self.code300_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + self.code250_cb.stateChanged.connect(lambda: self.apply_to_module(False)) + + for cb in self.db_checkboxes.values(): + cb.stateChanged.connect(lambda: self.apply_to_module(False)) - def add_group(self, parent_layout, title, keys): - box = QGroupBox(title) - v = QVBoxLayout() - for k in keys: - cb = QCheckBox(k) - cb.stateChanged.connect(lambda _=None: self.apply_to_module(False)) - v.addWidget(cb) - self.checkboxes[k] = cb - box.setLayout(v) - parent_layout.addWidget(box) def get_config(self): - return {k: cb.isChecked() for k, cb in self.checkboxes.items()} + """Get current configuration as dictionary.""" + config = { + "CODE101": self.code101_cb.isChecked(), + "CODE102": self.code102_cb.isChecked(), + "CODE150": self.code150_cb.isChecked(), + "CODE122": self.code122_cb.isChecked(), + "CODE210": self.code210_cb.isChecked(), + "CODE300": self.code300_cb.isChecked(), + "CODE250": self.code250_cb.isChecked(), + } + # Add database checkboxes + for key, cb in self.db_checkboxes.items(): + config[key] = cb.isChecked() + return config def reset_to_defaults(self): - for k, val in self.DEFAULT_CONFIG.items(): - if k in self.checkboxes: - self.checkboxes[k].setChecked(val) - self.apply_to_module() + """Reset all settings to default values without showing message.""" + self.code101_cb.setChecked(self.DEFAULT_CONFIG["CODE101"]) + self.code102_cb.setChecked(self.DEFAULT_CONFIG["CODE102"]) + self.code150_cb.setChecked(self.DEFAULT_CONFIG["CODE150"]) + self.code122_cb.setChecked(self.DEFAULT_CONFIG["CODE122"]) + self.code210_cb.setChecked(self.DEFAULT_CONFIG["CODE210"]) + self.code300_cb.setChecked(self.DEFAULT_CONFIG["CODE300"]) + self.code250_cb.setChecked(self.DEFAULT_CONFIG["CODE250"]) + + for key, cb in self.db_checkboxes.items(): + cb.setChecked(self.DEFAULT_CONFIG.get(key, False)) + + self.apply_to_module(False) + + def reset_to_defaults_with_message(self): + """Reset to defaults and show confirmation message.""" + self.reset_to_defaults() + QMessageBox.information( + self, + "Reset Complete", + "All settings have been reset to their default values." + ) def apply_to_module(self, show_message=False): + """Apply current configuration to wolf.py module.""" module_path = Path(__file__).parent.parent / 'modules' / 'wolf.py' if not module_path.exists(): if show_message: @@ -123,4 +245,3 @@ class WolfTab(QWidget): except Exception as e: if show_message: QMessageBox.critical(self, 'Error', f'Failed to apply settings: {e}') -