Skip to content
Open
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
74 changes: 69 additions & 5 deletions trace/widgets/control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def add_curves(self, pvs: list[str]) -> None:
List of PV names to add as curves
"""
for pv in pvs:
self.add_curve(pv)
self.add_curve(pv, duplicate=False)

def add_empty_axis(self, name: str = "") -> "AxisItem":
logger.debug("Adding new empty axis to the plot")
Expand Down Expand Up @@ -325,7 +325,14 @@ def curve_item_dict(self):
return plot_curves

@QtCore.Slot()
def add_curve(self, pv: str = None) -> "CurveItem":
def add_curve(self, pv: str = None, duplicate: bool = True) -> "CurveItem":
"""
Add curve to the plot. New curves are added to the last axis by default, then sorted by unit.

Parameters:
pv (str): EPICS PV or formula string
duplicate (bool): If True, duplicate PVs will be added on a new axis. If False, skip duplicates.
"""
if pv is None and self.sender():
pv = self.sender().text()

Expand All @@ -336,7 +343,51 @@ def add_curve(self, pv: str = None) -> "CurveItem":
if pv.startswith("f://"):
return last_axis.add_formula_curve(pv)
else:
return last_axis.add_curve(pv)
# Check for duplicate PVs. If pv already exists, either add to new axis or skip
# depending on 'duplicate' flag
curves = self.curve_item_dict
if pv in [curves[curve]["name"] for curve in curves]:
if duplicate:
logger.debug(f"Adding duplicate curve to new axis for PV '{pv}'.")
return self.add_curve_to_new_axis(pv)
else:
logger.debug(f"Skipping duplicate curve for PV '{pv}'.")
return None

curve = last_axis.add_curve(pv)
self.move_to_axis_from_unit(curve) # Move to axis based on unit

return curve

def add_curve_to_new_axis(self, pv: str) -> "CurveItem":
"""
Add curve to a new, empty axis. Used to avoid adding duplicate curves to the same axis.

Parameters
----------
pv (str): EPICS PV name
"""
if not pv:
return None
axis_item = self.add_empty_axis()
return axis_item.add_curve(pv)

def move_to_axis_from_unit(self, curve: "CurveItem") -> None:
"""
Detect curve units and automatically move curve to an axis matching its unit

Parameters
----------
curve : CurveItem
"""
if not isinstance(curve, CurveItem):
return
if not curve.is_formula_curve():
unit = curve.source.units
if unit:
self.move_curve_to_axis(curve, unit)
else:
curve.source.unitSignal.connect(lambda unit: self.move_curve_to_axis(curve, unit))

def clear_all(self) -> None:
"""Clear all axes and curves from the plot and control panel."""
Expand Down Expand Up @@ -606,6 +657,7 @@ def add_curve(self, pv: str, channel_args: dict = None) -> "CurveItem":
CurveItem
The created CurveItem widget.
"""

palette = self.control_panel.curve_palette
color = ColorButton.index_color(len(self.plot._curves), palette=palette)
args = {
Expand All @@ -622,6 +674,20 @@ def add_curve(self, pv: str, channel_args: dict = None) -> "CurveItem":

return self.make_curve_widget(plot_curve_item)

def get_curves(self) -> list["CurveItem"]:
"""Returns a list of CurveItems on this axis"""
curves = []
for i in range(self.layout().count() - 1, -1, -1):
item = self.layout().itemAt(i).widget()
if isinstance(item, CurveItem):
curves.append(item)

return curves

def get_curve_names(self) -> list[str]:
"""Returns a list of curve names on this axis"""
return [curve.source.name() for curve in self.get_curves()]

def add_formula_curve(self, formula: str) -> "CurveItem":
"""Create a new FormulaCurveItem for the given formula and add it
to this AxisItem. Also creates a CurveItem widget for it.
Expand Down Expand Up @@ -950,8 +1016,6 @@ def __init__(self, axis_item: AxisItem, source: ArchivePlotCurveItem | FormulaCu

self.variable_name = self.control_panel.key_gen.send(self.source)
self.control_panel.curve_dict[self.variable_name] = self.source
if not self.is_formula_curve():
self.source.unitSignal.connect(lambda unit: self.control_panel.move_curve_to_axis(self, unit))

self.setup_layout()

Expand Down
Loading