imagebaker 0.0.49__py3-none-any.whl → 0.0.51__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 CHANGED
@@ -1,5 +1,9 @@
1
1
  from loguru import logger # noqa
2
+ from importlib.metadata import version, PackageNotFoundError
2
3
 
3
4
  logger.info("imagebaker package loaded with loguru logger.")
4
5
 
5
- # __version__ = "0.0.49" # noqa
6
+ try:
7
+ __version__ = version("imagebaker")
8
+ except PackageNotFoundError:
9
+ __version__ = "0.0.0"
@@ -74,6 +74,22 @@ class BaseConfig(BaseModel):
74
74
  logger.info(f"Created assets folder at {asset_dir}")
75
75
  return asset_dir
76
76
 
77
+ @property
78
+ def cache_dir(self):
79
+ cache_dir = self.project_dir / ".imagebaker" / "cache"
80
+ if not cache_dir.exists():
81
+ cache_dir.mkdir(parents=True)
82
+ logger.info(f"Created cache folder at {cache_dir}")
83
+ return cache_dir
84
+
85
+ @property
86
+ def bake_dir(self):
87
+ bake_dir = self.project_dir / ".imagebaker" / "bake"
88
+ if not bake_dir.exists():
89
+ bake_dir.mkdir(parents=True)
90
+ logger.info(f"Created bake folder at {bake_dir}")
91
+ return bake_dir
92
+
77
93
  class Config:
78
94
  arbitrary_types_allowed = True
79
95
 
@@ -96,6 +112,7 @@ class LayerConfig(BaseConfig):
96
112
  )
97
113
  # whether to search image in subfolders as well
98
114
  full_search: bool = False
115
+ cleanup_on_exit: bool = False
99
116
 
100
117
  def get_label_color(self, label):
101
118
  for lbl in self.predefined_labels:
@@ -119,6 +136,7 @@ class CanvasConfig(BaseConfig):
119
136
  write_labels: bool = True
120
137
  write_masks: bool = True
121
138
  fps: int = 5
139
+ max_edge_width: int = 100
122
140
 
123
141
  @property
124
142
  def export_folder(self):
@@ -76,6 +76,8 @@ class LayerState:
76
76
  is_annotable: bool = True
77
77
  status: str = "Ready"
78
78
  drawing_states: list[DrawingState] = field(default_factory=list)
79
+ edge_opacity: int = 100
80
+ edge_width: int = 10
79
81
 
80
82
  def copy(self):
81
83
  return LayerState(
@@ -105,6 +107,8 @@ class LayerState:
105
107
  )
106
108
  for d in self.drawing_states
107
109
  ],
110
+ edge_opacity=self.edge_opacity,
111
+ edge_width=self.edge_width,
108
112
  )
109
113
 
110
114
 
@@ -206,6 +210,10 @@ class Annotation:
206
210
  def load_from_json(path: str):
207
211
  import json
208
212
 
213
+ # if path does not exist, return empty list
214
+ if not Path(path).exists():
215
+ return []
216
+
209
217
  with open(path, "r") as f:
210
218
  data = json.load(f)
211
219
 
@@ -42,6 +42,7 @@ class AnnotableLayer(BaseLayer):
42
42
  annotationCleared = Signal()
43
43
  annotationMoved = Signal()
44
44
  layersChanged = Signal()
45
+ labelUpdated = Signal(tuple)
45
46
 
46
47
  def __init__(self, parent, config: LayerConfig, canvas_config: CanvasConfig):
47
48
  super().__init__(parent, config)
@@ -54,6 +55,7 @@ class AnnotableLayer(BaseLayer):
54
55
  self.file_path: Path = Path("Runtime")
55
56
  self.layers: list[BaseLayer] = []
56
57
  self.is_annotable = True
58
+ self.handle_zoom: float = 1
57
59
 
58
60
  def init_ui(self):
59
61
  logger.info(f"Initializing Layer UI of {self.layer_name}")
@@ -157,14 +159,14 @@ class AnnotableLayer(BaseLayer):
157
159
  self.config.normal_draw_config.brush_alpha,
