Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.
| 1 | from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QTabWidget, QWidget, QGroupBox, |
| 2 | QGridLayout, QLabel, QComboBox, QSpinBox, QPushButton, |
| 3 | QCheckBox, QLineEdit, QHBoxLayout, QColorDialog, QFontDialog, |
| 4 | QFileDialog, QMessageBox, QApplication) |
| 5 | from PyQt5.QtCore import Qt |
| 6 | from PyQt5.QtGui import QFont, QColor, QFontDatabase |
| 7 | import os |
| 8 | import json |
| 9 | |
| 10 | # Modifica l'import relativo in import assoluto |
| 11 | from GraphicUserInterface.utils.style_manager import StyleManager |
| 12 | |
| 13 | class SettingsDialog(QDialog): |
| 14 | def __init__(self, parent=None): |
| 15 | super().__init__(parent) |
| 16 | self.parent = parent |
| 17 | self.setup_ui() |
| 18 | |
| 19 | def setup_ui(self): |
| 20 | """Setup the settings dialog UI""" |
| 21 | self.setWindowTitle("Settings") |
| 22 | self.setMinimumWidth(500) |
| 23 | |
| 24 | layout = QVBoxLayout(self) |
| 25 | |
| 26 | # Create tab widget |
| 27 | self.tab_widget = QTabWidget() |
| 28 | |
| 29 | # Add tabs |
| 30 | self.tab_widget.addTab(self.create_appearance_tab(), "Appearance") |
| 31 | self.tab_widget.addTab(self.create_behavior_tab(), "Behavior") |
| 32 | self.tab_widget.addTab(self.create_paths_tab(), "Paths") |
| 33 | |
| 34 | layout.addWidget(self.tab_widget) |
| 35 | |
| 36 | # Add buttons |
| 37 | button_layout = QHBoxLayout() |
| 38 | save_button = QPushButton("Save") |
| 39 | cancel_button = QPushButton("Cancel") |
| 40 | reset_button = QPushButton("Reset to Default") |
| 41 | |
| 42 | save_button.clicked.connect(self.save_settings) |
| 43 | cancel_button.clicked.connect(self.reject) |
| 44 | reset_button.clicked.connect(self.reset_settings) |
| 45 | |
| 46 | button_layout.addWidget(save_button) |
| 47 | button_layout.addWidget(cancel_button) |
| 48 | button_layout.addWidget(reset_button) |
| 49 | |
| 50 | layout.addLayout(button_layout) |
| 51 | |
| 52 | # Load current settings |
| 53 | self.load_settings() |
| 54 | |
| 55 | def create_appearance_tab(self): |
| 56 | """Create and return the appearance tab""" |
| 57 | tab = QWidget() |
| 58 | layout = QVBoxLayout(tab) |
| 59 | |
| 60 | # Theme group |
| 61 | theme_group = QGroupBox("Theme") |
| 62 | theme_layout = QGridLayout() |
| 63 | |
| 64 | theme_layout.addWidget(QLabel("Theme:"), 0, 0) |
| 65 | self.theme_combo = QComboBox() |
| 66 | self.theme_combo.addItems(["Light", "Dark", "System", "Custom"]) |
| 67 | self.theme_combo.currentTextChanged.connect(self.on_theme_changed) |
| 68 | theme_layout.addWidget(self.theme_combo, 0, 1) |
| 69 | |
| 70 | theme_group.setLayout(theme_layout) |
| 71 | layout.addWidget(theme_group) |
| 72 | |
| 73 | # Language group |
| 74 | language_group = QGroupBox("Language") |
| 75 | language_layout = QGridLayout() |
| 76 | |
| 77 | language_layout.addWidget(QLabel("Language:"), 0, 0) |
| 78 | self.language_combo = QComboBox() |
| 79 | self.language_combo.addItems(["English", "Italian", "Spanish", "French", "German", "Japanese"]) |
| 80 | self.language_combo.currentTextChanged.connect(self.on_language_changed) |
| 81 | language_layout.addWidget(self.language_combo, 0, 1) |
| 82 | |
| 83 | language_group.setLayout(language_layout) |
| 84 | layout.addWidget(language_group) |
| 85 | |
| 86 | # Font group |
| 87 | font_group = QGroupBox("Font") |
| 88 | font_layout = QGridLayout() |
| 89 | |
| 90 | # Font family combo |
| 91 | font_layout.addWidget(QLabel("Font:"), 0, 0) |
| 92 | self.font_combo = QComboBox() |
| 93 | self.font_combo.addItems(QFontDatabase().families()) |
| 94 | font_layout.addWidget(self.font_combo, 0, 1) |
| 95 | |
| 96 | # Font size spinner |
| 97 | font_layout.addWidget(QLabel("Size:"), 1, 0) |
| 98 | self.font_size_spin = QSpinBox() |
| 99 | self.font_size_spin.setRange(8, 24) |
| 100 | self.font_size_spin.setValue(12) |
| 101 | font_layout.addWidget(self.font_size_spin, 1, 1) |
| 102 | |
| 103 | # Font preview button |
| 104 | self.font_button = QPushButton("Preview Font") |
| 105 | self.font_button.clicked.connect(self.select_font) |
| 106 | font_layout.addWidget(self.font_button, 2, 0, 1, 2) |
| 107 | |
| 108 | font_group.setLayout(font_layout) |
| 109 | layout.addWidget(font_group) |
| 110 | |
| 111 | # Colors group |
| 112 | self.colors_group = QGroupBox("Colors") |
| 113 | colors_layout = QGridLayout() |
| 114 | |
| 115 | # Color buttons with labels and preview |
| 116 | self.bg_color_button = QPushButton() |
| 117 | self.text_color_button = QPushButton() |
| 118 | self.accent_color_button = QPushButton() |
| 119 | |
| 120 | colors_layout.addWidget(QLabel("Background:"), 0, 0) |
| 121 | colors_layout.addWidget(self.bg_color_button, 0, 1) |
| 122 | colors_layout.addWidget(QLabel("Text:"), 1, 0) |
| 123 | colors_layout.addWidget(self.text_color_button, 1, 1) |
| 124 | colors_layout.addWidget(QLabel("Accent:"), 2, 0) |
| 125 | colors_layout.addWidget(self.accent_color_button, 2, 1) |
| 126 | |
| 127 | self.bg_color_button.clicked.connect(lambda: self.pick_color("background")) |
| 128 | self.text_color_button.clicked.connect(lambda: self.pick_color("text")) |
| 129 | self.accent_color_button.clicked.connect(lambda: self.pick_color("accent")) |
| 130 | |
| 131 | # Disabilita i pulsanti dei colori se non è selezionato Custom |
| 132 | self.colors_group.setEnabled(self.theme_combo.currentText() == "Custom") |
| 133 | |
| 134 | self.colors_group.setLayout(colors_layout) |
| 135 | layout.addWidget(self.colors_group) |
| 136 | |
| 137 | return tab |
| 138 | |
| 139 | def create_behavior_tab(self): |
| 140 | """Create and return the behavior tab""" |
| 141 | tab = QWidget() |
| 142 | layout = QVBoxLayout(tab) |
| 143 | |
| 144 | # File browser settings |
| 145 | browser_group = QGroupBox("File Browser") |
| 146 | browser_layout = QVBoxLayout() |
| 147 | |
| 148 | self.auto_expand_check = QCheckBox("Auto-expand file tree") |
| 149 | self.show_hidden_check = QCheckBox("Show hidden files") |
| 150 | self.confirm_exit_check = QCheckBox("Confirm before exit") |
| 151 | |
| 152 | browser_layout.addWidget(self.auto_expand_check) |
| 153 | browser_layout.addWidget(self.show_hidden_check) |
| 154 | browser_layout.addWidget(self.confirm_exit_check) |
| 155 | |
| 156 | browser_group.setLayout(browser_layout) |
| 157 | layout.addWidget(browser_group) |
| 158 | |
| 159 | layout.addStretch() |
| 160 | return tab |
| 161 | |
| 162 | def create_paths_tab(self): |
| 163 | """Create and return the paths tab""" |
| 164 | tab = QWidget() |
| 165 | layout = QVBoxLayout(tab) |
| 166 | |
| 167 | paths_group = QGroupBox("Default Paths") |
| 168 | paths_layout = QGridLayout() |
| 169 | |
| 170 | self.output_path_edit = QLineEdit() |
| 171 | self.temp_path_edit = QLineEdit() |
| 172 | |
| 173 | output_browse = QPushButton("Browse") |
| 174 | temp_browse = QPushButton("Browse") |
| 175 | |
| 176 | output_browse.clicked.connect(lambda: self.browse_directory(self.output_path_edit)) |
| 177 | temp_browse.clicked.connect(lambda: self.browse_directory(self.temp_path_edit)) |
| 178 | |
| 179 | paths_layout.addWidget(QLabel("Output:"), 0, 0) |
| 180 | paths_layout.addWidget(self.output_path_edit, 0, 1) |
| 181 | paths_layout.addWidget(output_browse, 0, 2) |
| 182 | paths_layout.addWidget(QLabel("Temp:"), 1, 0) |
| 183 | paths_layout.addWidget(self.temp_path_edit, 1, 1) |
| 184 | paths_layout.addWidget(temp_browse, 1, 2) |
| 185 | |
| 186 | paths_group.setLayout(paths_layout) |
| 187 | layout.addWidget(paths_group) |
| 188 | |
| 189 | layout.addStretch() |
| 190 | return tab |
| 191 | |
| 192 | def select_font(self): |
| 193 | """Open font selection dialog""" |
| 194 | current_font = QApplication.font() |
| 195 | font, ok = QFontDialog.getFont(current_font, self) |
| 196 | if ok: |
| 197 | self.current_font = font |
| 198 | self.font_button.setText(f"{font.family()} - {font.pointSize()}pt") |
| 199 | QApplication.setFont(font) |
| 200 | |
| 201 | def pick_color(self, color_type): |
| 202 | """Open color picker dialog and immediately apply nested settings""" |
| 203 | color = QColorDialog.getColor() |
| 204 | if not color.isValid(): |
| 205 | return |
| 206 | color_hex = color.name() |
| 207 | |
| 208 | # Update the clicked button preview/text |
| 209 | button_style = f""" |
| 210 | QPushButton {{ |
| 211 | background-color: {color_hex}; |
| 212 | color: {"#ffffff" if color.lightness() < 128 else "#000000"}; |
| 213 | min-width: 100px; |
| 214 | padding: 5px; |
| 215 | border: 1px solid #bdc3c7; |
| 216 | border-radius: 4px; |
| 217 | }} |
| 218 | """ |
| 219 | |
| 220 | if color_type == "background": |
| 221 | self.bg_color_button.setStyleSheet(button_style) |
| 222 | self.bg_color_button.setText(color_hex) |
| 223 | elif color_type == "text": |
| 224 | self.text_color_button.setStyleSheet(button_style) |
| 225 | self.text_color_button.setText(color_hex) |
| 226 | elif color_type == "accent": |
| 227 | self.accent_color_button.setStyleSheet(button_style) |
| 228 | self.accent_color_button.setText(color_hex) |
| 229 | |
| 230 | # Build full nested settings snapshot and apply theme |
| 231 | if self.parent: |
| 232 | settings = { |
| 233 | "appearance": { |
| 234 | "theme": self.theme_combo.currentText(), |
| 235 | "night_mode": self.theme_combo.currentText() == "Dark", |
| 236 | "font_family": getattr(self, 'current_font', QFont("Arial", 12)).family(), |
| 237 | "font_size": getattr(self, 'current_font', QFont("Arial", 12)).pointSize(), |
| 238 | "colors": { |
| 239 | "background": self.bg_color_button.text() if self.bg_color_button.text().startswith('#') else "#ffffff", |
| 240 | "text": self.text_color_button.text() if self.text_color_button.text().startswith('#') else "#000000", |
| 241 | "accent": self.accent_color_button.text() if self.accent_color_button.text().startswith('#') else "#3498db", |
| 242 | }, |
| 243 | } |
| 244 | } |
| 245 | StyleManager.apply_theme(self.parent, settings) |
| 246 | self.parent.repaint() |
| 247 | QApplication.processEvents() |
| 248 | |
| 249 | def browse_directory(self, line_edit): |
| 250 | """Browse for directory""" |
| 251 | directory = QFileDialog.getExistingDirectory(self, "Select Directory") |
| 252 | if directory: |
| 253 | line_edit.setText(directory) |
| 254 | |
| 255 | def save_settings(self): |
| 256 | """Save settings in StyleManager format and apply them""" |
| 257 | try: |
| 258 | settings = { |
| 259 | "appearance": { |
| 260 | "theme": self.theme_combo.currentText(), |
| 261 | "night_mode": self.theme_combo.currentText() == "Dark", |
| 262 | "font_family": self.current_font.family() if hasattr(self, 'current_font') else "Arial", |
| 263 | "font_size": self.current_font.pointSize() if hasattr(self, 'current_font') else 12, |
| 264 | "colors": { |
| 265 | "background": self.bg_color_button.text() if self.bg_color_button.text().startswith('#') else "#ffffff", |
| 266 | "text": self.text_color_button.text() if self.text_color_button.text().startswith('#') else "#000000", |
| 267 | "accent": self.accent_color_button.text() if self.accent_color_button.text().startswith('#') else "#3498db", |
| 268 | }, |
| 269 | }, |
| 270 | "behavior": { |
| 271 | "auto_expand": self.auto_expand_check.isChecked(), |
| 272 | "show_hidden": self.show_hidden_check.isChecked(), |
| 273 | "confirm_exit": self.confirm_exit_check.isChecked(), |
| 274 | }, |
| 275 | "paths": { |
| 276 | "output": self.output_path_edit.text(), |
| 277 | "temp": self.temp_path_edit.text(), |
| 278 | }, |
| 279 | "language": self.language_combo.currentText(), |
| 280 | } |
| 281 | |
| 282 | StyleManager.save_settings(settings) |
| 283 | |
| 284 | if self.parent: |
| 285 | # Apply theme and font immediately |
| 286 | StyleManager.apply_theme(self.parent, settings) |
| 287 | font = QFont(settings["appearance"]["font_family"], settings["appearance"]["font_size"]) |
| 288 | QApplication.setFont(font) |
| 289 | |
| 290 | self.accept() |
| 291 | |
| 292 | except Exception as e: |
| 293 | QMessageBox.critical(self, "Error", f"Failed to save settings: {str(e)}") |
| 294 | |
| 295 | def load_settings(self): |
| 296 | """Load settings using StyleManager (nested format) and populate UI""" |
| 297 | try: |
| 298 | settings = StyleManager.load_settings() |
| 299 | |
| 300 | appearance = settings.get("appearance", {}) |
| 301 | colors = appearance.get("colors", {}) |
| 302 | |
| 303 | # Theme |
| 304 | self.theme_combo.setCurrentText(appearance.get("theme", "Light")) |
| 305 | self.colors_group.setEnabled(self.theme_combo.currentText() == "Custom") |
| 306 | |
| 307 | # Language (optional) |
| 308 | lang = settings.get("language", "English") |
| 309 | if self.language_combo.findText(lang) != -1: |
| 310 | self.language_combo.setCurrentText(lang) |
| 311 | |
| 312 | # Font |
| 313 | font = QFont(appearance.get("font_family", "Arial"), appearance.get("font_size", 12)) |
| 314 | self.current_font = font |
| 315 | self.font_button.setText(f"{font.family()} - {font.pointSize()}pt") |
| 316 | |
| 317 | # Colors |
| 318 | self.update_color_button(self.bg_color_button, colors.get("background", "#ffffff")) |
| 319 | self.update_color_button(self.text_color_button, colors.get("text", "#000000")) |
| 320 | self.update_color_button(self.accent_color_button, colors.get("accent", "#3498db")) |
| 321 | |
| 322 | # Behavior |
| 323 | behavior = settings.get("behavior", {}) |
| 324 | self.auto_expand_check.setChecked(behavior.get("auto_expand", True)) |
| 325 | self.show_hidden_check.setChecked(behavior.get("show_hidden", False)) |
| 326 | self.confirm_exit_check.setChecked(behavior.get("confirm_exit", True)) |
| 327 | |
| 328 | # Paths |
| 329 | paths = settings.get("paths", {}) |
| 330 | self.output_path_edit.setText(paths.get("output", "")) |
| 331 | self.temp_path_edit.setText(paths.get("temp", "")) |
| 332 | |
| 333 | except Exception as e: |
| 334 | QMessageBox.warning(self, "Warning", f"Failed to load settings: {str(e)}") |
| 335 | # Soft defaults without forcing save |
| 336 | self.reset_settings() |
| 337 | |
| 338 | def reset_settings(self): |
| 339 | """Reset settings to default""" |
| 340 | reply = QMessageBox.question(self, "Confirm Reset", |
| 341 | "Are you sure you want to reset all settings to default?", |
| 342 | QMessageBox.Yes | QMessageBox.No) |
| 343 | |
| 344 | if reply == QMessageBox.Yes: |
| 345 | # Reset to defaults |
| 346 | self.theme_combo.setCurrentText("Light") |
| 347 | |
| 348 | font = QFont("Arial", 12) |
| 349 | self.current_font = font |
| 350 | self.font_button.setText("Arial - 12pt") |
| 351 | |
| 352 | self.bg_color_button.setStyleSheet("") |
| 353 | self.text_color_button.setStyleSheet("") |
| 354 | self.accent_color_button.setStyleSheet("") |
| 355 | |
| 356 | self.auto_expand_check.setChecked(True) |
| 357 | self.show_hidden_check.setChecked(False) |
| 358 | self.confirm_exit_check.setChecked(True) |
| 359 | |
| 360 | self.output_path_edit.clear() |
| 361 | self.temp_path_edit.clear() |
| 362 | |
| 363 | # Save and apply default settings |
| 364 | self.save_settings() |
| 365 | |
| 366 | def apply_settings(self, settings): |
| 367 | """Apply settings to parent window""" |
| 368 | if self.parent: |
| 369 | try: |
| 370 | # Applica il tema |
| 371 | theme = settings.get("theme", "Light") |
| 372 | |
| 373 | # Gestisci i colori personalizzati |
| 374 | if theme == "Custom": |
| 375 | colors = { |
| 376 | 'bg_color': self.bg_color_button.text(), |
| 377 | 'text_color': self.text_color_button.text(), |
| 378 | 'accent_color': self.accent_color_button.text() |
| 379 | } |
| 380 | settings.update(colors) |
| 381 | |
| 382 | # Applica il font |
| 383 | font = QFont(settings["font_family"], settings["font_size"]) |
| 384 | QApplication.setFont(font) |
| 385 | |
| 386 | # Applica il tema |
| 387 | StyleManager.apply_theme(self.parent, settings) |
| 388 | |
| 389 | # Aggiorna le impostazioni del file browser |
| 390 | if hasattr(self.parent, 'file_browser'): |
| 391 | if settings.get("auto_expand", True): |
| 392 | self.parent.file_browser.file_tree.expandAll() |
| 393 | else: |
| 394 | self.parent.file_browser.file_tree.collapseAll() |
| 395 | |
| 396 | # Forza l'aggiornamento visuale |
| 397 | self.parent.repaint() |
| 398 | QApplication.processEvents() |
| 399 | |
| 400 | except Exception as e: |
| 401 | QMessageBox.critical(self, "Error", f"Failed to apply settings: {str(e)}") |
| 402 | |
| 403 | def on_theme_changed(self, theme): |
| 404 | """Handle theme change""" |
| 405 | self.colors_group.setEnabled(theme == "Custom") |
| 406 | if theme != "Custom": |
| 407 | # Imposta i colori predefiniti per il tema selezionato |
| 408 | if theme == "Light": |
| 409 | self.set_theme_colors("#ffffff", "#000000", "#3498db") |
| 410 | elif theme == "Dark": |
| 411 | self.set_theme_colors("#1e1e1e", "#ffffff", "#3498db") |
| 412 | elif theme == "System": |
| 413 | # Usa i colori di sistema |
| 414 | from PyQt5.QtGui import QPalette |
| 415 | palette = QApplication.palette() |
| 416 | self.set_theme_colors( |
| 417 | palette.color(QPalette.Window).name(), |
| 418 | palette.color(QPalette.WindowText).name(), |
| 419 | palette.color(QPalette.Highlight).name() |
| 420 | ) |
| 421 | |
| 422 | def set_theme_colors(self, bg, text, accent): |
| 423 | """Set theme colors""" |
| 424 | self.update_color_button(self.bg_color_button, bg) |
| 425 | self.update_color_button(self.text_color_button, text) |
| 426 | self.update_color_button(self.accent_color_button, accent) |
| 427 | |
| 428 | def update_color_button(self, button, color): |
| 429 | """Update color button appearance""" |
| 430 | button.setStyleSheet(f""" |
| 431 | QPushButton {{ |
| 432 | background-color: {color}; |
| 433 | color: {"#ffffff" if QColor(color).lightness() < 128 else "#000000"}; |
| 434 | min-width: 100px; |
| 435 | padding: 5px; |
| 436 | border: 1px solid #bdc3c7; |
| 437 | border-radius: 4px; |
| 438 | }} |
| 439 | """) |
| 440 | button.setText(color) |
| 441 | |
| 442 | def on_language_changed(self, language): |
| 443 | """Handle language change""" |
| 444 | lang_codes = { |
| 445 | "English": "en", |
| 446 | "Italian": "it", |
| 447 | "Spanish": "es", |
| 448 | "French": "fr", |
| 449 | "German": "de", |
| 450 | "Japanese": "ja" |
| 451 | } |
| 452 | |
| 453 | if language in lang_codes: |
| 454 | self.parent.translator.change_language(lang_codes[language]) |
| 455 | self.parent.retranslate_ui() |