imagebaker 0.0.50__py3-none-any.whl → 0.0.53__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.
@@ -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:
@@ -78,6 +78,7 @@ class LayerState:
78
78
  drawing_states: list[DrawingState] = field(default_factory=list)
79
79
  edge_opacity: int = 100
80
80
  edge_width: int = 10
81
+ caption: str = ""
81
82
 
82
83
  def copy(self):
83
84
  return LayerState(
@@ -109,6 +110,7 @@ class LayerState:
109
110
  ],
110
111
  edge_opacity=self.edge_opacity,
111
112
  edge_width=self.edge_width,
113
+ caption=self.caption,
112
114
  )
113
115
 
114
116
 
@@ -138,6 +140,7 @@ class Annotation:
138
140
  file_path: Path = field(default_factory=lambda: Path("Runtime"))
139
141
  is_model_generated: bool = False
140
142
  model_name: str = None
143
+ caption: str = ""
141
144
 
142
145
  def copy(self):
143
146
  ann = Annotation(
@@ -156,6 +159,7 @@ class Annotation:
156
159
  file_path=self.file_path,
157
160
  is_model_generated=self.is_model_generated,
158
161
  model_name=self.model_name,
162
+ caption=self.caption,
159
163
  )
160
164
  ann.is_selected = False
161
165
  return ann
@@ -200,6 +204,7 @@ class Annotation:
200
204
  "file_path": str(annotation.file_path),
201
205
  "is_model_generated": annotation.is_model_generated,
202
206
  "model_name": annotation.model_name,
207
+ "caption": annotation.caption,
203
208
  }
204
209
  annotations_dict.append(data)
205
210
 
@@ -210,6 +215,10 @@ class Annotation:
210
215
  def load_from_json(path: str):
211
216
  import json
212
217
 
218
+ # if path does not exist, return empty list
219
+ if not Path(path).exists():
220
+ return []
221
+
213
222
  with open(path, "r") as f:
214
223
  data = json.load(f)
215
224
 
@@ -230,6 +239,7 @@ class Annotation:
230
239
  file_path=Path(d.get("file_path", "Runtime")),
231
240
  is_model_generated=d.get("is_model_generated", False),
232
241
  model_name=d.get("model_name", None),
242
+ caption=d.get("caption", ""),
233
243
  )
234
244
 
235
245
  # Handle points safely
@@ -24,6 +24,7 @@ from PySide6.QtGui import (
24
24
  QWheelEvent,
25
25
  QMouseEvent,
26
26
  QKeyEvent,
27
+ QFont,
27
28
  )
