diff --git a/constants.py b/constants.py index cb7d5ea..4e0de24 100644 --- a/constants.py +++ b/constants.py @@ -29,7 +29,7 @@ if FORCE_X11: # --- CONFIGURATION --- PROG_NAME = "Bagheera Image Viewer" PROG_ID = "bagheeraview" -PROG_VERSION = "0.9.11-dev" +PROG_VERSION = "0.9.11" PROG_AUTHOR = "Ignacio Serantes" # --- CACHE SETTINGS --- diff --git a/imagescanner.py b/imagescanner.py index 86fc82c..6824015 100644 --- a/imagescanner.py +++ b/imagescanner.py @@ -64,7 +64,8 @@ class ThreadPoolManager: ) self.pool.setMaxThreadCount(self.default_thread_count) self.is_user_active = False - logger.info(f"ThreadPoolManager initialized with {self.default_thread_count} threads.") + logger.info(f"ThreadPoolManager initialized with " + f"{self.default_thread_count} threads.") def get_pool(self): """Returns the managed QThreadPool instance.""" @@ -87,7 +88,8 @@ class ThreadPoolManager: else: # User is idle, restore to default thread count. self.pool.setMaxThreadCount(self.default_thread_count) - logger.debug(f"User is idle, restoring thread pool to {self.default_thread_count}.") + logger.debug(f"User is idle, restoring thread pool to " + f"{self.default_thread_count}.") def update_default_thread_count(self): """Updates the default thread count from application settings.""" @@ -1329,6 +1331,7 @@ class ImageScanner(QThread): progress_percent = Signal(int) finished_scan = Signal(int) # Total images found more_files_available = Signal(int, int) # Last loaded index, remainder + def __init__(self, cache, paths, is_file_list=False, viewers=None, thread_pool_manager=None): # is_file_list is not used diff --git a/imageviewer.py b/imageviewer.py index 82f9ba2..ab638ac 100644 --- a/imageviewer.py +++ b/imageviewer.py @@ -15,8 +15,8 @@ import json from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QScrollArea, QLabel, QHBoxLayout, QListWidget, - QListWidgetItem, QAbstractItemView, QMenu, QInputDialog, QDialog, QDialogButtonBox, QGridLayout, - QApplication, QMessageBox, QLineEdit, QFileDialog + QListWidgetItem, QAbstractItemView, QMenu, QInputDialog, QDialog, QDialogButtonBox, + QGridLayout, QApplication, QMessageBox, QLineEdit, QFileDialog ) from PySide6.QtGui import ( QPixmap, QIcon, QTransform, QDrag, QPainter, QPen, QColor, QAction, QCursor, @@ -39,6 +39,7 @@ from imagecontroller import ImageController from widgets import FaceNameInputWidget from propertiesdialog import PropertiesDialog + class HighlightWidget(QWidget): """Widget to show a highlight border around the active pane.""" def __init__(self, parent=None): @@ -47,6 +48,7 @@ class HighlightWidget(QWidget): self.setStyleSheet("border: 2px solid #3498db; background: transparent;") self.hide() + class FaceNameDialog(QDialog): """A dialog to get a face name using the FaceNameInputWidget.""" def __init__(self, parent=None, history=None, current_name="", main_win=None, @@ -1194,7 +1196,7 @@ class ImagePane(QWidget): def __init__(self, parent_viewer, cache, image_list, index, initial_tags=None, initial_rating=0): super().__init__(parent_viewer) - self.viewer = parent_viewer # Reference to main ImageViewer + self.viewer = parent_viewer # Reference to main ImageViewer self.main_win = parent_viewer.main_win self.cache = cache @@ -1329,6 +1331,7 @@ class ImagePane(QWidget): if self.controller.get_current_path() != current_path: self.load_and_fit_image() + class ImageViewer(QWidget): """ A standalone window for viewing and manipulating a single image. @@ -1410,7 +1413,6 @@ class ImageViewer(QWidget): # self.canvas = FaceCanvas(self) ... Moved to ImagePane # self.scroll_area.setWidget(self.canvas) - self.filmstrip = FilmStripWidget(self.controller) self.filmstrip.setSpacing(2) self.filmstrip.itemClicked.connect(self.on_filmstrip_clicked) @@ -1540,7 +1542,8 @@ class ImageViewer(QWidget): return self.active_pane.movie if self.active_pane else None def add_pane(self, image_list, index, initial_tags, initial_rating): - pane = ImagePane(self, self.cache, image_list, index, initial_tags, initial_rating) + pane = ImagePane(self, self.cache, image_list, index, initial_tags, + initial_rating) self.panes.append(pane) self.update_grid_layout() return pane @@ -1553,7 +1556,7 @@ class ImageViewer(QWidget): self.active_pane.scrolled.disconnect(self._sync_scroll) self.active_pane.zoom_manager.zoomed.disconnect(self._sync_zoom) except RuntimeError: - pass # Signal wasn't connected + pass # Signal wasn't connected self.active_pane = pane @@ -1622,7 +1625,7 @@ class ImageViewer(QWidget): img_list = base_controller.image_list for i in range(count - current_panes): new_idx = (start_idx + i + 1) % len(img_list) - pane = self.add_pane(img_list, new_idx, None, 0) # Metadata will load + pane = self.add_pane(img_list, new_idx, None, 0) # Metadata will load pane.load_and_fit_image() else: # Remove panes (keep active if possible, else keep first) @@ -1636,8 +1639,10 @@ class ImageViewer(QWidget): # Restore default behavior (auto-resize) if we go back to single view if count == 1 and self.active_pane: - # Allow layout to settle before resizing window to ensure accurate sizing - QTimer.singleShot(0, lambda: self.active_pane.update_view(resize_win=True)) + # Allow layout to settle before resizing window to ensure accurate + # sizing + QTimer.singleShot( + 0, lambda: self.active_pane.update_view(resize_win=True)) def toggle_link_panes(self): """Toggles the synchronized zoom/scroll for comparison mode.""" @@ -1883,7 +1888,8 @@ class ImageViewer(QWidget): """ kwinoutputconfig.json """ - return self.screen().availableGeometry().width(), self.screen().availableGeometry().height() + return self.screen().availableGeometry().width(), \ + self.screen().availableGeometry().height() # We run kscreen-doctor and look for the primary monitor line. if FORCE_X11: if os.path.exists(KWINOUTPUTCONFIG_PATH): @@ -1986,7 +1992,8 @@ class ImageViewer(QWidget): if self.filmstrip.isVisible(): self.populate_filmstrip() pane.update_view(resize_win=False) - QTimer.singleShot(0, lambda: self.restore_scroll_for_pane(pane, restore_config)) + QTimer.singleShot( + 0, lambda: self.restore_scroll_for_pane(pane, restore_config)) elif reloaded: # Calculate zoom to fit the image on the screen if self.isFullScreen(): @@ -2004,7 +2011,8 @@ class ImageViewer(QWidget): else: # Tried to guess screen_width, screen_height = self.get_desktop_resolution() - if pane == self.panes[0]: self._first_load = False + if pane == self.panes[0]: + self._first_load = False else: screen_geo = self.screen().availableGeometry() screen_width = screen_geo.width() @@ -2274,11 +2282,13 @@ class ImageViewer(QWidget): def save_cropped_image(self): """Saves the area currently selected in crop mode as a new image.""" - if not self.active_pane or not self.active_pane.crop_mode or self.active_pane.canvas.crop_rect.isNull(): + if not self.active_pane or not self.active_pane.crop_mode \ + or self.active_pane.canvas.crop_rect.isNull(): return # Get normalized coordinates from the canvas rect - nx, ny, nw, nh = self.active_pane.canvas.map_to_source(self.active_pane.canvas.crop_rect) + nx, ny, nw, nh = self.active_pane.canvas.map_to_source( + self.active_pane.canvas.crop_rect) # Use original pixmap to extract high-quality crop orig = self.active_pane.controller.pixmap_original @@ -2403,8 +2413,10 @@ class ImageViewer(QWidget): "show_faces": self.controller.show_faces, "flip_h": self.controller.flip_h, "flip_v": self.controller.flip_v, - "scroll_x": self.scroll_area.horizontalScrollBar().value() if self.scroll_area else 0, - "scroll_y": self.scroll_area.verticalScrollBar().value() if self.scroll_area else 0, + "scroll_x": self.scroll_area.horizontalScrollBar().value() + if self.scroll_area else 0, + "scroll_y": self.scroll_area.verticalScrollBar().value() + if self.scroll_area else 0, "status_bar_visible": self.status_bar_container.isVisible(), "filmstrip_visible": self.filmstrip.isVisible() } @@ -2493,7 +2505,8 @@ class ImageViewer(QWidget): return False pos = self.canvas.mapFromGlobal(event.globalPos()) if self.canvas else QPoint() - clicked_face = self.active_pane._get_clicked_face(pos) if self.active_pane else None + clicked_face = self.active_pane._get_clicked_face(pos) \ + if self.active_pane else None if not clicked_face: return False @@ -2637,14 +2650,19 @@ class ImageViewer(QWidget): "icon": "zoom-fit-best"}, "separator", {"text": UITexts.VIEWER_MENU_CROP, "action": "toggle_crop", - "icon": "transform-crop", "checkable": True}, # checked updated later + "icon": "transform-crop", "checkable": True}, # checked updated later "separator", {"text": UITexts.VIEWER_MENU_COMPARE, "icon": "view-grid", "submenu": [ - {"text": UITexts.VIEWER_MENU_COMPARE_1, "action": "compare_1", "icon": "view-restore"}, - {"text": UITexts.VIEWER_MENU_COMPARE_2, "action": "compare_2", "icon": "view-split-left-right"}, - {"text": UITexts.VIEWER_MENU_COMPARE_4, "action": "compare_4", "icon": "view-grid"}, + {"text": UITexts.VIEWER_MENU_COMPARE_1, + "action": "compare_1", "icon": "view-restore"}, + {"text": UITexts.VIEWER_MENU_COMPARE_2, + "action": "compare_2", "icon": "view-split-left-right"}, + {"text": UITexts.VIEWER_MENU_COMPARE_4, + "action": "compare_4", "icon": "view-grid"}, "separator", - {"text": UITexts.VIEWER_MENU_LINK_PANES, "action": "link_panes", "icon": "object-link", "checkable": True, "checked": self.panes_linked} + {"text": UITexts.VIEWER_MENU_LINK_PANES, + "action": "link_panes", "icon": "object-link", + "checkable": True, "checked": self.panes_linked} ]}, "separator", ] @@ -2808,7 +2826,8 @@ class ImageViewer(QWidget): Args: event (QContextMenuEvent): The context menu event. """ - if self.active_pane and self.active_pane.crop_mode and not self.active_pane.canvas.crop_rect.isNull(): + if self.active_pane and self.active_pane.crop_mode \ + and not self.active_pane.canvas.crop_rect.isNull(): pos = self.active_pane.canvas.mapFromGlobal(event.globalPos()) if self.active_pane.canvas.crop_rect.contains(pos): self.show_crop_menu(event.globalPos()) @@ -2855,7 +2874,7 @@ class ImageViewer(QWidget): h = self.canvas.height() if self.canvas else 0 if self.scroll_area: self.scroll_area.ensureVisible(int(new_face.get('x', 0) * w), - int(new_face.get('y', 0) * h), 50, 50) + int(new_face.get('y', 0) * h), 50, 50) QApplication.processEvents() history = self.main_win.face_names_history if self.main_win else [] @@ -2912,7 +2931,7 @@ class ImageViewer(QWidget): h = self.canvas.height() if self.canvas else 0 if self.scroll_area: self.scroll_area.ensureVisible(int(new_pet.get('x', 0) * w), - int(new_pet.get('y', 0) * h), 50, 50) + int(new_pet.get('y', 0) * h), 50, 50) QApplication.processEvents() history = self.main_win.pet_names_history if self.main_win else [] @@ -2968,7 +2987,7 @@ class ImageViewer(QWidget): h = self.canvas.height() if self.canvas else 0 if self.scroll_area: self.scroll_area.ensureVisible(int(new_body.get('x', 0) * w), - int(new_body.get('y', 0) * h), 50, 50) + int(new_body.get('y', 0) * h), 50, 50) QApplication.processEvents() # For bodies, we typically don't ask for a name immediately unless desired