mirror of
				https://github.com/arsenetar/dupeguru.git
				synced 2025-09-11 17:58:17 +00:00 
			
		
		
		
	More updates mainly in preferences
This commit is contained in:
		
							parent
							
								
									ec35c2df8f
								
							
						
					
					
						commit
						2f23f34b91
					
				
							
								
								
									
										106
									
								
								qt/app.py
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								qt/app.py
									
									
									
									
									
								
							@ -6,15 +6,18 @@
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import os.path as op
 | 
			
		||||
from typing import Type
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import QTimer, QObject, QUrl, pyqtSignal, Qt
 | 
			
		||||
from PyQt5.QtGui import QColor, QDesktopServices, QPalette
 | 
			
		||||
from PyQt5.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox, QStyleFactory, QToolTip
 | 
			
		||||
from PyQt6.QtCore import QTimer, QObject, QUrl, pyqtSignal, Qt
 | 
			
		||||
from PyQt6.QtGui import QColor, QDesktopServices, QPalette
 | 
			
		||||
from PyQt6.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox, QStyleFactory, QToolTip
 | 
			
		||||
 | 
			
		||||
from hscommon.trans import trget
 | 
			
		||||
from hscommon import desktop, plat
 | 
			
		||||
 | 
			
		||||
from qt.about_box import AboutBox
 | 
			
		||||
from qt.details_dialog import DetailsDialog
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase
 | 
			
		||||
from qt.recent import Recent
 | 
			
		||||
from qt.util import create_actions
 | 
			
		||||
from qt.progress_window import ProgressWindow
 | 
			
		||||
@ -42,10 +45,10 @@ tr = trget("ui")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DupeGuru(QObject):
 | 
			
		||||
    LOGO_NAME = "logo_se"
 | 
			
		||||
    LOGO_NAME = "dgse_logo"
 | 
			
		||||
    NAME = "dupeGuru"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
    def __init__(self, **kwargs) -> None:
 | 
			
		||||
        super().__init__(**kwargs)
 | 
			
		||||
        self.prefs = Preferences()
 | 
			
		||||
        self.prefs.load()
 | 
			
		||||
@ -56,7 +59,7 @@ class DupeGuru(QObject):
 | 
			
		||||
        self._setup()
 | 
			
		||||
 | 
			
		||||
    # --- Private
 | 
			
		||||
    def _setup(self):
 | 
			
		||||
    def _setup(self) -> None:
 | 
			
		||||
        core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto
 | 
			
		||||
        self._setupActions()
 | 
			
		||||
        self.details_dialog = None
 | 
			
		||||
@ -108,7 +111,7 @@ class DupeGuru(QObject):
 | 
			
		||||
        # that the application haven't launched.
 | 
			
		||||
        QTimer.singleShot(0, self.finishedLaunching)
 | 
			
		||||
 | 
			
		||||
    def _setupActions(self):
 | 
			
		||||
    def _setupActions(self) -> None:
 | 
			
		||||
        # Setup actions that are common to both the directory dialog and the results window.
 | 
			
		||||
        # (name, shortcut, icon, desc, func)
 | 
			
		||||
        ACTIONS = [
 | 
			
		||||
@ -154,7 +157,7 @@ class DupeGuru(QObject):
 | 
			
		||||
        ]
 | 
			
		||||
        create_actions(ACTIONS, self)
 | 
			
		||||
 | 
			
		||||
    def _update_options(self):
 | 
			
		||||
    def _update_options(self) -> None:
 | 
			
		||||
        self.model.options["mix_file_kind"] = self.prefs.mix_file_kind
 | 
			
		||||
        self.model.options["escape_filter_regexp"] = not self.prefs.use_regexp
 | 
			
		||||
        self.model.options["clean_empty_dirs"] = self.prefs.remove_empty_folders
 | 
			
		||||
@ -200,7 +203,7 @@ class DupeGuru(QObject):
 | 
			
		||||
        self._set_style("dark" if self.prefs.use_dark_style else "light")
 | 
			
		||||
 | 
			
		||||
    # --- Private
 | 
			
		||||
    def _get_details_dialog_class(self):
 | 
			
		||||
    def _get_details_dialog_class(self) -> Type[DetailsDialog]:
 | 
			
		||||
        if self.model.app_mode == AppMode.PICTURE:
 | 
			
		||||
            return DetailsDialogPicture
 | 
			
		||||
        elif self.model.app_mode == AppMode.MUSIC:
 | 
			
		||||
@ -208,7 +211,7 @@ class DupeGuru(QObject):
 | 
			
		||||
        else:
 | 
			
		||||
            return DetailsDialogStandard
 | 
			
		||||
 | 
			
		||||
    def _get_preferences_dialog_class(self):
 | 
			
		||||
    def _get_preferences_dialog_class(self) -> Type[PreferencesDialogBase]:
 | 
			
		||||
        if self.model.app_mode == AppMode.PICTURE:
 | 
			
		||||
            return PreferencesDialogPicture
 | 
			
		||||
        elif self.model.app_mode == AppMode.MUSIC:
 | 
			
		||||
@ -216,7 +219,7 @@ class DupeGuru(QObject):
 | 
			
		||||
        else:
 | 
			
		||||
            return PreferencesDialogStandard
 | 
			
		||||
 | 
			
		||||
    def _set_style(self, style="light"):
 | 
			
		||||
    def _set_style(self, style: str = "light") -> None:
 | 
			
		||||
        # Only support this feature on windows for now
 | 
			
		||||
        if not plat.ISWINDOWS:
 | 
			
		||||
            return
 | 
			
		||||
@ -224,18 +227,18 @@ class DupeGuru(QObject):
 | 
			
		||||
            QApplication.setStyle(QStyleFactory.create("Fusion"))
 | 
			
		||||
            palette = QApplication.style().standardPalette()
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Window, QColor(53, 53, 53))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.WindowText, Qt.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.WindowText, Qt.GlobalColor.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Base, QColor(25, 25, 25))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.AlternateBase, QColor(53, 53, 53))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(53, 53, 53))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.ToolTipText, Qt.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Text, Qt.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.ToolTipText, Qt.GlobalColor.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Button, QColor(53, 53, 53))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.ButtonText, Qt.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.BrightText, Qt.red)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.ButtonText, Qt.GlobalColor.white)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218))
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.HighlightedText, Qt.black)
 | 
			
		||||
            palette.setColor(QPalette.ColorRole.HighlightedText, Qt.GlobalColor.black)
 | 
			
		||||
            palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor(164, 166, 168))
 | 
			
		||||
            palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, QColor(164, 166, 168))
 | 
			
		||||
            palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(164, 166, 168))
 | 
			
		||||