28
29
  from PySide6.QtWidgets import (
29
30
  QApplication,
@@ -42,6 +43,7 @@ class AnnotableLayer(BaseLayer):
42
43
  annotationCleared = Signal()
43
44
  annotationMoved = Signal()
44
45
  layersChanged = Signal()
46
+ labelUpdated = Signal(tuple)
45
47
 
46
48
  def __init__(self, parent, config: LayerConfig, canvas_config: CanvasConfig):
47
49
  super().__init__(parent, config)
@@ -67,14 +69,23 @@ class AnnotableLayer(BaseLayer):
67
69
  self.annotationCleared.emit()
68
70
  self.update()
69
71
 
72
+ def toggle_annotation_visibility(self):
73
+ """Toggle visibility of all annotations."""
74
+ selected_annotation = self._get_selected_annotation()
75
+ if selected_annotation is not None:
76
+ selected_annotation.visible = not selected_annotation.visible
77
+ self.annotationUpdated.emit(selected_annotation)
78
+ self.update()
79
+
70
80
  def handle_key_press(self, event: QKeyEvent):
71
81
  # Handle Ctrl key for panning
72
82
  if event.key() == Qt.Key_Control:
73
- if (
74
- self.mouse_mode != MouseMode.POLYGON
75
- ): # Only activate pan mode when not drawing polygons
83
+ # disable this for now to allow pannings
84
+ # if (
85
+ # self.mouse_mode != MouseMode.POLYGON
86
+ # ): # Only activate pan mode when not drawing polygons
76
87
 
77
- self.mouse_mode = MouseMode.PAN
88
+ self.mouse_mode = MouseMode.PAN
78
89
 
79
90
  # Handle Ctrl+C for copy
80
91
  if event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_C:
@@ -257,7 +268,7 @@ class AnnotableLayer(BaseLayer):
257
268
 
258
269
  # Draw labels
259
270
  if annotation.is_complete and annotation.label:
260
- painter.save()
271
+ # painter.save()
261
272
  label_pos = self.get_label_position(annotation)
262
273
  text = annotation.label
263
274
 
@@ -297,8 +308,58 @@ class AnnotableLayer(BaseLayer):
297
308
  # Draw text
298
309
  painter.setPen(Qt.white)
299
310
  painter.drawText(bg_rect, Qt.AlignCenter, text)
300
- painter.restore()
301
- self.label_rects.append((bg_rect, annotation))
311
+
312
+ # now if the annotations has caption,
313
+ # draw the caption below the label in itallic
314
+ # just below the label, in a font lighter than a label
315
+
316
+ if annotation.caption:
317
+ caption_font = painter.font()
318
+ # Draw a background rectangle for the caption to ensure visibility
319
+ caption_text = annotation.caption
320
+ caption_font.setItalic(True)
321
+ caption_font.setPixelSize(
322
+ self.config.selected_draw_config.label_font_size * self.scale + 2
323
+ )
324
+ caption_font.setWeight(QFont.Light)
325
+ painter.setFont(caption_font)
326
+ metrics = painter.fontMetrics()
327
+ text_width = metrics.horizontalAdvance(caption_text)
328
+ text_height = metrics.height()
329
+ # Draw background rectangle for caption
330
+ caption_rect = QRectF(
331
+ widget_pos.x() - text_width / 2 - 2,
332
+ widget_pos.y() + text_height / 2 - 2,
333
+ text_width + 4,
334
+ text_height + 4,
335
+ )
336
+ painter.setPen(Qt.NoPen)
337
+ painter.setBrush(
338
+ self.config.normal_draw_config.label_font_background_color
339
+ )
340
+ painter.drawRect(caption_rect)
341
+ # Draw caption text
342
+ painter.setPen(Qt.white)
343
+ painter.drawText(caption_rect, Qt.AlignCenter, caption_text)
344
+ caption_font.setItalic(True)
345
+ caption_font.setPixelSize(
346
+ self.config.selected_draw_config.label_font_size * self.scale + 2
347
+ )
348
+ caption_font.setWeight(QFont.Light)
349
+ painter.setFont(caption_font)
350
+ metrics = painter.fontMetrics()
351
+ text_width = metrics.horizontalAdvance(annotation.caption)
352
+ text_height = metrics.height()
353
+ painter.setPen(Qt.white)
354
+ painter.setBrush(Qt.gray)
355
+
356
+ caption_rect = QRectF(
357
+ widget_pos.x() - text_width / 2 - 2,
358
+ widget_pos.y() + text_height / 2 - 2,
359
+ text_width + 4,
360
+ text_height + 4,
361
+ )
362
+ painter.drawText(caption_rect, Qt.AlignCenter, annotation.caption)
302
363
 
303
364
  painter.restore()
304
365
 
@@ -397,7 +458,14 @@ class AnnotableLayer(BaseLayer):
397
458
  MouseMode.ZOOM_IN,
398
459
  MouseMode.ZOOM_OUT,
399
460
  ]:
400
- self.mouse_mode = MouseMode.IDLE
461
+ if self.current_annotation:
462
+ if (
463
+ self.current_annotation.polygon
464
+ and not self.current_annotation.is_complete
465
+ ):
466
+ self.mouse_mode = MouseMode.POLYGON
467
+ else:
468
+ self.mouse_mode = MouseMode.IDLE
401
469
 
402
470
  # Clean up transformation state
403
471
  if hasattr(self, "selected_annotation"):
@@ -534,13 +602,11 @@ class AnnotableLayer(BaseLayer):
534
602
  self.current_annotation.polygon = QPolygonF(
535
603
  [p for p in self.current_annotation.polygon][:-1]
536
604
  )
537
- self.update()
538
605
 
539
606
  # If the polygon is now empty, reset to idle mode
540
607
  if len(self.current_annotation.polygon) == 0:
541
608
  self.current_annotation = None
542
609
  self.mouse_mode = MouseMode.IDLE
543
- self.update()
544
610
 
545
611
  # If not drawing a polygon, go to idle mode
546
612
  if not self.current_annotation:
@@ -548,7 +614,7 @@ class AnnotableLayer(BaseLayer):
548
614
  for ann in self.annotations:
549
615
  ann.selected = False
550
616
  self.annotationUpdated.emit(ann)
551
- self.update()
617
+ self.update()
552
618
 
553
619
  # If left-clicked
554
620
  if event.button() == Qt.LeftButton:
@@ -603,6 +669,17 @@ class AnnotableLayer(BaseLayer):
603
669
  elif self.mouse_mode == MouseMode.POLYGON:
604
670
  # If not double-click
