Several fixes

This commit is contained in:
Ignacio Serantes
2026-04-06 20:44:49 +02:00
parent ca260d4219
commit a717acef87
8 changed files with 151 additions and 98 deletions

View File

@@ -34,9 +34,9 @@ from itertools import groupby
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit,
QTextEdit, QPushButton, QFileDialog, QComboBox, QSlider, QMessageBox, QSizePolicy,
QMenu, QInputDialog, QTableWidget, QTableWidgetItem, QHeaderView, QTabWidget, QProgressDialog,
QDockWidget, QAbstractItemView, QRadioButton, QButtonGroup, QListView,
QStyledItemDelegate, QStyle, QDialog, QKeySequenceEdit, QDialogButtonBox
QMenu, QInputDialog, QTableWidget, QTableWidgetItem, QHeaderView, QTabWidget,
QProgressDialog, QDockWidget, QAbstractItemView, QRadioButton, QButtonGroup,
QListView, QStyledItemDelegate, QStyle, QDialog, QKeySequenceEdit, QDialogButtonBox
)
from PySide6.QtGui import (
QDesktopServices, QFont, QFontMetrics, QIcon, QTransform, QImageReader, QPalette,
@@ -1290,12 +1290,6 @@ class MainWindow(QMainWindow):
self.history_tab = HistoryWidget(self)
self.tags_tabs.addTab(self.history_tab, UITexts.HISTORY_TAB)
# Initialize the shortcut controller
self.shortcut_controller = AppShortcutController(self)
self.favorites_tab.favorites_changed.connect(
self.shortcut_controller.refresh_favorite_shortcuts)
self.main_dock.setWidget(self.tags_tabs)
self.addDockWidget(Qt.RightDockWidgetArea, self.main_dock)
@@ -1758,32 +1752,36 @@ class MainWindow(QMainWindow):
menu.addSeparator()
duplicates_menu = menu.addMenu(QIcon.fromTheme("edit-find-replace"), UITexts.MENU_DUPLICATES)
duplicates_menu = menu.addMenu(
QIcon.fromTheme("edit-find-replace"), UITexts.MENU_DUPLICATES)
duplicates_menu.setEnabled(HAVE_IMAGEHASH)
detect_current_action = duplicates_menu.addAction(UITexts.MENU_DETECT_CURRENT_SEARCH)
detect_current_action = duplicates_menu.addAction(
UITexts.MENU_DETECT_CURRENT_SEARCH)
detect_current_action.triggered.connect(self.start_duplicate_detection)
detect_all_action = duplicates_menu.addAction(UITexts.MENU_DETECT_ALL)
detect_all_action.triggered.connect(self.detect_all_duplicates)
force_full_action = duplicates_menu.addAction(UITexts.MENU_FORCE_FULL_ANALYSIS)
force_full_action.triggered.connect(lambda: self.start_duplicate_detection(force_full=True))
force_full_action.triggered.connect(
lambda: self.start_duplicate_detection(force_full=True))
review_ignored_action = duplicates_menu.addAction(UITexts.MENU_REVIEW_IGNORED)
review_ignored_action.triggered.connect(self.review_ignored_duplicates)
duplicates_menu.addSeparator()
clean_hashes_action = duplicates_menu.addAction(QIcon.fromTheme("edit-clear-all"),
UITexts.MENU_CLEAN_UP_HASHES)
clean_hashes_action = duplicates_menu.addAction(
QIcon.fromTheme("edit-clear-all"), UITexts.MENU_CLEAN_UP_HASHES)
clean_hashes_action.triggered.connect(self.clean_duplicate_hashes)
if self.duplicate_cache:
count, size_bytes = self.duplicate_cache.get_hash_stats()
size_mb = size_bytes / (1024 * 1024)
clear_hashes_action = duplicates_menu.addAction(QIcon.fromTheme("user-trash-full"),
UITexts.MENU_CLEAR_HASHES.format(count, size_mb))
clear_hashes_action = duplicates_menu.addAction(
QIcon.fromTheme("user-trash-full"),
UITexts.MENU_CLEAR_HASHES.format(count, size_mb))
clear_hashes_action.triggered.connect(self.clear_duplicate_hashes)
menu.addSeparator()
@@ -1831,22 +1829,28 @@ class MainWindow(QMainWindow):
QApplication.restoreOverrideCursor()
if paths is None:
QMessageBox.warning(self, UITexts.WARNING, "Whitelist is empty. Please configure it in Settings.")
QMessageBox.warning(
self, UITexts.WARNING,
"Whitelist is empty. Please configure it in Settings.")
return
if not paths:
QMessageBox.information(self, UITexts.DUPLICATE_DETECTION_TITLE, UITexts.DUPLICATE_NONE_FOUND)
QMessageBox.information(
self, UITexts.DUPLICATE_DETECTION_TITLE, UITexts.DUPLICATE_NONE_FOUND)
return
self.start_duplicate_detection(custom_paths=paths)
# Por defecto usamos el modo optimizado (incremental) para no repetir comparaciones
self.start_duplicate_detection(force_full=False, custom_paths=paths)
def _gather_files_for_duplicates(self):
"""Helper to collect image paths based on whitelist and blacklist settings."""
whitelist_str = APP_CONFIG.get("duplicate_whitelist", "")
blacklist_str = APP_CONFIG.get("duplicate_blacklist", "")
whitelist = [os.path.abspath(os.path.expanduser(p.strip())) for p in whitelist_str.split(',') if p.strip()]
blacklist = [os.path.abspath(os.path.expanduser(p.strip())) for p in blacklist_str.split(',') if p.strip()]
whitelist = [os.path.abspath(os.path.expanduser(p.strip()))
for p in whitelist_str.split(',') if p.strip()]
blacklist = [os.path.abspath(os.path.expanduser(p.strip()))
for p in blacklist_str.split(',') if p.strip()]
if not whitelist:
return None
@@ -1861,7 +1865,8 @@ class MainWindow(QMainWindow):
for root, dirs, files in os.walk(root_path):
abs_root = os.path.abspath(root)
# Prune dirs to stop walking into blacklisted paths
dirs[:] = [d for d in dirs if os.path.join(abs_root, d) not in blacklist_set]
dirs[:] = [d for d in dirs
if os.path.join(abs_root, d) not in blacklist_set]
if abs_root in blacklist_set:
continue
@@ -1900,9 +1905,11 @@ class MainWindow(QMainWindow):
return
ignored = self.duplicate_cache.get_all_exceptions()
if not ignored:
QMessageBox.information(self, UITexts.DUPLICATE_DETECTION_TITLE, UITexts.DUPLICATE_NONE_FOUND)
QMessageBox.information(
self, UITexts.DUPLICATE_DETECTION_TITLE, UITexts.DUPLICATE_NONE_FOUND)
return
dialog = DuplicateManagerDialog(ignored, self.duplicate_cache, self, review_mode=True)
dialog = DuplicateManagerDialog(
ignored, self.duplicate_cache, self, review_mode=True)
dialog.show()
def show_about_dialog(self):
@@ -2428,7 +2435,8 @@ class MainWindow(QMainWindow):
confirm.setInformativeText(
UITexts.CONFIRM_DELETE_INFO.format(os.path.basename(paths[0])))
else:
confirm.setText(f"Are you sure you want to permanently delete {len(paths)} images?")
confirm.setText(
f"Are you sure you want to permanently delete {len(paths)} images?")
confirm.setInformativeText("This action CANNOT be undone.")
confirm.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
confirm.setDefaultButton(QMessageBox.No)
@@ -3401,7 +3409,7 @@ class MainWindow(QMainWindow):
"""Updates the circular progress bar value."""
self.progress_bar.setValue(value)
def on_thumbnail_loaded(self, path, size):
def on_thumbnail_loaded(self, _path, _size):
"""Called when a thumbnail has been loaded asynchronously from DB."""
self.thumbnail_view.viewport().update()
@@ -3856,7 +3864,8 @@ class MainWindow(QMainWindow):
self._setup_viewer_sync(viewer)
self.viewers.append(viewer)
viewer.destroyed.connect(
lambda obj=viewer: self.viewers.remove(obj) if obj in self.viewers else None)
lambda obj=viewer: self.viewers.remove(obj)
if obj in self.viewers else None)
if len(paths) > 1:
viewer.set_comparison_mode(len(paths))
@@ -4440,7 +4449,8 @@ class MainWindow(QMainWindow):
action_open_fullscreen = open_submenu.addAction(
QIcon.fromTheme("view-fullscreen"),
UITexts.CONTEXT_MENU_OPEN_FULLSCREEN_VIEWER)
action_open_fullscreen.triggered.connect(lambda: self.open_in_fullscreen_viewer(selected_indexes[0]))
action_open_fullscreen.triggered.connect(
lambda: self.open_in_fullscreen_viewer(selected_indexes[0]))
path = self.proxy_model.data(selected_indexes[0], PATH_ROLE)
action_open_location = menu.addAction(QIcon.fromTheme("folder-search"),
@@ -5089,7 +5099,8 @@ class MainWindow(QMainWindow):
return
# Get all image paths currently known to the application or provided list
paths_to_scan = custom_paths if custom_paths is not None else self.get_all_image_paths()
paths_to_scan = custom_paths \
if custom_paths is not None else self.get_all_image_paths()
if not paths_to_scan:
QMessageBox.information(self, UITexts.DUPLICATE_DETECTION_TITLE,
UITexts.DUPLICATE_NO_IMAGES)
@@ -5100,11 +5111,15 @@ class MainWindow(QMainWindow):
threshold = APP_CONFIG.get("duplicate_threshold", 90)
self.duplicate_detector = DuplicateDetector(
paths_to_scan, self.duplicate_cache, self.thread_pool_manager, method, threshold, force_full=force_full)
paths_to_scan, self.duplicate_cache,
self.thread_pool_manager, method, threshold, force_full=force_full)
self.duplicate_detector.progress_update.connect(self.on_duplicate_detection_progress)
self.duplicate_detector.duplicates_found.connect(self.on_duplicates_found)
self.duplicate_detector.detection_finished.connect(self.on_duplicate_detection_finished)
self.duplicate_detector.progress_update.connect(
self.on_duplicate_detection_progress)
self.duplicate_detector.duplicates_found.connect(
self.on_duplicates_found)
self.duplicate_detector.detection_finished.connect(
self.on_duplicate_detection_finished)
self.progress_bar.setValue(0)
self.progress_bar.setCustomColor(None)
@@ -5172,11 +5187,6 @@ def main():
thread_pool_manager = ThreadPoolManager()
cache = ThumbnailCache()
args = [a for a in sys.argv[1:] if a != "--x11"]
if args:
path = " ".join(args).strip()
if path.startswith("file:/"):
path = path[6:]
win = MainWindow(cache, args, thread_pool_manager, duplicate_cache)
app.installEventFilter(win.shortcut_controller)