1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2024-10-29 21:05:57 +00:00

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 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}"):

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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)