605
671
  if not self.current_annotation:
672
+ # if this point is equal to the last point of the previous polygon, then ignore it
673
+ if len(self.annotations) > 0:
674
+ last_polygon = self.annotations[-1].polygon
675
+ if last_polygon:
676
+ last_point = last_polygon[-1]
677
+ if last_point == clamped_pos:
678
+ logger.info(
679
+ "Ignoring point, same as last polygon point"
680
+ )
681
+ return
682
+
606
683
  self.current_annotation = Annotation(
607
684
  file_path=self.file_path,
608
685
  annotation_id=len(self.annotations),
@@ -751,8 +828,8 @@ class AnnotableLayer(BaseLayer):
751
828
  and len(self.current_annotation.polygon) >= 3
752
829
  ):
753
830
  self.current_annotation.is_complete = True
831
+
754
832
  self.finalize_annotation()
755
- self.annotationAdded.emit(self.current_annotation)
756
833
  self.current_annotation = None
757
834
 
758
835
  return
@@ -794,6 +871,7 @@ class AnnotableLayer(BaseLayer):
794
871
  self, "Edit Label", "Enter new label:", text=annotation.label
795
872
  )
796
873
  if ok and new_label:
874
+ self.labelUpdated.emit((annotation.label, new_label))
797
875
  annotation.label = new_label
798
876
  self.annotationUpdated.emit(annotation) # Emit signal
799
877
  self.update()
@@ -811,8 +889,6 @@ class AnnotableLayer(BaseLayer):
811
889
  self.current_annotation
812
890
  )
813
891
  self.annotationAdded.emit(self.current_annotation)
814
- self.current_annotation = None
815
- self.update()
816
892
  else:
817
893
  # Show custom label dialog
818
894
  label, ok = QInputDialog.getText(self, "Label", "Enter label name:")
@@ -827,8 +903,8 @@ class AnnotableLayer(BaseLayer):
827
903
  )
828
904
  self.annotationAdded.emit(self.current_annotation)
829
905
  self.current_annotation.annotation_id = len(self.annotations)
830
- self.current_annotation = None
831
- self.update()
906
+ self.current_annotation = None
907
+ self.update()
832
908
 
833
909
  # in update, update cursor
834
910
 
@@ -915,6 +991,7 @@ class AnnotableLayer(BaseLayer):
915
991
  new_layer.layer_name = (
916
992
  f"{annotation.label} {annotation.annotation_id} {annotation.annotator}"
917
993
  )
994
+ new_layer.caption = annotation.caption
918
995
 
919
996
  new_layer._apply_edge_opacity()
920
997
  new_layer.update()
@@ -240,7 +240,7 @@ class BaseLayer(QWidget):
240
240
  for step, state in enumerate(intermediate_states):
241
241
  step += self.current_step
242
242
 
243
- logger.info(f"Saving state {step} for layer {layer.layer_id}")
243
+ logger.info(f"Saving state {step} for layer {layer.layer_id}: {state}")
244
244
  state.selected = False
245
245
  if step not in curr_states:
246
246
  curr_states[step] = []
@@ -853,3 +853,11 @@ class BaseLayer(QWidget):
853
853
  @edge_width.setter
854
854
  def edge_width(self, value: int):
855
855
  self.layer_state.edge_width = value
856
+
857
+ @property
858
+ def caption(self) -> str:
859
+ return self.layer_state.caption
860
+
861
+ @caption.setter
862
+ def caption(self, value: str):
863
+ self.layer_state.caption = value
@@ -27,7 +27,6 @@ from PySide6.QtGui import (
27
27
  QMouseEvent,
28
28
  QKeyEvent,
29
29
  QTransform,
30
- QImage,
31
30
  )