158
160
  )
159
161
 
160
- pen = QPen(pen_color, self.config.normal_draw_config.line_width)
162
+ pen = QPen(pen_color, self.config.normal_draw_config.line_width / self.scale)
161
163
  brush = QBrush(brush_color, Qt.DiagCrossPattern)
162
164
 
163
165
  if annotation.selected:
164
166
  painter.setPen(
165
167
  QPen(
166
168
  self.config.selected_draw_config.color,
167
- self.config.selected_draw_config.line_width,
169
+ self.config.selected_draw_config.line_width / self.scale,
168
170
  )
169
171
  )
170
172
  painter.setBrush(
@@ -184,8 +186,8 @@ class AnnotableLayer(BaseLayer):
184
186
  elif annotation.points:
185
187
  painter.drawEllipse(
186
188
  annotation.points[0],
187
- self.config.selected_draw_config.ellipse_size,
188
- self.config.selected_draw_config.ellipse_size,
189
+ self.config.selected_draw_config.ellipse_size / self.scale,
190
+ self.config.selected_draw_config.ellipse_size / self.scale,
189
191
  )
190
192
 
191
193
  if is_temp:
@@ -200,8 +202,8 @@ class AnnotableLayer(BaseLayer):
200
202
  for point in annotation.points:
201
203
  painter.drawEllipse(
202
204
  point,
203
- self.config.normal_draw_config.point_size,
204
- self.config.normal_draw_config.point_size,
205
+ self.config.normal_draw_config.point_size / self.scale,
206
+ self.config.normal_draw_config.point_size / self.scale,
205
207
  )
206
208
  elif annotation.rectangle:
207
209
  painter.drawRect(annotation.rectangle)
@@ -223,28 +225,34 @@ class AnnotableLayer(BaseLayer):
223
225
  ]
224
226
  painter.save()
225
227
  painter.setPen(
226
- QPen(Qt.black, self.config.normal_draw_config.control_point_size)
228
+ QPen(
229
+ Qt.black,
230
+ self.config.normal_draw_config.control_point_size / self.scale,
231
+ )
227
232
  )
228
233
  painter.setBrush(QBrush(Qt.white))
229
234
  for corner in corners:
230
235
  painter.drawEllipse(
231
236
  corner,
232
- self.config.normal_draw_config.point_size,
233
- self.config.normal_draw_config.point_size,
237
+ self.config.normal_draw_config.point_size / self.scale,
238
+ self.config.normal_draw_config.point_size / self.scale,
234
239
  )
235
240
  painter.restore()
236
241
 
237
242
  if annotation.polygon and len(annotation.polygon) > 0:
238
243
  painter.save()
239
244
  painter.setPen(
240
- QPen(Qt.white, self.config.normal_draw_config.control_point_size)
245
+ QPen(
246
+ Qt.white,
247
+ self.config.normal_draw_config.control_point_size / self.scale,
248
+ )
241
249
  )
242
250
  painter.setBrush(QBrush(Qt.darkGray))
243
251
  for point in annotation.polygon:
244
252
  painter.drawEllipse(
245
253
  point,
246
- self.config.normal_draw_config.point_size,
247
- self.config.normal_draw_config.point_size,
254
+ self.config.normal_draw_config.point_size / self.scale,
255
+ self.config.normal_draw_config.point_size / self.scale,
248
256
  )
249
257
  painter.restore()
250
258
 
@@ -266,7 +274,7 @@ class AnnotableLayer(BaseLayer):
266
274
  # Set up font
267
275
  font = painter.font()
268
276
  font.setPixelSize(
269
- self.config.normal_draw_config.label_font_size
277
+ self.config.normal_draw_config.label_font_size * self.scale
270
278
  ) # Fixed screen size
271
279
  painter.setFont(font)
272
280
 
@@ -300,7 +308,10 @@ class AnnotableLayer(BaseLayer):
300
308
  painter.save()
301
309
  handle_color = self.config.selected_draw_config.handle_color