@ -250,29 +253,31 @@ class DupeGuru(QObject):
 | 
			
		||||
        QApplication.setPalette(palette)
 | 
			
		||||
 | 
			
		||||
    # --- Public
 | 
			
		||||
    def add_selected_to_ignore_list(self):
 | 
			
		||||
    def add_selected_to_ignore_list(self) -> None:
 | 
			
		||||
        self.model.add_selected_to_ignore_list()
 | 
			
		||||
 | 
			
		||||
    def remove_selected(self):
 | 
			
		||||
        self.model.remove_selected(self)
 | 
			
		||||
    def remove_selected(self) -> None:
 | 
			
		||||
        self.model.remove_selected()
 | 
			
		||||
 | 
			
		||||
    def confirm(self, title, msg, default_button=QMessageBox.Yes):
 | 
			
		||||
    def confirm(
 | 
			
		||||
        self, title: str, msg: str, default_button: QMessageBox.StandardButton = QMessageBox.StandardButton.Yes
 | 
			
		||||
    ) -> bool:
 | 
			
		||||
        active = QApplication.activeWindow()
 | 
			
		||||
        buttons = QMessageBox.Yes | QMessageBox.No
 | 
			
		||||
        buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
 | 
			
		||||
        answer = QMessageBox.question(active, title, msg, buttons, default_button)
 | 
			
		||||
        return answer == QMessageBox.Yes
 | 
			
		||||
        return answer == QMessageBox.StandardButton.Yes
 | 
			
		||||
 | 
			
		||||
    def invokeCustomCommand(self):
 | 
			
		||||
    def invokeCustomCommand(self) -> None:
 | 
			
		||||
        self.model.invoke_custom_command()
 | 
			
		||||
 | 
			
		||||
    def show_details(self):
 | 
			
		||||
    def show_details(self) -> None:
 | 
			
		||||
        if self.details_dialog is not None:
 | 
			
		||||
            if not self.details_dialog.isVisible():
 | 
			
		||||
                self.details_dialog.show()
 | 
			
		||||
            else:
 | 
			
		||||
                self.details_dialog.hide()
 | 
			
		||||
 | 
			
		||||
    def showResultsWindow(self):
 | 
			
		||||
    def showResultsWindow(self) -> None:
 | 
			
		||||
        if self.resultWindow is not None:
 | 
			
		||||
            if self.use_tabs:
 | 
			
		||||
                if self.main_window.indexOfWidget(self.resultWindow) < 0:
 | 
			
		||||
@ -282,14 +287,14 @@ class DupeGuru(QObject):
 | 
			
		||||
            else:
 | 
			
		||||
                self.resultWindow.show()
 | 
			
		||||
 | 
			
		||||
    def showDirectoriesWindow(self):
 | 
			
		||||
    def showDirectoriesWindow(self) -> None:
 | 
			
		||||
        if self.directories_dialog is not None:
 | 
			
		||||
            if self.use_tabs:
 | 
			
		||||
                self.main_window.showTab(self.directories_dialog)
 | 
			
		||||
            else:
 | 
			
		||||
                self.directories_dialog.show()
 | 
			
		||||
 | 
			
		||||
    def shutdown(self):
 | 
			
		||||
    def shutdown(self) -> None:
 | 
			
		||||
        self.willSavePrefs.emit()
 | 
			
		||||
        self.prefs.save()
 | 
			
		||||
        self.model.save()
 | 
			
		||||
