Files
object-segmentation/src/gui/dialogs/config_dialog.py

292 lines
9.4 KiB
Python

"""
Configuration dialog for the microscopy object detection application.
"""
from PySide6.QtWidgets import (
QDialog,
QVBoxLayout,
QHBoxLayout,
QFormLayout,
QPushButton,
QLineEdit,
QSpinBox,
QDoubleSpinBox,
QFileDialog,
QTabWidget,
QWidget,
QLabel,
QGroupBox,
)
from PySide6.QtCore import Qt
from src.utils.config_manager import ConfigManager
from src.utils.logger import get_logger
logger = get_logger(__name__)
class ConfigDialog(QDialog):
"""Configuration dialog window."""
def __init__(self, config_manager: ConfigManager, parent=None):
super().__init__(parent)
self.config_manager = config_manager
self.setWindowTitle("Settings")
self.setMinimumWidth(500)
self.setMinimumHeight(400)
self._setup_ui()
self._load_settings()
def _setup_ui(self):
"""Setup user interface."""
layout = QVBoxLayout()
# Create tab widget for different setting categories
self.tab_widget = QTabWidget()
# General settings tab
general_tab = self._create_general_tab()
self.tab_widget.addTab(general_tab, "General")
# Training settings tab
training_tab = self._create_training_tab()
self.tab_widget.addTab(training_tab, "Training")
# Detection settings tab
detection_tab = self._create_detection_tab()
self.tab_widget.addTab(detection_tab, "Detection")
layout.addWidget(self.tab_widget)
# Buttons
button_layout = QHBoxLayout()
button_layout.addStretch()
self.save_button = QPushButton("Save")
self.save_button.clicked.connect(self.accept)
button_layout.addWidget(self.save_button)
self.cancel_button = QPushButton("Cancel")
self.cancel_button.clicked.connect(self.reject)
button_layout.addWidget(self.cancel_button)
layout.addLayout(button_layout)
self.setLayout(layout)
def _create_general_tab(self) -> QWidget:
"""Create general settings tab."""
widget = QWidget()
layout = QVBoxLayout()
# Image repository group
repo_group = QGroupBox("Image Repository")
repo_layout = QFormLayout()
# Repository path
path_layout = QHBoxLayout()
self.repo_path_edit = QLineEdit()
self.repo_path_edit.setPlaceholderText("Path to image repository")
path_layout.addWidget(self.repo_path_edit)
browse_button = QPushButton("Browse...")
browse_button.clicked.connect(self._browse_repository)
path_layout.addWidget(browse_button)
repo_layout.addRow("Base Path:", path_layout)
repo_group.setLayout(repo_layout)
layout.addWidget(repo_group)
# Database group
db_group = QGroupBox("Database")
db_layout = QFormLayout()
self.db_path_edit = QLineEdit()
self.db_path_edit.setPlaceholderText("Path to database file")
db_layout.addRow("Database Path:", self.db_path_edit)
db_group.setLayout(db_layout)
layout.addWidget(db_group)
# Models group
models_group = QGroupBox("Models")
models_layout = QFormLayout()
self.models_dir_edit = QLineEdit()
self.models_dir_edit.setPlaceholderText("Directory for saved models")
models_layout.addRow("Models Directory:", self.models_dir_edit)
self.base_model_edit = QLineEdit()
self.base_model_edit.setPlaceholderText("yolov8s-seg.pt")
models_layout.addRow("Default Base Model:", self.base_model_edit)
models_group.setLayout(models_layout)
layout.addWidget(models_group)
layout.addStretch()
widget.setLayout(layout)
return widget
def _create_training_tab(self) -> QWidget:
"""Create training settings tab."""
widget = QWidget()
layout = QVBoxLayout()
form_layout = QFormLayout()
# Epochs
self.epochs_spin = QSpinBox()
self.epochs_spin.setRange(1, 1000)
self.epochs_spin.setValue(100)
form_layout.addRow("Default Epochs:", self.epochs_spin)
# Batch size
self.batch_size_spin = QSpinBox()
self.batch_size_spin.setRange(1, 128)
self.batch_size_spin.setValue(16)
form_layout.addRow("Default Batch Size:", self.batch_size_spin)
# Image size
self.imgsz_spin = QSpinBox()
self.imgsz_spin.setRange(320, 1280)
self.imgsz_spin.setSingleStep(32)
self.imgsz_spin.setValue(640)
form_layout.addRow("Default Image Size:", self.imgsz_spin)
# Patience
self.patience_spin = QSpinBox()
self.patience_spin.setRange(1, 200)
self.patience_spin.setValue(50)
form_layout.addRow("Default Patience:", self.patience_spin)
# Learning rate
self.lr_spin = QDoubleSpinBox()
self.lr_spin.setRange(0.0001, 0.1)
self.lr_spin.setSingleStep(0.001)
self.lr_spin.setDecimals(4)
self.lr_spin.setValue(0.01)
form_layout.addRow("Default Learning Rate:", self.lr_spin)
layout.addLayout(form_layout)
layout.addStretch()
widget.setLayout(layout)
return widget
def _create_detection_tab(self) -> QWidget:
"""Create detection settings tab."""
widget = QWidget()
layout = QVBoxLayout()
form_layout = QFormLayout()
# Confidence threshold
self.conf_spin = QDoubleSpinBox()
self.conf_spin.setRange(0.0, 1.0)
self.conf_spin.setSingleStep(0.05)
self.conf_spin.setDecimals(2)
self.conf_spin.setValue(0.25)
form_layout.addRow("Default Confidence:", self.conf_spin)
# IoU threshold
self.iou_spin = QDoubleSpinBox()
self.iou_spin.setRange(0.0, 1.0)
self.iou_spin.setSingleStep(0.05)
self.iou_spin.setDecimals(2)
self.iou_spin.setValue(0.45)
form_layout.addRow("Default IoU:", self.iou_spin)
# Max batch size
self.max_batch_spin = QSpinBox()
self.max_batch_spin.setRange(1, 1000)
self.max_batch_spin.setValue(100)
form_layout.addRow("Max Batch Size:", self.max_batch_spin)
layout.addLayout(form_layout)
layout.addStretch()
widget.setLayout(layout)
return widget
def _browse_repository(self):
"""Browse for image repository directory."""
directory = QFileDialog.getExistingDirectory(
self, "Select Image Repository", self.repo_path_edit.text()
)
if directory:
self.repo_path_edit.setText(directory)
def _load_settings(self):
"""Load current settings into dialog."""
# General settings
self.repo_path_edit.setText(
self.config_manager.get("image_repository.base_path", "")
)
self.db_path_edit.setText(
self.config_manager.get("database.path", "data/detections.db")
)
self.models_dir_edit.setText(
self.config_manager.get("models.models_directory", "data/models")
)
self.base_model_edit.setText(
self.config_manager.get("models.default_base_model", "yolov8s-seg.pt")
)
# Training settings
self.epochs_spin.setValue(
self.config_manager.get("training.default_epochs", 100)
)
self.batch_size_spin.setValue(
self.config_manager.get("training.default_batch_size", 16)
)
self.imgsz_spin.setValue(self.config_manager.get("training.default_imgsz", 640))
self.patience_spin.setValue(
self.config_manager.get("training.default_patience", 50)
)
self.lr_spin.setValue(self.config_manager.get("training.default_lr0", 0.01))
# Detection settings
self.conf_spin.setValue(
self.config_manager.get("detection.default_confidence", 0.25)
)
self.iou_spin.setValue(self.config_manager.get("detection.default_iou", 0.45))
self.max_batch_spin.setValue(
self.config_manager.get("detection.max_batch_size", 100)
)
def accept(self):
"""Save settings and close dialog."""
logger.info("Saving configuration")
# Save general settings
self.config_manager.set(
"image_repository.base_path", self.repo_path_edit.text()
)
self.config_manager.set("database.path", self.db_path_edit.text())
self.config_manager.set("models.models_directory", self.models_dir_edit.text())
self.config_manager.set(
"models.default_base_model", self.base_model_edit.text()
)
# Save training settings
self.config_manager.set("training.default_epochs", self.epochs_spin.value())
self.config_manager.set(
"training.default_batch_size", self.batch_size_spin.value()
)
self.config_manager.set("training.default_imgsz", self.imgsz_spin.value())
self.config_manager.set("training.default_patience", self.patience_spin.value())
self.config_manager.set("training.default_lr0", self.lr_spin.value())
# Save detection settings
self.config_manager.set("detection.default_confidence", self.conf_spin.value())
self.config_manager.set("detection.default_iou", self.iou_spin.value())
self.config_manager.set("detection.max_batch_size", self.max_batch_spin.value())
# Save to file
self.config_manager.save_config()
super().accept()