More updates mainly in preferences

This commit is contained in:
Andrew Senetar 2022-07-08 23:15:59 -05:00
parent ec35c2df8f
commit 2f23f34b91
Signed by: arsenetar
GPG Key ID: C63300DCE48AB2F1
7 changed files with 148 additions and 143 deletions

106
qt/app.py
View File

@ -6,15 +6,18 @@
import sys import sys
import os.path as op import os.path as op
from typing import Type
from PyQt5.QtCore import QTimer, QObject, QUrl, pyqtSignal, Qt from PyQt6.QtCore import QTimer, QObject, QUrl, pyqtSignal, Qt
from PyQt5.QtGui import QColor, QDesktopServices, QPalette from PyQt6.QtGui import QColor, QDesktopServices, QPalette
from PyQt5.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox, QStyleFactory, QToolTip from PyQt6.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox, QStyleFactory, QToolTip
from hscommon.trans import trget from hscommon.trans import trget
from hscommon import desktop, plat from hscommon import desktop, plat
from qt.about_box import AboutBox 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.recent import Recent
from qt.util import create_actions from qt.util import create_actions
from qt.progress_window import ProgressWindow from qt.progress_window import ProgressWindow
@ -42,10 +45,10 @@ tr = trget("ui")
class DupeGuru(QObject): class DupeGuru(QObject):
LOGO_NAME = "logo_se" LOGO_NAME = "dgse_logo"
NAME = "dupeGuru" NAME = "dupeGuru"
def __init__(self, **kwargs): def __init__(self, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
self.prefs = Preferences() self.prefs = Preferences()
self.prefs.load() self.prefs.load()
@ -56,7 +59,7 @@ class DupeGuru(QObject):
self._setup() self._setup()
# --- Private # --- Private
def _setup(self): def _setup(self) -> None:
core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto
self._setupActions() self._setupActions()
self.details_dialog = None self.details_dialog = None
@ -108,7 +111,7 @@ class DupeGuru(QObject):
# that the application haven't launched. # that the application haven't launched.
QTimer.singleShot(0, self.finishedLaunching) 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. # Setup actions that are common to both the directory dialog and the results window.
# (name, shortcut, icon, desc, func) # (name, shortcut, icon, desc, func)
ACTIONS = [ ACTIONS = [
@ -154,7 +157,7 @@ class DupeGuru(QObject):
] ]
create_actions(ACTIONS, self) 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["mix_file_kind"] = self.prefs.mix_file_kind
self.model.options["escape_filter_regexp"] = not self.prefs.use_regexp self.model.options["escape_filter_regexp"] = not self.prefs.use_regexp
self.model.options["clean_empty_dirs"] = self.prefs.remove_empty_folders 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") self._set_style("dark" if self.prefs.use_dark_style else "light")
# --- Private # --- Private
def _get_details_dialog_class(self): def _get_details_dialog_class(self) -> Type[DetailsDialog]:
if self.model.app_mode == AppMode.PICTURE: if self.model.app_mode == AppMode.PICTURE:
return DetailsDialogPicture return DetailsDialogPicture
elif self.model.app_mode == AppMode.MUSIC: elif self.model.app_mode == AppMode.MUSIC:
@ -208,7 +211,7 @@ class DupeGuru(QObject):
else: else:
return DetailsDialogStandard return DetailsDialogStandard
def _get_preferences_dialog_class(self): def _get_preferences_dialog_class(self) -> Type[PreferencesDialogBase]:
if self.model.app_mode == AppMode.PICTURE: if self.model.app_mode == AppMode.PICTURE:
return PreferencesDialogPicture return PreferencesDialogPicture
elif self.model.app_mode == AppMode.MUSIC: elif self.model.app_mode == AppMode.MUSIC:
@ -216,7 +219,7 @@ class DupeGuru(QObject):
else: else:
return PreferencesDialogStandard return PreferencesDialogStandard
def _set_style(self, style="light"): def _set_style(self, style: str = "light") -> None:
# Only support this feature on windows for now # Only support this feature on windows for now
if not plat.ISWINDOWS: if not plat.ISWINDOWS:
return return
@ -224,18 +227,18 @@ class DupeGuru(QObject):
QApplication.setStyle(QStyleFactory.create("Fusion")) QApplication.setStyle(QStyleFactory.create("Fusion"))
palette = QApplication.style().standardPalette() palette = QApplication.style().standardPalette()
palette.setColor(QPalette.ColorRole.Window, QColor(53, 53, 53)) 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.Base, QColor(25, 25, 25))
palette.setColor(QPalette.ColorRole.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ColorRole.AlternateBase, QColor(53, 53, 53))
palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(53, 53, 53)) palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(53, 53, 53))
palette.setColor(QPalette.ColorRole.ToolTipText, Qt.white) palette.setColor(QPalette.ColorRole.ToolTipText, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.Text, Qt.white) palette.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ColorRole.Button, QColor(53, 53, 53))
palette.setColor(QPalette.ColorRole.ButtonText, Qt.white) palette.setColor(QPalette.ColorRole.ButtonText, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.BrightText, Qt.red) palette.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red)
palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218)) palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218))
palette.setColor(QPalette.ColorRole.Highlight, 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.Text, QColor(164, 166, 168))
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, 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)) palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(164, 166, 168))
@ -250,29 +253,31 @@ class DupeGuru(QObject):
QApplication.setPalette(palette) QApplication.setPalette(palette)
# --- Public # --- Public
def add_selected_to_ignore_list(self): def add_selected_to_ignore_list(self) -> None:
self.model.add_selected_to_ignore_list() self.model.add_selected_to_ignore_list()
def remove_selected(self): def remove_selected(self) -> None:
self.model.remove_selected(self) 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() active = QApplication.activeWindow()
buttons = QMessageBox.Yes | QMessageBox.No buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
answer = QMessageBox.question(active, title, msg, buttons, default_button) 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() self.model.invoke_custom_command()
def show_details(self): def show_details(self) -> None:
if self.details_dialog is not None: if self.details_dialog is not None:
if not self.details_dialog.isVisible(): if not self.details_dialog.isVisible():
self.details_dialog.show() self.details_dialog.show()
else: else:
self.details_dialog.hide() self.details_dialog.hide()
def showResultsWindow(self): def showResultsWindow(self) -> None:
if self.resultWindow is not None: if self.resultWindow is not None:
if self.use_tabs: if self.use_tabs:
if self.main_window.indexOfWidget(self.resultWindow) < 0: if self.main_window.indexOfWidget(self.resultWindow) < 0:
@ -282,14 +287,14 @@ class DupeGuru(QObject):
else: else:
self.resultWindow.show() self.resultWindow.show()
def showDirectoriesWindow(self): def showDirectoriesWindow(self) -> None:
if self.directories_dialog is not None: if self.directories_dialog is not None:
if self.use_tabs: if self.use_tabs:
self.main_window.showTab(self.directories_dialog) self.main_window.showTab(self.directories_dialog)
else: else:
self.directories_dialog.show() self.directories_dialog.show()
def shutdown(self): def shutdown(self) -> None:
self.willSavePrefs.emit() self.willSavePrefs.emit()
self.prefs.save() self.prefs.save()
self.model.save() self.model.save()
@ -304,7 +309,7 @@ class DupeGuru(QObject):
SIGTERM = pyqtSignal() SIGTERM = pyqtSignal()
# --- Events # --- Events
def finishedLaunching(self): def finishedLaunching(self) -> None:
if sys.getfilesystemencoding() == "ascii": if sys.getfilesystemencoding() == "ascii":
# No need to localize this, it's a debugging message. # No need to localize this, it's a debugging message.
msg = ( msg = (
@ -324,28 +329,28 @@ class DupeGuru(QObject):
self.model.load_from(results) self.model.load_from(results)
self.recentResults.insertItem(results) self.recentResults.insertItem(results)
def clearCacheTriggered(self): def clearCacheTriggered(self) -> None:
title = tr("Clear Cache") title = tr("Clear Cache")
msg = tr("Do you really want to clear the cache? This will remove all cached file hashes and picture analysis.") 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_picture_cache()
self.model.clear_hash_cache() self.model.clear_hash_cache()
active = QApplication.activeWindow() active = QApplication.activeWindow()
QMessageBox.information(active, title, tr("Cache cleared.")) QMessageBox.information(active, title, tr("Cache cleared."))
def ignoreListTriggered(self): def ignoreListTriggered(self) -> None:
if self.use_tabs: if self.use_tabs:
self.showTriggeredTabbedDialog(self.ignoreListDialog, tr("Ignore List")) self.showTriggeredTabbedDialog(self.ignoreListDialog, tr("Ignore List"))
else: # floating windows else: # floating windows
self.model.ignore_list_dialog.show() self.model.ignore_list_dialog.show()
def excludeListTriggered(self): def excludeListTriggered(self) -> None:
if self.use_tabs: if self.use_tabs:
self.showTriggeredTabbedDialog(self.excludeListDialog, tr("Exclusion Filters")) self.showTriggeredTabbedDialog(self.excludeListDialog, tr("Exclusion Filters"))
else: # floating windows else: # floating windows
self.model.exclude_list_dialog.show() 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.""" """Add tab for dialog, name the tab with desc_string, then show it."""
index = self.main_window.indexOfWidget(dialog) index = self.main_window.indexOfWidget(dialog)
# Create the tab if it doesn't exist already # Create the tab if it doesn't exist already
@ -354,23 +359,22 @@ class DupeGuru(QObject):
# Show the tab for that widget # Show the tab for that widget
self.main_window.setCurrentIndex(index) self.main_window.setCurrentIndex(index)
def openDebugLogTriggered(self): def openDebugLogTriggered(self) -> None:
debug_log_path = op.join(self.model.appdata, "debug.log") debug_log_path = op.join(self.model.appdata, "debug.log")
desktop.open_path(debug_log_path) desktop.open_path(debug_log_path)
def preferencesTriggered(self): def preferencesTriggered(self) -> None:
preferences_dialog = self._get_preferences_dialog_class()( preferences_dialog = self._get_preferences_dialog_class()(
self.main_window if self.main_window else self.directories_dialog, self self.main_window if self.main_window else self.directories_dialog, self
) )
preferences_dialog.load() preferences_dialog.load()
result = preferences_dialog.exec() result = preferences_dialog.exec()
if result == QDialog.Accepted: if result == QDialog.DialogCode.Accepted:
preferences_dialog.save() preferences_dialog.save()
self.prefs.save() self.prefs.save()
self._update_options() self._update_options()
preferences_dialog.setParent(None)
def quitTriggered(self): def quitTriggered(self) -> None:
if self.details_dialog is not None: if self.details_dialog is not None:
self.details_dialog.close() self.details_dialog.close()
@ -379,10 +383,10 @@ class DupeGuru(QObject):
else: else:
self.directories_dialog.close() self.directories_dialog.close()
def showAboutBoxTriggered(self): def showAboutBoxTriggered(self) -> None:
self.about_box.show() self.about_box.show()
def showHelpTriggered(self): def showHelpTriggered(self) -> None:
base_path = platform.HELP_PATH base_path = platform.HELP_PATH
help_path = op.abspath(op.join(base_path, "index.html")) help_path = op.abspath(op.join(base_path, "index.html"))
if op.exists(help_path): if op.exists(help_path):
@ -391,7 +395,7 @@ class DupeGuru(QObject):
url = QUrl("https://dupeguru.voltaicideas.net/help/en/") url = QUrl("https://dupeguru.voltaicideas.net/help/en/")
QDesktopServices.openUrl(url) QDesktopServices.openUrl(url)
def handleSIGTERM(self): def handleSIGTERM(self) -> None:
self.shutdown() self.shutdown()
# --- model --> view # --- model --> view
@ -401,20 +405,20 @@ class DupeGuru(QObject):
def set_default(self, key, value): def set_default(self, key, value):
self.prefs.set_value(key, value) self.prefs.set_value(key, value)
def show_message(self, msg): def show_message(self, msg: str) -> None:
window = QApplication.activeWindow() window = QApplication.activeWindow()
QMessageBox.information(window, "", msg) QMessageBox.information(window, "", msg)
def ask_yes_no(self, prompt): def ask_yes_no(self, prompt: str) -> bool:
return self.confirm("", prompt) 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``.""" """Creates resultWindow and details_dialog depending on the selected ``app_mode``."""
if self.details_dialog is not None: if self.details_dialog is not None:
# The object is not deleted entirely, avoid saving its geometry in the future # The object is not deleted entirely, avoid saving its geometry in the future
# self.willSavePrefs.disconnect(self.details_dialog.appWillSavePrefs) # self.willSavePrefs.disconnect(self.details_dialog.appWillSavePrefs)
# or simply delete it on close which is probably cleaner: # 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() self.details_dialog.close()
# if we don't do the following, Qt will crash when we recreate the Results dialog # if we don't do the following, Qt will crash when we recreate the Results dialog
self.details_dialog.setParent(None) self.details_dialog.setParent(None)
@ -429,17 +433,17 @@ class DupeGuru(QObject):
self.directories_dialog._updateActionsState() self.directories_dialog._updateActionsState()
self.details_dialog = self._get_details_dialog_class()(self.resultWindow, self) self.details_dialog = self._get_details_dialog_class()(self.resultWindow, self)
def show_results_window(self): def show_results_window(self) -> None:
self.showResultsWindow() self.showResultsWindow()
def show_problem_dialog(self): def show_problem_dialog(self) -> None:
self.problemDialog.show() self.problemDialog.show()
def select_dest_folder(self, prompt): def select_dest_folder(self, prompt: str) -> str:
flags = QFileDialog.ShowDirsOnly flags = QFileDialog.Option.ShowDirsOnly
return QFileDialog.getExistingDirectory(self.resultWindow, prompt, "", flags) 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) files = tr("{} file (*.{})").format(extension.upper(), extension)
destination, chosen_filter = QFileDialog.getSaveFileName(self.resultWindow, prompt, "", files) destination, chosen_filter = QFileDialog.getSaveFileName(self.resultWindow, prompt, "", files)
if not destination.endswith(f".{extension}"): if not destination.endswith(f".{extension}"):

View File

@ -4,8 +4,8 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtCore import QSize from PyQt6.QtCore import QSize
from PyQt5.QtWidgets import QAbstractItemView from PyQt6.QtWidgets import QAbstractItemView
from hscommon.trans import trget from hscommon.trans import trget
from qt.details_dialog import DetailsDialog as DetailsDialogBase from qt.details_dialog import DetailsDialog as DetailsDialogBase
@ -15,12 +15,12 @@ tr = trget("ui")
class DetailsDialog(DetailsDialogBase): class DetailsDialog(DetailsDialogBase):
def _setupUi(self): def _setupUi(self) -> None:
self.setWindowTitle(tr("Details")) self.setWindowTitle(tr("Details"))
self.resize(502, 295) self.resize(502, 295)
self.setMinimumSize(QSize(250, 250)) self.setMinimumSize(QSize(250, 250))
self.tableView = DetailsTable(self) self.tableView = DetailsTable(self)
self.tableView.setAlternatingRowColors(True) self.tableView.setAlternatingRowColors(True)
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableView.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.tableView.setShowGrid(False) self.tableView.setShowGrid(False)
self.setWidget(self.tableView) self.setWidget(self.tableView)

View File

@ -4,27 +4,22 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtCore import QSize from typing import Callable
from PyQt5.QtWidgets import ( from PyQt6.QtCore import QSize
QVBoxLayout, from PyQt6.QtWidgets import QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget, QCheckBox
QHBoxLayout,
QLabel,
QSizePolicy,
QSpacerItem,
QWidget,
)
from hscommon.trans import trget from hscommon.trans import trget
from core.app import AppMode from core.app import AppMode
from core.scanner import ScanType 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") tr = trget("ui")
class PreferencesDialog(PreferencesDialogBase): class PreferencesDialog(PreferencesDialogBase):
def _setupPreferenceWidgets(self): def _setupPreferenceWidgets(self) -> None:
self._setupFilterHardnessBox() self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout) self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self.widget = QWidget(self) self.widget = QWidget(self)
@ -37,7 +32,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.verticalLayout_4.addWidget(self.label_6) self.verticalLayout_4.addWidget(self.label_6)
self.horizontalLayout_2 = QHBoxLayout() self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setSpacing(0) 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.horizontalLayout_2.addItem(spacer_item)
self._setupAddCheckbox("tagTrackBox", tr("Track"), self.widget) self._setupAddCheckbox("tagTrackBox", tr("Track"), self.widget)
self.horizontalLayout_2.addWidget(self.tagTrackBox) self.horizontalLayout_2.addWidget(self.tagTrackBox)
@ -70,7 +65,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches) self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
self._setupBottomPart() 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.tagTrackBox, prefs.scan_tag_track)
setchecked(self.tagArtistBox, prefs.scan_tag_artist) setchecked(self.tagArtistBox, prefs.scan_tag_artist)
setchecked(self.tagAlbumBox, prefs.scan_tag_album) setchecked(self.tagAlbumBox, prefs.scan_tag_album)
@ -99,7 +94,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.tagGenreBox.setEnabled(tag_based) self.tagGenreBox.setEnabled(tag_based)
self.tagYearBox.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_track = ischecked(self.tagTrackBox)
prefs.scan_tag_artist = ischecked(self.tagArtistBox) prefs.scan_tag_artist = ischecked(self.tagArtistBox)
prefs.scan_tag_album = ischecked(self.tagAlbumBox) prefs.scan_tag_album = ischecked(self.tagAlbumBox)

