creatumlibre.ui.manager.tab_manager

  1# pylint: disable=no-member
  2from pathlib import Path
  3
  4from PyQt6.QtCore import Qt
  5from PyQt6.QtWidgets import (
  6    QLabel,
  7    QScrollArea,
  8    QSizePolicy,
  9    QTabWidget,
 10    QVBoxLayout,
 11    QWidget,
 12)
 13
 14from creatumlibre.ui.manager.object_manager import ObjectManager
 15
 16
 17class TabManager:
 18    """Manages loading new image to object list (positon[0]), tab creation, and scrolling."""
 19
 20    def __init__(self, parent):
 21        self.parent = parent
 22        self.tab_widget = QTabWidget()
 23        self.object_manager_instances = {}  # Track objectManagers by tab index
 24
 25        self._init_layout()
 26
 27    def _init_layout(self):
 28        self.tab_widget.setTabsClosable(True)
 29        self.tab_widget.tabCloseRequested.connect(self.tab_widget.removeTab)
 30        # self.tab_widget.installEventFilter(self.parent.input_handler)
 31        self.tab_widget.setMovable(True)
 32        self.tab_widget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
 33        self.tab_widget.setFocus()
 34        self.tab_widget.setSizePolicy(
 35            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
 36        )
 37        self.parent.workspace_layout.addWidget(self.tab_widget)
 38
 39    def _add_scroll_container(self, filename):
 40        """Add a scroll container around the objects"""
 41        scroll_area_widget = QScrollArea()
 42        scroll_area_widget.setWidgetResizable(True)
 43        scroll_area_widget.setHorizontalScrollBarPolicy(
 44            Qt.ScrollBarPolicy.ScrollBarAlwaysOn
 45        )
 46        scroll_area_widget.setVerticalScrollBarPolicy(
 47            Qt.ScrollBarPolicy.ScrollBarAlwaysOn
 48        )
 49        # Fixed image/object-manger container inside the scroll area
 50        object_manager_container_widget = QWidget()
 51        object_manager_container_layout = QVBoxLayout(object_manager_container_widget)
 52        object_manager_container_layout.setContentsMargins(0, 0, 0, 0)
 53
 54        object_manager_label_widget = QLabel()
 55        object_manager_label_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
 56
 57        object_manager_container_layout.addWidget(object_manager_label_widget)
 58        object_manager_container_widget.setLayout(object_manager_container_layout)
 59
 60        # Place fixed widget inside the scrollable area
 61        scroll_area_widget.setWidget(object_manager_container_widget)
 62        tab_index = self.tab_widget.addTab(scroll_area_widget, filename)
 63        return tab_index, object_manager_label_widget
 64
 65    def load_new_image(self, file_path):
 66        """Load a new image with scroll support inside a fixed widget."""
 67        object_manager_instance = ObjectManager(file_path)
 68        filename = Path(file_path).name
 69
 70        tab_index, object_manager_label_widget = self._add_scroll_container(filename)
 71        self.object_manager_instances[tab_index] = {
 72            "manager": object_manager_instance,
 73            "widget": object_manager_label_widget,  # Store label reference per tab
 74            "file_apth": file_path,  # saved for saving
 75        }
 76
 77        self.refresh_tab_display(tab_index)
 78        self.tab_widget.setCurrentIndex(tab_index)
 79
 80    def refresh_tab_display(self, tab_index):
 81        """Redraws the image for a specific tab by requesting a refreshed pixmap."""
 82        if tab_index not in self.object_manager_instances:
 83            return
 84
 85        obj_ref = self.object_manager_instances[tab_index]
 86
 87        tab_pixmap = obj_ref[
 88            "manager"
 89        ].get_tab_pixmap()  # Get final image with all objects/layers
 90        self._refresh_widget(widget=obj_ref["widget"], pixmap=tab_pixmap)
 91
 92    def refresh_active_tab_display(self):
 93        """Convinience methot to update the tab"""
 94        active_tab = self.get_active_tab_index()
 95        self.refresh_tab_display(active_tab)
 96
 97    def _refresh_widget(self, widget: QWidget, pixmap):
 98        """update widget"""
 99        widget.setPixmap(pixmap)