@ -304,7 +309,7 @@ class DupeGuru(QObject):
 | 
			
		||||
    SIGTERM = pyqtSignal()
 | 
			
		||||
 | 
			
		||||
    # --- Events
 | 
			
		||||
    def finishedLaunching(self):
 | 
			
		||||
    def finishedLaunching(self) -> None:
 | 
			
		||||
        if sys.getfilesystemencoding() == "ascii":
 | 
			
		||||
            # No need to localize this, it's a debugging message.
 | 
			
		||||
            msg = (
 | 
			
		||||
@ -324,28 +329,28 @@ class DupeGuru(QObject):
 | 
			
		||||
                self.model.load_from(results)
 | 
			
		||||
                self.recentResults.insertItem(results)
 | 
			
		||||
 | 
			
		||||
    def clearCacheTriggered(self):
 | 
			
		||||
    def clearCacheTriggered(self) -> None:
 | 
			
		||||
        title = tr("Clear Cache")
 | 
			
		||||
        msg = tr("Do you really want to clear the cache? This will remove all cached file hashes and picture analysis.")
 | 
			
		||||
        if self.confirm(title, msg, QMessageBox.No):
 | 
			
		||||
        if self.confirm(title, msg, QMessageBox.StandardButton.No):
 | 
			
		||||
            self.model.clear_picture_cache()
 | 
			
		||||
            self.model.clear_hash_cache()
 | 
			
		||||
            active = QApplication.activeWindow()
 | 
			
		||||
            QMessageBox.information(active, title, tr("Cache cleared."))
 | 
			
		||||
 | 
			
		||||
    def ignoreListTriggered(self):
 | 
			
		||||
    def ignoreListTriggered(self) -> None:
 | 
			
		||||
        if self.use_tabs:
 | 
			
		||||
            self.showTriggeredTabbedDialog(self.ignoreListDialog, tr("Ignore List"))
 | 
			
		||||
        else:  # floating windows
 | 
			
		||||
            self.model.ignore_list_dialog.show()
 | 
			
		||||
 | 
			
		||||
    def excludeListTriggered(self):
 | 
			
		||||
    def excludeListTriggered(self) -> None:
 | 
			
		||||
        if self.use_tabs:
 | 
			
		||||
            self.showTriggeredTabbedDialog(self.excludeListDialog, tr("Exclusion Filters"))
 | 
			
		||||
        else:  # floating windows
 | 
			
		||||
            self.model.exclude_list_dialog.show()
 | 
			
		||||
 | 
			
		||||
    def showTriggeredTabbedDialog(self, dialog, desc_string):
 | 
			
		||||
    def showTriggeredTabbedDialog(self, dialog, desc_string: str) -> None:
 | 
			
		||||
        """Add tab for dialog, name the tab with desc_string, then show it."""
 | 
			
		||||
        index = self.main_window.indexOfWidget(dialog)
 | 
			
		||||
        # Create the tab if it doesn't exist already
 | 
			
		||||
@ -354,23 +359,22 @@ class DupeGuru(QObject):
 | 
			
		||||
        # Show the tab for that widget
 | 
			
		||||
        self.main_window.setCurrentIndex(index)
 | 
			
		||||
 | 
			
		||||
    def openDebugLogTriggered(self):
 | 
			
		||||
    def openDebugLogTriggered(self) -> None:
 | 
			
		||||
        debug_log_path = op.join(self.model.appdata, "debug.log")
 | 
			
		||||
        desktop.open_path(debug_log_path)
 | 
			
		||||
 | 
			
		||||
    def preferencesTriggered(self):
 | 
			
		||||
    def preferencesTriggered(self) -> None:
 | 
			
		||||
        preferences_dialog = self._get_preferences_dialog_class()(
 | 
			
		||||
            self.main_window if self.main_window else self.directories_dialog, self
 | 
			
		||||
        )
 | 
			
		||||
        preferences_dialog.load()
 | 
			
		||||
        result = preferences_dialog.exec()
 | 
			
		||||
        if result == QDialog.Accepted:
 | 
			
		||||
        if result == QDialog.DialogCode.Accepted:
 | 
			
		||||
            preferences_dialog.save()
 | 
			
		||||
            self.prefs.save()
 | 
			
		||||
            self._update_options()
 | 
			
		||||
        preferences_dialog.setParent(None)
 | 
			
		||||
 | 
			
		||||
    def quitTriggered(self):
 | 
			
		||||
    def quitTriggered(self) -> None:
 | 
			
		||||
        if self.details_dialog is not None:
 | 
			
		||||
            self.details_dialog.close()
 | 
			
		||||
 | 
			
		||||
@ -379,10 +383,10 @@ class DupeGuru(QObject):
 | 
			
		||||
        else:
 | 
			
		||||
            self.directories_dialog.close()
 | 
			
		||||
 | 
			
		||||
    def showAboutBoxTriggered(self):
 | 
			
		||||
    def showAboutBoxTriggered(self) -> None:
 | 
			
		||||
        self.about_box.show()
 | 
			
		||||
 | 
			
		||||
    def showHelpTriggered(self):
 | 
			
		||||
    def showHelpTriggered(self) -> None:
 | 
			
		||||
        base_path = platform.HELP_PATH
 | 
			
		||||
        help_path = op.abspath(op.join(base_path, "index.html"))
 | 
			
		||||
        if op.exists(help_path):
 | 
			
		||||
@ -391,7 +395,7 @@ class DupeGuru(QObject):
 | 
			
		||||
            url = QUrl("https://dupeguru.voltaicideas.net/help/en/")
 | 
			
		||||
        QDesktopServices.openUrl(url)
 | 
			
		||||
 | 
			
		||||
    def handleSIGTERM(self):
 | 
			
		||||
    def handleSIGTERM(self) -> None:
 | 
			
		||||
        self.shutdown()
 | 
			
		||||
 | 
			
		||||
    # --- model --> view
 | 
			
		||||
@ -401,20 +405,20 @@ class DupeGuru(QObject):
 | 
			
		||||
    def set_default(self, key, value):
 | 
			
		||||
        self.prefs.set_value(key, value)
 | 
			
		||||
 | 
			
		||||
    def show_message(self, msg):
 | 
			
		||||
    def show_message(self, msg: str) -> None:
 | 
			
		||||
        window = QApplication.activeWindow()
 | 
			
		||||
        QMessageBox.information(window, "", msg)
 | 
			
		||||
 | 
			
		||||
    def ask_yes_no(self, prompt):
 | 
			
		||||
    def ask_yes_no(self, prompt: str) -> bool:
 | 
			
		||||
        return self.confirm("", prompt)
 | 
			
		||||
 | 
			
		||||
    def create_results_window(self):
 | 
			
		||||
    def create_results_window(self) -> None:
 | 
			
		||||
        """Creates resultWindow and details_dialog depending on the selected ``app_mode``."""
 | 
			
		||||
        if self.details_dialog is not None:
 | 
			
		||||
            # The object is not deleted entirely, avoid saving its geometry in the future
 | 
			
		||||
            # self.willSavePrefs.disconnect(self.details_dialog.appWillSavePrefs)
 | 
			
		||||
            # or simply delete it on close which is probably cleaner:
 | 
			
		||||
            self.details_dialog.setAttribute(Qt.WA_DeleteOnClose)
 | 
			
		||||
            self.details_dialog.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
 | 
			
		||||
            self.details_dialog.close()
 | 
			
		||||
            # if we don't do the following, Qt will crash when we recreate the Results dialog
 | 
			
		||||
            self.details_dialog.setParent(None)
 | 
			
		||||
@ -429,17 +433,17 @@ class DupeGuru(QObject):
 | 
			
		||||
            self.directories_dialog._updateActionsState()
 | 
			
		||||
        self.details_dialog = self._get_details_dialog_class()(self.resultWindow, self)
 | 
			
		||||
 | 
			
		||||
    def show_results_window(self):
 | 
			
		||||
    def show_results_window(self) -> None:
 | 
			
		||||
        self.showResultsWindow()
 | 
			
		||||
 | 
			
		||||
    def show_problem_dialog(self):
 | 
			
		||||
    def show_problem_dialog(self) -> None:
 | 
			
		||||
        self.problemDialog.show()
 | 
			
		||||
 | 
			
		||||
    def select_dest_folder(self, prompt):
 | 
			
		||||
        flags = QFileDialog.ShowDirsOnly
 | 
			
		||||
    def select_dest_folder(self, prompt: str) -> str:
 | 
			
		||||
        flags = QFileDialog.Option.ShowDirsOnly
 | 
			
		||||
        return QFileDialog.getExistingDirectory(self.resultWindow, prompt, "", flags)
 | 
			
		||||
 | 
			
		||||
    def select_dest_file(self, prompt, extension):
 | 
			
		||||
    def select_dest_file(self, prompt: str, extension: str) -> str:
 | 
			
		||||
        files = tr("{} file (*.{})").format(extension.upper(), extension)
 | 
			
		||||
        destination, chosen_filter = QFileDialog.getSaveFileName(self.resultWindow, prompt, "", files)
 | 
			
		||||
        if not destination.endswith(f".{extension}"):
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,8 @@
 | 
			
		||||
# which should be included with this package. The terms are also available at
 | 
			
		||||
# http://www.gnu.org/licenses/gpl-3.0.html
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import QSize
 | 
			
		||||
from PyQt5.QtWidgets import QAbstractItemView
 | 
			
		||||
from PyQt6.QtCore import QSize
 | 
			
		||||
from PyQt6.QtWidgets import QAbstractItemView
 | 
			
		||||
 | 
			
		||||
from hscommon.trans import trget
 | 
			
		||||
from qt.details_dialog import DetailsDialog as DetailsDialogBase
 | 
			
		||||
@ -15,12 +15,12 @@ tr = trget("ui")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DetailsDialog(DetailsDialogBase):
 | 
			
		||||
    def _setupUi(self):
 | 
			
		||||
    def _setupUi(self) -> None:
 | 
			
		||||
        self.setWindowTitle(tr("Details"))
 | 
			
		||||
        self.resize(502, 295)
 | 
			
		||||
        self.setMinimumSize(QSize(250, 250))
 | 
			
		||||
        self.tableView = DetailsTable(self)
 | 
			
		||||
        self.tableView.setAlternatingRowColors(True)
 | 
			
		||||
        self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
 | 
			
		||||
        self.tableView.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
 | 
			
		||||
        self.tableView.setShowGrid(False)
 | 
			
		||||
        self.setWidget(self.tableView)
 | 
			
		||||
 | 
			
		||||
@ -4,27 +4,22 @@
 | 
			
		||||
# which should be included with this package. The terms are also available at
 | 
			
		||||
# http://www.gnu.org/licenses/gpl-3.0.html
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import QSize
 | 
			
		||||
from PyQt5.QtWidgets import (
 | 
			
		||||
    QVBoxLayout,
 | 
			
		||||
    QHBoxLayout,
 | 
			
		||||
    QLabel,
 | 
			
		||||
    QSizePolicy,
 | 
			
		||||
    QSpacerItem,
 | 
			
		||||
    QWidget,
 | 
			
		||||
)
 | 
			
		||||
from typing import Callable
 | 
			
		||||
from PyQt6.QtCore import QSize
 | 
			
		||||
from PyQt6.QtWidgets import QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget, QCheckBox
 | 
			
		||||
 | 
			
		||||
from hscommon.trans import trget
 | 
			
		||||
from core.app import AppMode
 | 
			
		||||
from core.scanner import ScanType
 | 
			
		||||
from qt.preferences import Preferences
 | 
			
		||||
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase, Sections
 | 
			
		||||
 | 
			
		||||
tr = trget("ui")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
    def _setupPreferenceWidgets(self):
 | 
			
		||||
    def _setupPreferenceWidgets(self) -> None:
 | 
			
		||||
        self._setupFilterHardnessBox()
 | 
			
		||||
        self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
 | 
			
		||||
        self.widget = QWidget(self)
 | 
			
		||||
@ -37,7 +32,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.verticalLayout_4.addWidget(self.label_6)
 | 
			
		||||
        self.horizontalLayout_2 = QHBoxLayout()
 | 
			
		||||
        self.horizontalLayout_2.setSpacing(0)
 | 
			
		||||
        spacer_item = QSpacerItem(15, 20, QSizePolicy.Fixed, QSizePolicy.Minimum)
 | 
			
		||||
        spacer_item = QSpacerItem(15, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
 | 
			
		||||
        self.horizontalLayout_2.addItem(spacer_item)
 | 
			
		||||
        self._setupAddCheckbox("tagTrackBox", tr("Track"), self.widget)
 | 
			
		||||
        self.horizontalLayout_2.addWidget(self.tagTrackBox)
 | 
			
		||||
@ -70,7 +65,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
 | 
			
		||||
        self._setupBottomPart()
 | 
			
		||||
 | 
			
		||||
    def _load(self, prefs, setchecked, section):
 | 
			
		||||
    def _load(self, prefs: Preferences, setchecked: Callable[[QCheckBox, bool], None], section: Sections) -> None:
 | 
			
		||||
        setchecked(self.tagTrackBox, prefs.scan_tag_track)
 | 
			
		||||
        setchecked(self.tagArtistBox, prefs.scan_tag_artist)
 | 
			
		||||
        setchecked(self.tagAlbumBox, prefs.scan_tag_album)
 | 
			
		||||
@ -99,7 +94,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.tagGenreBox.setEnabled(tag_based)
 | 
			
		||||
        self.tagYearBox.setEnabled(tag_based)
 | 
			
		||||
 | 
			
		||||
    def _save(self, prefs, ischecked):
 | 
			
		||||
    def _save(self, prefs: Preferences, ischecked: Callable[[QCheckBox], bool]) -> None:
 | 
			
		||||
        prefs.scan_tag_track = ischecked(self.tagTrackBox)
 | 
			
		||||
        prefs.scan_tag_artist = ischecked(self.tagArtistBox)
 | 
			
		||||
        prefs.scan_tag_album = ischecked(self.tagAlbumBox)
 | 
			
		||||
 | 
			
		||||
@ -4,21 +4,23 @@
 | 
			
		||||
# which should be included with this package. The terms are also available at
 | 
			
		||||
# http://www.gnu.org/licenses/gpl-3.0.html
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtWidgets import QFormLayout
 | 
			
		||||
from PyQt5.QtCore import Qt
 | 
			
		||||
from typing import Callable
 | 
			
		||||
from PyQt6.QtWidgets import QFormLayout, QCheckBox
 | 
			
		||||
from PyQt6.QtCore import Qt
 | 
			
		||||
from hscommon.trans import trget
 | 
			
		||||
from hscommon.plat import ISLINUX
 | 
			
		||||
from qt.preferences import Preferences
 | 
			
		||||
from qt.radio_box import RadioBox
 | 
			
		||||
from core.scanner import ScanType
 | 
			
		||||
from core.app import AppMode
 | 
			
		||||
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase, Sections
 | 
			
		||||
 | 
			
		||||
tr = trget("ui")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
    def _setupPreferenceWidgets(self):
 | 
			
		||||
    def _setupPreferenceWidgets(self) -> None:
 | 
			
		||||
        self._setupFilterHardnessBox()
 | 
			
		||||
        self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
 | 
			
		||||
        self._setupAddCheckbox("matchScaledBox", tr("Match pictures of different dimensions"))
 | 
			
		||||
@ -37,12 +39,12 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
 | 
			
		||||
        self.cacheTypeRadio = RadioBox(self, items=["Sqlite", "Shelve"], spread=False)
 | 
			
		||||
        cache_form = QFormLayout()
 | 
			
		||||
        cache_form.setLabelAlignment(Qt.AlignLeft)
 | 
			
		||||
        cache_form.setLabelAlignment(Qt.AlignmentFlag.AlignLeft)
 | 
			
		||||
        cache_form.addRow(tr("Picture cache mode:"), self.cacheTypeRadio)
 | 
			
		||||
        self.widgetsVLayout.addLayout(cache_form)
 | 
			
		||||
        self._setupBottomPart()
 | 
			
		||||
 | 
			
		||||
    def _setupDisplayPage(self):
 | 
			
		||||
    def _setupDisplayPage(self) -> None:
 | 
			
		||||
        super()._setupDisplayPage()
 | 
			
		||||
        self._setupAddCheckbox("details_dialog_override_theme_icons", tr("Override theme icons in viewer toolbar"))
 | 
			
		||||
        self.details_dialog_override_theme_icons.setToolTip(
 | 
			
		||||
@ -62,7 +64,7 @@ show scrollbars to span the view around"
 | 
			
		||||
        )
 | 
			
		||||
        self.details_groupbox_layout.insertWidget(index + 2, self.details_dialog_viewers_show_scrollbars)
 | 
			
		||||
 | 
			
		||||
    def _load(self, prefs, setchecked, section):
 | 
			
		||||
    def _load(self, prefs: Preferences, setchecked: Callable[[QCheckBox, bool], None], section: Sections) -> None:
 | 
			
		||||
        setchecked(self.matchScaledBox, prefs.match_scaled)
 | 
			
		||||
        self.cacheTypeRadio.selected_index = 1 if prefs.picture_cache_type == "shelve" else 0
 | 
			
		||||
 | 
			
		||||
@ -73,7 +75,7 @@ show scrollbars to span the view around"
 | 
			
		||||
        setchecked(self.details_dialog_override_theme_icons, prefs.details_dialog_override_theme_icons)
 | 
			
		||||
        setchecked(self.details_dialog_viewers_show_scrollbars, prefs.details_dialog_viewers_show_scrollbars)
 | 
			
		||||
 | 
			
		||||
    def _save(self, prefs, ischecked):
 | 
			
		||||
    def _save(self, prefs: Preferences, ischecked: Callable[[QCheckBox], bool]) -> None:
 | 
			
		||||
        prefs.match_scaled = ischecked(self.matchScaledBox)
 | 
			
		||||
        prefs.picture_cache_type = "shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite"
 | 
			
		||||
        prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons)
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,10 @@
 | 
			
		||||
# which should be included with this package. The terms are also available at
 | 
			
		||||
# http://www.gnu.org/licenses/gpl-3.0.html
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtWidgets import QApplication, QDockWidget
 | 
			
		||||
from PyQt5.QtCore import Qt, QRect, QObject, pyqtSignal
 | 
			
		||||
from PyQt5.QtGui import QColor
 | 
			
		||||
from typing import Any, Tuple
 | 
			
		||||
from PyQt6.QtWidgets import QApplication, QDockWidget
 | 
			
		||||
from PyQt6.QtCore import Qt, QRect, QObject, pyqtSignal
 | 
			
		||||
from PyQt6.QtGui import QColor
 | 
			
		||||
 | 
			
		||||
from hscommon import trans
 | 
			
		||||
from hscommon.plat import ISLINUX
 | 
			
		||||
@ -126,7 +127,7 @@ class PreferencesBase(QObject):
 | 
			
		||||
    def set_value(self, name, value):
 | 
			
		||||
        self._settings.setValue(name, _normalize_for_serialization(value))
 | 
			
		||||
 | 
			
		||||
    def saveGeometry(self, name, widget):
 | 
			
		||||
    def saveGeometry(self, name, widget) -> None:
 | 
			
		||||
        # We save geometry under a 7-sized int array: first item is a flag
 | 
			
		||||
        # for whether the widget is maximized, second item is a flag for whether
 | 
			
		||||
        # the widget is docked, third item is a Qt::DockWidgetArea enum value,
 | 
			
		||||
@ -138,12 +139,12 @@ class PreferencesBase(QObject):
 | 
			
		||||
        rect_as_list = [r.x(), r.y(), r.width(), r.height()]
 | 
			
		||||
        self.set_value(name, [m, d, area] + rect_as_list)
 | 
			
		||||
 | 
			
		||||
    def restoreGeometry(self, name, widget):
 | 
			
		||||
    def restoreGeometry(self, name, widget) -> Tuple[bool, Any]:
 | 
			
		||||
        geometry = self.get_value(name)
 | 
			
		||||
        if geometry and len(geometry) == 7:
 | 
			
		||||
            m, d, area, x, y, w, h = geometry
 | 
			
		||||
            if m:
 | 
			
		||||
                widget.setWindowState(Qt.WindowMaximized)
 | 
			
		||||
                widget.setWindowState(Qt.WindowState.WindowMaximized)
 | 
			
		||||
            else:
 | 
			
		||||
                r = QRect(x, y, w, h)
 | 
			
		||||
                widget.setGeometry(r)
 | 
			
		||||
@ -154,7 +155,7 @@ class PreferencesBase(QObject):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Preferences(PreferencesBase):
 | 
			
		||||
    def _load_values(self, settings):
 | 
			
		||||
    def _load_values(self, settings) -> None:
 | 
			
		||||
        get = self.get_value
 | 
			
		||||
        self.filter_hardness = get("FilterHardness", self.filter_hardness)
 | 
			
		||||
        self.mix_file_kind = get("MixFileKind", self.mix_file_kind)
 | 
			
		||||
@ -225,7 +226,7 @@ class Preferences(PreferencesBase):
 | 
			
		||||
        self.match_scaled = get("MatchScaled", self.match_scaled)
 | 
			
		||||
        self.picture_cache_type = get("PictureCacheType", self.picture_cache_type)
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
    def reset(self) -> None:
 | 
			
		||||
        self.filter_hardness = 95
 | 
			
		||||
        self.mix_file_kind = True
 | 
			
		||||
        self.use_regexp = False
 | 
			
		||||
@ -247,8 +248,8 @@ class Preferences(PreferencesBase):
 | 
			
		||||
        # By default use internal icons on platforms other than Linux for now
 | 
			
		||||
        self.details_dialog_override_theme_icons = False if not ISLINUX else True
 | 
			
		||||
        self.details_dialog_viewers_show_scrollbars = True
 | 
			
		||||
        self.result_table_ref_foreground_color = QColor(Qt.blue)
 | 
			
		||||
        self.result_table_ref_background_color = QColor(Qt.lightGray)
 | 
			
		||||
        self.result_table_ref_foreground_color = QColor(Qt.GlobalColor.blue)
 | 
			
		||||
        self.result_table_ref_background_color = QColor(Qt.GlobalColor.lightGray)
 | 
			
		||||
        self.result_table_delta_foreground_color = QColor(255, 142, 40)  # orange
 | 
			
		||||
        self.resultWindowIsMaximized = False
 | 
			
		||||
        self.resultWindowRect = None
 | 
			
		||||
@ -276,7 +277,7 @@ class Preferences(PreferencesBase):
 | 
			
		||||
        self.match_scaled = False
 | 
			
		||||
        self.picture_cache_type = "sqlite"
 | 
			
		||||
 | 
			
		||||
    def _save_values(self, settings):
 | 
			
		||||
    def _save_values(self, settings) -> None:
 | 
			
		||||
        set_ = self.set_value
 | 
			
		||||
        set_("FilterHardness", self.filter_hardness)
 | 
			
		||||
        set_("MixFileKind", self.mix_file_kind)
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,9 @@
 | 
			
		||||
# which should be included with this package. The terms are also available at
 | 
			
		||||
# http://www.gnu.org/licenses/gpl-3.0.html
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import Qt, QSize, pyqtSlot
 | 
			
		||||
from PyQt5.QtWidgets import (
 | 
			
		||||
from typing import Union
 | 
			
		||||
from PyQt6.QtCore import Qt, QSize, pyqtSlot
 | 
			
		||||
from PyQt6.QtWidgets import (
 | 
			
		||||
    QDialog,
 | 
			
		||||
    QDialogButtonBox,
 | 
			
		||||
    QVBoxLayout,
 | 
			
		||||
@ -28,7 +29,7 @@ from PyQt5.QtWidgets import (
 | 
			
		||||
    QGroupBox,
 | 
			
		||||
    QFormLayout,
 | 
			
		||||
)
 | 
			
		||||
from PyQt5.QtGui import QPixmap, QIcon
 | 
			
		||||
from PyQt6.QtGui import QPixmap, QIcon, QShowEvent
 | 
			
		||||
from hscommon import desktop, plat
 | 
			
		||||
 | 
			
		||||
from hscommon.trans import trget
 | 
			
		||||
@ -39,6 +40,11 @@ from enum import Flag, auto
 | 
			
		||||
 | 
			
		||||
from qt.preferences import Preferences
 | 
			
		||||
 | 
			
		||||
from typing import TYPE_CHECKING
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from qt.app import DupeGuru
 | 
			
		||||
 | 
			
		||||
tr = trget("ui")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -52,8 +58,8 @@ class Sections(Flag):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreferencesDialogBase(QDialog):
 | 
			
		||||
    def __init__(self, parent, app, **kwargs):
 | 
			
		||||
        flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
 | 
			
		||||
    def __init__(self, parent: QWidget, app: "DupeGuru", **kwargs):
 | 
			
		||||
        flags = Qt.WindowType.CustomizeWindowHint | Qt.WindowType.WindowTitleHint | Qt.WindowType.WindowSystemMenuHint
 | 
			
		||||
        super().__init__(parent, flags, **kwargs)
 | 
			
		||||
        self.app = app
 | 
			
		||||
        self.supportedLanguages = dict(sorted(get_langnames().items(), key=lambda item: item[1]))
 | 
			
		||||
@ -65,7 +71,7 @@ class PreferencesDialogBase(QDialog):
 | 
			
		||||
        self.buttonBox.accepted.connect(self.accept)
 | 
			
		||||
        self.buttonBox.rejected.connect(self.reject)
 | 
			
		||||
 | 
			
		||||
    def _setupFilterHardnessBox(self):
 | 
			
		||||
    def _setupFilterHardnessBox(self) -> None:
 | 
			
		||||
        self.filterHardnessHLayout = QHBoxLayout()
 | 
			
		||||
        self.filterHardnessLabel = QLabel(self)
 | 
			
		||||
        self.filterHardnessLabel.setText(tr("Filter Hardness:"))
 | 
			
		||||
@ -76,7 +82,7 @@ class PreferencesDialogBase(QDialog):
 | 
			
		||||
        self.filterHardnessHLayoutSub1 = QHBoxLayout()
 | 
			
		||||
        self.filterHardnessHLayoutSub1.setSpacing(12)
 | 
			
		||||
        self.filterHardnessSlider = QSlider(self)
 | 
			
		||||
        size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
 | 
			
		||||
        size_policy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
 | 
			
		||||
        size_policy.setHorizontalStretch(0)
 | 
			
		||||
        size_policy.setVerticalStretch(0)
 | 
			
		||||
        size_policy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
 | 
			
		||||
@ -84,7 +90,7 @@ class PreferencesDialogBase(QDialog):
 | 
			
		||||
        self.filterHardnessSlider.setMinimum(1)
 | 
			
		||||
        self.filterHardnessSlider.setMaximum(100)
 | 
			
		||||
        self.filterHardnessSlider.setTracking(True)
 | 
			
		||||
        self.filterHardnessSlider.setOrientation(Qt.Horizontal)
 | 
			
		||||
        self.filterHardnessSlider.setOrientation(Qt.Orientation.Horizontal)
 | 
			
		||||
        self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider)
 | 
			
		||||
        self.filterHardnessLabel = QLabel(self)
 | 
			
		||||
        self.filterHardnessLabel.setText("100")
 | 
			
		||||
@ -96,7 +102,7 @@ class PreferencesDialogBase(QDialog):
 | 
			
		||||
        self.moreResultsLabel = QLabel(self)
 | 
			
		||||
        self.moreResultsLabel.setText(tr("More Results"))
 | 
			
		||||
        self.filterHardnessHLayoutSub2.addWidget(self.moreResultsLabel)
 | 
			
		||||
        spacer_item = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
 | 
			
		||||
        spacer_item = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
 | 
			
		||||
        self.filterHardnessHLayoutSub2.addItem(spacer_item)
 | 
			
		||||
        self.fewerResultsLabel = QLabel(self)
 | 
			
		||||
        self.fewerResultsLabel.setText(tr("Fewer Results"))
 | 
			
		||||
@ -104,7 +110,7 @@ class PreferencesDialogBase(QDialog):
 | 
			
		||||
        self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2)
 | 
			
		||||
        self.filterHardnessHLayout.addLayout(self.filterHardnessVLayout)
 | 
			
		||||
 | 
			
		||||
    def _setupBottomPart(self):
 | 
			
		||||
    def _setupBottomPart(self) -> None:
 | 
			
		||||
        # The bottom part of the pref panel is always the same in all editions.
 | 
			
		||||
        self.copyMoveLabel = QLabel(self)
 | 
			
		||||
        self.copyMoveLabel.setText(tr("Copy and Move:"))
 | 
			
		||||
@ -120,7 +126,7 @@ class PreferencesDialogBase(QDialog):
 | 
			
		||||
        self.customCommandEdit = QLineEdit(self)
 | 
			
		||||
        self.widgetsVLayout.addWidget(self.customCommandEdit)
 | 
			
		||||
 | 
			
		||||
    def _setupDisplayPage(self):
 | 
			
		||||
    def _setupDisplayPage(self) -> None:
 | 
			
		||||
        self.ui_groupbox = QGroupBox("&" + tr("General Interface"))
 | 
			
		||||
        layout = QVBoxLayout()
 | 
			
		||||
        self.languageLabel = QLabel(tr("Language:"), self)
 | 
			
		||||
@ -171,7 +177,7 @@ On MacOS, the tab bar will fill up the window's width instead."
 | 
			
		||||
        formlayout.addRow(tr("Reference background color:"), self.result_table_ref_background_color)
 | 
			
		||||
        self.result_table_delta_foreground_color = ColorPickerButton(self)
 | 
			
		||||
        formlayout.addRow(tr("Delta foreground color:"), self.result_table_delta_foreground_color)
 | 
			
		||||
        formlayout.setLabelAlignment(Qt.AlignLeft)
 | 
			
		||||
        formlayout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft)
 | 
			
		||||
 | 
			
		||||
        # Keep same vertical spacing as parent layout for consistency
 | 
			
		||||
        formlayout.setVerticalSpacing(self.displayVLayout.spacing())
 | 
			
		||||
