GUI: added face_detection_panel.py

This commit is contained in:
2025-10-24 17:32:21 +02:00
parent 883d04e7cd
commit 87aed7a2ad
2 changed files with 115 additions and 1 deletions
+20 -1
View File
@@ -22,6 +22,7 @@ from aaregui.panels.manual_sample_panel import ManualSamplePanel
from aaregui.panels.reference_tools_panel import ReferenceToolsPanel
from aaregui.panels.sample_queue_panel import SampleQueuePanel
from aaregui.panels.tell_sample_panel import TellSamplePanel
from aaregui.panels.face_detection_panel import FaceDetectionPanel
from aaregui.scan_logic.raster_grid_manager import RasterGridManager
from aaregui.scan_logic.rotation_scan_manager import RotationScanManager
from aaregui.scan_logic.sample_mount_logic import SampleMountLogic
@@ -154,6 +155,12 @@ class MainWindow(QMainWindow):
self.manual_sample_dock.setAllowedAreas(Qt.DockWidgetArea.BottomDockWidgetArea)
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.manual_sample_dock)
self.face_panel = FaceDetectionPanel(api_base_url=self.__base_url, token=self.__token)
self.face_panel_dock = QDockWidget("Face detection", self)
self.face_panel_dock.setWidget(self.face_panel)
self.face_panel_dock.setAllowedAreas(Qt.DockWidgetArea.RightDockWidgetArea | Qt.DockWidgetArea.LeftDockWidgetArea)
self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.face_panel_dock)
self.setCentralWidget(top_widget)
self.setWindowTitle("AareGUI")
@@ -297,6 +304,8 @@ class MainWindow(QMainWindow):
self.daq.raster_scan_completed.connect(self.raster.grid_scan_completed)
self.manual_sample_panel.sample_manual.connect(self.daq.sample_manual)
self.face_panel.face_detection.connect(self.daq.face_detection)
self.daq.face_detection_result.connect(self.face_panel.update_plot)
def create_menu_bar(self):
"""Create a menu bar with File->Quit and Help->About."""
@@ -332,8 +341,18 @@ class MainWindow(QMainWindow):
show_manual_sample_action.setChecked(True)
show_manual_sample_action.triggered.connect(self.manual_sample_dock.setVisible)
# When dock visibility changes, update the action's checked state
show_manual_sample_action.triggered.connect(lambda: self.manual_sample_dock.setVisible(show_job_list_action.isChecked()))
show_manual_sample_action.triggered.connect(lambda: self.manual_sample_dock.setVisible(show_manual_sample_action.isChecked()))
view_menu.addAction(show_manual_sample_action)
show_face_panel_action = QAction("Show face detection", self)
show_face_panel_action.setCheckable(True)
show_face_panel_action.setChecked(True)
show_face_panel_action.triggered.connect(self.face_panel_dock.setVisible)
# When dock visibility changes, update the action's checked state
# show_face_panel_action.triggered.connect(
# lambda: self.face_panel_dock.setVisible(show_face_panel_action.isChecked()))
self.face_panel_dock.visibilityChanged.connect(show_face_panel_action.setChecked)
view_menu.addAction(show_face_panel_action)
# Help menu
help_menu = menu_bar.addMenu("Help")
about_action = QAction("About", self) # Action for 'About'
@@ -0,0 +1,95 @@
# Python
from PySide6.QtCore import Slot, Qt, Signal
from PySide6.QtWidgets import QWidget, QGridLayout, QLabel, QFrame, QPushButton, QSpacerItem, QSizePolicy, QVBoxLayout, \
QHBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
from aaregui.widgets.title_label import TitleLabel
class FaceDetectionPanel(QWidget):
face_detection = Signal()
def __init__(self, parent=None, api_base_url: str | None = None, token: str | None = None):
super().__init__(parent)
self.api_base_url = api_base_url or ""
self.token = token or ""
self.fig = Figure(figsize=(5, 4), tight_layout=True)
self.canvas = FigureCanvas(self.fig)
self.ax1 = self.fig.add_subplot(2, 1, 1) # Height vs angle
self.ax2 = self.fig.add_subplot(2, 1, 2) # Area vs angle
_top_layout = QHBoxLayout()
_top_layout.addWidget(TitleLabel("TELL sample changer", self))
self.status_lbl = QLabel("")
_top_layout.addWidget(self.status_lbl)
self.face_detection_button = QPushButton("Face Detection")
self.face_detection_button.setStyleSheet("color: rgb(78, 154, 6);")
self.face_detection_button.clicked.connect(self.run_and_refresh)
_top_layout.addWidget(self.face_detection_button)
_layout = QVBoxLayout(self)
_layout.addLayout(_top_layout)
_layout.addWidget(self.canvas)
self.setLayout(_layout)
def run_and_refresh(self):
try:
self.face_detection.emit()
except Exception as e:
self.status_lbl.setText(f"Error: {e}")
def update_plot(self, data):
samples = data.get("samples", [])
if not samples:
self.ax1.clear()
self.ax2.clear()
self.ax1.text(0.5, 0.5, "No data", ha="center", va="center")
self.ax2.text(0.5, 0.5, "No data", ha="center", va="center")
self.canvas.draw_idle()
return
angles = np.array([s["angle_deg"] for s in samples], dtype=float)
heights = np.array([s["height"] for s in samples], dtype=float)
areas = np.array([s["area"] for s in samples], dtype=float)
self.ax1.clear()
self.ax2.clear()
self.ax1.scatter(angles, heights, s=16, c="tab:blue", label="Height")
self.ax2.scatter(angles, areas, s=16, c="tab:green", label="Area")
ang_grid = np.linspace(angles.min(), angles.max(), 400)
hf = data.get("height_fit", {})
if {"A", "B", "phi_rad"} <= hf.keys():
A, B, phi = hf["A"], hf["B"], hf["phi_rad"]
height_fit = A + B * np.cos(np.deg2rad(ang_grid) - phi)
self.ax1.plot(ang_grid, height_fit, color="tab:orange", label="Height fit")
if "best_angle_deg" in hf:
self.ax1.axvline(hf["best_angle_deg"], color="tab:orange", ls="--", alpha=0.6)
af = data.get("area_fit", {})
if {"A", "B", "phi_rad"} <= af.keys():
A2, B2, phi2 = af["A"], af["B"], af["phi_rad"]
area_fit = A2 + B2 * np.cos(np.deg2rad(ang_grid) - phi2)
self.ax2.plot(ang_grid, area_fit, color="tab:red", label="Area fit")
if "best_angle_deg" in af:
self.ax2.axvline(af["best_angle_deg"], color="tab:red", ls="--", alpha=0.6)
self.ax1.set_xlabel("Angle (deg)")
self.ax1.set_ylabel("Height")
self.ax1.legend()
self.ax1.grid(True, alpha=0.3)
self.ax2.set_xlabel("Angle (deg)")
self.ax2.set_ylabel("Area")
self.ax2.legend()
self.ax2.grid(True, alpha=0.3)
self.canvas.draw_idle()