imagebaker 0.0.41__py3-none-any.whl → 0.0.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- imagebaker/__init__.py +1 -1
- imagebaker/core/__init__.py +0 -0
- imagebaker/core/configs/__init__.py +1 -0
- imagebaker/core/configs/configs.py +156 -0
- imagebaker/core/defs/__init__.py +1 -0
- imagebaker/core/defs/defs.py +258 -0
- imagebaker/core/plugins/__init__.py +0 -0
- imagebaker/core/plugins/base_plugin.py +39 -0
- imagebaker/core/plugins/cosine_plugin.py +39 -0
- imagebaker/layers/__init__.py +3 -0
- imagebaker/layers/annotable_layer.py +847 -0
- imagebaker/layers/base_layer.py +724 -0
- imagebaker/layers/canvas_layer.py +1007 -0
- imagebaker/list_views/__init__.py +3 -0
- imagebaker/list_views/annotation_list.py +203 -0
- imagebaker/list_views/canvas_list.py +185 -0
- imagebaker/list_views/image_list.py +138 -0
- imagebaker/list_views/layer_list.py +390 -0
- imagebaker/list_views/layer_settings.py +219 -0
- imagebaker/models/__init__.py +0 -0
- imagebaker/models/base_model.py +150 -0
- imagebaker/tabs/__init__.py +2 -0
- imagebaker/tabs/baker_tab.py +496 -0
- imagebaker/tabs/layerify_tab.py +837 -0
- imagebaker/utils/__init__.py +0 -0
- imagebaker/utils/image.py +105 -0
- imagebaker/utils/state_utils.py +92 -0
- imagebaker/utils/transform_mask.py +107 -0
- imagebaker/window/__init__.py +1 -0
- imagebaker/window/app.py +136 -0
- imagebaker/window/main_window.py +181 -0
- imagebaker/workers/__init__.py +3 -0
- imagebaker/workers/baker_worker.py +247 -0
- imagebaker/workers/layerify_worker.py +91 -0
- imagebaker/workers/model_worker.py +54 -0
- {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/METADATA +6 -6
- imagebaker-0.0.48.dist-info/RECORD +41 -0
- {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/WHEEL +1 -1
- imagebaker-0.0.41.dist-info/RECORD +0 -7
- {imagebaker-0.0.41.dist-info/licenses → imagebaker-0.0.48.dist-info}/LICENSE +0 -0
- {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/entry_points.txt +0 -0
- {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,203 @@ | |
| 1 | 
            +
            from PySide6.QtCore import QSize, Qt, Signal
         | 
| 2 | 
            +
            from PySide6.QtGui import (
         | 
| 3 | 
            +
                QColor,
         | 
| 4 | 
            +
                QPixmap,
         | 
| 5 | 
            +
                QIcon,
         | 
| 6 | 
            +
            )
         | 
| 7 | 
            +
            from PySide6.QtWidgets import (
         | 
| 8 | 
            +
                QWidget,
         | 
| 9 | 
            +
                QVBoxLayout,
         | 
| 10 | 
            +
                QHBoxLayout,
         | 
| 11 | 
            +
                QPushButton,
         | 
| 12 | 
            +
                QLabel,
         | 
| 13 | 
            +
                QSizePolicy,
         | 
| 14 | 
            +
                QDockWidget,
         | 
| 15 | 
            +
                QListWidget,
         | 
| 16 | 
            +
                QListWidgetItem,
         | 
| 17 | 
            +
            )
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            from imagebaker.layers.annotable_layer import AnnotableLayer
         | 
| 20 | 
            +
            from imagebaker import logger
         | 
| 21 | 
            +
             | 
| 22 | 
            +
             | 
| 23 | 
            +
            class AnnotationList(QDockWidget):
         | 
| 24 | 
            +
                messageSignal = Signal(str)
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def __init__(self, layer: AnnotableLayer, parent=None):
         | 
| 27 | 
            +
                    super().__init__("Annotations", parent)
         | 
| 28 | 
            +
                    self.layer = layer
         | 
| 29 | 
            +
                    self.init_ui()
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def init_ui(self):
         | 
| 32 | 
            +
                    logger.info("Initializing AnnotationList")
         | 
| 33 | 
            +
                    self.setMinimumWidth(150)
         | 
| 34 | 
            +
                    self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    widget = QWidget()
         | 
| 37 | 
            +
                    layout = QVBoxLayout(widget)
         | 
| 38 | 
            +
                    self.list_widget = QListWidget()
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    # Set the size policy for the list widget to expand dynamically
         | 
| 41 | 
            +
                    self.list_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    layout.addWidget(self.list_widget)
         | 
| 44 | 
            +
                    self.setWidget(widget)
         | 
| 45 | 
            +
                    self.setFeatures(
         | 
| 46 | 
            +
                        QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
         | 
| 47 | 
            +
                    )
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def update_list(self):
         | 
| 50 | 
            +
                    self.list_widget.clear()
         | 
| 51 | 
            +
                    if self.layer is None:
         | 
| 52 | 
            +
                        return
         | 
| 53 | 
            +
                    for idx, ann in enumerate(self.layer.annotations):
         | 
| 54 | 
            +
                        item = QListWidgetItem(self.list_widget)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                        # Create container widget
         | 
| 57 | 
            +
                        widget = QWidget()
         | 
| 58 | 
            +
                        widget.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                        layout = QHBoxLayout(widget)
         | 
| 61 | 
            +
                        layout.setContentsMargins(1, 1, 2, 2)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                        # on clicking this element, select the annotation
         | 
| 64 | 
            +
                        widget.mousePressEvent = lambda event, i=idx: self.on_annotation_selected(i)
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                        widget.setCursor(Qt.PointingHandCursor)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                        # Color indicator
         | 
| 69 | 
            +
                        color_label = QLabel()
         | 
| 70 | 
            +
                        color = QColor(ann.color)
         | 
| 71 | 
            +
                        if not ann.visible:
         | 
| 72 | 
            +
                            color.setAlpha(128)
         | 
| 73 | 
            +
                        pixmap = QPixmap(20, 20)
         | 
| 74 | 
            +
                        pixmap.fill(ann.color)
         | 
| 75 | 
            +
                        color_label.setPixmap(pixmap)
         | 
| 76 | 
            +
                        layout.addWidget(color_label)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                        # Text container
         | 
| 79 | 
            +
                        text_container = QWidget()
         | 
| 80 | 
            +
                        text_layout = QVBoxLayout(text_container)
         | 
| 81 | 
            +
                        text_layout.setContentsMargins(0, 0, 0, 0)
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                        # Main label with conditional color
         | 
| 84 | 
            +
                        main_label = QLabel(f"{ann.name}")
         | 
| 85 | 
            +
                        main_color = "#666" if not ann.visible else "black"
         | 
| 86 | 
            +
                        main_label.setStyleSheet(f"font-weight: bold; color: {main_color};")
         | 
| 87 | 
            +
                        text_layout.addWidget(main_label)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                        # Change the text color of the selected annotation
         | 
| 90 | 
            +
                        if ann.selected:
         | 
| 91 | 
            +
                            main_label.setStyleSheet("font-weight: bold; color: blue;")
         | 
| 92 | 
            +
                        else:
         | 
| 93 | 
            +
                            main_label.setStyleSheet(f"font-weight: bold; color: {main_color};")
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                        # Secondary info
         | 
| 96 | 
            +
                        secondary_text = []
         | 
| 97 | 
            +
                        score_text = (
         | 
| 98 | 
            +
                            f"{ann.annotator}: {ann.score:.2f}"
         | 
| 99 | 
            +
                            if ann.score is not None
         | 
| 100 | 
            +
                            else ann.annotator
         | 
| 101 | 
            +
                        )
         | 
| 102 | 
            +
                        secondary_text.append(score_text)
         | 
| 103 | 
            +
                        short_path = ann.file_path.stem
         | 
| 104 | 
            +
                        secondary_text.append(f"<span style='color:#666;'>{short_path}</span>")
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                        if secondary_text:
         | 
| 107 | 
            +
                            info_color = "#888" if not ann.visible else "#444"
         | 
| 108 | 
            +
                            info_label = QLabel("<br>".join(secondary_text))
         | 
| 109 | 
            +
                            info_label.setStyleSheet(f"color: {info_color}; font-size: 10px;")
         | 
| 110 | 
            +
                            text_layout.addWidget(info_label)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                        text_layout.addStretch()
         | 
| 113 | 
            +
                        layout.addWidget(text_container)
         | 
| 114 | 
            +
                        layout.addStretch()
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                        # Buttons
         | 
| 117 | 
            +
                        btn_container = QWidget()
         | 
| 118 | 
            +
                        btn_layout = QHBoxLayout(btn_container)
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                        layerify_btn = QPushButton("🖨")
         | 
| 121 | 
            +
                        layerify_btn.setFixedWidth(30)
         | 
| 122 | 
            +
                        layerify_btn.setToolTip("Make AnnotableLayer from annotation")
         | 
| 123 | 
            +
                        layerify_btn.clicked.connect(lambda _, i=idx: self.layerify_annotation(i))
         | 
| 124 | 
            +
                        btn_layout.addWidget(layerify_btn)
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                        vis_btn = QPushButton("👀" if ann.visible else "👁️")
         | 
| 127 | 
            +
                        vis_btn.setFixedWidth(30)
         | 
| 128 | 
            +
                        vis_btn.setCheckable(True)
         | 
| 129 | 
            +
                        vis_btn.setChecked(ann.visible)
         | 
| 130 | 
            +
                        vis_btn.setToolTip("Visible" if ann.visible else "Hidden")
         | 
| 131 | 
            +
                        vis_btn.clicked.connect(lambda _, i=idx: self.toggle_visibility(i))
         | 
| 132 | 
            +
                        btn_layout.addWidget(vis_btn)
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                        del_btn = QPushButton("🗑️")
         | 
| 135 | 
            +
                        del_btn.setFixedWidth(30)
         | 
| 136 | 
            +
                        del_btn.setToolTip("Delete annotation")
         | 
| 137 | 
            +
                        del_btn.clicked.connect(lambda _, i=idx: self.delete_annotation(i))
         | 
| 138 | 
            +
                        btn_layout.addWidget(del_btn)
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                        layout.addWidget(btn_container)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                        item.setSizeHint(widget.sizeHint())
         | 
| 143 | 
            +
                        self.list_widget.setItemWidget(item, widget)
         | 
| 144 | 
            +
                    self.update()
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                def create_color_icon(self, color):
         | 
| 147 | 
            +
                    pixmap = QPixmap(16, 16)
         | 
| 148 | 
            +
                    pixmap.fill(color)
         | 
| 149 | 
            +
                    return QIcon(pixmap)
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                def on_annotation_selected(self, index):
         | 
| 152 | 
            +
                    if 0 <= index < len(self.layer.annotations):
         | 
| 153 | 
            +
                        ann = self.layer.annotations[index]
         | 
| 154 | 
            +
                        ann.selected = not ann.selected
         | 
| 155 | 
            +
                        # Set other annotations to not selected
         | 
| 156 | 
            +
                        for i, a in enumerate(self.layer.annotations):
         | 
| 157 | 
            +
                            if i != index:
         | 
| 158 | 
            +
                                a.selected = False
         | 
| 159 | 
            +
                        self.layer.update()
         | 
| 160 | 
            +
                        self.update_list()
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                def delete_annotation(self, index):
         | 
| 163 | 
            +
                    if 0 <= index < len(self.layer.annotations):
         | 
| 164 | 
            +
                        logger.info(f"Deleting annotation: {self.layer.annotations[index].label}")
         | 
| 165 | 
            +
                        del self.layer.annotations[index]
         | 
| 166 | 
            +
                        self.layer.update()
         | 
| 167 | 
            +
                        self.update_list()
         | 
| 168 | 
            +
                        logger.info("Annotation deleted")
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                def toggle_visibility(self, index):
         | 
| 171 | 
            +
                    if 0 <= index < len(self.layer.annotations):
         | 
| 172 | 
            +
                        ann = self.layer.annotations[index]
         | 
| 173 | 
            +
                        ann.visible = not ann.visible
         | 
| 174 | 
            +
                        self.layer.update()
         | 
| 175 | 
            +
                        self.update_list()
         | 
| 176 | 
            +
                        logger.info(f"Annotation visibility toggled: {ann.label}, {ann.visible}")
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                def layerify_annotation(self, index):
         | 
| 179 | 
            +
                    if 0 <= index < len(self.layer.annotations):
         | 
| 180 | 
            +
                        ann = self.layer.annotations[index]
         | 
| 181 | 
            +
                        logger.info(f"Layerifying annotation: {ann.label}")
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                        self.update_list()
         | 
| 184 | 
            +
                        self.layer.layerify_annotation([ann])
         | 
| 185 | 
            +
                        self.layer.update()
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                def sizeHint(self):
         | 
| 188 | 
            +
                    """Calculate the preferred size based on the content."""
         | 
| 189 | 
            +
                    base_size = super().sizeHint()
         | 
| 190 | 
            +
                    content_width = self.list_widget.sizeHintForColumn(0) + 40  # Add padding
         | 
| 191 | 
            +
                    return QSize(max(base_size.width(), content_width), base_size.height())
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                def keyPressEvent(self, event):
         | 
| 194 | 
            +
                    key = event.key()
         | 
| 195 | 
            +
                    logger.info(f"Key pressed in AnnotationList: {key}")
         | 
| 196 | 
            +
                    if event.key() == Qt.Key_C and event.modifiers() == Qt.ControlModifier:
         | 
| 197 | 
            +
                        self.layer.copy_annotation()
         | 
| 198 | 
            +
                    elif event.key() == Qt.Key_V and event.modifiers() == Qt.ControlModifier:
         | 
| 199 | 
            +
                        self.layer.paste_annotation()
         | 
| 200 | 
            +
                    elif event.key() == Qt.Key_Delete:
         | 
| 201 | 
            +
                        self.delete_annotation(self.layer.selected_annotation_index)
         | 
| 202 | 
            +
                    else:
         | 
| 203 | 
            +
                        self.parentWidget().keyPressEvent(event)
         | 
| @@ -0,0 +1,185 @@ | |
| 1 | 
            +
            from PySide6.QtCore import Qt, Signal
         | 
| 2 | 
            +
            from PySide6.QtWidgets import (
         | 
| 3 | 
            +
                QWidget,
         | 
| 4 | 
            +
                QVBoxLayout,
         | 
| 5 | 
            +
                QHBoxLayout,
         | 
| 6 | 
            +
                QPushButton,
         | 
| 7 | 
            +
                QLabel,
         | 
| 8 | 
            +
                QSizePolicy,
         | 
| 9 | 
            +
                QDockWidget,
         | 
| 10 | 
            +
                QListWidget,
         | 
| 11 | 
            +
                QListWidgetItem,
         | 
| 12 | 
            +
                QInputDialog,
         | 
| 13 | 
            +
            )
         | 
| 14 | 
            +
            from functools import partial
         | 
| 15 | 
            +
            from imagebaker.layers.canvas_layer import CanvasLayer
         | 
| 16 | 
            +
            from imagebaker import logger
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
            class CanvasList(QDockWidget):
         | 
| 20 | 
            +
                canvasSelected = Signal(CanvasLayer)
         | 
| 21 | 
            +
                canvasDeleted = Signal(CanvasLayer)
         | 
| 22 | 
            +
                canvasAdded = Signal(CanvasLayer)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def __init__(self, canvases: list[CanvasLayer], parent=None):
         | 
| 25 | 
            +
                    """
         | 
| 26 | 
            +
                    :param canvases: List of CanvasLayer objects to display.
         | 
| 27 | 
            +
                    """
         | 
| 28 | 
            +
                    super().__init__("Canvas List", parent)
         | 
| 29 | 
            +
                    self.canvases = canvases
         | 
| 30 | 
            +
                    self.current_page = 0
         | 
| 31 | 
            +
                    self.canvases_per_page = 10
         | 
| 32 | 
            +
                    self.init_ui()
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def init_ui(self):
         | 
| 35 | 
            +
                    """Initialize the UI for the canvas list panel."""
         | 
| 36 | 
            +
                    logger.info("Initializing CanvasList")
         | 
| 37 | 
            +
                    self.setMinimumWidth(150)
         | 
| 38 | 
            +
                    self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    widget = QWidget()
         | 
| 41 | 
            +
                    layout = QVBoxLayout(widget)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # Add "Create New Canvas" button
         | 
| 44 | 
            +
                    self.create_canvas_button = QPushButton("Create New Canvas")
         | 
| 45 | 
            +
                    self.create_canvas_button.clicked.connect(self.create_new_canvas)
         | 
| 46 | 
            +
                    layout.addWidget(self.create_canvas_button)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    # Canvas list widget
         | 
| 49 | 
            +
                    self.list_widget = QListWidget()
         | 
| 50 | 
            +
                    self.list_widget.itemClicked.connect(self.handle_item_clicked)
         | 
| 51 | 
            +
                    layout.addWidget(self.list_widget)
         | 
| 52 | 
            +
                    # set first item as selected
         | 
| 53 | 
            +
                    if len(self.canvases) > 0:
         | 
| 54 | 
            +
                        self.list_widget.setCurrentRow(0)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    # Pagination controls
         | 
| 57 | 
            +
                    pagination_layout = QHBoxLayout()
         | 
| 58 | 
            +
                    self.prev_page_btn = QPushButton("Prev")
         | 
| 59 | 
            +
                    self.prev_page_btn.clicked.connect(self.show_prev_page)
         | 
| 60 | 
            +
                    self.next_page_btn = QPushButton("Next")
         | 
| 61 | 
            +
                    self.next_page_btn.clicked.connect(self.show_next_page)
         | 
| 62 | 
            +
                    pagination_layout.addWidget(self.prev_page_btn)
         | 
| 63 | 
            +
                    pagination_layout.addWidget(self.next_page_btn)
         | 
| 64 | 
            +
                    layout.addLayout(pagination_layout)
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    # Pagination info
         | 
| 67 | 
            +
                    self.pagination_label = QLabel("Showing 0 of 0")
         | 
| 68 | 
            +
                    layout.addWidget(self.pagination_label)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    self.setWidget(widget)
         | 
| 71 | 
            +
                    self.setFeatures(
         | 
| 72 | 
            +
                        QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
         | 
| 73 | 
            +
                    )
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    self.update_canvas_list()
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def create_new_canvas(self):
         | 
| 78 | 
            +
                    """Create a new canvas and emit the canvasAdded signal."""
         | 
| 79 | 
            +
                    canvas_idx = len(self.canvases) + 1
         | 
| 80 | 
            +
                    default_name = f"Canvas {canvas_idx}"
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    # Show input dialog to ask for canvas name
         | 
| 83 | 
            +
                    canvas_name, ok = QInputDialog.getText(
         | 
| 84 | 
            +
                        self, "New Canvas", "Enter canvas name:", text=default_name
         | 
| 85 | 
            +
                    )
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    # If the user cancels or provides an empty name, use the default name
         | 
| 88 | 
            +
                    if not ok or not canvas_name.strip():
         | 
| 89 | 
            +
                        canvas_name = default_name
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    # Create the new canvas
         | 
| 92 | 
            +
                    new_canvas = CanvasLayer(parent=self.parent())
         | 
| 93 | 
            +
                    new_canvas.layer_name = canvas_name  # Assign the name to the canvas
         | 
| 94 | 
            +
                    self.canvases.append(new_canvas)  # Add the new canvas to the list
         | 
| 95 | 
            +
                    self.canvasAdded.emit(
         | 
| 96 | 
            +
                        new_canvas
         | 
| 97 | 
            +
                    )  # Emit the signal to notify about the new canvas
         | 
| 98 | 
            +
                    self.update_canvas_list()  # Refresh the canvas list
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def show_next_page(self):
         | 
| 101 | 
            +
                    """Show the next page of canvases."""
         | 
| 102 | 
            +
                    if (self.current_page + 1) * self.canvases_per_page < len(self.canvases):
         | 
| 103 | 
            +
                        self.current_page += 1
         | 
| 104 | 
            +
                        self.update_canvas_list()
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                def show_prev_page(self):
         | 
| 107 | 
            +
                    """Show the previous page of canvases."""
         | 
| 108 | 
            +
                    if self.current_page > 0:
         | 
| 109 | 
            +
                        self.current_page -= 1
         | 
| 110 | 
            +
                        self.update_canvas_list()
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                def update_canvas_list(self):
         | 
| 113 | 
            +
                    """Update the canvas list with pagination."""
         | 
| 114 | 
            +
                    self.list_widget.clear()
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    canvases_list = list(self.canvases)
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    start_idx = self.current_page * self.canvases_per_page
         | 
| 119 | 
            +
                    end_idx = min(start_idx + self.canvases_per_page, len(canvases_list))
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    for idx, canvas in enumerate(canvases_list[start_idx:end_idx], start=start_idx):
         | 
| 122 | 
            +
                        item_widget = QWidget()
         | 
| 123 | 
            +
                        item_layout = QHBoxLayout(item_widget)
         | 
| 124 | 
            +
                        item_layout.setContentsMargins(5, 5, 5, 5)
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                        # Thumbnail
         | 
| 127 | 
            +
                        thumbnail_label = QLabel()
         | 
| 128 | 
            +
                        thumbnail_pixmap = canvas.get_thumbnail().scaled(
         | 
| 129 | 
            +
                            50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation
         | 
| 130 | 
            +
                        )
         | 
| 131 | 
            +
                        thumbnail_label.setPixmap(thumbnail_pixmap)
         | 
| 132 | 
            +
                        item_layout.addWidget(thumbnail_label)
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                        # Canvas name
         | 
| 135 | 
            +
                        canvas_name = getattr(canvas, "layer_name", f"Canvas {idx + 1}")
         | 
| 136 | 
            +
                        name_label = QLabel(canvas_name)
         | 
| 137 | 
            +
                        name_label.setStyleSheet("font-weight: bold;")
         | 
| 138 | 
            +
                        item_layout.addWidget(name_label)
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                        # Delete button
         | 
| 141 | 
            +
                        delete_button = QPushButton("Delete")
         | 
| 142 | 
            +
                        delete_button.setStyleSheet(
         | 
| 143 | 
            +
                            "background-color: red; color: white; font-weight: bold;"
         | 
| 144 | 
            +
                        )
         | 
| 145 | 
            +
                        delete_button.clicked.connect(partial(self.delete_canvas, canvas))
         | 
| 146 | 
            +
                        item_layout.addWidget(delete_button)
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                        item_layout.addStretch()
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                        # Add the custom widget to the list
         | 
| 151 | 
            +
                        list_item = QListWidgetItem(self.list_widget)
         | 
| 152 | 
            +
                        list_item.setSizeHint(item_widget.sizeHint())
         | 
| 153 | 
            +
                        self.list_widget.addItem(list_item)
         | 
| 154 | 
            +
                        self.list_widget.setItemWidget(list_item, item_widget)
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                        # Store metadata for the canvas
         | 
| 157 | 
            +
                        list_item.setData(Qt.UserRole, canvas)
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                    # Select the first item by default if it exists
         | 
| 160 | 
            +
                    if self.list_widget.count() > 0:
         | 
| 161 | 
            +
                        self.list_widget.setCurrentRow(0)
         | 
| 162 | 
            +
                        first_item = self.list_widget.item(0)
         | 
| 163 | 
            +
                        self.handle_item_clicked(first_item)
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                    self.pagination_label.setText(
         | 
| 166 | 
            +
                        f"Showing {start_idx + 1} to {end_idx} of {len(canvases_list)}"
         | 
| 167 | 
            +
                    )
         | 
| 168 | 
            +
                    self.update()
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                def handle_item_clicked(self, item: QListWidgetItem):
         | 
| 171 | 
            +
                    """Handle item click and emit the canvasSelected signal."""
         | 
| 172 | 
            +
                    canvas = item.data(Qt.UserRole)
         | 
| 173 | 
            +
                    if canvas:
         | 
| 174 | 
            +
                        self.canvasSelected.emit(canvas)
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def delete_canvas(self, canvas: CanvasLayer):
         | 
| 177 | 
            +
                    """Delete a canvas from the list."""
         | 
| 178 | 
            +
                    if canvas in self.canvases:
         | 
| 179 | 
            +
                        canvas.layers.clear()
         | 
| 180 | 
            +
                        logger.info(f"Deleting canvas: {canvas.layer_name}")
         | 
| 181 | 
            +
                        canvas.setVisible(False)
         | 
| 182 | 
            +
                        canvas.deleteLater()
         | 
| 183 | 
            +
                        self.canvases.remove(canvas)
         | 
| 184 | 
            +
                        self.canvasDeleted.emit(canvas)
         | 
| 185 | 
            +
                        self.update_canvas_list()
         | 
| @@ -0,0 +1,138 @@ | |
| 1 | 
            +
            from imagebaker import logger
         | 
| 2 | 
            +
             | 
| 3 | 
            +
             | 
| 4 | 
            +
            from PySide6.QtCore import Qt, Signal
         | 
| 5 | 
            +
            from PySide6.QtGui import QPixmap
         | 
| 6 | 
            +
            from PySide6.QtWidgets import (
         | 
| 7 | 
            +
                QWidget,
         | 
| 8 | 
            +
                QVBoxLayout,
         | 
| 9 | 
            +
                QHBoxLayout,
         | 
| 10 | 
            +
                QPushButton,
         | 
| 11 | 
            +
                QLabel,
         | 
| 12 | 
            +
                QSizePolicy,
         | 
| 13 | 
            +
                QDockWidget,
         | 
| 14 | 
            +
                QListWidget,
         | 
| 15 | 
            +
                QListWidgetItem,
         | 
| 16 | 
            +
            )
         | 
| 17 | 
            +
            from pathlib import Path
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
            class ImageListPanel(QDockWidget):
         | 
| 21 | 
            +
                imageSelected = Signal(Path)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def __init__(
         | 
| 24 | 
            +
                    self,
         | 
| 25 | 
            +
                    image_entries: list["ImageEntry"],
         | 
| 26 | 
            +
                    processed_images: set[Path],
         | 
| 27 | 
            +
                    parent=None,
         | 
| 28 | 
            +
                ):
         | 
| 29 | 
            +
                    """
         | 
| 30 | 
            +
                    :param image_entries: List of image paths to display.
         | 
| 31 | 
            +
                    :param processed_images: Set of image paths that have already been processed.
         | 
| 32 | 
            +
                    """
         | 
| 33 | 
            +
                    super().__init__("Image List", parent)
         | 
| 34 | 
            +
                    self.image_entries: list["ImageEntry"] = image_entries
         | 
| 35 | 
            +
                    self.processed_images = processed_images
         | 
| 36 | 
            +
                    self.current_page = 0
         | 
| 37 | 
            +
                    self.images_per_page = 10
         | 
| 38 | 
            +
                    self.init_ui()
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def init_ui(self):
         | 
| 41 | 
            +
                    """Initialize the UI for the image list panel."""
         | 
| 42 | 
            +
                    logger.info("Initializing ImageListPanel")
         | 
| 43 | 
            +
                    self.setMinimumWidth(150)
         | 
| 44 | 
            +
                    self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    widget = QWidget()
         | 
| 47 | 
            +
                    layout = QVBoxLayout(widget)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    # Image list widget
         | 
| 50 | 
            +
                    self.list_widget = QListWidget()
         | 
| 51 | 
            +
                    self.list_widget.itemClicked.connect(self.handle_item_clicked)  # Connect signal
         | 
| 52 | 
            +
                    layout.addWidget(self.list_widget)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    # Pagination controls
         | 
| 55 | 
            +
                    pagination_layout = QHBoxLayout()
         | 
| 56 | 
            +
                    self.prev_page_btn = QPushButton("Prev")
         | 
| 57 | 
            +
                    self.prev_page_btn.clicked.connect(self.show_prev_page)
         | 
| 58 | 
            +
                    self.next_page_btn = QPushButton("Next")
         | 
| 59 | 
            +
                    self.next_page_btn.clicked.connect(self.show_next_page)
         | 
| 60 | 
            +
                    pagination_layout.addWidget(self.prev_page_btn)
         | 
| 61 | 
            +
                    pagination_layout.addWidget(self.next_page_btn)
         | 
| 62 | 
            +
                    layout.addLayout(pagination_layout)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    # Pagination info
         | 
| 65 | 
            +
                    self.pagination_label = QLabel("Showing 0 of 0")
         | 
| 66 | 
            +
                    layout.addWidget(self.pagination_label)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    self.setWidget(widget)
         | 
| 69 | 
            +
                    self.setFeatures(
         | 
| 70 | 
            +
                        QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
         | 
| 71 | 
            +
                    )
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    self.update_image_list(self.image_entries)
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def show_next_page(self):
         | 
| 76 | 
            +
                    """Show the next page of images"""
         | 
| 77 | 
            +
                    if (self.current_page + 1) * self.images_per_page < len(self.image_entries):
         | 
| 78 | 
            +
                        self.current_page += 1
         | 
| 79 | 
            +
                        self.update_image_list(self.image_entries)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def show_prev_page(self):
         | 
| 82 | 
            +
                    """Show the previous page of images"""
         | 
| 83 | 
            +
                    if self.current_page > 0:
         | 
| 84 | 
            +
                        self.current_page -= 1
         | 
| 85 | 
            +
                        self.update_image_list(self.image_entries)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                def update_image_list(self, image_entries):
         | 
| 88 | 
            +
                    """Update the image list with image paths and baked results."""
         | 
| 89 | 
            +
                    self.list_widget.clear()
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    for idx, image_entry in enumerate(image_entries):
         | 
| 92 | 
            +
                        item_widget = QWidget()
         | 
| 93 | 
            +
                        item_layout = QHBoxLayout(item_widget)
         | 
| 94 | 
            +
                        item_layout.setContentsMargins(5, 5, 5, 5)
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                        # Generate thumbnail
         | 
| 97 | 
            +
                        thumbnail_label = QLabel()
         | 
| 98 | 
            +
                        if image_entry.is_baked_result:
         | 
| 99 | 
            +
                            thumbnail_pixmap = (
         | 
| 100 | 
            +
                                image_entry.data.get_thumbnail()
         | 
| 101 | 
            +
                            )  # Baked result thumbnail
         | 
| 102 | 
            +
                            name_label_text = f"Baked Result {idx + 1}"
         | 
| 103 | 
            +
                        else:
         | 
| 104 | 
            +
                            thumbnail_pixmap = QPixmap(str(image_entry.data)).scaled(
         | 
| 105 | 
            +
                                50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation
         | 
| 106 | 
            +
                            )
         | 
| 107 | 
            +
                            name_label_text = Path(image_entry.data).name
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                        thumbnail_label.setPixmap(thumbnail_pixmap)
         | 
| 110 | 
            +
                        item_layout.addWidget(thumbnail_label)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                        # Text for image
         | 
| 113 | 
            +
                        name_label = QLabel(name_label_text)
         | 
| 114 | 
            +
                        name_label.setStyleSheet("font-weight: bold;")
         | 
| 115 | 
            +
                        item_layout.addWidget(name_label)
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                        item_layout.addStretch()
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                        # Add the custom widget to the list
         | 
| 120 | 
            +
                        list_item = QListWidgetItem(self.list_widget)
         | 
| 121 | 
            +
                        list_item.setSizeHint(item_widget.sizeHint())
         | 
| 122 | 
            +
                        self.list_widget.addItem(list_item)
         | 
| 123 | 
            +
                        self.list_widget.setItemWidget(list_item, item_widget)
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                        # Store metadata for the image
         | 
| 126 | 
            +
                        list_item.setData(Qt.UserRole, image_entry)
         | 
| 127 | 
            +
                    self.pagination_label.setText(
         | 
| 128 | 
            +
                        f"Showing {self.current_page * self.images_per_page + 1} to "
         | 
| 129 | 
            +
                        f"{min((self.current_page + 1) * self.images_per_page, len(image_entries))} "
         | 
| 130 | 
            +
                        f"of {len(image_entries)}"
         | 
| 131 | 
            +
                    )
         | 
| 132 | 
            +
                    self.update()
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                def handle_item_clicked(self, item: QListWidgetItem):
         | 
| 135 | 
            +
                    """Handle item click and emit the imageSelected signal."""
         | 
| 136 | 
            +
                    item_data = item.data(Qt.UserRole)
         | 
| 137 | 
            +
                    if item_data:
         | 
| 138 | 
            +
                        self.imageSelected.emit(item_data)
         |