v0.9.11
This commit is contained in:
@@ -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 ---
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -2640,11 +2653,16 @@ class ImageViewer(QWidget):
|
||||
"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())
|
||||
|
||||
Reference in New Issue
Block a user