302
310
  painter.setPen(
303
- QPen(handle_color, self.config.selected_draw_config.handle_width)
311
+ QPen(
312
+ handle_color,
313
+ self.config.selected_draw_config.handle_width / self.scale,
314
+ )
304
315
  )
305
316
  painter.setBrush(QBrush(handle_color))
306
317
 
@@ -315,8 +326,8 @@ class AnnotableLayer(BaseLayer):
315
326
  ]:
316
327
  painter.drawEllipse(
317
328
  corner,
318
- self.config.selected_draw_config.handle_point_size,
319
- self.config.selected_draw_config.handle_point_size,
329
+ self.config.selected_draw_config.handle_point_size / self.scale,
330
+ self.config.selected_draw_config.handle_point_size / self.scale,
320
331
  )
321
332
  # Draw edge handles
322
333
  for edge in [
@@ -327,8 +338,8 @@ class AnnotableLayer(BaseLayer):
327
338
  ]:
328
339
  painter.drawEllipse(
329
340
  edge,
330
- self.config.selected_draw_config.handle_edge_size,
331
- self.config.selected_draw_config.handle_edge_size,
341
+ self.config.selected_draw_config.handle_edge_size / self.scale,
342
+ self.config.selected_draw_config.handle_edge_size / self.scale,
332
343
  )
333
344
 
334
345
  elif annotation.polygon:
@@ -336,8 +347,8 @@ class AnnotableLayer(BaseLayer):
336
347
  for point in annotation.polygon:
337
348
  painter.drawEllipse(
338
349
  point,
339
- self.config.selected_draw_config.handle_point_size,
340
- self.config.selected_draw_config.handle_point_size,
350
+ self.config.selected_draw_config.handle_point_size / self.scale,
351
+ self.config.selected_draw_config.handle_point_size / self.scale,
341
352
  )
342
353
 
343
354
  painter.restore()
@@ -524,13 +535,11 @@ class AnnotableLayer(BaseLayer):
524
535
  self.current_annotation.polygon = QPolygonF(
525
536
  [p for p in self.current_annotation.polygon][:-1]
526
537
  )
527
- self.update()
528
538
 
529
539
  # If the polygon is now empty, reset to idle mode
530
540
  if len(self.current_annotation.polygon) == 0:
531
541
  self.current_annotation = None
532
542
  self.mouse_mode = MouseMode.IDLE
533
- self.update()
534
543
 
535
544
  # If not drawing a polygon, go to idle mode
536
545
  if not self.current_annotation:
@@ -538,7 +547,7 @@ class AnnotableLayer(BaseLayer):
538
547
  for ann in self.annotations:
539
548
  ann.selected = False
540
549
  self.annotationUpdated.emit(ann)
541
- self.update()
550
+ self.update()
542
551
 
543
552
  # If left-clicked
544
553
  if event.button() == Qt.LeftButton:
@@ -741,8 +750,8 @@ class AnnotableLayer(BaseLayer):
741
750
  and len(self.current_annotation.polygon) >= 3
742
751
  ):
743
752
  self.current_annotation.is_complete = True
753
+
744
754
  self.finalize_annotation()
745
- self.annotationAdded.emit(self.current_annotation)
746
755
  self.current_annotation = None
747
756
 
748
757
  return
@@ -784,6 +793,7 @@ class AnnotableLayer(BaseLayer):
784
793
  self, "Edit Label", "Enter new label:", text=annotation.label
785
794
  )
786
795
  if ok and new_label:
796
+ self.labelUpdated.emit((annotation.label, new_label))
787
797
  annotation.label = new_label
788
798
  self.annotationUpdated.emit(annotation) # Emit signal
789
799
  self.update()
@@ -906,6 +916,8 @@ class AnnotableLayer(BaseLayer):
906
916
  f"{annotation.label} {annotation.annotation_id} {annotation.annotator}"
907
917
  )
908
918
 
919
+ new_layer._apply_edge_opacity()
920
+ new_layer.update()
909
921
  self.messageSignal.emit(f"Layerified: {new_layer.layer_name}")