@ -213,7 +219,7 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
        details_groupbox.setLayout(self.details_groupbox_layout)
 | 
			
		||||
        self.displayVLayout.addWidget(details_groupbox)
 | 
			
		||||
 | 
			
		||||
    def _setupDebugPage(self):
 | 
			
		||||
    def _setupDebugPage(self) -> None:
 | 
			
		||||
        self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
 | 
			
		||||
        self._setupAddCheckbox("profile_scan_box", tr("Profile scan operation"))
 | 
			
		||||
        self.profile_scan_box.setToolTip(tr("Profile the scan operation and save logs for optimization."))
 | 
			
		||||
@ -225,7 +231,7 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
        )
 | 
			
		||||
        self.debugVLayout.addWidget(self.debug_location_label)
 | 
			
		||||
 | 
			
		||||
    def _setupAddCheckbox(self, name, label, parent=None):
 | 
			
		||||
    def _setupAddCheckbox(self, name: str, label: str, parent: Union[QWidget, None] = None) -> None:
 | 
			
		||||
        if parent is None:
 | 
			
		||||
            parent = self
 | 
			
		||||
        cb = QCheckBox(parent)
 | 
			
		||||
@ -236,7 +242,7 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
        # Edition-specific
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _setupUi(self):
 | 
			
		||||
    def _setupUi(self) -> None:
 | 
			
		||||
        self.setWindowTitle(tr("Options"))
 | 
			
		||||
        self.setSizeGripEnabled(False)
 | 
			
		||||
        self.setModal(True)
 | 
			
		||||