100        widget.repaint()
101
102    def get_active_tab(self):
103        """Retrieves the current ObjectManager instance safely."""
104        if (current_index := self.tab_widget.currentIndex()) == -1:
105            return None
106
107        return self.object_manager_instances.get(current_index, None)
108
109    def get_active_tab_index(self):
110        return self.tab_widget.currentIndex()
111
112    def zoom_in(self):
113        """Increase the zoom level of the active image."""
114        self.apply_zoom(1.2)
115
116    def zoom_out(self):
117        """Decrease the zoom level of the active image."""
118        self.apply_zoom(0.8)
119
120    def apply_zoom(self, factor: float):
121        """Zoom in or out while maintaining aspect ratio."""
122        if (active_tab := self.get_active_tab()) is None:
123            return
124        active_tab["manager"].zoom_factor = max(
125            0.1, min(3.0, active_tab["manager"].zoom_factor * factor)
126        )  # Prevent extreme zoom
127        self._refresh_widget(
128            active_tab["widget"], active_tab["manager"].get_tab_pixmap()
129        )
130
131    def fit_to_container(self):
132        """Resize the image while preserving aspect ratio."""
133        print("Fitting image to container with aspect ratio")
134
135        if (active_tab := self.get_active_tab()) is None:
136            return
137
138        container_width = self.tab_widget.width()
139        container_height = self.tab_widget.height()
140
141        image_width, image_height = (
142            active_tab["manager"].get_base_image().shape[1],
143            active_tab["manager"].get_base_image().shape[0],
144        )
145
146        # Calculate the best zoom factor
147        scale_x = container_width / image_width
148        scale_y = container_height / image_height
149        active_tab["manager"].zoom_factor = max(
150            scale_x, scale_y
151        )  #  Ensures aspect ratio is preserved
152
153        print(
154            f" Calculated zoom factor: {active_tab["manager"].zoom_factor}, {scale_x=}, {scale_y=}"
155        )
156        # Resize the image
157        self.apply_zoom(
158            active_tab["manager"].zoom_factor
159        )  #  Use zoom logic to resize properly
160
161    def reset_zoom(self):
162        """Reset zoom level for active image."""
163        if (active_tab := self.get_active_tab()) is None:
164            return
165
166        active_tab["manager"].zoom_factor = 1.0
167        self._refresh_widget(
168            active_tab["widget"], active_tab["manager"].get_tab_pixmap()
169        )
class TabManager:
 18class TabManager:
 19    """Manages loading new image to object list (positon[0]), tab creation, and scrolling."""
 20
 21    def __init__(self, parent):
 22        self.parent = parent
 23        self.tab_widget = QTabWidget()
 24        self.object_manager_instances = {}  # Track objectManagers by tab index
 25
 26        self._init_layout()
 27
 28    def _init_layout(self):
 29        self.tab_widget.setTabsClosable(True)
 30        self.tab_widget.tabCloseRequested.connect(self.tab_widget.removeTab)
 31        # self.tab_widget.installEventFilter(self.parent.input_handler)
 32        self.tab_widget.setMovable(True)
 33        self.tab_widget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
 34        self.tab_widget.setFocus()
 35        self.tab_widget.setSizePolicy(
 36            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
 37        )
 38        self.parent.workspace_layout.addWidget(self.tab_widget)
 39
 40    def _add_scroll_container(self, filename):
 41        """Add a scroll container around the objects"""
 42        scroll_area_widget = QScrollArea()
 43        scroll_area_widget.setWidgetResizable(True)
 44        scroll_area_widget.setHorizontalScrollBarPolicy(
 45            Qt.ScrollBarPolicy.ScrollBarAlwaysOn
 46        )
 47        scroll_area_widget.setVerticalScrollBarPolicy(
 48            Qt.ScrollBarPolicy.ScrollBarAlwaysOn
 49        )
 50        # Fixed image/object-manger container inside the scroll area
 51        object_manager_container_widget = QWidget()
 52        object_manager_container_layout = QVBoxLayout(object_manager_container_widget)
 53        object_manager_container_layout.setContentsMargins(0, 0, 0, 0)
 54
 55        object_manager_label_widget = QLabel()
 56        object_manager_label_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
 57
 58        object_manager_container_layout.addWidget(object_manager_label_widget)
 59        object_manager_container_widget.setLayout(object_manager_container_layout)
 60
 61        # Place fixed widget inside the scrollable area
 62        scroll_area_widget.setWidget(object_manager_container_widget)
 63        tab_index = self.tab_widget.addTab(scroll_area_widget, filename)
 64        return tab_index, object_manager_label_widget
 65
 66    def load_new_image(self, file_path):
 67        """Load a new image with scroll support inside a fixed widget."""
 68        object_manager_instance = ObjectManager(file_path)
 69        filename = Path(file_path).name
 70
 71        tab_index, object_manager_label_widget = self._add_scroll_container(filename)
 72        self.object_manager_instances[tab_index] = {
 73            "manager": object_manager_instance,
 74            "widget": object_manager_label_widget,  # Store label reference per tab
 75            "file_apth": file_path,  # saved for saving
 76        }
 77
 78        self.refresh_tab_display(tab_index)
 79        self.tab_widget.setCurrentIndex(tab_index)
 80
 81    def refresh_tab_display(self, tab_index):
 82        """Redraws the image for a specific tab by requesting a refreshed pixmap."""
 83        if tab_index not in self.object_manager_instances:
 84            return
 85
 86        obj_ref = self.object_manager_instances[tab_index]
 87
 88        tab_pixmap = obj_ref[
 89            "manager"
 90        ].get_tab_pixmap()  # Get final image with all objects/layers
 91        self._refresh_widget(widget=obj_ref["widget"], pixmap=tab_pixmap)
 92
 93    def refresh_active_tab_display(self):
 94        """Convinience methot to update the tab"""
 95        active_tab = self.get_active_tab_index()
 96        self.refresh_tab_display(active_tab)
 97
 98    def _refresh_widget(self, widget: QWidget, pixmap):
 99        """update widget"""