910
922
  logger.info(f"Num annotations: {len(self.annotations)}")
911
923
 
@@ -18,6 +18,8 @@ from PySide6.QtWidgets import QWidget
18
18
 
19
19
  from typing import Optional
20
20
  from pathlib import Path
21
+ import cv2
22
+ import numpy as np
21
23
 
22
24
 
23
25
  class BaseLayer(QWidget):
@@ -157,7 +159,8 @@ class BaseLayer(QWidget):
157
159
  self.offset: QPointF = QPointF(0, 0)
158
160
  self.pan_start: QPointF = None
159
161
  self.pan_offset: QPointF = None
160
- self.image = QPixmap()
162
+ self._image = QPixmap()
163
+ self._original_image = QPixmap()
161
164
  self.annotations: list[Annotation] = []
162
165
  self.current_annotation: Optional[Annotation] = None
163
166
  self.copied_annotation: Optional[Annotation] = None
@@ -365,8 +368,120 @@ class BaseLayer(QWidget):
365
368
  self.reset_view()
366
369
  self.update()
367
370
 
371
+ self._original_image = self.image.copy() # Store a copy of the original image
368
372
  self.original_size = QSizeF(self.image.size()) # Store original size
369
373
 
374
+ @property
375
+ def image(self):
376
+ """
377
+ Get the current image of the canvas layer.
378
+
379
+ Returns:
380
+ QPixmap: The current image of the canvas layer.
381
+ """
382
+ return self._image
383
+
384
+ @image.setter
385
+ def image(self, value: QPixmap):
386
+ """
387
+ Set the image of the canvas layer.
388
+
389
+ Args:
390
+ value (QPixmap): The new image for the canvas layer.
391
+ """
392
+ self._image = value
393
+
394
+ def _apply_edge_opacity(self):
395
+ """
396
+ Apply edge opacity to the image. This function modifies the edges of the image
397
+ to have reduced opacity based on the configuration.
398
+ """
399
+ logger.debug("Applying edge opacity to the image.")
400
+ edge_width = self.edge_width
401
+ edge_opacity = self.edge_opacity
402
+
403
+ # Convert QPixmap to QImage for pixel manipulation
404
+ image = self._original_image.toImage()
405
+ image = image.convertToFormat(
406
+ QImage.Format_ARGB32
407
+ ) # Ensure format supports alpha
408
+
409
+ width = image.width()
410
+ height = image.height()
411
+ annotation = self.annotations[0] if self.annotations else None
412
+ if annotation is None:
413
+ return
414
+
415
+ if annotation.rectangle:
416
+ for x in range(width):
417
+ for y in range(height):
418
+ color = image.pixelColor(x, y)
419
+ if color.alpha() != 0: # If the pixel is not fully transparent
420
+ # Calculate horizontal and vertical distances to the edges
421
+ horizontal_distance = min(x, width - x - 1)
422
+ vertical_distance = min(y, height - y - 1)
423
+
424
+ # If the pixel is within the edge region
425
+ if (
426
+ horizontal_distance < edge_width
427
+ or vertical_distance < edge_width
428
+ ):
429
+ distance_to_edge = min(
430
+ horizontal_distance, vertical_distance
431
+ )
432
+ # Calculate the new alpha based on the distance to the edge
433
+ factor = (edge_width - distance_to_edge) / edge_width
434
+ new_alpha = int(
435
+ color.alpha()
436
+ * ((1 - factor) + (factor * (edge_opacity / 255.0)))
437
+ )
438
+ color.setAlpha(new_alpha)
439
+ image.setPixelColor(x, y, color)
440
+
441
+ elif annotation.polygon:
442
+ # Extract alpha channel and find contours
443
+ alpha_image = image.convertToFormat(QImage.Format_Alpha8)
444
+ bytes_per_line = (
445
+ alpha_image.bytesPerLine()
446
+ ) # Get the stride (bytes per line)
447
+ alpha_data = alpha_image.bits().tobytes()
448
+
449
+ # Extract only the valid data (remove padding)
450
+ alpha_array = np.frombuffer(alpha_data, dtype=np.uint8).reshape(
451
+ (alpha_image.height(), bytes_per_line)
452
+ )[
453
+ :, : alpha_image.width()
454
+ ] # Remove padding to match the actual width
455
+
456
+ # Use OpenCV to find contours
457
+ contours, _ = cv2.findContours(
458
+ alpha_array, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
459
+ )
460
+
461
+ # Iterate over each pixel and apply edge opacity
462
+ for x in range(width):
463
+ for y in range(height):
464
+ color = image.pixelColor(x, y)
465
+ if color.alpha() != 0: # If the pixel is not fully transparent
466
+ # Calculate distance to the nearest contour
467
+ distance_to_edge = cv2.pointPolygonTest(
468
+ contours[0], (x, y), True
469
+ ) # True for distance calculation
470
+
471
+ # If the pixel is within the edge region
472
+ if 0 <= distance_to_edge < edge_width:
473
+ # Calculate the new alpha based on the distance to the edge
474
+ factor = (edge_width - distance_to_edge) / edge_width
475
+ new_alpha = int(
476
+ color.alpha()
477
+ * ((1 - factor) + (factor * (edge_opacity / 255.0)))
478
+ )
479
+ color.setAlpha(new_alpha)
480
+ image.setPixelColor(x, y, color)
481
+
482
+ # Convert the modified QImage back to QPixmap
483
+ self.image = QPixmap.fromImage(image)
484
+
370
485
  def get_thumbnail(self, annotation: Annotation = None):
