2025-12-05 09:50:50 +02:00
|
|
|
"""
|
|
|
|
|
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()
|
2025-12-05 15:51:16 +02:00
|
|
|
self.base_model_edit.setPlaceholderText("yolov8s-seg.pt")
|
2025-12-05 09:50:50 +02:00
|
|
|
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(
|
2025-12-05 15:51:16 +02:00
|
|
|
self.config_manager.get("models.default_base_model", "yolov8s-seg.pt")
|
2025-12-05 09:50:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 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()
|