2026-05-30 00:42:45 +09:00
|
|
|
"""
|
|
|
|
|
Plot control widget: pick which metric to display and refresh the current plot.
|
|
|
|
|
|
|
|
|
|
Mirrors FontControlWidget's clustered-groupbox style so the two sit naturally
|
|
|
|
|
next to each other in the left panel.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
from PyQt5.QtWidgets import (
|
|
|
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QPushButton, QGroupBox,
|
2026-06-14 00:35:10 +09:00
|
|
|
QCheckBox,
|
2026-05-30 00:42:45 +09:00
|
|
|
)
|
|
|
|
|
from PyQt5.QtCore import pyqtSignal
|
|
|
|
|
|
|
|
|
|
from metrics import METRICS, DEFAULT_METRIC_ID
|
2026-06-14 00:35:10 +09:00
|
|
|
from plotspec import ViewState
|
2026-05-30 00:42:45 +09:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlotControlWidget(QWidget):
|
2026-06-14 00:35:10 +09:00
|
|
|
"""Metric selector, view-scale toggle, and manual plot refresh.
|
|
|
|
|
|
|
|
|
|
Overlay/compare is driven by the file-list checkboxes, not here — this cluster
|
|
|
|
|
only governs *what* metric and *how* its axes are scaled.
|
|
|
|
|
"""
|
2026-05-30 00:42:45 +09:00
|
|
|
|
|
|
|
|
metricChanged = pyqtSignal(str) # metric_id
|
2026-06-14 00:35:10 +09:00
|
|
|
viewChanged = pyqtSignal() # view-state (scale) changed
|
2026-05-30 00:42:45 +09:00
|
|
|
plotRefreshRequested = pyqtSignal()
|
2026-06-14 00:35:10 +09:00
|
|
|
addReferenceLineRequested = pyqtSignal()
|
|
|
|
|
clearReferenceLinesRequested = pyqtSignal()
|
2026-05-30 00:42:45 +09:00
|
|
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
|
self.initUI()
|
|
|
|
|
|
|
|
|
|
def initUI(self):
|
|
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
|
layout.setContentsMargins(5, 5, 5, 5)
|
|
|
|
|
|
|
|
|
|
group_box = QGroupBox("Plot")
|
|
|
|
|
group_layout = QVBoxLayout(group_box)
|
|
|
|
|
|
|
|
|
|
group_layout.addWidget(QLabel("Metric:"))
|
|
|
|
|
self.metric_combo = QComboBox()
|
|
|
|
|
for metric_id, metric in METRICS.items():
|
|
|
|
|
self.metric_combo.addItem(metric.display_name, metric_id)
|
|
|
|
|
default_idx = self.metric_combo.findData(DEFAULT_METRIC_ID)
|
|
|
|
|
if default_idx >= 0:
|
|
|
|
|
self.metric_combo.setCurrentIndex(default_idx)
|
|
|
|
|
self.metric_combo.currentIndexChanged.connect(self._on_metric_changed)
|
|
|
|
|
group_layout.addWidget(self.metric_combo)
|
|
|
|
|
|
2026-06-14 00:35:10 +09:00
|
|
|
# Frequency-axis scale. Only the spectrogram honours it today; harmless
|
|
|
|
|
# elsewhere (build_spec ignores unsupported toggles).
|
|
|
|
|
self.log_freq_check = QCheckBox("Log frequency (spectrogram)")
|
|
|
|
|
self.log_freq_check.setChecked(True)
|
|
|
|
|
self.log_freq_check.toggled.connect(lambda _: self.viewChanged.emit())
|
|
|
|
|
group_layout.addWidget(self.log_freq_check)
|
|
|
|
|
|
|
|
|
|
# Custom reference lines: drop a draggable horizontal marker (e.g. an
|
|
|
|
|
# eyeballed effective average) onto whatever metric is showing.
|
|
|
|
|
ref_row = QHBoxLayout()
|
|
|
|
|
self.add_ref_button = QPushButton("Add ref line")
|
|
|
|
|
self.add_ref_button.setToolTip("Drop a draggable horizontal reference line")
|
|
|
|
|
self.add_ref_button.clicked.connect(self.addReferenceLineRequested.emit)
|
|
|
|
|
ref_row.addWidget(self.add_ref_button)
|
|
|
|
|
self.clear_ref_button = QPushButton("Clear")
|
|
|
|
|
self.clear_ref_button.setToolTip("Remove all custom reference lines")
|
|
|
|
|
self.clear_ref_button.clicked.connect(self.clearReferenceLinesRequested.emit)
|
|
|
|
|
ref_row.addWidget(self.clear_ref_button)
|
|
|
|
|
group_layout.addLayout(ref_row)
|
|
|
|
|
|
2026-05-30 00:42:45 +09:00
|
|
|
button_row = QHBoxLayout()
|
|
|
|
|
self.refresh_button = QPushButton("Refresh Plot")
|
|
|
|
|
self.refresh_button.setToolTip("Re-render the current plot with current settings")
|
|
|
|
|
self.refresh_button.clicked.connect(self.plotRefreshRequested.emit)
|
|
|
|
|
button_row.addWidget(self.refresh_button)
|
|
|
|
|
group_layout.addLayout(button_row)
|
|
|
|
|
|
|
|
|
|
layout.addWidget(group_box)
|
|
|
|
|
|
|
|
|
|
def _on_metric_changed(self, _index: int):
|
|
|
|
|
metric_id = self.metric_combo.currentData()
|
|
|
|
|
if metric_id:
|
|
|
|
|
self.logger.info(f"Metric changed: {metric_id}")
|
|
|
|
|
self.metricChanged.emit(metric_id)
|
|
|
|
|
|
|
|
|
|
def current_metric_id(self) -> str:
|
|
|
|
|
return self.metric_combo.currentData() or DEFAULT_METRIC_ID
|
2026-06-14 00:35:10 +09:00
|
|
|
|
|
|
|
|
def current_view_state(self) -> ViewState:
|
|
|
|
|
return ViewState(y_log=self.log_freq_check.isChecked())
|