371
486
  """
372
487
  Generate a thumbnail for the layer or a specific annotation.
@@ -722,3 +837,19 @@ class BaseLayer(QWidget):
722
837
  @drawing_states.setter
723
838
  def drawing_states(self, value: list[DrawingState]):
724
839
  self.layer_state.drawing_states = value
840
+
841
+ @property
842
+ def edge_opacity(self) -> int:
843
+ return self.layer_state.edge_opacity
844
+
845
+ @edge_opacity.setter
846
+ def edge_opacity(self, value: int):
847
+ self.layer_state.edge_opacity = value
848
+
849
+ @property
850
+ def edge_width(self) -> int:
851
+ return self.layer_state.edge_width
852
+
853
+ @edge_width.setter
854
+ def edge_width(self, value: int):
855
+ self.layer_state.edge_width = value
@@ -27,6 +27,7 @@ from PySide6.QtGui import (
27
27
  QMouseEvent,
28
28
  QKeyEvent,
29
29
  QTransform,
30
+ QImage,
30
31
  )
31
32
  from PySide6.QtWidgets import (
32
33
  QApplication,
@@ -57,7 +58,6 @@ class CanvasLayer(BaseLayer):
57
58
  config (CanvasConfig): Configuration settings for the canvas layer.
58
59
  """
59
60
  super().__init__(parent, config)
60
- self.image = QPixmap()
61
61
  self.is_annotable = False
62
62
  self.last_pan_point = None
63
63
  self.state_thumbnail = dict()
@@ -178,6 +178,7 @@ class CanvasLayer(BaseLayer):
178
178
  opacity = layer.opacity / 255.0
179
179
  temp_painter.setOpacity(opacity) # Scale opacity to 0.0-1.0
180
180
  temp_painter.drawPixmap(0, 0, layer.image)
181
+
181
182
  temp_painter.end()
182
183
 
183
184
  # Draw the modified pixmap
@@ -287,20 +288,20 @@ class CanvasLayer(BaseLayer):
287
288
  painter.setPen(
288
289
  QPen(
289
290
  self.config.selected_draw_config.handle_color,
290
- self.config.selected_draw_config.handle_width,
291
+ self.config.selected_draw_config.handle_width / self.scale,
291
292
  )
292
293
  )
293
294
  painter.setBrush(self.config.selected_draw_config.handle_color)
