Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.
| 1 | import json |
| 2 | from PyQt5.QtGui import QFont, QColor |
| 3 | from PyQt5.QtCore import Qt |
| 4 | from PyQt5.QtWidgets import QWidget |
| 5 | import os |
| 6 | import logging |
| 7 | |
| 8 | class StyleManager: |
| 9 | DEFAULT_SETTINGS = { |
| 10 | "appearance": { |
| 11 | "theme": "Light", |
| 12 | "night_mode": False, |
| 13 | "font_family": "Arial", |
| 14 | "font_size": 12, |
| 15 | "colors": { |
| 16 | "background": "#ffffff", |
| 17 | "text": "#000000", |
| 18 | "accent": "#3498db" |
| 19 | } |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | # Temi predefiniti |
| 24 | THEMES = { |
| 25 | 'Light': { |
| 26 | 'background': '#ffffff', |
| 27 | 'secondary_bg': '#f5f6fa', |
| 28 | 'text': '#2c3e50', |
| 29 | 'secondary_text': '#7f8c8d', |
| 30 | 'accent': '#3498db', |
| 31 | 'accent_hover': '#2980b9', |
| 32 | 'border': '#bdc3c7', |
| 33 | 'selection': '#3498db', |
| 34 | 'hover': '#e8f0fe', |
| 35 | 'error': '#e74c3c', |
| 36 | 'success': '#2ecc71', |
| 37 | 'warning': '#f1c40f' |
| 38 | }, |
| 39 | 'Dark': { |
| 40 | 'background': '#1e1e1e', |
| 41 | 'secondary_bg': '#2d2d2d', |
| 42 | 'text': '#ffffff', |
| 43 | 'secondary_text': '#cccccc', |
| 44 | 'accent': '#3498db', |
| 45 | 'accent_hover': '#2980b9', |
| 46 | 'border': '#3d3d3d', |
| 47 | 'selection': '#0d47a1', |
| 48 | 'hover': '#353535', |
| 49 | 'error': '#e74c3c', |
| 50 | 'success': '#2ecc71', |
| 51 | 'warning': '#f1c40f' |
| 52 | }, |
| 53 | 'Custom': { |
| 54 | 'background': '#ffffff', |
| 55 | 'secondary_bg': '#f5f6fa', |
| 56 | 'text': '#2c3e50', |
| 57 | 'secondary_text': '#7f8c8d', |
| 58 | 'accent': '#3498db', |
| 59 | 'accent_hover': '#2980b9', |
| 60 | 'border': '#bdc3c7', |
| 61 | 'selection': '#3498db', |
| 62 | 'hover': '#e8f0fe', |
| 63 | 'error': '#e74c3c', |
| 64 | 'success': '#2ecc71', |
| 65 | 'warning': '#f1c40f' |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | @staticmethod |
| 70 | def get_theme_colors(theme_name, custom_colors=None): |
| 71 | """Get colors for specified theme""" |
| 72 | if theme_name == 'Custom' and custom_colors: |
| 73 | colors = StyleManager.THEMES['Custom'].copy() |
| 74 | colors.update({ |
| 75 | 'background': custom_colors.get('bg_color', colors['background']), |
| 76 | 'text': custom_colors.get('text_color', colors['text']), |
| 77 | 'accent': custom_colors.get('accent_color', colors['accent']) |
| 78 | }) |
| 79 | return colors |
| 80 | return StyleManager.THEMES.get(theme_name, StyleManager.THEMES['Light']) |
| 81 | |
| 82 | @staticmethod |
| 83 | def load_settings(filename="settings.json"): |
| 84 | """Load settings from file""" |
| 85 | try: |
| 86 | config_dir = os.path.join(os.path.expanduser("~"), ".pkgtoolbox") |
| 87 | config_file = os.path.join(config_dir, filename) |
| 88 | |
| 89 | if os.path.exists(config_file): |
| 90 | with open(config_file, "r", encoding='utf-8') as f: |
| 91 | settings = json.load(f) |
| 92 | # Assicurati che tutte le chiavi necessarie esistano |
| 93 | if "appearance" not in settings: |
| 94 | settings["appearance"] = StyleManager.DEFAULT_SETTINGS["appearance"] |
| 95 | if "colors" not in settings["appearance"]: |
| 96 | settings["appearance"]["colors"] = StyleManager.DEFAULT_SETTINGS["appearance"]["colors"] |
| 97 | return settings |
| 98 | return StyleManager.DEFAULT_SETTINGS |
| 99 | except Exception as e: |
| 100 | logging.error(f"Error loading settings: {e}") |
| 101 | return StyleManager.DEFAULT_SETTINGS |
| 102 | |
| 103 | @staticmethod |
| 104 | def save_settings(settings, filename="settings.json"): |
| 105 | """Save settings to file""" |
| 106 | try: |
| 107 | config_dir = os.path.join(os.path.expanduser("~"), ".pkgtoolbox") |
| 108 | if not os.path.exists(config_dir): |
| 109 | os.makedirs(config_dir) |
| 110 | |
| 111 | config_file = os.path.join(config_dir, filename) |
| 112 | |
| 113 | # Assicurati che le impostazioni siano nel formato corretto |
| 114 | if "appearance" not in settings: |
| 115 | settings["appearance"] = {} |
| 116 | if "colors" not in settings["appearance"]: |
| 117 | settings["appearance"]["colors"] = {} |
| 118 | |
| 119 | # Salva le impostazioni |
| 120 | with open(config_file, "w", encoding='utf-8') as f: |
| 121 | json.dump(settings, f, indent=4, ensure_ascii=False) |
| 122 | except Exception as e: |
| 123 | logging.error(f"Error saving settings: {e}") |
| 124 | raise |
| 125 | |
| 126 | @staticmethod |
| 127 | def apply_theme(widget, settings): |
| 128 | """Apply theme to widget""" |
| 129 | # Estrai i colori dalle impostazioni |
| 130 | appearance = settings.get("appearance", {}) |
| 131 | colors = appearance.get("colors", {}) |
| 132 | |
| 133 | # Colori predefiniti |
| 134 | default_colors = { |
| 135 | 'background': colors.get("background", "#ffffff"), |
| 136 | 'text': colors.get("text", "#000000"), |
| 137 | 'accent': colors.get("accent", "#3498db"), |
| 138 | 'secondary_bg': colors.get("background", "#ffffff"), # Usa il colore di sfondo come fallback |
| 139 | 'secondary_text': colors.get("text", "#000000"), # Usa il colore del testo come fallback |
| 140 | 'accent_hover': "#2980b9", |
| 141 | 'border': "#bdc3c7", |
| 142 | 'selection': "#3498db", |
| 143 | 'hover': "#e8f0fe", |
| 144 | 'error': "#e74c3c", |
| 145 | 'success': "#2ecc71", |
| 146 | 'warning': "#f1c40f" |
| 147 | } |
| 148 | |
| 149 | # Applica lo stile |
| 150 | widget.setStyleSheet(f""" |
| 151 | /* Base */ |
| 152 | QMainWindow, QWidget {{ |
| 153 | background-color: {default_colors['background']}; |
| 154 | color: {default_colors['text']}; |
| 155 | }} |
| 156 | |
| 157 | /* Input Fields */ |
| 158 | QLineEdit, QTextEdit, QPlainTextEdit {{ |
| 159 | background-color: {default_colors['secondary_bg']}; |
| 160 | color: {default_colors['text']}; |
| 161 | border: 1px solid {default_colors['border']}; |
| 162 | border-radius: 4px; |
| 163 | padding: 5px; |
| 164 | selection-background-color: {default_colors['selection']}; |
| 165 | selection-color: white; |
| 166 | }} |
| 167 | |
| 168 | /* Buttons */ |
| 169 | QPushButton {{ |
| 170 | background-color: {default_colors['accent']}; |
| 171 | color: white; |
| 172 | border: none; |
| 173 | padding: 8px; |
| 174 | border-radius: 4px; |
| 175 | }} |
| 176 | QPushButton:hover {{ |
| 177 | background-color: {default_colors['accent_hover']}; |
| 178 | }} |
| 179 | QPushButton:pressed {{ |
| 180 | background-color: {default_colors['selection']}; |
| 181 | }} |
| 182 | QPushButton:disabled {{ |
| 183 | background-color: {default_colors['secondary_bg']}; |
| 184 | color: {default_colors['secondary_text']}; |
| 185 | }} |
| 186 | |
| 187 | /* Tree/List Widgets */ |
| 188 | QTreeWidget, QListWidget {{ |
| 189 | background-color: {default_colors['background']}; |
| 190 | alternate-background-color: {default_colors['secondary_bg']}; |
| 191 | color: {default_colors['text']}; |
| 192 | border: 1px solid {default_colors['border']}; |
| 193 | border-radius: 4px; |
| 194 | }} |
| 195 | QTreeWidget::item:hover, QListWidget::item:hover {{ |
| 196 | background-color: {default_colors['hover']}; |
| 197 | }} |
| 198 | QTreeWidget::item:selected, QListWidget::item:selected {{ |
| 199 | background-color: {default_colors['selection']}; |
| 200 | color: white; |
| 201 | }} |
| 202 | |
| 203 | /* Headers */ |
| 204 | QHeaderView::section {{ |
| 205 | background-color: {default_colors['secondary_bg']}; |
| 206 | color: {default_colors['text']}; |
| 207 | padding: 5px; |
| 208 | border: none; |
| 209 | }} |
| 210 | |
| 211 | /* Tabs */ |
| 212 | QTabWidget::pane {{ |
| 213 | border: 1px solid {default_colors['border']}; |
| 214 | border-radius: 4px; |
| 215 | }} |
| 216 | QTabBar::tab {{ |
| 217 | background: {default_colors['secondary_bg']}; |
| 218 | color: {default_colors['text']}; |
| 219 | padding: 8px; |
| 220 | margin: 2px; |
| 221 | border-radius: 4px; |
| 222 | }} |
| 223 | QTabBar::tab:selected {{ |
| 224 | background: {default_colors['accent']}; |
| 225 | color: white; |
| 226 | }} |
| 227 | QTabBar::tab:hover {{ |
| 228 | background: {default_colors['hover']}; |
| 229 | }} |
| 230 | |
| 231 | /* Menus */ |
| 232 | QMenuBar {{ |
| 233 | background-color: {default_colors['background']}; |
| 234 | color: {default_colors['text']}; |
| 235 | }} |
| 236 | QMenuBar::item:selected {{ |
| 237 | background-color: {default_colors['hover']}; |
| 238 | }} |
| 239 | QMenu {{ |
| 240 | background-color: {default_colors['background']}; |
| 241 | color: {default_colors['text']}; |
| 242 | border: 1px solid {default_colors['border']}; |
| 243 | }} |
| 244 | QMenu::item:selected {{ |
| 245 | background-color: {default_colors['selection']}; |
| 246 | color: white; |
| 247 | }} |
| 248 | |
| 249 | /* Combo/Spin Boxes */ |
| 250 | QComboBox, QSpinBox {{ |
| 251 | background-color: {default_colors['secondary_bg']}; |
| 252 | color: {default_colors['text']}; |
| 253 | border: 1px solid {default_colors['border']}; |
| 254 | border-radius: 4px; |
| 255 | padding: 5px; |
| 256 | }} |
| 257 | QComboBox::drop-down {{ |
| 258 | border: none; |
| 259 | }} |
| 260 | QComboBox::down-arrow {{ |
| 261 | image: none; |
| 262 | }} |
| 263 | |
| 264 | /* Scroll Bars */ |
| 265 | QScrollBar:vertical {{ |
| 266 | background-color: {default_colors['secondary_bg']}; |
| 267 | width: 12px; |
| 268 | margin: 0px; |
| 269 | border-radius: 6px; |
| 270 | }} |
| 271 | QScrollBar::handle:vertical {{ |
| 272 | background-color: {default_colors['accent']}; |
| 273 | min-height: 20px; |
| 274 | border-radius: 6px; |
| 275 | }} |
| 276 | QScrollBar:horizontal {{ |
| 277 | background-color: {default_colors['secondary_bg']}; |
| 278 | height: 12px; |
| 279 | margin: 0px; |
| 280 | border-radius: 6px; |
| 281 | }} |
| 282 | QScrollBar::handle:horizontal {{ |
| 283 | background-color: {default_colors['accent']}; |
| 284 | min-width: 20px; |
| 285 | border-radius: 6px; |
| 286 | }} |
| 287 | |
| 288 | /* Group Box */ |
| 289 | QGroupBox {{ |
| 290 | border: 1px solid {default_colors['border']}; |
| 291 | border-radius: 4px; |
| 292 | margin-top: 1ex; |
| 293 | padding: 5px; |
| 294 | color: {default_colors['text']}; |
| 295 | }} |
| 296 | QGroupBox::title {{ |
| 297 | subcontrol-origin: margin; |
| 298 | subcontrol-position: top left; |
| 299 | padding: 0 3px; |
| 300 | color: {default_colors['text']}; |
| 301 | }} |
| 302 | |
| 303 | /* Tool Tips */ |
| 304 | QToolTip {{ |
| 305 | background-color: {default_colors['background']}; |
| 306 | color: {default_colors['text']}; |
| 307 | border: 1px solid {default_colors['border']}; |
| 308 | border-radius: 4px; |
| 309 | padding: 5px; |
| 310 | }} |
| 311 | |
| 312 | /* Progress Bar */ |
| 313 | QProgressBar {{ |
| 314 | border: 1px solid {default_colors['border']}; |
| 315 | border-radius: 4px; |
| 316 | text-align: center; |
| 317 | }} |
| 318 | QProgressBar::chunk {{ |
| 319 | background-color: {default_colors['accent']}; |
| 320 | }} |
| 321 | """) |
| 322 | |
| 323 | # Forza l'aggiornamento dello stile per tutti i widget figli |
| 324 | for child in widget.findChildren(QWidget): |
| 325 | child.setStyleSheet(child.styleSheet()) |
| 326 | |
| 327 | @staticmethod |
| 328 | def adjust_color(color, amount): |
| 329 | """Adjust color brightness""" |
| 330 | if color.startswith('#'): |
| 331 | color = color[1:] |
| 332 | rgb = tuple(int(color[i:i+2], 16) for i in (0, 2, 4)) |
| 333 | rgb = tuple(min(255, max(0, c + amount)) for c in rgb) |
| 334 | return f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}" |