100        widget.setPixmap(pixmap)
101        widget.repaint()
102
103    def get_active_tab(self):
104        """Retrieves the current ObjectManager instance safely."""
105        if (current_index := self.tab_widget.currentIndex()) == -1:
106            return None
107
108        return self.object_manager_instances.get(current_index, None)
109
110    def get_active_tab_index(self):
111        return self.tab_widget.currentIndex()
112
113    def zoom_in(self):
114        """Increase the zoom level of the active image."""
115        self.apply_zoom(1.2)
116
117    def zoom_out(self):
118        """Decrease the zoom level of the active image."""
119        self.apply_zoom(0.8)
120
121    def apply_zoom(self, factor: float):
122        """Zoom in or out while maintaining aspect ratio."""
123        if (active_tab := self.get_active_tab()) is None:
124            return
125        active_tab["manager"].zoom_factor = max(
126            0.1, min(3.0, active_tab["manager"].zoom_factor * factor)
127        )  # Prevent extreme zoom
128        self._refresh_widget(
129            active_tab["widget"], active_tab["manager"].get_tab_pixmap()
130        )
131
132    def fit_to_container(self):
133        """Resize the image while preserving aspect ratio."""
134        print("Fitting image to container with aspect ratio")
135
136        if (active_tab := self.get_active_tab()) is None:
137            return
138
139        container_width = self.tab_widget.width()
140        container_height = self.tab_widget.height()
141
142        image_width, image_height = (
143            active_tab["manager"].get_base_image().shape[1],
144            active_tab["manager"].get_base_image().shape[0],
145        )
146
147        # Calculate the best zoom factor
148        scale_x = container_width / image_width
149        scale_y = container_height / image_height
150        active_tab["manager"].zoom_factor = max(
151            scale_x, scale_y
152        )  #  Ensures aspect ratio is preserved
153
154        print(
155            f" Calculated zoom factor: {active_tab["manager"].zoom_factor}, {scale_x=}, {scale_y=}"
156        )
157        # Resize the image
158        self.apply_zoom(
159            active_tab["manager"].zoom_factor
160        )  #  Use zoom logic to resize properly
161
162    def reset_zoom(self):
163        """Reset zoom level for active image."""
164        if (active_tab := self.get_active_tab()) is None:
165            return
166
167        active_tab["manager"].zoom_factor = 1.0
168        self._refresh_widget(
169            active_tab["widget"], active_tab["manager"].get_tab_pixmap()
170        )

Manages loading new image to object list (positon[0]), tab creation, and scrolling.