@ -258,11 +264,13 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
        # self.mainVLayout.addLayout(self.widgetsVLayout)
 | 
			
		||||
        self.buttonBox = QDialogButtonBox(self)
 | 
			
		||||
        self.buttonBox.setStandardButtons(
 | 
			
		||||
            QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.RestoreDefaults
 | 
			
		||||
            QDialogButtonBox.StandardButton.Cancel
 | 
			
		||||
            | QDialogButtonBox.StandardButton.Ok
 | 
			
		||||
            | QDialogButtonBox.StandardButton.RestoreDefaults
 | 
			
		||||
        )
 | 
			
		||||
        self.mainVLayout.addWidget(self.tabwidget)
 | 
			
		||||
        self.mainVLayout.addWidget(self.buttonBox)
 | 
			
		||||
        self.layout().setSizeConstraint(QLayout.SetFixedSize)
 | 
			
		||||
        self.layout().setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
 | 
			
		||||
        self.tabwidget.addTab(self.page_general, tr("General"))
 | 
			
		||||
        self.tabwidget.addTab(self.page_display, tr("Display"))
 | 
			
		||||
        self.tabwidget.addTab(self.page_debug, tr("Debug"))
 | 
			
		||||
@ -270,20 +278,20 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
        self.widgetsVLayout.addStretch(0)
 | 
			
		||||
        self.debugVLayout.addStretch(0)
 | 
			
		||||
 | 
			
		||||
    def _load(self, prefs, setchecked, section):
 | 
			
		||||
    def _load(self, prefs, setchecked, section) -> None:
 | 
			
		||||
        # Edition-specific
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _save(self, prefs, ischecked):
 | 
			
		||||
    def _save(self, prefs, ischecked) -> None:
 | 
			
		||||
        # Edition-specific
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def load(self, prefs=None, section=Sections.ALL):
 | 
			
		||||
    def load(self, prefs: Preferences = None, section: Sections = Sections.ALL) -> None:
 | 
			
		||||
        if prefs is None:
 | 
			
		||||
            prefs = self.app.prefs
 | 
			
		||||
 | 
			
		||||
        def setchecked(cb, b):
 | 
			
		||||
            cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
 | 
			
		||||
        def setchecked(cb: QCheckBox, b: bool) -> None:
 | 
			
		||||
            cb.setCheckState(Qt.CheckState.Checked if b else Qt.CheckState.Unchecked)
 | 
			
		||||
 | 
			
		||||
        if section & Sections.GENERAL:
 | 
			
		||||
            self.filterHardnessSlider.setValue(prefs.filter_hardness)
 | 
			
		||||