32
31
  from PySide6.QtWidgets import (
33
32
  QApplication,
@@ -127,7 +126,6 @@ class CanvasLayer(BaseLayer):
127
126
 
128
127
  ## Helper functions ##
129
128
  def handle_key_press(self, event: QKeyEvent):
130
-
131
129
  # Handle Delete key
132
130
  if event.key() == Qt.Key_Delete:
133
131
  self._delete_layer()
@@ -149,6 +147,15 @@ class CanvasLayer(BaseLayer):
149
147
  if event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_V:
150
148
  self._paste_layer()
151
149
  return # Important: return after handling
150
+ # if event.key() == Qt.Key_H:
151
+ # if self.selected_layer:
152
+ # # Toggle visibility of the selected layer
153
+ # logger.info(
154
+ # f"Toggling visibility of layer: {self.selected_layer.layer_name}"
155
+ # )
156
+ # self.selected_layer.visible = not self.selected_layer.visible
157
+ # self.selected_layer.update()
158
+ # return # Important: return after handling
152
159
 
153
160
  def paint_layer(self, painter: QPainter):
154
161
  """
@@ -362,7 +369,16 @@ class CanvasLayer(BaseLayer):
362
369
  """
363
370
  """Add a new drawing state."""
364
371
  self.selected_layer = self._get_selected_layer()
365
- layer = self.selected_layer if self.selected_layer else self
372
+ layer = self.selected_layer
373
+ if not layer:
374
+ logger.debug("No layer selected for drawing.")
375
+ # show popup message window that closes in 2 seconds
376
+ QMessageBox.information(
377
+ self.parent(),
378
+ "No Layer Selected",
379
+ "Please select a layer to draw on.",
380
+ )
381
+ return
366
382
 
367
383
  # Convert the position to be relative to the layer
368
384
  relative_pos = pos - layer.position
@@ -384,8 +400,14 @@ class CanvasLayer(BaseLayer):
384
400
  )
385
401
  layer.layer_state.drawing_states.append(drawing_state)
386
402
  self._last_draw_point = relative_pos # Update the last draw point
387
- # logger.debug(f"Added drawing state at position: {relative_pos}")
388
-
403
+ logger.debug(
404
+ f"Added drawing state at position: {relative_pos} to layer {layer.layer_name}"
405
+ )
406
+ else:
407
+ logger.debug(
408
+ f"Mouse mode {self.mouse_mode} does not support drawing states."
409
+ )
410
+ return
389
411
  self.update() # Refresh the canvas to show the new drawing
390
412
 
391
413
  def handle_wheel(self, event: QWheelEvent):
@@ -822,6 +844,7 @@ class CanvasLayer(BaseLayer):
822
844
  filename = self.config.export_folder / f"{filename}.png"
823
845
  logger.info(f"Exporting baked image to {filename}")
824
846
  self.states = {0: [layer.layer_state for layer in self.layers]}
847
+ logger.debug(f"Exporting states: {self.states}")
825
848
 
826
849
  self.loading_dialog = QProgressDialog(
827
850
  "Baking Please wait...", "Cancel", 0, 0, self.parentWidget()
@@ -1030,7 +1053,7 @@ class CanvasLayer(BaseLayer):
1030
1053
  logger.info(f"Saved baked image to {filename}")
1031
1054
  if self.config.write_annotations:
1032
1055
  image = qpixmap_to_numpy(image.copy())
1033
- image = cv2.cvtColor(image, cv2.COLOR_RGBA2BGR)
1056
+ image = cv2.cvtColor(image, cv2.COLOR_RGBA2BGRA)
1034
1057
  drawn = draw_annotations(image, annotations)
1035
1058
  write_to = filename.parent / f"annotated_{filename.name}"
1036
1059
 
@@ -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
@@ -200,5 +201,6 @@ class AnnotationList(QDockWidget):
200
201
  self.layer.paste_annotation()
201
202
  elif event.key() == Qt.Key_Delete:
202
203
  self.delete_annotation(self.layer.selected_annotation_index)
203
- else:
204
- self.parentWidget().keyPressEvent(event)
204
+ elif event.key() == Qt.Key_H:
205
+ self.layer.toggle_annotation_visibility()
206
+ # self.parentWidget().keyPressEvent(event)
@@ -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,14 @@ 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)
147
+ # also set first item as selected if there are any items
148
+ if self.list_widget.count() > 0:
149
+ self.list_widget.setCurrentRow(0)
150
+ self.list_widget.setFocus()
151
+
134
152
  self.update()
135
153
 
136
154
  def handle_item_clicked(self, item: QListWidgetItem):
@@ -8,6 +8,7 @@ from PySide6.QtWidgets import (
8
8
  QSizePolicy,
9
9
  QDockWidget,
10
10
  QSlider,
11
+ QLineEdit,
11
12
  )
12
13
 
13
14
  from imagebaker.core.defs import LayerState
@@ -82,6 +83,32 @@ class LayerSettings(QDockWidget):
82
83
  )
83
84
  self.main_layout.addWidget(self.edge_width_slider["widget"])
84
85
 
