Several fixes
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user