@ -323,12 +331,12 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
            setchecked(self.profile_scan_box, prefs.profile_scan)
 | 
			
		||||
        self._load(prefs, setchecked, section)
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
    def save(self) -> None:
 | 
			
		||||
        prefs = self.app.prefs
 | 
			
		||||
        prefs.filter_hardness = self.filterHardnessSlider.value()
 | 
			
		||||
 | 
			
		||||
        def ischecked(cb):
 | 
			
		||||
            return cb.checkState() == Qt.Checked
 | 
			
		||||
        def ischecked(cb: QCheckBox) -> bool:
 | 
			
		||||
            return cb.checkState() == Qt.CheckState.Checked
 | 
			
		||||
 | 
			
		||||
        prefs.mix_file_kind = ischecked(self.mixFileKindBox)
 | 
			
		||||
        prefs.use_regexp = ischecked(self.useRegexpBox)
 | 
			
		||||
@ -363,13 +371,13 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
        self.app.prefs.language = lang_code
 | 
			
		||||
        self._save(prefs, ischecked)
 | 
			
		||||
 | 
			
		||||
    def resetToDefaults(self, section_to_update):
 | 
			
		||||
    def resetToDefaults(self, section_to_update: Sections) -> None:
 | 
			
		||||
        self.load(Preferences(), section_to_update)
 | 
			
		||||
 | 
			
		||||
    # --- Events
 | 
			
		||||
    def buttonClicked(self, button):
 | 
			
		||||
    def buttonClicked(self, button: QPushButton) -> None:
 | 
			
		||||
        role = self.buttonBox.buttonRole(button)
 | 
			
		||||
        if role == QDialogButtonBox.ResetRole:
 | 
			
		||||
        if role == QDialogButtonBox.ButtonRole.ResetRole:
 | 
			
		||||
            current_tab = self.tabwidget.currentWidget()
 | 
			
		||||
            section_to_update = Sections.ALL
 | 
			
		||||
            if current_tab is self.page_general:
 | 
			
		||||
@ -380,30 +388,31 @@ use the modifier key to drag the floating window around"
 | 
			
		||||
                section_to_update = Sections.DEBUG
 | 
			
		||||
            self.resetToDefaults(section_to_update)
 | 
			
		||||
 | 
			
		||||
    def showEvent(self, event):
 | 
			
		||||
    def showEvent(self, event: QShowEvent) -> None:
 | 
			
		||||
        # have to do this here as the frameGeometry is not correct until shown
 | 
			
		||||
        move_to_screen_center(self)
 | 
			
		||||
        super().showEvent(event)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ColorPickerButton(QPushButton):
 | 
			
		||||
    def __init__(self, parent):
 | 
			
		||||
    def __init__(self, parent: QWidget) -> None:
 | 
			
		||||
        super().__init__(parent)
 | 
			
		||||
        self.parent = parent
 | 
			
		||||
        self.color = None
 | 
			
		||||
        self.clicked.connect(self.onClicked)
 | 
			
		||||
 | 
			
		||||
    @pyqtSlot()
 | 
			
		||||
    def onClicked(self):
 | 
			
		||||
        color = QColorDialog.getColor(self.color if self.color is not None else Qt.white, self.parent)
 | 
			
		||||
    def onClicked(self) -> None:
 | 
			
		||||
        color = QColorDialog.getColor(
 | 
			
		||||
            self.color if self.color is not None else Qt.GlobalColor.white, self.parentWidget()
 | 
			
		||||
        )
 | 
			
		||||
        self.setColor(color)
 | 
			
		||||
 | 
			
		||||
    def setColor(self, color):
 | 
			
		||||
    def setColor(self, color) -> None:
 | 
			
		||||
        size = QSize(16, 16)
 | 
			
		||||
        px = QPixmap(size)
 | 
			
		||||
        if color is None:
 | 
			
		||||
            size.width = 0
 | 
			
		||||
            size.height = 0
 | 
			
		||||
            size.setWidth(0)
 | 
			
		||||
            size.setHeight(0)
 | 
			
		||||
        elif not color.isValid():
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
 | 
			
		||||