TabManager(parent)
21    def __init__(self, parent):
22        self.parent = parent
23        self.tab_widget = QTabWidget()
24        self.object_manager_instances = {}  # Track objectManagers by tab index
25
26        self._init_layout()
parent
tab_widget
object_manager_instances
def load_new_image(self, file_path):
66    def load_new_image(self, file_path):
67        """Load a new image with scroll support inside a fixed widget."""
68        object_manager_instance = ObjectManager(file_path)
69        filename = Path(file_path).name
70
71        tab_index, object_manager_label_widget = self._add_scroll_container(filename)
72        self.object_manager_instances[tab_index] = {
73            "manager": object_manager_instance,
74            "widget": object_manager_label_widget,  # Store label reference per tab
75            "file_apth": file_path,  # saved for saving
76        }
77
78        self.refresh_tab_display(tab_index)
79        self.tab_widget.setCurrentIndex(tab_index)

Load a new image with scroll support inside a fixed widget.

def refresh_tab_display(self, tab_index):
81    def refresh_tab_display(self, tab_index):
82        """Redraws the image for a specific tab by requesting a refreshed pixmap."""
83        if tab_index not in self.object_manager_instances:
84            return
85
86        obj_ref = self.object_manager_instances[tab_index]
87
88        tab_pixmap = obj_ref[
89            "manager"
90        ].get_tab_pixmap()  # Get final image with all objects/layers
91        self._refresh_widget(widget=obj_ref["widget"], pixmap=tab_pixmap)

Redraws the image for a specific tab by requesting a refreshed pixmap.

def refresh_active_tab_display(self):
93    def refresh_active_tab_display(self):
94        """Convinience methot to update the tab"""
95        active_tab = self.get_active_tab_index()
96        self.refresh_tab_display(active_tab)

Convinience methot to update the tab

def get_active_tab(self):
103    def get_active_tab(self):
104        """Retrieves the current ObjectManager instance safely."""
105        if (current_index := self.tab_widget.currentIndex()) == -1:
106            return None
107
108        return self.object_manager_instances.get(current_index, None)

Retrieves the current ObjectManager instance safely.

def get_active_tab_index(self):
110    def get_active_tab_index(self):
111        return self.tab_widget.currentIndex()
def zoom_in(self):
113    def zoom_in(self):
114        """Increase the zoom level of the active image."""
115        self.apply_zoom(1.2)

Increase the zoom level of the active image.

def zoom_out(self):
117    def zoom_out(self):
118        """Decrease the zoom level of the active image."""
119        self.apply_zoom(0.8)

Decrease the zoom level of the active image.

def apply_zoom(self, factor: float):
121    def apply_zoom(self, factor: float):
122        """Zoom in or out while maintaining aspect ratio."""
123        if (active_tab := self.get_active_tab()) is None:
124            return
125        active_tab["manager"].zoom_factor = max(
126            0.1, min(3.0, active_tab["manager"].zoom_factor * factor)
127        )  # Prevent extreme zoom
128        self._refresh_widget(
129            active_tab["widget"], active_tab["manager"].get_tab_pixmap()
130        )

Zoom in or out while maintaining aspect ratio.

def fit_to_container(self):
132    def fit_to_container(self):
133        """Resize the image while preserving aspect ratio."""
134        print("Fitting image to container with aspect ratio")
135
136        if (active_tab := self.get_active_tab()) is None:
137            return
138
139        container_width = self.tab_widget.width()
140        container_height = self.tab_widget.height()
141
142        image_width, image_height = (
143            active_tab["manager"].get_base_image().shape[1],
144            active_tab["manager"].get_base_image().shape[0],
145        )
146
147        # Calculate the best zoom factor
148        scale_x = container_width / image_width
149        scale_y = container_height / image_height
150        active_tab["manager"].zoom_factor = max(
151            scale_x, scale_y
152        )  #  Ensures aspect ratio is preserved
153
154        print(
155            f" Calculated zoom factor: {active_tab["manager"].zoom_factor}, {scale_x=}, {scale_y=}"
156        )
157        # Resize the image
158        self.apply_zoom(
159            active_tab["manager"].zoom_factor
160        )  #  Use zoom logic to resize properly

Resize the image while preserving aspect ratio.

def reset_zoom(self):
162    def reset_zoom(self):
163        """Reset zoom level for active image."""
164        if (active_tab := self.get_active_tab()) is None:
165            return
166
167        active_tab["manager"].zoom_factor = 1.0
168        self._refresh_widget(
169            active_tab["widget"], active_tab["manager"].get_tab_pixmap()
170        )

Reset zoom level for active image.