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)
|