From 8d30e6bb7aa1314e85b87d6dbfc595eca62f8255 Mon Sep 17 00:00:00 2001 From: Martin Laasmaa Date: Fri, 16 Jan 2026 14:20:12 +0200 Subject: [PATCH] Adding auto zoom for annotation image view and changing tab order --- src/gui/main_window.py | 4 +- src/gui/tabs/annotation_tab.py | 94 ++++++++++------------------------ 2 files changed, 28 insertions(+), 70 deletions(-) diff --git a/src/gui/main_window.py b/src/gui/main_window.py index 811310f..33626ef 100644 --- a/src/gui/main_window.py +++ b/src/gui/main_window.py @@ -125,10 +125,10 @@ class MainWindow(QMainWindow): # Add tabs to widget self.tab_widget.addTab(self.detection_tab, "Detection") + self.tab_widget.addTab(self.results_tab, "Results") + self.tab_widget.addTab(self.annotation_tab, "Annotation") self.tab_widget.addTab(self.training_tab, "Training") self.tab_widget.addTab(self.validation_tab, "Validation") - self.tab_widget.addTab(self.results_tab, "Results") - self.tab_widget.addTab(self.annotation_tab, "Annotation (Future)") # Connect tab change signal self.tab_widget.currentChanged.connect(self._on_tab_changed) diff --git a/src/gui/tabs/annotation_tab.py b/src/gui/tabs/annotation_tab.py index 83ca894..e813c9d 100644 --- a/src/gui/tabs/annotation_tab.py +++ b/src/gui/tabs/annotation_tab.py @@ -29,9 +29,7 @@ logger = get_logger(__name__) class AnnotationTab(QWidget): """Annotation tab for manual image annotation.""" - def __init__( - self, db_manager: DatabaseManager, config_manager: ConfigManager, parent=None - ): + def __init__(self, db_manager: DatabaseManager, config_manager: ConfigManager, parent=None): super().__init__(parent) self.db_manager = db_manager self.config_manager = config_manager @@ -62,6 +60,9 @@ class AnnotationTab(QWidget): # Use the AnnotationCanvasWidget self.annotation_canvas = AnnotationCanvasWidget() + # Auto-zoom so newly loaded images fill the available canvas viewport. + # (Matches the behavior used in ResultsTab.) + self.annotation_canvas.set_auto_fit_to_view(True) 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) @@ -72,9 +73,7 @@ class AnnotationTab(QWidget): self.left_splitter.addWidget(canvas_group) # Controls info - controls_info = QLabel( - "Zoom: Mouse wheel or +/- keys | Drawing: Enable pen and drag mouse" - ) + controls_info = QLabel("Zoom: Mouse wheel or +/- keys | Drawing: Enable pen and drag mouse") controls_info.setStyleSheet("QLabel { color: #888; font-style: italic; }") self.left_splitter.addWidget(controls_info) # } @@ -85,36 +84,20 @@ class AnnotationTab(QWidget): # Annotation tools section self.annotation_tools = AnnotationToolsWidget(self.db_manager) - self.annotation_tools.polyline_enabled_changed.connect( - self.annotation_canvas.set_polyline_enabled - ) - self.annotation_tools.polyline_pen_color_changed.connect( - self.annotation_canvas.set_polyline_pen_color - ) - self.annotation_tools.polyline_pen_width_changed.connect( - self.annotation_canvas.set_polyline_pen_width - ) + self.annotation_tools.polyline_enabled_changed.connect(self.annotation_canvas.set_polyline_enabled) + self.annotation_tools.polyline_pen_color_changed.connect(self.annotation_canvas.set_polyline_pen_color) + self.annotation_tools.polyline_pen_width_changed.connect(self.annotation_canvas.set_polyline_pen_width) # Show / hide bounding boxes - self.annotation_tools.show_bboxes_changed.connect( - self.annotation_canvas.set_show_bboxes - ) + self.annotation_tools.show_bboxes_changed.connect(self.annotation_canvas.set_show_bboxes) # RDP simplification controls - self.annotation_tools.simplify_on_finish_changed.connect( - self._on_simplify_on_finish_changed - ) - self.annotation_tools.simplify_epsilon_changed.connect( - self._on_simplify_epsilon_changed - ) + self.annotation_tools.simplify_on_finish_changed.connect(self._on_simplify_on_finish_changed) + self.annotation_tools.simplify_epsilon_changed.connect(self._on_simplify_epsilon_changed) # Class selection and class-color changes self.annotation_tools.class_selected.connect(self._on_class_selected) self.annotation_tools.class_color_changed.connect(self._on_class_color_changed) - self.annotation_tools.clear_annotations_requested.connect( - self._on_clear_annotations - ) + 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.annotation_tools.delete_selected_annotation_requested.connect(self._on_delete_selected_annotation) self.right_splitter.addWidget(self.annotation_tools) # Image loading section @@ -180,9 +163,7 @@ class AnnotationTab(QWidget): self.current_image_path = file_path # Store the directory for next time - settings.setValue( - "annotation_tab/last_directory", str(Path(file_path).parent) - ) + settings.setValue("annotation_tab/last_directory", str(Path(file_path).parent)) # Get or create image in database relative_path = str(Path(file_path).name) # Simplified for now @@ -206,9 +187,7 @@ class AnnotationTab(QWidget): except ImageLoadError as e: logger.error(f"Failed to load image: {e}") - QMessageBox.critical( - self, "Error Loading Image", f"Failed to load image:\n{str(e)}" - ) + QMessageBox.critical(self, "Error Loading Image", f"Failed to load image:\n{str(e)}") except Exception as e: logger.error(f"Unexpected error loading image: {e}") QMessageBox.critical(self, "Error", f"Unexpected error:\n{str(e)}") @@ -340,9 +319,7 @@ class AnnotationTab(QWidget): if not self.current_image_id: return - logger.debug( - f"Class color changed; reloading annotations for image ID {self.current_image_id}" - ) + logger.debug(f"Class color changed; reloading annotations for image ID {self.current_image_id}") self._load_annotations_for_current_image() def _on_class_selected(self, class_data): @@ -355,9 +332,7 @@ class AnnotationTab(QWidget): if class_data: logger.debug(f"Object class selected: {class_data['class_name']}") else: - logger.debug( - 'No class selected ("-- Select Class --"), showing all annotations' - ) + logger.debug('No class selected ("-- Select Class --"), showing all annotations') # Changing the class filter invalidates any previous selection self.selected_annotation_ids = [] @@ -390,9 +365,7 @@ class AnnotationTab(QWidget): 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?" - ) + question = f"Are you sure you want to delete the {count} selected annotations?" title = "Delete Annotations" reply = QMessageBox.question( @@ -420,13 +393,11 @@ class AnnotationTab(QWidget): QMessageBox.warning( self, "Partial Failure", - "Some annotations could not be deleted:\n" - + ", ".join(str(a) for a in failed_ids), + "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) + 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 @@ -456,17 +427,13 @@ class AnnotationTab(QWidget): return try: - self.current_annotations = self.db_manager.get_annotations_for_image( - self.current_image_id - ) + 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( - f"Failed to load annotations for image {self.current_image_id}: {e}" - ) + logger.error(f"Failed to load annotations for image {self.current_image_id}: {e}") QMessageBox.critical( self, "Error", @@ -490,10 +457,7 @@ class AnnotationTab(QWidget): drawn_count = 0 for ann in self.current_annotations: # Filter by class if one is selected - if ( - selected_class_id is not None - and ann.get("class_id") != selected_class_id - ): + if selected_class_id is not None and ann.get("class_id") != selected_class_id: continue if ann.get("segmentation_mask"): @@ -545,19 +509,13 @@ class AnnotationTab(QWidget): settings = QSettings("microscopy_app", "object_detection") # Save main splitter state - settings.setValue( - "annotation_tab/main_splitter_state", self.main_splitter.saveState() - ) + settings.setValue("annotation_tab/main_splitter_state", self.main_splitter.saveState()) # Save left splitter state - settings.setValue( - "annotation_tab/left_splitter_state", self.left_splitter.saveState() - ) + settings.setValue("annotation_tab/left_splitter_state", self.left_splitter.saveState()) # Save right splitter state - settings.setValue( - "annotation_tab/right_splitter_state", self.right_splitter.saveState() - ) + settings.setValue("annotation_tab/right_splitter_state", self.right_splitter.saveState()) logger.debug("Saved annotation tab splitter states")