294
295
  painter.drawEllipse(
295
296
  rotation_pos,
296
- self.config.selected_draw_config.handle_point_size * 2,
297
- self.config.selected_draw_config.handle_point_size * 2,
297
+ self.config.selected_draw_config.handle_point_size * 1.1 / self.scale,
298
+ self.config.selected_draw_config.handle_point_size * 1.1 / self.scale,
298
299
  )
299
300
  # now draw rotation symbol
300
301
  painter.setPen(
301
302
  QPen(
302
303
  self.config.selected_draw_config.handle_color,
303
- self.config.selected_draw_config.handle_width,
304
+ self.config.selected_draw_config.handle_width / self.scale,
304
305
  )
305
306
  )
306
307
  painter.drawLine(
@@ -323,21 +324,23 @@ class CanvasLayer(BaseLayer):
323
324
  # Draw scale handles
324
325
  handle_color = self.config.selected_draw_config.handle_color
325
326
  painter.setPen(
326
- QPen(handle_color, self.config.selected_draw_config.handle_width)
327
+ QPen(
328
+ handle_color, self.config.selected_draw_config.handle_width / self.scale
329
+ )
327
330
  )
328
331
  painter.setBrush(self.config.selected_draw_config.handle_color)
329
332
  for corner in corners:
330
333
  painter.drawEllipse(
331
334
  corner,
332
- self.config.selected_draw_config.handle_point_size,
333
- self.config.selected_draw_config.handle_point_size,
335
+ self.config.selected_draw_config.handle_point_size / self.scale,
336
+ self.config.selected_draw_config.handle_point_size / self.scale,
334
337
  )
335
338
  for edge in edges:
336
339
  # draw small circles on the edges
337
340
  painter.drawEllipse(
338
341
  edge,
339
- self.config.selected_draw_config.handle_edge_size,
340
- self.config.selected_draw_config.handle_edge_size,
342
+ self.config.selected_draw_config.handle_edge_size / self.scale,
343
+ self.config.selected_draw_config.handle_edge_size / self.scale,
341
344
  )
342
345
  # draw sides
343
346
  painter.drawLine(
@@ -874,6 +877,33 @@ class CanvasLayer(BaseLayer):
874
877
  """
875
878
  self.export_current_state(export_to_annotation_tab=True)
876
879
 
880
+ def seek_state(self, step):
881
+ """Seek to a specific state using the timeline slider."""
882
+ self.messageSignal.emit(f"Seeking to step {step}")
883
+ logger.info(f"Seeking to step {step}")
884
+
885
+ # Get the states for the selected step
886
+ if step in self.states:
887
+ states = self.states[step]
888
+ for state in states:
889
+ layer = self.get_layer(state.layer_id)
890
+ if layer:
891
+ # Update the layer's state
892
+ update_opacities = False
893
+ logger.debug(
894
+ f"Updating layer {layer.layer_name} with state: {state}"
895
+ )
896
+
897
+ if (
898
+ layer.edge_width != state.edge_width
899
+ or layer.edge_opacity != state.edge_opacity
900
+ ):
901
+ update_opacities = True
902
+ layer.layer_state = state
903
+ if update_opacities:
904
+ layer._apply_edge_opacity()
905
+ layer.update()
906
+
877
907
  def play_states(self):
878
908
  """Play all the states stored in self.states."""
879
909
  if len(self.states) == 0:
@@ -896,7 +926,19 @@ class CanvasLayer(BaseLayer):
896
926
  layer = self.get_layer(state.layer_id)
897
927
  if layer:
898
928
  # Update the layer's state
929
+ update_opacities = False
930
+ logger.debug(
931
+ f"Updating layer {layer.layer_name} with state: {state}"
932
+ )
933
+
934
+ if (
935
+ layer.edge_width != state.edge_width
936
+ or layer.edge_opacity != state.edge_opacity
937
+ ):
938
+ update_opacities = True
899
939
  layer.layer_state = state
940
+ if update_opacities:
941
+ layer._apply_edge_opacity()
900
942
  layer.update()
901
943
 
902
944
  # Update the UI to reflect the changes
@@ -988,7 +1030,7 @@ class CanvasLayer(BaseLayer):
988
1030
  logger.info(f"Saved baked image to {filename}")
989
1031
  if self.config.write_annotations:
990
1032
  image = qpixmap_to_numpy(image.copy())
991
- image = cv2.cvtColor(image, cv2.COLOR_RGBA2BGR)
1033
+ image = cv2.cvtColor(image, cv2.COLOR_RGBA2BGRA)
992
1034
  drawn = draw_annotations(image, annotations)
993
1035
  write_to = filename.parent / f"annotated_{filename.name}"
994
1036
 
@@ -52,6 +52,7 @@ class AnnotationList(QDockWidget):
52
52
  if self.layer is None:
53
53
  return
54
54
  for idx, ann in enumerate(self.layer.annotations):
55
+
55
56
  item = QListWidgetItem(self.list_widget)
56
57
 
57
58
  # Create container widget
@@ -19,6 +19,7 @@ from pathlib import Path
19
19
 
20
20
  class ImageListPanel(QDockWidget):
21
21
  imageSelected = Signal(Path)
22
+ activeImageEntries = Signal(list)
22
23
 
23
24
  def __init__(
24
25
  self,
@@ -26,6 +27,7 @@ class ImageListPanel(QDockWidget):
26
27
  processed_images: set[Path],
27
28
  parent=None,
28
29
  max_name_length=15,
30
+ images_per_page=10,
29
31
  ):
30
32
  """
31
33
  :param image_entries: List of image paths to display.
@@ -35,7 +37,7 @@ class ImageListPanel(QDockWidget):
35
37
  self.image_entries: list["ImageEntry"] = image_entries
36
38
  self.processed_images = processed_images
37
39
  self.current_page = 0
38
- self.images_per_page = 10
40
+ self.images_per_page = images_per_page
39
41
  self.max_name_length = max_name_length
40
42
  self.init_ui()
41
43
 
@@ -90,7 +92,20 @@ class ImageListPanel(QDockWidget):
90
92
  """Update the image list with image paths and baked results."""
91
93
  self.list_widget.clear()
92
94
 
93
- for idx, image_entry in enumerate(image_entries):
95
+ # Calculate the range of images to display for the current page
96
+ start_index = self.current_page * self.images_per_page
97
+ end_index = min(start_index + self.images_per_page, len(image_entries))
98
+
99
+ # Update the pagination label
100
+ self.pagination_label.setText(
101
+ f"Showing {start_index + 1} to {end_index} of {len(image_entries)}"
102
+ )
103
+
104
+ # Display only the images for the current page
105
+ active_image_entries = []
106
+ for idx, image_entry in enumerate(
107
+ image_entries[start_index:end_index], start=start_index + 1
108
+ ):
94
109
  item_widget = QWidget()
95
110
  item_layout = QHBoxLayout(item_widget)
96
111
  item_layout.setContentsMargins(5, 5, 5, 5)
@@ -101,7 +116,7 @@ class ImageListPanel(QDockWidget):
101
116
  thumbnail_pixmap = (
102
117
  image_entry.data.get_thumbnail()
103
118
  ) # Baked result thumbnail
104
- name_label_text = f"Baked Result {idx + 1}"
119
+ name_label_text = f"Baked Result {idx}"
105
120
  else:
106
121
  thumbnail_pixmap = QPixmap(str(image_entry.data)).scaled(
107
122
  50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation
@@ -126,11 +141,9 @@ class ImageListPanel(QDockWidget):
126
141
 
127
142
  # Store metadata for the image
128
143
  list_item.setData(Qt.UserRole, image_entry)
129
- self.pagination_label.setText(
130
- f"Showing {self.current_page * self.images_per_page + 1} to "
131
- f"{min((self.current_page + 1) * self.images_per_page, len(image_entries))} "
132
- f"of {len(image_entries)}"
133
- )
144
+ active_image_entries.append(image_entry)
145
+
146
+ self.activeImageEntries.emit(active_image_entries)
134
147
  self.update()
135
148
 
136
149
  def handle_item_clicked(self, item: QListWidgetItem):