Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 8 additions & 58 deletions src/sc_linac_physics/applications/microphonics/plots/base_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def autoRangeEnabled(self):

pg.PlotWidget.autoRangeEnabled = autoRangeEnabled
from typing import Tuple, Optional

from sc_linac_physics.utils.plot_tooltip import PlotTooltip
import numpy as np
import pyqtgraph as pg
from PyQt5.QtCore import Qt
Expand All @@ -34,7 +34,6 @@ def __init__(self, parent=None, plot_type=None, config=None):
self.plot_type = plot_type
self.config = config or {}
self.plot_curves = {}
self.tooltips = {}

# Setup UI components common to all plots
self.setup_ui()
Expand Down Expand Up @@ -117,13 +116,14 @@ def _setup_plot_widget(self, plot_type, config):
if "y_range" in config:
widget.setYRange(*config["y_range"])

# Connect signal for tooltips
widget.scene().sigMouseMoved.connect(
lambda ev: self._show_tooltip(plot_type, ev)
)
self.tooltip = PlotTooltip(widget, self.get_formatter())

return widget

def get_formatter(self):
"""Override in subclasses for custom tooltip formatting."""
return None

def _get_cavity_pen(self, cavity_num):
"""
Create a pen for the specified cavity w/ appropriate styling
Expand Down Expand Up @@ -153,56 +153,6 @@ def _get_cavity_pen(self, cavity_num):

return pg.mkPen(qcolor, width=2, style=style)

def _show_tooltip(self, plot_type, ev):
"""
Show tooltip w/ data values on hover

Args:
plot_type: Type of plot
ev: Mouse event
"""
try:
plot = self.plot_widget
view = plot.plotItem.vb
if plot.sceneBoundingRect().contains(ev):
mouse_point = view.mapSceneToView(ev)
x, y = mouse_point.x(), mouse_point.y()

# Format tooltip based on plot type
tooltip = self._format_tooltip(plot_type, x, y)
if not tooltip:
return

# Update/create tooltip label
if plot_type not in self.tooltips:
self.tooltips[plot_type] = pg.TextItem(
text=tooltip,
color=(255, 255, 255),
border="k",
fill=(0, 0, 0, 180),
)
plot.addItem(self.tooltips[plot_type])

self.tooltips[plot_type].setText(tooltip)
self.tooltips[plot_type].setPos(x, y)
self.tooltips[plot_type].show()
except Exception as e:
print(f"Tooltip error: {str(e)}")

def _format_tooltip(self, plot_type, x, y):
"""
Format tooltip text based on plot type

Args:
plot_type: Type of plot
x: X coordinate
y: Y coordinate

Returns:
str: Tooltip text
"""
return f"X: {x:.2f}, Y: {y:.2f}"

def _preprocess_data(
self, cavity_channel_data: dict, channel_type: str = "DF"
) -> Tuple[Optional[np.ndarray], bool]:
Expand Down Expand Up @@ -263,8 +213,8 @@ def clear_plot(self):
self.plot_widget.clear()
self.plot_curves = {}
# Hide tooltip if it exists
if self.plot_type in self.tooltips:
self.tooltips[self.plot_type].hide()
if self.tooltip:
self.tooltip.hide()

def update_plot(self, cavity_num, data):
"""
Expand Down
16 changes: 4 additions & 12 deletions src/sc_linac_physics/applications/microphonics/plots/fft_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sc_linac_physics.applications.microphonics.utils.data_processing import (
calculate_fft,
)
from sc_linac_physics.utils.plot_tooltip import PlotTooltip


class FFTPlot(BasePlot):
Expand Down Expand Up @@ -67,18 +68,9 @@ def set_plot_config(self, panel_wide_config):
default_y_range = self.config.get("y_range", (0, 1.5))
self.plot_widget.setYRange(*default_y_range)

def _format_tooltip(self, plot_type, x, y):
"""Format tooltip text for FFT plot

Args:
plot_type: Type of plot (unused in this implementation)
x: X coordinate (frequency in Hz)
y: Y coordinate (amplitude)

Returns:
str: Formatted tooltip text
"""
return f"Frequency: {x:.1f} Hz\nAmplitude: {y:.3f}"
def get_formatter(self):
"""Tooltip formatter for FFT plot."""
return PlotTooltip.make_formatter("Frequency (Hz)", "Amplitude")