86
+ # Caption input
87
+ caption_layout = QHBoxLayout()
88
+ caption_label = QLabel("Caption:")
89
+ caption_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
90
+ self.caption_input = QLineEdit()
91
+ self.caption_input.setPlaceholderText("Enter caption...")
92
+ self.caption_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
93
+ caption_layout.addWidget(caption_label)
94
+ caption_layout.addWidget(self.caption_input)
95
+ self.main_layout.addLayout(caption_layout)
96
+
97
+ # Set initial value if layer is selected
98
+ if self.selected_layer:
99
+ self.caption_input.setText(self.selected_layer.caption)
100
+
101
+ # Update layer caption on editing finished
102
+ def update_caption():
103
+ if self.selected_layer:
104
+ logger.info(
105
+ f"Updating caption for layer {self.selected_layer.layer_name} to {self.caption_input.text()}"
106
+ )
107
+ self.selected_layer.caption = self.caption_input.text()
108
+ self.selected_layer.update()
109
+
110
+ self.caption_input.editingFinished.connect(update_caption)
111
+
85
112
  # Add stretch to push content to the top
86
113
  self.main_layout.addStretch()
87
114
 
@@ -177,6 +204,7 @@ class LayerSettings(QDockWidget):
177
204
  edge_opacity=self.selected_layer.edge_opacity,
178
205
  edge_width=self.selected_layer.edge_width,
179
206
  visible=self.selected_layer.visible,
207
+ caption=self.selected_layer.caption,
180
208
  )
181
209
  logger.info(f"Storing state {bake_settings}")
182
210
  self.messageSignal.emit(f"Stored state for {bake_settings.layer_name}")
@@ -240,6 +268,7 @@ class LayerSettings(QDockWidget):
240
268
  self.edge_width_slider["slider"].setValue(
241
269
  int(self.selected_layer.edge_width)
242
270
  )
271
+ self.caption_input.setText(self.selected_layer.caption)
243
272
  else:
244
273
  self.widget.setEnabled(False)
245
274
  self.layer_name_label.setText("No BaseLayer")
@@ -18,7 +18,6 @@ from PySide6.QtWidgets import (
18
18
  QSlider,
19
19
  QLabel,
20
20
  QSpinBox,
21
- QComboBox,
22
21
  )
23
22
  from collections import deque
24
23
 
@@ -485,5 +484,54 @@ class BakerTab(QWidget):
485
484
  self.layer_list.update_list()
486
485
  self.messageSignal.emit(f"Added new layer: {new_layer.layer_name}")
487
486
 
487
+ if event.key() == Qt.Key_H:
488
+ # if pressed h key, then toggle visibility of layer
489
+ selected_layer = self.current_canvas._get_selected_layer()
490
+ if selected_layer:
491
+ selected_layer.visible = not selected_layer.visible
492
+ selected_layer.update()
493
+ self.current_canvas.update()
494
+ self.layer_settings.update_sliders()
495
+ self.messageSignal.emit(
496
+ f"Toggled visibility of annotations in layer: {selected_layer.layer_name}"
497
+ )
498
+ # if pressed W key, move the order of the selected layer up
499
+ # make it circular, if the first layer is selected, then move it to the last
500
+ if event.key() == Qt.Key_W:
501
+ selected_layer = self.current_canvas._get_selected_layer()
502
+ if selected_layer:
503
+ index = self.current_canvas.layers.index(selected_layer)
504
+ if index > 0:
505
+ # Move the layer up
506
+ self.current_canvas.layers.insert(index - 1, selected_layer)
507
+ del self.current_canvas.layers[index + 1]
508
+ else:
509
+ # Move the first layer to the end
510
+ self.current_canvas.layers.append(selected_layer)
511
+ del self.current_canvas.layers[0]
512
+ self.current_canvas.update()
513
+ self.layer_list.update_list()
514
+ self.messageSignal.emit(
515
+ f"Moved layer {selected_layer.layer_name} up in order."
516
+ )
517
+ # if pressed S key, move the order of the selected layer down
518
+ # make it circular, if the last layer is selected, then move it to the first
519
+ if event.key() == Qt.Key_S:
520
+ selected_layer = self.current_canvas._get_selected_layer()
521
+ if selected_layer:
522
+ index = self.current_canvas.layers.index(selected_layer)
523
+ if index < len(self.current_canvas.layers) - 1:
524
+ # Move the layer down
525
+ self.current_canvas.layers.insert(index + 2, selected_layer)
526
+ del self.current_canvas.layers[index]
527
+ else:
528
+ # Move the last layer to the beginning
529
+ self.current_canvas.layers.insert(0, selected_layer)
530
+ del self.current_canvas.layers[-1]
531
+ self.current_canvas.update()
532
+ self.layer_list.update_list()
533
+ self.messageSignal.emit(
534
+ f"Moved layer {selected_layer.layer_name} down in order."
535
+ )
488
536
  self.update()
489
537
  return super().keyPressEvent(event)