View File

@ -4,21 +4,23 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtWidgets import QFormLayout from typing import Callable
from PyQt5.QtCore import Qt from PyQt6.QtWidgets import QFormLayout, QCheckBox
from PyQt6.QtCore import Qt
from hscommon.trans import trget from hscommon.trans import trget
from hscommon.plat import ISLINUX from hscommon.plat import ISLINUX
from qt.preferences import Preferences
from qt.radio_box import RadioBox from qt.radio_box import RadioBox
from core.scanner import ScanType from core.scanner import ScanType
from core.app import AppMode from core.app import AppMode
from qt.preferences_dialog import PreferencesDialogBase from qt.preferences_dialog import PreferencesDialogBase, Sections
tr = trget("ui") tr = trget("ui")
class PreferencesDialog(PreferencesDialogBase): class PreferencesDialog(PreferencesDialogBase):
def _setupPreferenceWidgets(self): def _setupPreferenceWidgets(self) -> None:
self._setupFilterHardnessBox() self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout) self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self._setupAddCheckbox("matchScaledBox", tr("Match pictures of different dimensions")) 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) self.cacheTypeRadio = RadioBox(self, items=["Sqlite", "Shelve"], spread=False)
cache_form = QFormLayout() cache_form = QFormLayout()
cache_form.setLabelAlignment(Qt.AlignLeft) cache_form.setLabelAlignment(Qt.AlignmentFlag.AlignLeft)
cache_form.addRow(tr("Picture cache mode:"), self.cacheTypeRadio) cache_form.addRow(tr("Picture cache mode:"), self.cacheTypeRadio)
self.widgetsVLayout.addLayout(cache_form) self.widgetsVLayout.addLayout(cache_form)
self._setupBottomPart() self._setupBottomPart()
def _setupDisplayPage(self): def _setupDisplayPage(self) -> None:
super()._setupDisplayPage() super()._setupDisplayPage()
self._setupAddCheckbox("details_dialog_override_theme_icons", tr("Override theme icons in viewer toolbar")) self._setupAddCheckbox("details_dialog_override_theme_icons", tr("Override theme icons in viewer toolbar"))
self.details_dialog_override_theme_icons.setToolTip( 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) 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) setchecked(self.matchScaledBox, prefs.match_scaled)
self.cacheTypeRadio.selected_index = 1 if prefs.picture_cache_type == "shelve" else 0 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_override_theme_icons, prefs.details_dialog_override_theme_icons)
setchecked(self.details_dialog_viewers_show_scrollbars, prefs.details_dialog_viewers_show_scrollbars) 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.match_scaled = ischecked(self.matchScaledBox)
prefs.picture_cache_type = "shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite" 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) prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons)