@ -4,29 +4,23 @@
 | 
			
		||||
# which should be included with this package. The terms are also available at
 | 
			
		||||
# http://www.gnu.org/licenses/gpl-3.0.html
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import QSize
 | 
			
		||||
from PyQt5.QtWidgets import (
 | 
			
		||||
    QSpinBox,
 | 
			
		||||
    QVBoxLayout,
 | 
			
		||||
    QHBoxLayout,
 | 
			
		||||
    QLabel,
 | 
			
		||||
    QSizePolicy,
 | 
			
		||||
    QSpacerItem,
 | 
			
		||||
    QWidget,
 | 
			
		||||
)
 | 
			
		||||
from typing import Callable
 | 
			
		||||
from PyQt6.QtCore import QSize
 | 
			
		||||
from PyQt6.QtWidgets import QSpinBox, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget, QCheckBox
 | 
			
		||||
 | 
			
		||||
from hscommon.trans import trget
 | 
			
		||||
 | 
			
		||||
from core.app import AppMode
 | 
			
		||||
from core.scanner import ScanType
 | 
			
		||||
from qt.preferences import Preferences
 | 
			
		||||
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase
 | 
			
		||||
from qt.preferences_dialog import PreferencesDialogBase, Sections
 | 
			
		||||
 | 
			
		||||
tr = trget("ui")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
    def _setupPreferenceWidgets(self):
 | 
			
		||||
    def _setupPreferenceWidgets(self) -> None:
 | 
			
		||||
        self._setupFilterHardnessBox()
 | 
			
		||||
        self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
 | 
			
		||||
        self.widget = QWidget(self)
 | 
			
		||||
@ -50,7 +44,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self._setupAddCheckbox("ignoreSmallFilesBox", tr("Ignore files smaller than"), self.widget)
 | 
			
		||||
        self.horizontalLayout_2.addWidget(self.ignoreSmallFilesBox)
 | 
			
		||||
        self.sizeThresholdSpinBox = QSpinBox(self.widget)
 | 
			
		||||
        size_policy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
 | 
			
		||||
        size_policy = QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed)
 | 
			
		||||
        size_policy.setHorizontalStretch(0)
 | 
			
		||||
        size_policy.setVerticalStretch(0)
 | 
			
		||||
        size_policy.setHeightForWidth(self.sizeThresholdSpinBox.sizePolicy().hasHeightForWidth())
 | 
			
		||||
@ -61,14 +55,14 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.label_6 = QLabel(self.widget)
 | 
			
		||||
        self.label_6.setText(tr("KB"))
 | 
			
		||||
        self.horizontalLayout_2.addWidget(self.label_6)
 | 
			
		||||
        spacer_item1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
 | 
			
		||||
        spacer_item1 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
 | 
			
		||||
        self.horizontalLayout_2.addItem(spacer_item1)
 | 
			
		||||
        self.verticalLayout_4.addLayout(self.horizontalLayout_2)
 | 
			
		||||
        self.horizontalLayout_2a = QHBoxLayout()
 | 
			
		||||
        self._setupAddCheckbox("ignoreLargeFilesBox", tr("Ignore files larger than"), self.widget)
 | 
			
		||||
        self.horizontalLayout_2a.addWidget(self.ignoreLargeFilesBox)
 | 
			
		||||
        self.sizeSaturationSpinBox = QSpinBox(self.widget)
 | 
			
		||||
        size_policy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
 | 
			
		||||
        size_policy = QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed)
 | 
			
		||||
        self.sizeSaturationSpinBox.setSizePolicy(size_policy)
 | 
			
		||||
        self.sizeSaturationSpinBox.setMaximumSize(QSize(300, 16777215))
 | 
			
		||||
        self.sizeSaturationSpinBox.setRange(0, 1000000)
 | 
			
		||||
@ -76,7 +70,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.label_6a = QLabel(self.widget)
 | 
			
		||||
        self.label_6a.setText(tr("MB"))
 | 
			
		||||
        self.horizontalLayout_2a.addWidget(self.label_6a)
 | 
			
		||||
        spacer_item3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
 | 
			
		||||
        spacer_item3 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
 | 
			
		||||
        self.horizontalLayout_2a.addItem(spacer_item3)
 | 
			
		||||
        self.verticalLayout_4.addLayout(self.horizontalLayout_2a)
 | 
			
		||||
        self.horizontalLayout_2b = QHBoxLayout()
 | 
			
		||||
@ -94,7 +88,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.label_6b = QLabel(self.widget)
 | 
			
		||||
        self.label_6b.setText(tr("MB"))
 | 
			
		||||
        self.horizontalLayout_2b.addWidget(self.label_6b)
 | 
			
		||||
        spacer_item2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
 | 
			
		||||
        spacer_item2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
 | 
			
		||||
        self.horizontalLayout_2b.addItem(spacer_item2)
 | 
			
		||||
        self.verticalLayout_4.addLayout(self.horizontalLayout_2b)
 | 
			
		||||
        self._setupAddCheckbox(
 | 
			
		||||
@ -106,7 +100,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.widgetsVLayout.addWidget(self.widget)
 | 
			
		||||
        self._setupBottomPart()
 | 
			
		||||
 | 
			
		||||
    def _load(self, prefs, setchecked, section):
 | 
			
		||||
    def _load(self, prefs: Preferences, setchecked: Callable[[QCheckBox, bool], None], section: Sections) -> None:
 | 
			
		||||
        setchecked(self.matchSimilarBox, prefs.match_similar)
 | 
			
		||||
        setchecked(self.wordWeightingBox, prefs.word_weighting)
 | 
			
		||||
        setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files)
 | 
			
		||||
@ -123,7 +117,7 @@ class PreferencesDialog(PreferencesDialogBase):
 | 
			
		||||
        self.matchSimilarBox.setEnabled(word_based)
 | 
			
		||||
        self.wordWeightingBox.setEnabled(word_based)
 | 
			
		||||
 | 
			
		||||
    def _save(self, prefs, ischecked):
 | 
			
		||||
    def _save(self, prefs: Preferences, ischecked: Callable[[QCheckBox], bool]) -> None:
 | 
			
		||||
        prefs.match_similar = ischecked(self.matchSimilarBox)
 | 
			
		||||
        prefs.word_weighting = ischecked(self.wordWeightingBox)
 | 
			
		||||
        prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user