Adding feature to remove annotations
This commit is contained in:
@@ -39,6 +39,8 @@ class AnnotationTab(QWidget):
|
||||
self.current_image_path = None
|
||||
self.current_image_id = None
|
||||
self.current_annotations = []
|
||||
# IDs of annotations currently selected on the canvas (multi-select)
|
||||
self.selected_annotation_ids = []
|
||||
|
||||
self._setup_ui()
|
||||
|
||||
@@ -62,6 +64,8 @@ class AnnotationTab(QWidget):
|
||||
self.annotation_canvas = AnnotationCanvasWidget()
|
||||
self.annotation_canvas.zoom_changed.connect(self._on_zoom_changed)
|
||||
self.annotation_canvas.annotation_drawn.connect(self._on_annotation_drawn)
|
||||
# Selection of existing polylines (when tool is not in drawing mode)
|
||||
self.annotation_canvas.annotation_selected.connect(self._on_annotation_selected)
|
||||
canvas_layout.addWidget(self.annotation_canvas)
|
||||
|
||||
canvas_group.setLayout(canvas_layout)
|
||||
@@ -107,6 +111,10 @@ class AnnotationTab(QWidget):
|
||||
self.annotation_tools.clear_annotations_requested.connect(
|
||||
self._on_clear_annotations
|
||||
)
|
||||
# Delete selected annotation on canvas
|
||||
self.annotation_tools.delete_selected_annotation_requested.connect(
|
||||
self._on_delete_selected_annotation
|
||||
)
|
||||
self.right_splitter.addWidget(self.annotation_tools)
|
||||
|
||||
# Image loading section
|
||||
@@ -292,6 +300,25 @@ class AnnotationTab(QWidget):
|
||||
logger.error(f"Failed to save annotation: {e}")
|
||||
QMessageBox.critical(self, "Error", f"Failed to save annotation:\n{str(e)}")
|
||||
|
||||
def _on_annotation_selected(self, annotation_ids):
|
||||
"""
|
||||
Handle selection of existing annotations on the canvas.
|
||||
|
||||
Args:
|
||||
annotation_ids: List of selected annotation IDs, or None/empty if cleared.
|
||||
"""
|
||||
if not annotation_ids:
|
||||
self.selected_annotation_ids = []
|
||||
self.annotation_tools.set_has_selected_annotation(False)
|
||||
logger.debug("Annotation selection cleared on canvas")
|
||||
return
|
||||
|
||||
# Normalize to a unique, sorted list of integer IDs
|
||||
ids = sorted({int(aid) for aid in annotation_ids if isinstance(aid, int)})
|
||||
self.selected_annotation_ids = ids
|
||||
self.annotation_tools.set_has_selected_annotation(bool(ids))
|
||||
logger.debug(f"Annotations selected on canvas: IDs={ids}")
|
||||
|
||||
def _on_simplify_on_finish_changed(self, enabled: bool):
|
||||
"""Update canvas simplify-on-finish flag from tools widget."""
|
||||
self.annotation_canvas.simplify_on_finish = enabled
|
||||
@@ -323,7 +350,7 @@ class AnnotationTab(QWidget):
|
||||
Handle when an object class is selected or cleared.
|
||||
|
||||
When a specific class is selected, only annotations of that class are drawn.
|
||||
When the selection is cleared (\"-- Select Class --\"), all annotations are shown.
|
||||
When the selection is cleared ("-- Select Class --"), all annotations are shown.
|
||||
"""
|
||||
if class_data:
|
||||
logger.debug(f"Object class selected: {class_data['class_name']}")
|
||||
@@ -332,14 +359,89 @@ class AnnotationTab(QWidget):
|
||||
'No class selected ("-- Select Class --"), showing all annotations'
|
||||
)
|
||||
|
||||
# Changing the class filter invalidates any previous selection
|
||||
self.selected_annotation_ids = []
|
||||
self.annotation_tools.set_has_selected_annotation(False)
|
||||
|
||||
# Whenever the selection changes, update which annotations are visible
|
||||
self._redraw_annotations_for_current_filter()
|
||||
|
||||
def _on_clear_annotations(self):
|
||||
"""Handle clearing all annotations."""
|
||||
self.annotation_canvas.clear_annotations()
|
||||
# Clear in-memory state and selection, but keep DB entries unchanged
|
||||
self.current_annotations = []
|
||||
self.selected_annotation_ids = []
|
||||
self.annotation_tools.set_has_selected_annotation(False)
|
||||
logger.info("Cleared all annotations")
|
||||
|
||||
def _on_delete_selected_annotation(self):
|
||||
"""Handle deleting the currently selected annotation(s) (if any)."""
|
||||
if not self.selected_annotation_ids:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"No Selection",
|
||||
"No annotation is currently selected.",
|
||||
)
|
||||
return
|
||||
|
||||
count = len(self.selected_annotation_ids)
|
||||
if count == 1:
|
||||
question = "Are you sure you want to delete the selected annotation?"
|
||||
title = "Delete Annotation"
|
||||
else:
|
||||
question = (
|
||||
f"Are you sure you want to delete the {count} selected annotations?"
|
||||
)
|
||||
title = "Delete Annotations"
|
||||
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
title,
|
||||
question,
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.No,
|
||||
)
|
||||
if reply != QMessageBox.Yes:
|
||||
return
|
||||
|
||||
failed_ids = []
|
||||
try:
|
||||
for ann_id in self.selected_annotation_ids:
|
||||
try:
|
||||
deleted = self.db_manager.delete_annotation(ann_id)
|
||||
if not deleted:
|
||||
failed_ids.append(ann_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete annotation ID {ann_id}: {e}")
|
||||
failed_ids.append(ann_id)
|
||||
|
||||
if failed_ids:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Partial Failure",
|
||||
"Some annotations could not be deleted:\n"
|
||||
+ ", ".join(str(a) for a in failed_ids),
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f"Deleted {count} annotation(s): "
|
||||
+ ", ".join(str(a) for a in self.selected_annotation_ids)
|
||||
)
|
||||
|
||||
# Clear selection and reload annotations for the current image from DB
|
||||
self.selected_annotation_ids = []
|
||||
self.annotation_tools.set_has_selected_annotation(False)
|
||||
self._load_annotations_for_current_image()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete annotations: {e}")
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
"Error",
|
||||
f"Failed to delete annotations:\n{str(e)}",
|
||||
)
|
||||
|
||||
def _load_annotations_for_current_image(self):
|
||||
"""
|
||||
Load all annotations for the current image from the database and
|
||||
@@ -349,12 +451,17 @@ class AnnotationTab(QWidget):
|
||||
if not self.current_image_id:
|
||||
self.current_annotations = []
|
||||
self.annotation_canvas.clear_annotations()
|
||||
self.selected_annotation_ids = []
|
||||
self.annotation_tools.set_has_selected_annotation(False)
|
||||
return
|
||||
|
||||
try:
|
||||
self.current_annotations = self.db_manager.get_annotations_for_image(
|
||||
self.current_image_id
|
||||
)
|
||||
# New annotations loaded; reset any selection
|
||||
self.selected_annotation_ids = []
|
||||
self.annotation_tools.set_has_selected_annotation(False)
|
||||
self._redraw_annotations_for_current_filter()
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
@@ -393,7 +500,12 @@ class AnnotationTab(QWidget):
|
||||
polyline = ann["segmentation_mask"]
|
||||
color = ann.get("class_color", "#FF0000")
|
||||
|
||||
self.annotation_canvas.draw_saved_polyline(polyline, color, width=3)
|
||||
self.annotation_canvas.draw_saved_polyline(
|
||||
polyline,
|
||||
color,
|
||||
width=3,
|
||||
annotation_id=ann["id"],
|
||||
)
|
||||
self.annotation_canvas.draw_saved_bbox(
|
||||
[ann["x_min"], ann["y_min"], ann["x_max"], ann["y_max"]],
|
||||
color,
|
||||
|
||||
Reference in New Issue
Block a user