From f84dea0bff0327128f36270b973a23b030a2c29d Mon Sep 17 00:00:00 2001 From: Martin Laasmaa Date: Mon, 8 Dec 2025 22:40:07 +0200 Subject: [PATCH] Adding splitter and saving layout state when closing the app --- src/gui/main_window.py | 31 ++++++++- src/gui/tabs/annotation_tab.py | 117 ++++++++++++++++++++++++++------- 2 files changed, 121 insertions(+), 27 deletions(-) diff --git a/src/gui/main_window.py b/src/gui/main_window.py index 71e322b..99eefa9 100644 --- a/src/gui/main_window.py +++ b/src/gui/main_window.py @@ -13,7 +13,7 @@ from PySide6.QtWidgets import ( QVBoxLayout, QLabel, ) -from PySide6.QtCore import Qt, QTimer +from PySide6.QtCore import Qt, QTimer, QSettings from PySide6.QtGui import QAction, QKeySequence from src.database.db_manager import DatabaseManager @@ -52,8 +52,8 @@ class MainWindow(QMainWindow): self._create_tab_widget() self._create_status_bar() - # Center window on screen - self._center_window() + # Restore window geometry or center window on screen + self._restore_window_state() logger.info("Main window initialized") @@ -156,6 +156,24 @@ class MainWindow(QMainWindow): (screen.width() - size.width()) // 2, (screen.height() - size.height()) // 2 ) + def _restore_window_state(self): + """Restore window geometry from settings or center window.""" + settings = QSettings("microscopy_app", "object_detection") + geometry = settings.value("main_window/geometry") + + if geometry: + self.restoreGeometry(geometry) + logger.debug("Restored window geometry from settings") + else: + self._center_window() + logger.debug("Centered window on screen") + + def _save_window_state(self): + """Save window geometry to settings.""" + settings = QSettings("microscopy_app", "object_detection") + settings.setValue("main_window/geometry", self.saveGeometry()) + logger.debug("Saved window geometry to settings") + def _show_settings(self): """Show settings dialog.""" logger.info("Opening settings dialog") @@ -276,6 +294,13 @@ class MainWindow(QMainWindow): ) if reply == QMessageBox.Yes: + # Save window state before closing + self._save_window_state() + + # Save annotation tab state if it exists + if hasattr(self, "annotation_tab"): + self.annotation_tab.save_state() + logger.info("Application closing") event.accept() else: diff --git a/src/gui/tabs/annotation_tab.py b/src/gui/tabs/annotation_tab.py index 6aa2462..a373ad0 100644 --- a/src/gui/tabs/annotation_tab.py +++ b/src/gui/tabs/annotation_tab.py @@ -12,6 +12,7 @@ from PySide6.QtWidgets import ( QPushButton, QFileDialog, QMessageBox, + QSplitter, ) from PySide6.QtCore import Qt, QSettings from pathlib import Path @@ -43,25 +44,13 @@ class AnnotationTab(QWidget): """Setup user interface.""" layout = QVBoxLayout() - # Image loading section - load_group = QGroupBox("Image Loading") - load_layout = QVBoxLayout() + # Main horizontal splitter to divide left (image) and right (controls) + self.main_splitter = QSplitter(Qt.Horizontal) + self.main_splitter.setHandleWidth(10) - # Load image button - button_layout = QHBoxLayout() - self.load_image_btn = QPushButton("Load Image") - self.load_image_btn.clicked.connect(self._load_image) - button_layout.addWidget(self.load_image_btn) - button_layout.addStretch() - - load_layout.addLayout(button_layout) - - # Image info label - self.image_info_label = QLabel("No image loaded") - load_layout.addWidget(self.image_info_label) - - load_group.setLayout(load_layout) - layout.addWidget(load_group) + # { Left splitter for image display and zoom info + self.left_splitter = QSplitter(Qt.Vertical) + self.left_splitter.setHandleWidth(10) # Image display section display_group = QGroupBox("Image Display") @@ -73,7 +62,21 @@ class AnnotationTab(QWidget): display_layout.addWidget(self.image_display_widget) display_group.setLayout(display_layout) - layout.addWidget(display_group) + self.left_splitter.addWidget(display_group) + + # Zoom controls info + zoom_info = QLabel("Zoom: Mouse wheel or +/- keys to zoom in/out") + zoom_info.setStyleSheet("QLabel { color: #888; font-style: italic; }") + self.left_splitter.addWidget(zoom_info) + # } + + # { Right splitter for annotation tools and controls + self.right_splitter = QSplitter(Qt.Vertical) + self.right_splitter.setHandleWidth(10) + + # Image loading section + load_group = QGroupBox("Image Loading") + load_layout = QVBoxLayout() # Future features info info_group = QGroupBox("Annotation Tool (Future Feature)") @@ -86,18 +89,41 @@ class AnnotationTab(QWidget): "- Export annotations to YOLO format\n" "- Annotation verification" ) + info_label.setWordWrap(True) info_layout.addWidget(info_label) info_group.setLayout(info_layout) - layout.addWidget(info_group) + self.right_splitter.addWidget(info_group) - # Zoom controls info - zoom_info = QLabel("Zoom: Mouse wheel or +/- keys to zoom in/out") - zoom_info.setStyleSheet("QLabel { color: #888; font-style: italic; }") - layout.addWidget(zoom_info) + # Load image button + button_layout = QHBoxLayout() + self.load_image_btn = QPushButton("Load Image") + self.load_image_btn.clicked.connect(self._load_image) + button_layout.addWidget(self.load_image_btn) + button_layout.addStretch() + load_layout.addLayout(button_layout) + # Image info label + self.image_info_label = QLabel("No image loaded") + load_layout.addWidget(self.image_info_label) + + load_group.setLayout(load_layout) + self.right_splitter.addWidget(load_group) + # } + + # Add both splitters to the main horizontal splitter + self.main_splitter.addWidget(self.left_splitter) + self.main_splitter.addWidget(self.right_splitter) + + # Set initial sizes: 75% for left (image), 25% for right (controls) + self.main_splitter.setSizes([750, 250]) + + layout.addWidget(self.main_splitter) self.setLayout(layout) + # Restore splitter positions from settings + self._restore_state() + def _load_image(self): """Load and display an image file.""" # Get last opened directory from QSettings @@ -171,6 +197,49 @@ class AnnotationTab(QWidget): """Handle zoom level changes from the image display widget.""" self._update_image_info() + def _restore_state(self): + """Restore splitter positions from settings.""" + settings = QSettings("microscopy_app", "object_detection") + + # Restore main splitter state + main_state = settings.value("annotation_tab/main_splitter_state") + if main_state: + self.main_splitter.restoreState(main_state) + logger.debug("Restored main splitter state") + + # Restore left splitter state + left_state = settings.value("annotation_tab/left_splitter_state") + if left_state: + self.left_splitter.restoreState(left_state) + logger.debug("Restored left splitter state") + + # Restore right splitter state + right_state = settings.value("annotation_tab/right_splitter_state") + if right_state: + self.right_splitter.restoreState(right_state) + logger.debug("Restored right splitter state") + + def save_state(self): + """Save splitter positions to settings.""" + settings = QSettings("microscopy_app", "object_detection") + + # Save main splitter state + 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() + ) + + # Save right splitter state + settings.setValue( + "annotation_tab/right_splitter_state", self.right_splitter.saveState() + ) + + logger.debug("Saved annotation tab splitter states") + def refresh(self): """Refresh the tab.""" pass