From 2f95d522f088685bc0f768e25d7a0c32c1e300e7 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Fri, 5 Dec 2025 11:17:33 +0100 Subject: [PATCH] GUI: Save fluorescence scan as CSV --- gui/src/aaregui/panels/fluorescence_panel.py | 50 ++++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/gui/src/aaregui/panels/fluorescence_panel.py b/gui/src/aaregui/panels/fluorescence_panel.py index c45e9bf6..f625cbc5 100644 --- a/gui/src/aaregui/panels/fluorescence_panel.py +++ b/gui/src/aaregui/panels/fluorescence_panel.py @@ -38,6 +38,13 @@ class FluorescencePanel(QWidget): self.chart_view.setRenderHint(QPainter.Antialiasing) grid.addWidget(self.chart_view, 0, 0) + # Enable context menu on chart view + self.chart_view.setContextMenuPolicy(Qt.CustomContextMenu) + self.chart_view.customContextMenuRequested.connect(self._show_context_menu) + + self._last_x_keV = None + self._last_y_counts = None + # Average dead time label self.avg_dead_label = QLabel("Average dead time: -") self.avg_dead_label.setAlignment(Qt.AlignLeft) @@ -104,6 +111,39 @@ class FluorescencePanel(QWidget): logger.debug(f"eventFilter error: {e}") return False + def _show_context_menu(self, pos): + try: + from PySide6.QtWidgets import QMenu, QFileDialog + menu = QMenu(self.chart_view) + act_save_csv = menu.addAction("Save spectrum as CSV...") + global_pos = self.chart_view.mapToGlobal(pos) + action = menu.exec(global_pos) + if action == act_save_csv: + if self.series.count() == 0: + logger.info("No data to save.") + return + # Prefer cached arrays; fall back to reading from series + x_vals = self._last_x_keV + y_vals = self._last_y_counts + if x_vals is None or y_vals is None or len(x_vals) != self.series.count(): + x_vals = [self.series.at(i).x() for i in range(self.series.count())] + y_vals = [self.series.at(i).y() for i in range(self.series.count())] + path, _ = QFileDialog.getSaveFileName(self, "Save Spectrum CSV", "spectrum.csv", "CSV files (*.csv)") + if not path: + return + try: + import csv + with open(path, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(["Energy_keV", "Counts"]) + for x, y in zip(x_vals, y_vals): + writer.writerow([f"{x:.6f}", f"{y:.6f}"]) + logger.info(f"Spectrum saved to {path}") + except Exception as e: + logger.error(f"Failed to save CSV: {e}") + except Exception as e: + logger.error(f"Context menu error: {e}") + def _update_vline(self, x_val: float): # Draw vertical line spanning current Y axis range at x_val try: @@ -145,11 +185,13 @@ class FluorescencePanel(QWidget): self._marker.setVisible(False) self._vline.setVisible(False) self._vline.clear() + self._last_x_keV = None + self._last_y_counts = None # Update average dead time label (if provided) try: if hasattr(f, "average_dead_time") and f.average_dead_time is not None: - self.avg_dead_label.setText(f"Average dead time: {f.average_dead_time*100.0:.3f}%") + self.avg_dead_label.setText(f"Average dead time: {f.average_dead_time * 100.0:.3f}%") else: self.avg_dead_label.setText("Average dead time: -") except Exception: @@ -166,18 +208,18 @@ class FluorescencePanel(QWidget): order = np.argsort(x) x = x[order] y = y[order] - + # Cache arrays for export + self._last_x_keV = x.tolist() + self._last_y_counts = y.tolist() # Append data for xi, yi in zip(x, y): self.series.append(float(xi), float(yi)) - # Peak info (highest Y) if y.size: max_idx = int(np.argmax(y)) peak_keV = float(x[max_idx]) peak_counts = float(y[max_idx]) self.peak_info_label.setText(f"Peak: {peak_keV:.3f} keV, {peak_counts:.3f} counts") - # Axes xmin = float(np.min(x)) if x.size else -1.0 xmax = float(np.max(x)) if x.size else 1.0