def update_plot(self, cavity_num, cavity_channel_data):
"""Update FFT plot w/ new data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from sc_linac_physics.applications.microphonics.utils.data_processing import (
calculate_histogram,
)
from sc_linac_physics.utils.plot_tooltip import PlotTooltip


class HistogramPlot(BasePlot):
Expand All @@ -26,18 +27,9 @@ def __init__(self, parent=None):
self.num_bins = 140 # Default number of bins
super().__init__(parent, plot_type="histogram", config=config)

def _format_tooltip(self, plot_type, x, y):
"""Format tooltip text specifically for histogram plot

Args:
plot_type: Type of plot (unused in this implementation)
x: X coordinate (detuning in Hz)
y: Y coordinate (count)

Returns:
str: Formatted tooltip text
"""
return f"Detuning: {x:.1f} Hz\nCount: {int(max(1, y))}"
def get_formatter(self):
"""Tooltip formatter for histogram plot."""
return PlotTooltip.make_formatter("Detuning (Hz)", "Count")

def _update_data_range(self, df_data):
"""Update data range based on current data."""
Expand Down
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the spectrogram plot only remove the specific tooltip, but not add the new shared tooltip?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spectrogram uses a grid of separate sub plots for each cavity so the shared PlotTooltip can't just attach to it like the other plots. Also it already has the colorbar for power values and it's more about looking at patterns than reading individual points so I didn't think a tooltip would really add much here. I can look into adding one though if you think we need it.

Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,6 @@ def _refresh_grid_layout(self):
if visible_cavities:
self._add_colorbar(num_rows)

def _format_tooltip(self, plot_type, x, y):
"""Format tooltip text specifically for spectrogram plot"""
return f"Time: {x:.3f} s\nFrequency: {y:.1f} Hz"

def update_plot(self, cavity_num, cavity_channel_data):
df_data, is_valid = self._preprocess_data(
cavity_channel_data, channel_type="DF"
Expand Down Expand Up @@ -270,10 +266,6 @@ def set_plot_config(self, config):
elif self.cavity_data_cache:
self._refresh_grid_layout()

def _show_tooltip(self, plot_type, ev):
"""Override tooltip behavior"""
pass

def clear_plot(self):
"""Clear all plot data"""
self.cavity_data_cache.clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sc_linac_physics.applications.microphonics.utils.constants import (
BASE_HARDWARE_SAMPLE_RATE,
)
from sc_linac_physics.utils.plot_tooltip import PlotTooltip


class TimeSeriesPlot(BasePlot):
Expand Down Expand Up @@ -39,9 +40,9 @@ def __init__(self, parent=None):
vb.setLimits(xMin=0) # Prevent negative time
vb.sigRangeChangedManually.connect(self._on_range_changed)

def _format_tooltip(self, plot_type, x, y):
"""Override base tooltip formatting"""
return f"Time: {x:.3f} s\nDetuning: {y:.2f} Hz"
def get_formatter(self):
"""Tooltip formatter for time series plot."""
return PlotTooltip.make_formatter("Time (s)", "Detuning (Hz)")

def _decimate_data(self, times, values, target_points):
"""Decimation that still preserves important features"""
Expand Down
12 changes: 11 additions & 1 deletion src/sc_linac_physics/utils/plot_tooltip.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(
self._container = plot
else:
self._plot_item = plot
self._container = plot
self._container = plot.getViewBox()

self.formatter = formatter or format_default
self.enabled = True
Expand Down Expand Up @@ -98,3 +98,13 @@ def cleanup(self):
self._plot_item.removeItem(self._tooltip)
except (RuntimeError, AttributeError):
pass

@staticmethod
def make_formatter(
x_label: str,
y_label: str,
x_fmt: str = ".2f",
y_fmt: str = ".2f",
) -> Callable[[float, float], str]:
"""Create a formatter from axis labels and optional format specs."""
return lambda x, y: f"{x_label}: {x:{x_fmt}}\n{y_label}: {y:{y_fmt}}"
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,12 @@ def test_end_zoom_resets_flag(self, plot_widget):

def test_format_tooltip(self, plot_widget):
"""Test tooltip formatting"""
tooltip = plot_widget._format_tooltip("time_series", x=5.123, y=123.456)
formatter = plot_widget.get_formatter()
tooltip = formatter(5.123, 123.456)

assert "Time:" in tooltip
assert "5.123" in tooltip
assert "Detuning:" in tooltip
assert "Time" in tooltip
assert "5.12" in tooltip
assert "Detuning" in tooltip
assert "123.46" in tooltip

# ===== Clear Plot Tests =====
Expand Down
Loading