""" Settings Module for Bagheera Image Viewer. This module provides the main configuration dialog for the application, allowing users to customize various aspects of its behavior, such as scanner settings, face detection options, and thumbnail appearance. Classes: ModelDownloader: A QThread worker for downloading the MediaPipe model file. SettingsDialog: The main QDialog that presents all configurable options in a tabbed interface. """ import os import shutil import urllib.request from PySide6.QtCore import Qt, QThread, Signal from PySide6.QtGui import QColor, QIcon, QFont from PySide6.QtWidgets import ( QCheckBox, QColorDialog, QComboBox, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QMessageBox, QProgressDialog, QPushButton, QSpinBox, QTabWidget, QVBoxLayout, QWidget ) from constants import ( APP_CONFIG, AVAILABLE_FACE_ENGINES, DEFAULT_FACE_BOX_COLOR, DEFAULT_PET_BOX_COLOR, DEFAULT_OBJECT_BOX_COLOR, DEFAULT_LANDMARK_BOX_COLOR, FACES_MENU_MAX_ITEMS_DEFAULT, MEDIAPIPE_FACE_MODEL_PATH, MEDIAPIPE_FACE_MODEL_URL, AVAILABLE_PET_ENGINES, DEFAULT_BODY_BOX_COLOR, MEDIAPIPE_OBJECT_MODEL_PATH, MEDIAPIPE_OBJECT_MODEL_URL, SCANNER_SETTINGS_DEFAULTS, SEARCH_CMD, TAGS_MENU_MAX_ITEMS_DEFAULT, THUMBNAILS_FILENAME_LINES_DEFAULT, THUMBNAILS_REFRESH_INTERVAL_DEFAULT, THUMBNAILS_BG_COLOR_DEFAULT, THUMBNAILS_FILENAME_COLOR_DEFAULT, THUMBNAILS_TAGS_COLOR_DEFAULT, THUMBNAILS_RATING_COLOR_DEFAULT, THUMBNAILS_TOOLTIP_BG_COLOR_DEFAULT, THUMBNAILS_TOOLTIP_FG_COLOR_DEFAULT, THUMBNAILS_FILENAME_FONT_SIZE_DEFAULT, THUMBNAILS_TAGS_LINES_DEFAULT, THUMBNAILS_TAGS_FONT_SIZE_DEFAULT, VIEWER_AUTO_RESIZE_WINDOW_DEFAULT, VIEWER_WHEEL_SPEED_DEFAULT, UITexts, save_app_config, ) class ModelDownloader(QThread): """A thread to download the MediaPipe model file without freezing the UI.""" download_complete = Signal(bool, str) # success (bool), message (str) def __init__(self, url, dest_path, parent=None): super().__init__(parent) self.url = url self.dest_path = dest_path def run(self): try: os.makedirs(os.path.dirname(self.dest_path), exist_ok=True) with urllib.request.urlopen(self.url) as response, \ open(self.dest_path, 'wb') as out_file: shutil.copyfileobj(response, out_file) self.download_complete.emit(True, "") except Exception as e: if os.path.exists(self.dest_path): os.remove(self.dest_path) self.download_complete.emit(False, str(e)) class SettingsDialog(QDialog): """A dialog to configure application settings.""" def __init__(self, parent=None): """Initializes the settings dialog window. This sets up the tabbed interface and all the individual configuration widgets for scanner, faces, thumbnails, and viewer settings. Args: parent (QWidget, optional): The parent widget. Defaults to None. """ super().__init__(parent) self.setWindowTitle(UITexts.MENU_SETTINGS) self.setMinimumWidth(500) self.scan_max_level_min = 0 self.scan_max_level_max = 10 self.current_face_color = DEFAULT_FACE_BOX_COLOR self.current_pet_color = DEFAULT_PET_BOX_COLOR self.current_body_color = DEFAULT_BODY_BOX_COLOR self.current_object_color = DEFAULT_OBJECT_BOX_COLOR self.current_landmark_color = DEFAULT_LANDMARK_BOX_COLOR self.current_thumbs_bg_color = THUMBNAILS_BG_COLOR_DEFAULT self.current_thumbs_filename_color = THUMBNAILS_FILENAME_COLOR_DEFAULT self.current_thumbs_tags_color = THUMBNAILS_TAGS_COLOR_DEFAULT self.current_thumbs_rating_color = THUMBNAILS_RATING_COLOR_DEFAULT self.current_thumbs_tooltip_bg_color = THUMBNAILS_TOOLTIP_BG_COLOR_DEFAULT self.current_thumbs_tooltip_fg_color = THUMBNAILS_TOOLTIP_FG_COLOR_DEFAULT self.downloader_thread = None layout = QVBoxLayout(self) tabs = QTabWidget() layout.addWidget(tabs) # --- Create all tabs and layouts first --- thumbs_tab = QWidget() thumbs_layout = QVBoxLayout(thumbs_tab) viewer_tab = QWidget() viewer_layout = QVBoxLayout(viewer_tab) faces_tab = QWidget() faces_layout = QVBoxLayout(faces_tab) scanner_tab = QWidget() scanner_layout = QVBoxLayout(scanner_tab) # --- Thumbnails Tab --- mru_tags_layout = QHBoxLayout() self.mru_tags_spin = QSpinBox() self.mru_tags_spin.setRange(5, 100) mru_label = QLabel(UITexts.SETTINGS_MRU_TAGS_COUNT_LABEL) mru_tags_layout.addWidget(mru_label) mru_tags_layout.addWidget(self.mru_tags_spin) mru_label.setToolTip(UITexts.SETTINGS_MRU_TAGS_TOOLTIP) self.mru_tags_spin.setToolTip(UITexts.SETTINGS_MRU_TAGS_TOOLTIP) thumbs_layout.addLayout(mru_tags_layout) thumbs_refresh_layout = QHBoxLayout() self.thumbs_refresh_spin = QSpinBox() self.thumbs_refresh_spin.setRange(50, 1000) self.thumbs_refresh_spin.setSingleStep(10) self.thumbs_refresh_spin.setSuffix(" ms") thumbs_refresh_label = QLabel(UITexts.SETTINGS_THUMBS_REFRESH_LABEL) thumbs_refresh_layout.addWidget(thumbs_refresh_label) thumbs_refresh_layout.addWidget(self.thumbs_refresh_spin) thumbs_refresh_label.setToolTip(UITexts.SETTINGS_THUMBS_REFRESH_TOOLTIP) self.thumbs_refresh_spin.setToolTip(UITexts.SETTINGS_THUMBS_REFRESH_TOOLTIP) thumbs_layout.addLayout(thumbs_refresh_layout) thumbs_bg_color_layout = QHBoxLayout() thumbs_bg_color_label = QLabel(UITexts.SETTINGS_THUMBS_BG_COLOR_LABEL) self.thumbs_bg_color_btn = QPushButton() self.thumbs_bg_color_btn.clicked.connect(self.choose_thumbs_bg_color) thumbs_bg_color_layout.addWidget(thumbs_bg_color_label) thumbs_bg_color_layout.addWidget(self.thumbs_bg_color_btn) thumbs_bg_color_label.setToolTip(UITexts.SETTINGS_THUMBS_BG_COLOR_TOOLTIP) self.thumbs_bg_color_btn.setToolTip(UITexts.SETTINGS_THUMBS_BG_COLOR_TOOLTIP) thumbs_layout.addLayout(thumbs_bg_color_layout) thumbs_filename_color_layout = QHBoxLayout() thumbs_filename_color_label = QLabel( UITexts.SETTINGS_THUMBS_FILENAME_COLOR_LABEL) self.thumbs_filename_color_btn = QPushButton() self.thumbs_filename_color_btn.clicked.connect( self.choose_thumbs_filename_color) thumbs_filename_color_layout.addWidget(thumbs_filename_color_label) thumbs_filename_color_layout.addWidget(self.thumbs_filename_color_btn) thumbs_filename_color_label.setToolTip( UITexts.SETTINGS_THUMBS_FILENAME_COLOR_TOOLTIP) self.thumbs_filename_color_btn.setToolTip( UITexts.SETTINGS_THUMBS_FILENAME_COLOR_TOOLTIP) thumbs_layout.addLayout(thumbs_filename_color_layout) thumbs_tags_color_layout = QHBoxLayout() thumbs_tags_color_label = QLabel(UITexts.SETTINGS_THUMBS_TAGS_COLOR_LABEL) self.thumbs_tags_color_btn = QPushButton() self.thumbs_tags_color_btn.clicked.connect(self.choose_thumbs_tags_color) thumbs_tags_color_layout.addWidget(thumbs_tags_color_label) thumbs_tags_color_layout.addWidget(self.thumbs_tags_color_btn) thumbs_tags_color_label.setToolTip(UITexts.SETTINGS_THUMBS_TAGS_COLOR_TOOLTIP) self.thumbs_tags_color_btn.setToolTip( UITexts.SETTINGS_THUMBS_TAGS_COLOR_TOOLTIP) thumbs_layout.addLayout(thumbs_tags_color_layout) thumbs_rating_color_layout = QHBoxLayout() thumbs_rating_color_label = QLabel(UITexts.SETTINGS_THUMBS_RATING_COLOR_LABEL) self.thumbs_rating_color_btn = QPushButton() self.thumbs_rating_color_btn.clicked.connect(self.choose_thumbs_rating_color) thumbs_rating_color_layout.addWidget(thumbs_rating_color_label) thumbs_rating_color_layout.addWidget(self.thumbs_rating_color_btn) thumbs_rating_color_label.setToolTip( UITexts.SETTINGS_THUMBS_RATING_COLOR_TOOLTIP) self.thumbs_rating_color_btn.setToolTip( UITexts.SETTINGS_THUMBS_RATING_COLOR_TOOLTIP) thumbs_layout.addLayout(thumbs_rating_color_layout) filename_font_size_layout = QHBoxLayout() self.filename_font_size_spin = QSpinBox() self.filename_font_size_spin.setRange(6, 16) self.filename_font_size_spin.setSuffix(" pt") filename_font_label = QLabel(UITexts.SETTINGS_THUMBS_FILENAME_FONT_SIZE_LABEL) filename_font_size_layout.addWidget(filename_font_label) filename_font_size_layout.addWidget(self.filename_font_size_spin) filename_font_label.setToolTip( UITexts.SETTINGS_THUMBS_FILENAME_FONT_SIZE_TOOLTIP) self.filename_font_size_spin.setToolTip( UITexts.SETTINGS_THUMBS_FILENAME_FONT_SIZE_TOOLTIP) thumbs_layout.addLayout(filename_font_size_layout) tags_font_size_layout = QHBoxLayout() self.tags_font_size_spin = QSpinBox() self.tags_font_size_spin.setRange(6, 16) self.tags_font_size_spin.setSuffix(" pt") tags_font_label = QLabel(UITexts.SETTINGS_THUMBS_TAGS_FONT_SIZE_LABEL) tags_font_size_layout.addWidget(tags_font_label) tags_font_size_layout.addWidget(self.tags_font_size_spin) tags_font_label.setToolTip(UITexts.SETTINGS_THUMBS_TAGS_FONT_SIZE_TOOLTIP) self.tags_font_size_spin.setToolTip( UITexts.SETTINGS_THUMBS_TAGS_FONT_SIZE_TOOLTIP) thumbs_layout.addLayout(tags_font_size_layout) # --- Thumbs Tooltip Background Color --- thumbs_tooltip_bg_color_layout = QHBoxLayout() thumbs_tooltip_bg_color_label = QLabel( UITexts.SETTINGS_THUMBS_TOOLTIP_BG_COLOR_LABEL) self.thumbs_tooltip_bg_color_btn = QPushButton() self.thumbs_tooltip_bg_color_btn.clicked.connect( self.choose_thumbs_tooltip_bg_color) thumbs_tooltip_bg_color_layout.addWidget(thumbs_tooltip_bg_color_label) thumbs_tooltip_bg_color_layout.addWidget(self.thumbs_tooltip_bg_color_btn) thumbs_tooltip_bg_color_label.setToolTip( UITexts.SETTINGS_THUMBS_TOOLTIP_BG_COLOR_TOOLTIP) self.thumbs_tooltip_bg_color_btn.setToolTip( UITexts.SETTINGS_THUMBS_TOOLTIP_BG_COLOR_TOOLTIP) thumbs_layout.addLayout(thumbs_tooltip_bg_color_layout) # --- Thumbs Tooltip Foreground Color --- thumbs_tooltip_fg_color_layout = QHBoxLayout() thumbs_tooltip_fg_color_label = QLabel( UITexts.SETTINGS_THUMBS_TOOLTIP_FG_COLOR_LABEL) self.thumbs_tooltip_fg_color_btn = QPushButton() self.thumbs_tooltip_fg_color_btn.clicked.connect( self.choose_thumbs_tooltip_fg_color) thumbs_tooltip_fg_color_layout.addWidget(thumbs_tooltip_fg_color_label) thumbs_tooltip_fg_color_layout.addWidget(self.thumbs_tooltip_fg_color_btn) thumbs_tooltip_fg_color_label.setToolTip( UITexts.SETTINGS_THUMBS_TOOLTIP_FG_COLOR_TOOLTIP) self.thumbs_tooltip_fg_color_btn.setToolTip( UITexts.SETTINGS_THUMBS_TOOLTIP_FG_COLOR_TOOLTIP) thumbs_layout.addLayout(thumbs_tooltip_fg_color_layout) show_filename_layout = QHBoxLayout() self.show_filename_check = QCheckBox( UITexts.SETTINGS_THUMBS_SHOW_FILENAME_LABEL) self.show_filename_check.setToolTip( UITexts.SETTINGS_THUMBS_SHOW_FILENAME_TOOLTIP) show_filename_layout.addWidget(self.show_filename_check) thumbs_layout.addLayout(show_filename_layout) show_rating_layout = QHBoxLayout() self.show_rating_check = QCheckBox(UITexts.SETTINGS_THUMBS_SHOW_RATING_LABEL) self.show_rating_check.setToolTip(UITexts.SETTINGS_THUMBS_SHOW_RATING_TOOLTIP) show_rating_layout.addWidget(self.show_rating_check) thumbs_layout.addLayout(show_rating_layout) show_tags_layout = QHBoxLayout() self.show_tags_check = QCheckBox(UITexts.SETTINGS_THUMBS_SHOW_TAGS_LABEL) self.show_tags_check.setToolTip(UITexts.SETTINGS_THUMBS_SHOW_TAGS_TOOLTIP) show_tags_layout.addWidget(self.show_tags_check) thumbs_layout.addLayout(show_tags_layout) filename_lines_layout = QHBoxLayout() self.filename_lines_spin = QSpinBox() self.filename_lines_spin.setRange(1, 3) filename_lines_label = QLabel(UITexts.SETTINGS_THUMBS_FILENAME_LINES_LABEL) filename_lines_layout.addWidget(filename_lines_label) filename_lines_layout.addWidget(self.filename_lines_spin) filename_lines_label.setToolTip(UITexts.SETTINGS_THUMBS_FILENAME_LINES_TOOLTIP) self.filename_lines_spin.setToolTip( UITexts.SETTINGS_THUMBS_FILENAME_LINES_TOOLTIP) thumbs_layout.addLayout(filename_lines_layout) tags_lines_layout = QHBoxLayout() self.tags_lines_spin = QSpinBox() self.tags_lines_spin.setRange(1, 4) tags_lines_label = QLabel(UITexts.SETTINGS_THUMBS_TAGS_LINES_LABEL) tags_lines_layout.addWidget(tags_lines_label) tags_lines_layout.addWidget(self.tags_lines_spin) tags_lines_label.setToolTip(UITexts.SETTINGS_THUMBS_TAGS_LINES_TOOLTIP) self.tags_lines_spin.setToolTip(UITexts.SETTINGS_THUMBS_TAGS_LINES_TOOLTIP) thumbs_layout.addLayout(tags_lines_layout) thumbs_layout.addStretch() # --- Scanner Tab --- scan_max_level_layout = QHBoxLayout() scan_max_level_label = QLabel(UITexts.SETTINGS_SCAN_MAX_LEVEL_LABEL) self.scan_max_level_spin = QSpinBox() self.scan_max_level_spin.setRange(self.scan_max_level_min, self.scan_max_level_max) scan_max_level_layout.addWidget(scan_max_level_label) scan_max_level_layout.addWidget(self.scan_max_level_spin) scan_max_level_label.setToolTip(UITexts.SETTINGS_SCAN_MAX_LEVEL_TOOLTIP) self.scan_max_level_spin.setToolTip(UITexts.SETTINGS_SCAN_MAX_LEVEL_TOOLTIP) scanner_layout.addLayout(scan_max_level_layout) # --- Search Engine --- search_engine_layout = QHBoxLayout() search_engine_label = QLabel(UITexts.SETTINGS_SCANNER_SEARCH_ENGINE_LABEL) self.search_engine_combo = QComboBox() self.search_engine_combo.addItem(UITexts.SEARCH_ENGINE_NATIVE, "Bagheera") if SEARCH_CMD: self.search_engine_combo.addItem(UITexts.SEARCH_ENGINE_BALOO, "Baloo") search_engine_layout.addWidget(search_engine_label) search_engine_layout.addWidget(self.search_engine_combo) search_engine_label.setToolTip(UITexts.SETTINGS_SCANNER_SEARCH_ENGINE_TOOLTIP) self.search_engine_combo.setToolTip( UITexts.SETTINGS_SCANNER_SEARCH_ENGINE_TOOLTIP) scanner_layout.addLayout(search_engine_layout) scan_batch_size_layout = QHBoxLayout() scan_batch_size_label = QLabel(UITexts.SETTINGS_SCAN_BATCH_SIZE_LABEL) self.scan_batch_size_spin = QSpinBox() self.scan_batch_size_spin.setRange(16, 128) scan_batch_size_layout.addWidget(scan_batch_size_label) scan_batch_size_layout.addWidget(self.scan_batch_size_spin) scan_batch_size_label.setToolTip(UITexts.SETTINGS_SCAN_BATCH_SIZE_TOOLTIP) self.scan_batch_size_spin.setToolTip(UITexts.SETTINGS_SCAN_BATCH_SIZE_TOOLTIP) scanner_layout.addLayout(scan_batch_size_layout) scan_full_on_start_layout = QHBoxLayout() scan_full_on_start_label = QLabel(UITexts.SETTINGS_SCAN_FULL_ON_START_LABEL) self.scan_full_on_start_checkbox = QCheckBox() self.scan_full_on_start_checkbox.setText("") scan_full_on_start_layout.addWidget(scan_full_on_start_label) scan_full_on_start_layout.addStretch() scan_full_on_start_layout.addWidget(self.scan_full_on_start_checkbox) scan_full_on_start_label.setToolTip( UITexts.SETTINGS_SCAN_FULL_ON_START_TOOLTIP) self.scan_full_on_start_checkbox.setToolTip( UITexts.SETTINGS_SCAN_FULL_ON_START_TOOLTIP) # Threads threads_layout = QHBoxLayout() threads_label = QLabel(UITexts.SETTINGS_SCAN_THREADS_LABEL) self.threads_spin = QSpinBox() self.threads_spin.setRange(1, 32) threads_layout.addWidget(threads_label) threads_layout.addWidget(self.threads_spin) threads_label.setToolTip(UITexts.SETTINGS_SCAN_THREADS_TOOLTIP) self.threads_spin.setToolTip(UITexts.SETTINGS_SCAN_THREADS_TOOLTIP) scanner_layout.addLayout(threads_layout) scanner_layout.addLayout(scan_full_on_start_layout) scanner_layout.addStretch() # --- Faces & People Tab --- faces_tab = QWidget() faces_layout = QVBoxLayout(faces_tab) # Faces Header faces_header = QLabel("Faces") faces_header.setFont(QFont("Sans", 10, QFont.Bold)) faces_layout.addWidget(faces_header) # --- Person Tags --- person_tags_layout = QHBoxLayout() person_tags_label = QLabel(UITexts.SETTINGS_PERSON_TAGS_LABEL) self.person_tags_edit = QLineEdit() self.person_tags_edit.setPlaceholderText("tag1, tag2, tag3/subtag") self.person_tags_edit.setClearButtonEnabled(True) person_tags_layout.addWidget(person_tags_label) person_tags_layout.addWidget(self.person_tags_edit) person_tags_label.setToolTip(UITexts.SETTINGS_PERSON_TAGS_TOOLTIP) self.person_tags_edit.setToolTip(UITexts.SETTINGS_PERSON_TAGS_TOOLTIP) faces_layout.addLayout(person_tags_layout) if AVAILABLE_FACE_ENGINES: face_engine_layout = QHBoxLayout() face_engine_label = QLabel(UITexts.SETTINGS_FACE_ENGINE_LABEL) self.face_engine_combo = QComboBox() self.face_engine_combo.addItems(AVAILABLE_FACE_ENGINES) self.download_model_btn = QPushButton( UITexts.SETTINGS_DOWNLOAD_MEDIAPIPE_MODEL) self.download_model_btn.setIcon(QIcon.fromTheme("download")) self.download_model_btn.setToolTip( UITexts.SETTINGS_DOWNLOAD_MEDIAPIPE_MODEL_TOOLTIP) self.download_model_btn.clicked.connect(self.start_model_download) face_engine_layout.addWidget(face_engine_label) face_engine_layout.addWidget(self.face_engine_combo, 1) face_engine_layout.addWidget(self.download_model_btn) face_engine_label.setToolTip(UITexts.SETTINGS_FACE_ENGINE_TOOLTIP) self.face_engine_combo.setToolTip(UITexts.SETTINGS_FACE_ENGINE_TOOLTIP) faces_layout.addLayout(face_engine_layout) else: self.face_engine_combo = None self.download_model_btn = None face_color_layout = QHBoxLayout() face_color_label = QLabel(UITexts.SETTINGS_FACE_COLOR_LABEL) self.face_color_btn = QPushButton() self.face_color_btn.clicked.connect(self.choose_face_color) face_color_layout.addWidget(face_color_label) face_color_layout.addWidget(self.face_color_btn) face_color_label.setToolTip(UITexts.SETTINGS_FACE_COLOR_TOOLTIP) self.face_color_btn.setToolTip(UITexts.SETTINGS_FACE_COLOR_TOOLTIP) faces_layout.addLayout(face_color_layout) face_history_layout = QHBoxLayout() self.face_history_spin = QSpinBox() self.face_history_spin.setRange(5, 100) face_hist_label = QLabel(UITexts.SETTINGS_FACE_HISTORY_COUNT_LABEL) face_history_layout.addWidget(face_hist_label) face_history_layout.addWidget(self.face_history_spin) face_hist_label.setToolTip(UITexts.SETTINGS_FACE_HISTORY_TOOLTIP) self.face_history_spin.setToolTip(UITexts.SETTINGS_FACE_HISTORY_TOOLTIP) faces_layout.addLayout(face_history_layout) # --- Pets Section --- faces_layout.addSpacing(10) pets_header = QLabel("Pets") pets_header.setFont(QFont("Sans", 10, QFont.Bold)) faces_layout.addWidget(pets_header) pet_tags_layout = QHBoxLayout() pet_tags_label = QLabel(UITexts.SETTINGS_PET_TAGS_LABEL) self.pet_tags_edit = QLineEdit() self.pet_tags_edit.setPlaceholderText("tag1, tag2, tag3/subtag") self.pet_tags_edit.setClearButtonEnabled(True) pet_tags_layout.addWidget(pet_tags_label) pet_tags_layout.addWidget(self.pet_tags_edit) pet_tags_label.setToolTip(UITexts.SETTINGS_PET_TAGS_TOOLTIP) self.pet_tags_edit.setToolTip(UITexts.SETTINGS_PET_TAGS_TOOLTIP) faces_layout.addLayout(pet_tags_layout) pet_engine_layout = QHBoxLayout() pet_engine_label = QLabel(UITexts.SETTINGS_PET_ENGINE_LABEL) self.pet_engine_combo = QComboBox() self.pet_engine_combo.addItems(AVAILABLE_PET_ENGINES) self.download_pet_model_btn = QPushButton( UITexts.SETTINGS_DOWNLOAD_MEDIAPIPE_MODEL) self.download_pet_model_btn.setIcon(QIcon.fromTheme("download")) self.download_pet_model_btn.setToolTip( UITexts.SETTINGS_DOWNLOAD_MEDIAPIPE_MODEL_TOOLTIP) self.download_pet_model_btn.clicked.connect(self.start_pet_model_download) pet_engine_layout.addWidget(pet_engine_label) pet_engine_layout.addWidget(self.pet_engine_combo, 1) pet_engine_layout.addWidget(self.download_pet_model_btn) pet_engine_label.setToolTip(UITexts.SETTINGS_PET_ENGINE_TOOLTIP) self.pet_engine_combo.setToolTip(UITexts.SETTINGS_PET_ENGINE_TOOLTIP) faces_layout.addLayout(pet_engine_layout) pet_color_layout = QHBoxLayout() pet_color_label = QLabel(UITexts.SETTINGS_PET_COLOR_LABEL) self.pet_color_btn = QPushButton() self.pet_color_btn.clicked.connect(self.choose_pet_color) pet_color_layout.addWidget(pet_color_label) pet_color_layout.addWidget(self.pet_color_btn) pet_color_label.setToolTip(UITexts.SETTINGS_PET_COLOR_TOOLTIP) self.pet_color_btn.setToolTip(UITexts.SETTINGS_PET_COLOR_TOOLTIP) faces_layout.addLayout(pet_color_layout) pet_history_layout = QHBoxLayout() self.pet_history_spin = QSpinBox() self.pet_history_spin.setRange(5, 100) pet_hist_label = QLabel(UITexts.SETTINGS_PET_HISTORY_COUNT_LABEL) pet_history_layout.addWidget(pet_hist_label) pet_history_layout.addWidget(self.pet_history_spin) pet_hist_label.setToolTip(UITexts.SETTINGS_PET_HISTORY_TOOLTIP) self.pet_history_spin.setToolTip(UITexts.SETTINGS_PET_HISTORY_TOOLTIP) faces_layout.addLayout(pet_history_layout) # --- Body Section --- faces_layout.addSpacing(10) body_header = QLabel("Body") body_header.setFont(QFont("Sans", 10, QFont.Bold)) faces_layout.addWidget(body_header) body_tags_layout = QHBoxLayout() body_tags_label = QLabel(UITexts.SETTINGS_BODY_TAGS_LABEL) self.body_tags_edit = QLineEdit() self.body_tags_edit.setPlaceholderText("tag1, tag2, tag3/subtag") self.body_tags_edit.setClearButtonEnabled(True) body_tags_layout.addWidget(body_tags_label) body_tags_layout.addWidget(self.body_tags_edit) body_tags_label.setToolTip(UITexts.SETTINGS_BODY_TAGS_TOOLTIP) self.body_tags_edit.setToolTip(UITexts.SETTINGS_BODY_TAGS_TOOLTIP) faces_layout.addLayout(body_tags_layout) # body_engine_layout = QHBoxLayout() # body_engine_label = QLabel(UITexts.SETTINGS_BODY_ENGINE_LABEL) # self.body_engine_combo = QComboBox() # self.body_engine_combo.addItems(AVAILABLE_BODY_ENGINES) # body_engine_layout.addWidget(body_engine_label) # body_engine_layout.addWidget(self.body_engine_combo, 1) # body_engine_label.setToolTip(UITexts.SETTINGS_BODY_ENGINE_TOOLTIP) # self.body_engine_combo.setToolTip(UITexts.SETTINGS_BODY_ENGINE_TOOLTIP) # faces_layout.addLayout(body_engine_layout) body_color_layout = QHBoxLayout() body_color_label = QLabel(UITexts.SETTINGS_BODY_COLOR_LABEL) self.body_color_btn = QPushButton() self.body_color_btn.clicked.connect(self.choose_body_color) body_color_layout.addWidget(body_color_label) body_color_layout.addWidget(self.body_color_btn) body_color_label.setToolTip(UITexts.SETTINGS_BODY_COLOR_TOOLTIP) self.body_color_btn.setToolTip(UITexts.SETTINGS_BODY_COLOR_TOOLTIP) faces_layout.addLayout(body_color_layout) body_history_layout = QHBoxLayout() self.body_history_spin = QSpinBox() self.body_history_spin.setRange(5, 100) body_hist_label = QLabel(UITexts.SETTINGS_BODY_HISTORY_COUNT_LABEL) body_history_layout.addWidget(body_hist_label) body_history_layout.addWidget(self.body_history_spin) body_hist_label.setToolTip(UITexts.SETTINGS_BODY_HISTORY_TOOLTIP) self.body_history_spin.setToolTip(UITexts.SETTINGS_BODY_HISTORY_TOOLTIP) faces_layout.addLayout(body_history_layout) # --- Object Section --- faces_layout.addSpacing(10) object_header = QLabel("Object") object_header.setFont(QFont("Sans", 10, QFont.Bold)) faces_layout.addWidget(object_header) object_tags_layout = QHBoxLayout() object_tags_label = QLabel(UITexts.SETTINGS_OBJECT_TAGS_LABEL) self.object_tags_edit = QLineEdit() self.object_tags_edit.setPlaceholderText("tag1, tag2, tag3/subtag") self.object_tags_edit.setClearButtonEnabled(True) object_tags_layout.addWidget(object_tags_label) object_tags_layout.addWidget(self.object_tags_edit) object_tags_label.setToolTip(UITexts.SETTINGS_OBJECT_TAGS_TOOLTIP) self.object_tags_edit.setToolTip(UITexts.SETTINGS_OBJECT_TAGS_TOOLTIP) faces_layout.addLayout(object_tags_layout) # object_engine_layout = QHBoxLayout() # object_engine_label = QLabel(UITexts.SETTINGS_OBJECT_ENGINE_LABEL) # self.object_engine_combo = QComboBox() # object_engine_layout.addWidget(object_engine_label) # object_engine_layout.addWidget(self.object_engine_combo, 1) # object_engine_label.setToolTip(UITexts.SETTINGS_OBJECT_ENGINE_TOOLTIP) # self.object_engine_combo.setToolTip(UITexts.SETTINGS_OBJECT_ENGINE_TOOLTIP) # faces_layout.addLayout(object_engine_layout) object_color_layout = QHBoxLayout() object_color_label = QLabel(UITexts.SETTINGS_OBJECT_COLOR_LABEL) self.object_color_btn = QPushButton() self.object_color_btn.clicked.connect(self.choose_object_color) object_color_layout.addWidget(object_color_label) object_color_layout.addWidget(self.object_color_btn) object_color_label.setToolTip(UITexts.SETTINGS_OBJECT_COLOR_TOOLTIP) self.object_color_btn.setToolTip(UITexts.SETTINGS_OBJECT_COLOR_TOOLTIP) faces_layout.addLayout(object_color_layout) object_history_layout = QHBoxLayout() self.object_history_spin = QSpinBox() self.object_history_spin.setRange(5, 100) object_hist_label = QLabel(UITexts.SETTINGS_OBJECT_HISTORY_COUNT_LABEL) object_history_layout.addWidget(object_hist_label) object_history_layout.addWidget(self.object_history_spin) object_hist_label.setToolTip(UITexts.SETTINGS_OBJECT_HISTORY_TOOLTIP) self.object_history_spin.setToolTip(UITexts.SETTINGS_OBJECT_HISTORY_TOOLTIP) faces_layout.addLayout(object_history_layout) # --- Landmark Section --- faces_layout.addSpacing(10) landmark_header = QLabel("Landmark") landmark_header.setFont(QFont("Sans", 10, QFont.Bold)) faces_layout.addWidget(landmark_header) landmark_tags_layout = QHBoxLayout() landmark_tags_label = QLabel(UITexts.SETTINGS_LANDMARK_TAGS_LABEL) self.landmark_tags_edit = QLineEdit() self.landmark_tags_edit.setPlaceholderText("tag1, tag2, tag3/subtag") self.landmark_tags_edit.setClearButtonEnabled(True) landmark_tags_layout.addWidget(landmark_tags_label) landmark_tags_layout.addWidget(self.landmark_tags_edit) landmark_tags_label.setToolTip(UITexts.SETTINGS_LANDMARK_TAGS_TOOLTIP) self.landmark_tags_edit.setToolTip(UITexts.SETTINGS_LANDMARK_TAGS_TOOLTIP) faces_layout.addLayout(landmark_tags_layout) # landmark_engine_layout = QHBoxLayout() # landmark_engine_label = QLabel(UITexts.SETTINGS_LANDMARK_ENGINE_LABEL) # self.landmark_engine_combo = QComboBox() # landmark_engine_layout.addWidget(landmark_engine_label) # landmark_engine_layout.addWidget(self.landmark_engine_combo, 1) # landmark_engine_label.setToolTip(UITexts.SETTINGS_LANDMARK_ENGINE_TOOLTIP) # self.landmark_engine_combo.setToolTip(UITexts.SETTINGS_LANDMARK_ENGINE_TOOLTIP) # faces_layout.addLayout(landmark_engine_layout) landmark_color_layout = QHBoxLayout() landmark_color_label = QLabel(UITexts.SETTINGS_LANDMARK_COLOR_LABEL) self.landmark_color_btn = QPushButton() self.landmark_color_btn.clicked.connect(self.choose_landmark_color) landmark_color_layout.addWidget(landmark_color_label) landmark_color_layout.addWidget(self.landmark_color_btn) landmark_color_label.setToolTip(UITexts.SETTINGS_LANDMARK_COLOR_TOOLTIP) self.landmark_color_btn.setToolTip(UITexts.SETTINGS_LANDMARK_COLOR_TOOLTIP) faces_layout.addLayout(landmark_color_layout) landmark_history_layout = QHBoxLayout() self.landmark_history_spin = QSpinBox() self.landmark_history_spin.setRange(5, 100) landmark_hist_label = QLabel(UITexts.SETTINGS_LANDMARK_HISTORY_COUNT_LABEL) landmark_history_layout.addWidget(landmark_hist_label) landmark_history_layout.addWidget(self.landmark_history_spin) landmark_hist_label.setToolTip(UITexts.SETTINGS_LANDMARK_HISTORY_TOOLTIP) self.landmark_history_spin.setToolTip(UITexts.SETTINGS_LANDMARK_HISTORY_TOOLTIP) faces_layout.addLayout(landmark_history_layout) faces_layout.addStretch() # --- Viewer Tab --- viewer_wheel_layout = QHBoxLayout() viewer_wheel_label = QLabel(UITexts.SETTINGS_VIEWER_WHEEL_SPEED_LABEL) self.viewer_wheel_spin = QSpinBox() self.viewer_wheel_spin.setRange(1, 10) viewer_wheel_layout.addWidget(viewer_wheel_label) viewer_wheel_layout.addWidget(self.viewer_wheel_spin) viewer_wheel_label.setToolTip(UITexts.SETTINGS_VIEWER_WHEEL_SPEED_TOOLTIP) self.viewer_wheel_spin.setToolTip(UITexts.SETTINGS_VIEWER_WHEEL_SPEED_TOOLTIP) viewer_layout.addLayout(viewer_wheel_layout) viewer_auto_resize_layout = QHBoxLayout() self.viewer_auto_resize_check = QCheckBox( UITexts.SETTINGS_VIEWER_AUTO_RESIZE_LABEL) self.viewer_auto_resize_check.setToolTip( UITexts.SETTINGS_VIEWER_AUTO_RESIZE_TOOLTIP) viewer_auto_resize_layout.addWidget(self.viewer_auto_resize_check) viewer_layout.addLayout(viewer_auto_resize_layout) filmstrip_pos_layout = QHBoxLayout() filmstrip_pos_label = QLabel(UITexts.MENU_FILMSTRIP_POSITION) self.filmstrip_pos_combo = QComboBox() self.filmstrip_pos_combo.addItems([ UITexts.FILMSTRIP_BOTTOM, UITexts.FILMSTRIP_LEFT, UITexts.FILMSTRIP_TOP, UITexts.FILMSTRIP_RIGHT ]) filmstrip_pos_layout.addWidget(filmstrip_pos_label) filmstrip_pos_layout.addWidget(self.filmstrip_pos_combo) filmstrip_pos_label.setToolTip(UITexts.FILMSTRIP_POS_CHANGED_INFO) self.filmstrip_pos_combo.setToolTip(UITexts.FILMSTRIP_POS_CHANGED_INFO) viewer_layout.addLayout(filmstrip_pos_layout) viewer_layout.addStretch() # Add tabs in the new order tabs.addTab(thumbs_tab, UITexts.SETTINGS_GROUP_THUMBNAILS) tabs.addTab(viewer_tab, UITexts.SETTINGS_GROUP_VIEWER) tabs.addTab(faces_tab, UITexts.SETTINGS_GROUP_AREAS) tabs.addTab(scanner_tab, UITexts.SETTINGS_GROUP_SCANNER) # --- Button Box --- button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) layout.addWidget(button_box) # Load current settings self.load_settings() def load_settings(self): """Loads settings from the application configuration.""" # Example: # theme = APP_CONFIG.get("theme", "System") # self.theme_combo.setCurrentText(theme) scan_max_level = APP_CONFIG.get( "scan_max_level", SCANNER_SETTINGS_DEFAULTS["scan_max_level"]) scan_batch_size = APP_CONFIG.get( "scan_batch_size", SCANNER_SETTINGS_DEFAULTS["scan_batch_size"]) scan_full_on_start = APP_CONFIG.get( "scan_full_on_start", SCANNER_SETTINGS_DEFAULTS["scan_full_on_start"]) scan_threads = APP_CONFIG.get( "generation_threads", SCANNER_SETTINGS_DEFAULTS.get("generation_threads", 4)) search_engine = APP_CONFIG.get( "search_engine", SCANNER_SETTINGS_DEFAULTS.get("search_engine", "Native")) person_tags = APP_CONFIG.get( "person_tags", SCANNER_SETTINGS_DEFAULTS["person_tags"]) pet_tags = APP_CONFIG.get("pet_tags", "") body_tags = APP_CONFIG.get("body_tags", "") object_tags = APP_CONFIG.get("object_tags", "") landmark_tags = APP_CONFIG.get("landmark_tags", "") face_detection_engine = APP_CONFIG.get("face_detection_engine") pet_detection_engine = APP_CONFIG.get("pet_detection_engine") body_detection_engine = APP_CONFIG.get("body_detection_engine") object_detection_engine = APP_CONFIG.get("object_detection_engine") landmark_detection_engine = APP_CONFIG.get("landmark_detection_engine") face_color = APP_CONFIG.get("face_box_color", DEFAULT_FACE_BOX_COLOR) pet_color = APP_CONFIG.get("pet_box_color", DEFAULT_PET_BOX_COLOR) body_color = APP_CONFIG.get("body_box_color", DEFAULT_BODY_BOX_COLOR) object_color = APP_CONFIG.get("object_box_color", DEFAULT_OBJECT_BOX_COLOR) landmark_color = APP_CONFIG.get("landmark_box_color", DEFAULT_LANDMARK_BOX_COLOR) mru_tags_count = APP_CONFIG.get( "tags_menu_max_items", TAGS_MENU_MAX_ITEMS_DEFAULT) face_history_count = APP_CONFIG.get( "faces_menu_max_items", FACES_MENU_MAX_ITEMS_DEFAULT) pet_history_count = APP_CONFIG.get( "pets_menu_max_items", FACES_MENU_MAX_ITEMS_DEFAULT) body_history_count = APP_CONFIG.get( "body_menu_max_items", FACES_MENU_MAX_ITEMS_DEFAULT) object_history_count = APP_CONFIG.get( "object_menu_max_items", FACES_MENU_MAX_ITEMS_DEFAULT) landmark_history_count = APP_CONFIG.get( "landmark_menu_max_items", FACES_MENU_MAX_ITEMS_DEFAULT) thumbs_refresh_interval = APP_CONFIG.get( "thumbnails_refresh_interval", THUMBNAILS_REFRESH_INTERVAL_DEFAULT) thumbs_bg_color = APP_CONFIG.get( "thumbnails_bg_color", THUMBNAILS_BG_COLOR_DEFAULT) thumbs_filename_color = APP_CONFIG.get( "thumbnails_filename_color", THUMBNAILS_FILENAME_COLOR_DEFAULT) thumbs_tags_color = APP_CONFIG.get( "thumbnails_tags_color", THUMBNAILS_TAGS_COLOR_DEFAULT) thumbs_rating_color = APP_CONFIG.get("thumbnails_rating_color", THUMBNAILS_RATING_COLOR_DEFAULT) thumbs_tooltip_bg_color = APP_CONFIG.get("thumbnails_tooltip_bg_color", THUMBNAILS_TOOLTIP_BG_COLOR_DEFAULT) thumbs_tooltip_fg_color = APP_CONFIG.get("thumbnails_tooltip_fg_color", THUMBNAILS_TOOLTIP_FG_COLOR_DEFAULT) thumbs_filename_font_size = \ APP_CONFIG.get("thumbnails_filename_font_size", THUMBNAILS_FILENAME_FONT_SIZE_DEFAULT) thumbs_tags_font_size = APP_CONFIG.get("thumbnails_tags_font_size", THUMBNAILS_TAGS_FONT_SIZE_DEFAULT) viewer_wheel_speed = APP_CONFIG.get("viewer_wheel_speed", VIEWER_WHEEL_SPEED_DEFAULT) viewer_auto_resize = APP_CONFIG.get("viewer_auto_resize_window", VIEWER_AUTO_RESIZE_WINDOW_DEFAULT) thumbs_filename_lines = APP_CONFIG.get("thumbnails_filename_lines", THUMBNAILS_FILENAME_LINES_DEFAULT) thumbs_tags_lines = APP_CONFIG.get("thumbnails_tags_lines", THUMBNAILS_TAGS_LINES_DEFAULT) show_filename = APP_CONFIG.get("thumbnails_show_filename", True) show_rating = APP_CONFIG.get("thumbnails_show_rating", True) show_tags = APP_CONFIG.get("thumbnails_show_tags", True) filmstrip_position = APP_CONFIG.get("filmstrip_position", "bottom") self.scan_max_level_spin.setValue(scan_max_level) self.scan_batch_size_spin.setValue(scan_batch_size) self.threads_spin.setValue(scan_threads) # Set search engine index = self.search_engine_combo.findData(search_engine) if index != -1: self.search_engine_combo.setCurrentIndex(index) self.scan_full_on_start_checkbox.setChecked(scan_full_on_start) self.person_tags_edit.setText(person_tags) self.pet_tags_edit.setText(pet_tags) self.body_tags_edit.setText(body_tags) self.object_tags_edit.setText(object_tags) self.landmark_tags_edit.setText(landmark_tags) self.set_button_color(face_color) self.set_pet_button_color(pet_color) self.set_body_button_color(body_color) self.set_object_button_color(object_color) self.set_landmark_button_color(landmark_color) if self.face_engine_combo and face_detection_engine in AVAILABLE_FACE_ENGINES: self.face_engine_combo.setCurrentText(face_detection_engine) if self.pet_engine_combo and pet_detection_engine in AVAILABLE_PET_ENGINES: self.pet_engine_combo.setCurrentText(pet_detection_engine) if body_detection_engine and hasattr(self, "body_detection_engine_combo"): self.body_engine_combo.setCurrentText(body_detection_engine) if object_detection_engine and hasattr(self, "object_engine_combo"): self.object_engine_combo.setCurrentText(object_detection_engine) if landmark_detection_engine and hasattr(self, "landmark_engine_combo"): self.landmark_engine_combo.setCurrentText(landmark_detection_engine) self.mru_tags_spin.setValue(mru_tags_count) self.face_history_spin.setValue(face_history_count) self.pet_history_spin.setValue(pet_history_count) self.body_history_spin.setValue(body_history_count) self.object_history_spin.setValue(object_history_count) self.landmark_history_spin.setValue(landmark_history_count) self.thumbs_refresh_spin.setValue(thumbs_refresh_interval) self.set_thumbs_bg_button_color(thumbs_bg_color) self.set_thumbs_filename_button_color(thumbs_filename_color) self.set_thumbs_tags_button_color(thumbs_tags_color) self.set_thumbs_rating_button_color(thumbs_rating_color) self.set_thumbs_tooltip_bg_button_color(thumbs_tooltip_bg_color) self.set_thumbs_tooltip_fg_button_color(thumbs_tooltip_fg_color) self.filename_font_size_spin.setValue(thumbs_filename_font_size) self.tags_font_size_spin.setValue(thumbs_tags_font_size) self.viewer_wheel_spin.setValue(viewer_wheel_speed) self.viewer_auto_resize_check.setChecked(viewer_auto_resize) self.filename_lines_spin.setValue(thumbs_filename_lines) self.tags_lines_spin.setValue(thumbs_tags_lines) self.show_filename_check.setChecked(show_filename) self.show_rating_check.setChecked(show_rating) self.show_tags_check.setChecked(show_tags) pos_map = { 'bottom': UITexts.FILMSTRIP_BOTTOM, 'left': UITexts.FILMSTRIP_LEFT, 'top': UITexts.FILMSTRIP_TOP, 'right': UITexts.FILMSTRIP_RIGHT } self.filmstrip_pos_combo.setCurrentText( pos_map.get(filmstrip_position, UITexts.FILMSTRIP_BOTTOM)) self.update_mediapipe_status() def set_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.face_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_face_color = color_str def choose_face_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor(QColor(self.current_face_color), self) if color.isValid(): self.set_button_color(color.name()) def set_pet_button_color(self, color_str): """Sets the background color of the pet button and stores the value.""" self.pet_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_pet_color = color_str def choose_pet_color(self): """Opens a color picker dialog for pet box.""" color = QColorDialog.getColor(QColor(self.current_pet_color), self) if color.isValid(): self.set_pet_button_color(color.name()) def set_body_button_color(self, color_str): """Sets the background color of the body button and stores the value.""" self.body_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_body_color = color_str def choose_body_color(self): """Opens a color picker dialog for body box.""" color = QColorDialog.getColor(QColor(self.current_body_color), self) if color.isValid(): self.set_body_button_color(color.name()) def set_object_button_color(self, color_str): """Sets the background color of the object button.""" self.object_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_object_color = color_str def choose_object_color(self): """Opens a color picker dialog for object box.""" color = QColorDialog.getColor(QColor(self.current_object_color), self) if color.isValid(): self.set_object_button_color(color.name()) def set_landmark_button_color(self, color_str): """Sets the background color of the landmark button.""" self.landmark_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_landmark_color = color_str def choose_landmark_color(self): """Opens a color picker dialog for landmark box.""" color = QColorDialog.getColor(QColor(self.current_landmark_color), self) if color.isValid(): self.set_landmark_button_color(color.name()) def set_thumbs_bg_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.thumbs_bg_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_thumbs_bg_color = color_str def choose_thumbs_bg_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor(QColor(self.current_thumbs_bg_color), self) if color.isValid(): self.set_thumbs_bg_button_color(color.name()) def set_thumbs_filename_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.thumbs_filename_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_thumbs_filename_color = color_str def choose_thumbs_filename_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor(QColor(self.current_thumbs_filename_color), self) if color.isValid(): self.set_thumbs_filename_button_color(color.name()) def set_thumbs_tags_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.thumbs_tags_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_thumbs_tags_color = color_str def choose_thumbs_tags_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor(QColor(self.current_thumbs_tags_color), self) if color.isValid(): self.set_thumbs_tags_button_color(color.name()) def set_thumbs_rating_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.thumbs_rating_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_thumbs_rating_color = color_str def choose_thumbs_rating_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor(QColor(self.current_thumbs_rating_color), self) if color.isValid(): self.set_thumbs_rating_button_color(color.name()) def set_thumbs_tooltip_fg_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.thumbs_tooltip_fg_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_thumbs_tooltip_fg_color = color_str def choose_thumbs_tooltip_fg_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor( QColor(self.current_thumbs_tooltip_fg_color), self) if color.isValid(): self.set_thumbs_tooltip_fg_button_color(color.name()) def set_thumbs_tooltip_bg_button_color(self, color_str): """Sets the background color of the button and stores the value.""" self.thumbs_tooltip_bg_color_btn.setStyleSheet( f"background-color: {color_str}; border: 1px solid gray;") self.current_thumbs_tooltip_bg_color = color_str def choose_thumbs_tooltip_bg_color(self): """Opens a color picker dialog.""" color = QColorDialog.getColor( QColor(self.current_thumbs_tooltip_bg_color), self) if color.isValid(): self.set_thumbs_tooltip_bg_button_color(color.name()) def update_mediapipe_status(self): """Checks for MediaPipe model file and updates UI accordingly.""" if "mediapipe" not in AVAILABLE_FACE_ENGINES or not self.face_engine_combo: if self.download_model_btn: self.download_model_btn.hide() return model_exists = os.path.exists(MEDIAPIPE_FACE_MODEL_PATH) mediapipe_index = self.face_engine_combo.findText("mediapipe") if mediapipe_index != -1: item = self.face_engine_combo.model().item(mediapipe_index) if item: item.setEnabled(model_exists) if self.download_model_btn: self.download_model_btn.setVisible(not model_exists) if self.face_engine_combo.currentText() == "mediapipe" and not model_exists: if self.face_engine_combo.count() > 1: for i in range(self.face_engine_combo.count()): if self.face_engine_combo.model().item(i).isEnabled(): self.face_engine_combo.setCurrentIndex(i) break if "mediapipe" not in AVAILABLE_PET_ENGINES or not self.pet_engine_combo: if self.download_pet_model_btn: self.download_pet_model_btn.hide() return pet_model_exists = os.path.exists(MEDIAPIPE_OBJECT_MODEL_PATH) if self.download_pet_model_btn: self.download_pet_model_btn.setVisible(not pet_model_exists) def start_model_download(self): """Starts the background thread to download the MediaPipe model.""" self.downloader_thread = ModelDownloader( MEDIAPIPE_FACE_MODEL_URL, MEDIAPIPE_FACE_MODEL_PATH, self) self.downloader_thread.download_complete.connect(self.on_download_finished) self.downloader_thread.finished.connect(self._on_downloader_finished) self.progress_dialog = QProgressDialog( UITexts.MEDIAPIPE_DOWNLOADING_TEXT, UITexts.CANCEL, 0, 0, self) self.progress_dialog.setWindowTitle(UITexts.MEDIAPIPE_DOWNLOADING_TITLE) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.show() self.downloader_thread.start() def start_pet_model_download(self): """Starts the background thread to download the MediaPipe Object model.""" self.downloader_thread = ModelDownloader( MEDIAPIPE_OBJECT_MODEL_URL, MEDIAPIPE_OBJECT_MODEL_PATH, self) self.downloader_thread.download_complete.connect(self.on_download_finished) self.downloader_thread.finished.connect(self._on_downloader_finished) self.progress_dialog = QProgressDialog( UITexts.MEDIAPIPE_DOWNLOADING_TEXT, UITexts.CANCEL, 0, 0, self) self.progress_dialog.setWindowTitle(UITexts.MEDIAPIPE_DOWNLOADING_TITLE) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.show() self.downloader_thread.start() def accept(self): """Saves settings and closes the dialog.""" APP_CONFIG["scan_max_level"] = self.scan_max_level_spin.value() APP_CONFIG["generation_threads"] = self.threads_spin.value() APP_CONFIG["scan_batch_size"] = self.scan_batch_size_spin.value() APP_CONFIG["search_engine"] = self.search_engine_combo.currentData() APP_CONFIG["scan_full_on_start"] = self.scan_full_on_start_checkbox.isChecked() APP_CONFIG["person_tags"] = self.person_tags_edit.text() APP_CONFIG["pet_tags"] = self.pet_tags_edit.text() APP_CONFIG["body_tags"] = self.body_tags_edit.text() APP_CONFIG["object_tags"] = self.object_tags_edit.text() APP_CONFIG["landmark_tags"] = self.landmark_tags_edit.text() APP_CONFIG["face_box_color"] = self.current_face_color APP_CONFIG["pet_box_color"] = self.current_pet_color APP_CONFIG["body_box_color"] = self.current_body_color APP_CONFIG["object_box_color"] = self.current_object_color APP_CONFIG["landmark_box_color"] = self.current_landmark_color APP_CONFIG["tags_menu_max_items"] = self.mru_tags_spin.value() APP_CONFIG["faces_menu_max_items"] = self.face_history_spin.value() APP_CONFIG["pets_menu_max_items"] = self.pet_history_spin.value() APP_CONFIG["body_menu_max_items"] = self.body_history_spin.value() APP_CONFIG["object_menu_max_items"] = self.object_history_spin.value() APP_CONFIG["landmark_menu_max_items"] = self.landmark_history_spin.value() APP_CONFIG["thumbnails_refresh_interval"] = self.thumbs_refresh_spin.value() APP_CONFIG["thumbnails_bg_color"] = self.current_thumbs_bg_color APP_CONFIG["thumbnails_filename_color"] = self.current_thumbs_filename_color APP_CONFIG["thumbnails_tags_color"] = self.current_thumbs_tags_color APP_CONFIG["thumbnails_rating_color"] = self.current_thumbs_rating_color APP_CONFIG["thumbnails_tooltip_bg_color"] = \ self.current_thumbs_tooltip_bg_color APP_CONFIG["thumbnails_tooltip_fg_color"] = \ self.current_thumbs_tooltip_fg_color APP_CONFIG["thumbnails_filename_font_size"] = \ self.filename_font_size_spin.value() APP_CONFIG["thumbnails_tags_font_size"] = self.tags_font_size_spin.value() APP_CONFIG["thumbnails_filename_lines"] = self.filename_lines_spin.value() APP_CONFIG["thumbnails_tags_lines"] = self.tags_lines_spin.value() APP_CONFIG["thumbnails_show_filename"] = self.show_filename_check.isChecked() APP_CONFIG["thumbnails_show_rating"] = self.show_rating_check.isChecked() APP_CONFIG["thumbnails_show_tags"] = self.show_tags_check.isChecked() APP_CONFIG["viewer_wheel_speed"] = self.viewer_wheel_spin.value() APP_CONFIG["viewer_auto_resize_window"] = \ self.viewer_auto_resize_check.isChecked() APP_CONFIG["face_detection_engine"] = self.face_engine_combo.currentText() APP_CONFIG["pet_detection_engine"] = self.pet_engine_combo.currentText() if hasattr(self, "object_engine_combo"): APP_CONFIG["body_detection_engine"] = self.body_engine_combo.currentText() if hasattr(self, "object_engine_combo"): APP_CONFIG["object_detection_engine"] = \ self.object_engine_combo.currentText() if hasattr(self, "landmark_engine_combo"): APP_CONFIG["landmark_detection_engine"] = \ self.landmark_engine_combo.currentText() pos_map_inv = { UITexts.FILMSTRIP_BOTTOM: 'bottom', UITexts.FILMSTRIP_LEFT: 'left', UITexts.FILMSTRIP_TOP: 'top', UITexts.FILMSTRIP_RIGHT: 'right' } selected_text = self.filmstrip_pos_combo.currentText() APP_CONFIG["filmstrip_position"] = pos_map_inv.get(selected_text, 'bottom') save_app_config() super().accept() def on_download_finished(self, success, message): """Handles the result of the model download thread.""" self.progress_dialog.close() if success: QMessageBox.information( self, UITexts.MEDIAPIPE_DOWNLOAD_SUCCESS_TITLE, UITexts.MEDIAPIPE_DOWNLOAD_SUCCESS_TEXT) else: QMessageBox.critical(self, UITexts.MEDIAPIPE_DOWNLOAD_ERROR_TITLE, UITexts.MEDIAPIPE_DOWNLOAD_ERROR_TEXT.format(message)) self.update_mediapipe_status() def _on_downloader_finished(self): self.downloader_thread = None