View File

@ -4,9 +4,10 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtWidgets import QApplication, QDockWidget from typing import Any, Tuple
from PyQt5.QtCore import Qt, QRect, QObject, pyqtSignal from PyQt6.QtWidgets import QApplication, QDockWidget
from PyQt5.QtGui import QColor from PyQt6.QtCore import Qt, QRect, QObject, pyqtSignal
from PyQt6.QtGui import QColor
from hscommon import trans from hscommon import trans
from hscommon.plat import ISLINUX from hscommon.plat import ISLINUX
@ -126,7 +127,7 @@ class PreferencesBase(QObject):
def set_value(self, name, value): def set_value(self, name, value):
self._settings.setValue(name, _normalize_for_serialization(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 # 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 # 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, # 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()] rect_as_list = [r.x(), r.y(), r.width(), r.height()]
self.set_value(name, [m, d, area] + rect_as_list) 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) geometry = self.get_value(name)
if geometry and len(geometry) == 7: if geometry and len(geometry) == 7:
m, d, area, x, y, w, h = geometry m, d, area, x, y, w, h = geometry
if m: if m:
widget.setWindowState(Qt.WindowMaximized) widget.setWindowState(Qt.WindowState.WindowMaximized)
else: else:
r = QRect(x, y, w, h) r = QRect(x, y, w, h)
widget.setGeometry(r) widget.setGeometry(r)
@ -154,7 +155,7 @@ class PreferencesBase(QObject):
class Preferences(PreferencesBase): class Preferences(PreferencesBase):
def _load_values(self, settings): def _load_values(self, settings) -> None:
get = self.get_value get = self.get_value
self.filter_hardness = get("FilterHardness", self.filter_hardness) self.filter_hardness = get("FilterHardness", self.filter_hardness)
self.mix_file_kind = get("MixFileKind", self.mix_file_kind) 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.match_scaled = get("MatchScaled", self.match_scaled)
self.picture_cache_type = get("PictureCacheType", self.picture_cache_type) self.picture_cache_type = get("PictureCacheType", self.picture_cache_type)
def reset(self): def reset(self) -> None:
self.filter_hardness = 95 self.filter_hardness = 95
self.mix_file_kind = True self.mix_file_kind = True
self.use_regexp = False self.use_regexp = False
@ -247,8 +248,8 @@ class Preferences(PreferencesBase):
# By default use internal icons on platforms other than Linux for now # 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_override_theme_icons = False if not ISLINUX else True
self.details_dialog_viewers_show_scrollbars = True self.details_dialog_viewers_show_scrollbars = True
self.result_table_ref_foreground_color = QColor(Qt.blue) self.result_table_ref_foreground_color = QColor(Qt.GlobalColor.blue)
self.result_table_ref_background_color = QColor(Qt.lightGray) self.result_table_ref_background_color = QColor(Qt.GlobalColor.lightGray)
self.result_table_delta_foreground_color = QColor(255, 142, 40) # orange self.result_table_delta_foreground_color = QColor(255, 142, 40) # orange
self.resultWindowIsMaximized = False self.resultWindowIsMaximized = False
self.resultWindowRect = None self.resultWindowRect = None
@ -276,7 +277,7 @@ class Preferences(PreferencesBase):
self.match_scaled = False self.match_scaled = False
self.picture_cache_type = "sqlite" self.picture_cache_type = "sqlite"
def _save_values(self, settings): def _save_values(self, settings) -> None:
set_ = self.set_value set_ = self.set_value
set_("FilterHardness", self.filter_hardness) set_("FilterHardness", self.filter_hardness)
set_("MixFileKind", self.mix_file_kind) set_("MixFileKind", self.mix_file_kind)

View File

@ -4,8 +4,9 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtCore import Qt, QSize, pyqtSlot from typing import Union
from PyQt5.QtWidgets import ( from PyQt6.QtCore import Qt, QSize, pyqtSlot
from PyQt6.QtWidgets import (
QDialog, QDialog,
QDialogButtonBox, QDialogButtonBox,
QVBoxLayout, QVBoxLayout,
@ -28,7 +29,7 @@ from PyQt5.QtWidgets import (
QGroupBox, QGroupBox,
QFormLayout, QFormLayout,
) )
from PyQt5.QtGui import QPixmap, QIcon from PyQt6.QtGui import QPixmap, QIcon, QShowEvent
from hscommon import desktop, plat from hscommon import desktop, plat
from hscommon.trans import trget from hscommon.trans import trget
@ -39,6 +40,11 @@ from enum import Flag, auto
from qt.preferences import Preferences from qt.preferences import Preferences
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from qt.app import DupeGuru
tr = trget("ui") tr = trget("ui")
@ -52,8 +58,8 @@ class Sections(Flag):
class PreferencesDialogBase(QDialog): class PreferencesDialogBase(QDialog):
def __init__(self, parent, app, **kwargs): def __init__(self, parent: QWidget, app: "DupeGuru", **kwargs):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint flags = Qt.WindowType.CustomizeWindowHint | Qt.WindowType.WindowTitleHint | Qt.WindowType.WindowSystemMenuHint
super().__init__(parent, flags, **kwargs) super().__init__(parent, flags, **kwargs)
self.app = app self.app = app
self.supportedLanguages = dict(sorted(get_langnames().items(), key=lambda item: item[1])) 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.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject) self.buttonBox.rejected.connect(self.reject)
def _setupFilterHardnessBox(self): def _setupFilterHardnessBox(self) -> None:
self.filterHardnessHLayout = QHBoxLayout() self.filterHardnessHLayout = QHBoxLayout()
self.filterHardnessLabel = QLabel(self) self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText(tr("Filter Hardness:")) self.filterHardnessLabel.setText(tr("Filter Hardness:"))
@ -76,7 +82,7 @@ class PreferencesDialogBase(QDialog):
self.filterHardnessHLayoutSub1 = QHBoxLayout() self.filterHardnessHLayoutSub1 = QHBoxLayout()
self.filterHardnessHLayoutSub1.setSpacing(12) self.filterHardnessHLayoutSub1.setSpacing(12)
self.filterHardnessSlider = QSlider(self) 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.setHorizontalStretch(0)
size_policy.setVerticalStretch(0) size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) size_policy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth())
@ -84,7 +90,7 @@ class PreferencesDialogBase(QDialog):
self.filterHardnessSlider.setMinimum(1) self.filterHardnessSlider.setMinimum(1)
self.filterHardnessSlider.setMaximum(100) self.filterHardnessSlider.setMaximum(100)
self.filterHardnessSlider.setTracking(True) self.filterHardnessSlider.setTracking(True)
self.filterHardnessSlider.setOrientation(Qt.Horizontal) self.filterHardnessSlider.setOrientation(Qt.Orientation.Horizontal)
self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider) self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider)
self.filterHardnessLabel = QLabel(self) self.filterHardnessLabel = QLabel(self)
self.filterHardnessLabel.setText("100") self.filterHardnessLabel.setText("100")
@ -96,7 +102,7 @@ class PreferencesDialogBase(QDialog):
self.moreResultsLabel = QLabel(self) self.moreResultsLabel = QLabel(self)
self.moreResultsLabel.setText(tr("More Results")) self.moreResultsLabel.setText(tr("More Results"))
self.filterHardnessHLayoutSub2.addWidget(self.moreResultsLabel) 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.filterHardnessHLayoutSub2.addItem(spacer_item)
self.fewerResultsLabel = QLabel(self) self.fewerResultsLabel = QLabel(self)
self.fewerResultsLabel.setText(tr("Fewer Results")) self.fewerResultsLabel.setText(tr("Fewer Results"))
@ -104,7 +110,7 @@ class PreferencesDialogBase(QDialog):
self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2) self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2)
self.filterHardnessHLayout.addLayout(self.filterHardnessVLayout) 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. # The bottom part of the pref panel is always the same in all editions.
self.copyMoveLabel = QLabel(self) self.copyMoveLabel = QLabel(self)
self.copyMoveLabel.setText(tr("Copy and Move:")) self.copyMoveLabel.setText(tr("Copy and Move:"))
@ -120,7 +126,7 @@ class PreferencesDialogBase(QDialog):
self.customCommandEdit = QLineEdit(self) self.customCommandEdit = QLineEdit(self)
self.widgetsVLayout.addWidget(self.customCommandEdit) self.widgetsVLayout.addWidget(self.customCommandEdit)
def _setupDisplayPage(self): def _setupDisplayPage(self) -> None:
self.ui_groupbox = QGroupBox("&" + tr("General Interface")) self.ui_groupbox = QGroupBox("&" + tr("General Interface"))
layout = QVBoxLayout() layout = QVBoxLayout()
self.languageLabel = QLabel(tr("Language:"), self) 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) formlayout.addRow(tr("Reference background color:"), self.result_table_ref_background_color)
self.result_table_delta_foreground_color = ColorPickerButton(self) self.result_table_delta_foreground_color = ColorPickerButton(self)
formlayout.addRow(tr("Delta foreground color:"), self.result_table_delta_foreground_color) 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 # Keep same vertical spacing as parent layout for consistency
formlayout.setVerticalSpacing(self.displayVLayout.spacing()) 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) details_groupbox.setLayout(self.details_groupbox_layout)
self.displayVLayout.addWidget(details_groupbox) self.displayVLayout.addWidget(details_groupbox)
def _setupDebugPage(self): def _setupDebugPage(self) -> None:
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)")) self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
self._setupAddCheckbox("profile_scan_box", tr("Profile scan operation")) self._setupAddCheckbox("profile_scan_box", tr("Profile scan operation"))
self.profile_scan_box.setToolTip(tr("Profile the scan operation and save logs for optimization.")) 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) 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: if parent is None:
parent = self parent = self
cb = QCheckBox(parent) cb = QCheckBox(parent)
@ -236,7 +242,7 @@ use the modifier key to drag the floating window around"
# Edition-specific # Edition-specific
pass pass
def _setupUi(self): def _setupUi(self) -> None:
self.setWindowTitle(tr("Options")) self.setWindowTitle(tr("Options"))
self.setSizeGripEnabled(False) self.setSizeGripEnabled(False)
self.setModal(True) self.setModal(True)
@ -258,11 +264,13 @@ use the modifier key to drag the floating window around"
# self.mainVLayout.addLayout(self.widgetsVLayout) # self.mainVLayout.addLayout(self.widgetsVLayout)
self.buttonBox = QDialogButtonBox(self) self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setStandardButtons( 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.tabwidget)
self.mainVLayout.addWidget(self.buttonBox) 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_general, tr("General"))
self.tabwidget.addTab(self.page_display, tr("Display")) self.tabwidget.addTab(self.page_display, tr("Display"))
self.tabwidget.addTab(self.page_debug, tr("Debug")) 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.widgetsVLayout.addStretch(0)
self.debugVLayout.addStretch(0) self.debugVLayout.addStretch(0)
def _load(self, prefs, setchecked, section): def _load(self, prefs, setchecked, section) -> None:
# Edition-specific # Edition-specific
pass pass
def _save(self, prefs, ischecked): def _save(self, prefs, ischecked) -> None:
# Edition-specific # Edition-specific
pass pass
def load(self, prefs=None, section=Sections.ALL): def load(self, prefs: Preferences = None, section: Sections = Sections.ALL) -> None:
if prefs is None: if prefs is None:
prefs = self.app.prefs prefs = self.app.prefs
def setchecked(cb, b): def setchecked(cb: QCheckBox, b: bool) -> None:
cb.setCheckState(Qt.Checked if b else Qt.Unchecked) cb.setCheckState(Qt.CheckState.Checked if b else Qt.CheckState.Unchecked)
if section & Sections.GENERAL: if section & Sections.GENERAL:
self.filterHardnessSlider.setValue(prefs.filter_hardness) 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) setchecked(self.profile_scan_box, prefs.profile_scan)
self._load(prefs, setchecked, section) self._load(prefs, setchecked, section)
def save(self): def save(self) -> None:
prefs = self.app.prefs prefs = self.app.prefs
prefs.filter_hardness = self.filterHardnessSlider.value() prefs.filter_hardness = self.filterHardnessSlider.value()
def ischecked(cb): def ischecked(cb: QCheckBox) -> bool:
return cb.checkState() == Qt.Checked return cb.checkState() == Qt.CheckState.Checked
prefs.mix_file_kind = ischecked(self.mixFileKindBox) prefs.mix_file_kind = ischecked(self.mixFileKindBox)
prefs.use_regexp = ischecked(self.useRegexpBox) 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.app.prefs.language = lang_code
self._save(prefs, ischecked) self._save(prefs, ischecked)
def resetToDefaults(self, section_to_update): def resetToDefaults(self, section_to_update: Sections) -> None:
self.load(Preferences(), section_to_update) self.load(Preferences(), section_to_update)
# --- Events # --- Events
def buttonClicked(self, button): def buttonClicked(self, button: QPushButton) -> None:
role = self.buttonBox.buttonRole(button) role = self.buttonBox.buttonRole(button)
if role == QDialogButtonBox.ResetRole: if role == QDialogButtonBox.ButtonRole.ResetRole:
current_tab = self.tabwidget.currentWidget() current_tab = self.tabwidget.currentWidget()
section_to_update = Sections.ALL section_to_update = Sections.ALL
if current_tab is self.page_general: 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 section_to_update = Sections.DEBUG
self.resetToDefaults(section_to_update) 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 # have to do this here as the frameGeometry is not correct until shown
move_to_screen_center(self) move_to_screen_center(self)
super().showEvent(event) super().showEvent(event)
class ColorPickerButton(QPushButton): class ColorPickerButton(QPushButton):
def __init__(self, parent): def __init__(self, parent: QWidget) -> None:
super().__init__(parent) super().__init__(parent)
self.parent = parent
self.color = None self.color = None
self.clicked.connect(self.onClicked) self.clicked.connect(self.onClicked)
@pyqtSlot() @pyqtSlot()
def onClicked(self): def onClicked(self) -> None:
color = QColorDialog.getColor(self.color if self.color is not None else Qt.white, self.parent) color = QColorDialog.getColor(
self.color if self.color is not None else Qt.GlobalColor.white, self.parentWidget()
)
self.setColor(color) self.setColor(color)
def setColor(self, color): def setColor(self, color) -> None:
size = QSize(16, 16) size = QSize(16, 16)
px = QPixmap(size) px = QPixmap(size)
if color is None: if color is None:
size.width = 0 size.setWidth(0)
size.height = 0 size.setHeight(0)
elif not color.isValid(): elif not color.isValid():
return return
else: else:

View File

@ -4,29 +4,23 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtCore import QSize from typing import Callable
from PyQt5.QtWidgets import ( from PyQt6.QtCore import QSize
QSpinBox, from PyQt6.QtWidgets import QSpinBox, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget, QCheckBox
QVBoxLayout,
QHBoxLayout,
QLabel,
QSizePolicy,
QSpacerItem,
QWidget,
)
from hscommon.trans import trget from hscommon.trans import trget
from core.app import AppMode from core.app import AppMode
from core.scanner import ScanType 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") tr = trget("ui")
class PreferencesDialog(PreferencesDialogBase): class PreferencesDialog(PreferencesDialogBase):
def _setupPreferenceWidgets(self): def _setupPreferenceWidgets(self) -> None:
self._setupFilterHardnessBox() self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout) self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self.widget = QWidget(self) self.widget = QWidget(self)
@ -50,7 +44,7 @@ class PreferencesDialog(PreferencesDialogBase):
self._setupAddCheckbox("ignoreSmallFilesBox", tr("Ignore files smaller than"), self.widget) self._setupAddCheckbox("ignoreSmallFilesBox", tr("Ignore files smaller than"), self.widget)
self.horizontalLayout_2.addWidget(self.ignoreSmallFilesBox) self.horizontalLayout_2.addWidget(self.ignoreSmallFilesBox)
self.sizeThresholdSpinBox = QSpinBox(self.widget) 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.setHorizontalStretch(0)
size_policy.setVerticalStretch(0) size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.sizeThresholdSpinBox.sizePolicy().hasHeightForWidth()) size_policy.setHeightForWidth(self.sizeThresholdSpinBox.sizePolicy().hasHeightForWidth())
@ -61,14 +55,14 @@ class PreferencesDialog(PreferencesDialogBase):
self.label_6 = QLabel(self.widget) self.label_6 = QLabel(self.widget)
self.label_6.setText(tr("KB")) self.label_6.setText(tr("KB"))
self.horizontalLayout_2.addWidget(self.label_6) 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.horizontalLayout_2.addItem(spacer_item1)
self.verticalLayout_4.addLayout(self.horizontalLayout_2) self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.horizontalLayout_2a = QHBoxLayout() self.horizontalLayout_2a = QHBoxLayout()
self._setupAddCheckbox("ignoreLargeFilesBox", tr("Ignore files larger than"), self.widget) self._setupAddCheckbox("ignoreLargeFilesBox", tr("Ignore files larger than"), self.widget)
self.horizontalLayout_2a.addWidget(self.ignoreLargeFilesBox) self.horizontalLayout_2a.addWidget(self.ignoreLargeFilesBox)
self.sizeSaturationSpinBox = QSpinBox(self.widget) 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.setSizePolicy(size_policy)
self.sizeSaturationSpinBox.setMaximumSize(QSize(300, 16777215)) self.sizeSaturationSpinBox.setMaximumSize(QSize(300, 16777215))
self.sizeSaturationSpinBox.setRange(0, 1000000) self.sizeSaturationSpinBox.setRange(0, 1000000)
@ -76,7 +70,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.label_6a = QLabel(self.widget) self.label_6a = QLabel(self.widget)
self.label_6a.setText(tr("MB")) self.label_6a.setText(tr("MB"))
self.horizontalLayout_2a.addWidget(self.label_6a) 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.horizontalLayout_2a.addItem(spacer_item3)
self.verticalLayout_4.addLayout(self.horizontalLayout_2a) self.verticalLayout_4.addLayout(self.horizontalLayout_2a)
self.horizontalLayout_2b = QHBoxLayout() self.horizontalLayout_2b = QHBoxLayout()
@ -94,7 +88,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.label_6b = QLabel(self.widget) self.label_6b = QLabel(self.widget)
self.label_6b.setText(tr("MB")) self.label_6b.setText(tr("MB"))
self.horizontalLayout_2b.addWidget(self.label_6b) 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.horizontalLayout_2b.addItem(spacer_item2)
self.verticalLayout_4.addLayout(self.horizontalLayout_2b) self.verticalLayout_4.addLayout(self.horizontalLayout_2b)
self._setupAddCheckbox( self._setupAddCheckbox(
@ -106,7 +100,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.widgetsVLayout.addWidget(self.widget) self.widgetsVLayout.addWidget(self.widget)
self._setupBottomPart() 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.matchSimilarBox, prefs.match_similar)
setchecked(self.wordWeightingBox, prefs.word_weighting) setchecked(self.wordWeightingBox, prefs.word_weighting)
setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files) setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files)
@ -123,7 +117,7 @@ class PreferencesDialog(PreferencesDialogBase):
self.matchSimilarBox.setEnabled(word_based) self.matchSimilarBox.setEnabled(word_based)
self.wordWeightingBox.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.match_similar = ischecked(self.matchSimilarBox)
prefs.word_weighting = ischecked(self.wordWeightingBox) prefs.word_weighting = ischecked(self.wordWeightingBox)
prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox) prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox)