Skip to content

Commit

Permalink
Merge pull request #1097 from aksharsarvesh/axes
Browse files Browse the repository at this point in the history
ENH: Axes Handling and Hiding in Multi Axis Plots
  • Loading branch information
jbellister-slac authored Sep 3, 2024
2 parents fb09a5e + bf44b17 commit 9d4d377
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 20 deletions.
18 changes: 9 additions & 9 deletions pydm/tests/widgets/test_baseplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ def test_baseplot_multiple_y_axes(qtbot):
assert base_plot.plotItem.axes["Test Axis 3"]["item"].orientation == "left"

# Verify the curves got assigned to the correct axes
assert base_plot.plotItem.curvesPerAxis["Test Axis 1"] == 2
assert base_plot.plotItem.curvesPerAxis["Test Axis 2"] == 1
assert base_plot.plotItem.curvesPerAxis["Test Axis 3"] == 1
assert len(base_plot.plotItem.axes["Test Axis 1"]["item"]._curves) == 2
assert len(base_plot.plotItem.axes["Test Axis 2"]["item"]._curves) == 1
assert len(base_plot.plotItem.axes["Test Axis 3"]["item"]._curves) == 1


def test_baseplot_no_added_y_axes(qtbot):
Expand Down Expand Up @@ -224,10 +224,10 @@ def test_timeplot_add_multiple_axes(qtbot):

# Verify the curves got assigned to the correct axes
assert len(time_plot.curves) == 5
assert time_plot.plotItem.curvesPerAxis["Axis 1"] == 2
assert time_plot.plotItem.curvesPerAxis["Axis 2"] == 1
assert time_plot.plotItem.curvesPerAxis["Axis 3"] == 1
assert time_plot.plotItem.curvesPerAxis["Axis 4"] == 1
assert len(time_plot.plotItem.axes["Axis 1"]["item"]._curves) == 2
assert len(time_plot.plotItem.axes["Axis 2"]["item"]._curves) == 1
assert len(time_plot.plotItem.axes["Axis 3"]["item"]._curves) == 1
assert len(time_plot.plotItem.axes["Axis 4"]["item"]._curves) == 1


def test_multiaxis_plot_no_designer_flow(qtbot):
Expand All @@ -249,8 +249,8 @@ def test_multiaxis_plot_no_designer_flow(qtbot):
assert "Axis 2" in plot.plotItem.axes
assert "right" in plot.plotItem.axes
assert "top" in plot.plotItem.axes
assert plot.plotItem.curvesPerAxis["Axis 1"] == 2
assert plot.plotItem.curvesPerAxis["Axis 2"] == 1
assert len(plot.plotItem.axes["Axis 1"]["item"]._curves) == 2
assert len(plot.plotItem.axes["Axis 2"]["item"]._curves) == 1

# Now check the case where no new y-axis name is specified for any of the new channels.
plot = PyDMTimePlot()
Expand Down
16 changes: 14 additions & 2 deletions pydm/widgets/baseplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ def __init__(
**kws,
) -> None:
super(BasePlotAxisItem, self).__init__(orientation, **kws)

self._curves: List[BasePlotCurveItem] = []
self._name = name
self._orientation = orientation
self._label = label
Expand Down Expand Up @@ -640,6 +640,17 @@ def log_mode(self, log_mode: bool) -> None:
self.setLogMode(x=False, y=log_mode)
self.log_mode_updated.emit(self.name, log_mode)

def setHidden(self, shouldHide: bool):
"""Set an axis to hide/show and do the same for all of its connected curves"""
if shouldHide:
for curve in self._curves:
curve.hide()
self.hide()
else:
for curve in self._curves:
curve.show()
self.show()

def to_dict(self) -> OrderedDict:
"""
Returns an OrderedDict representation with values for all properties
Expand Down Expand Up @@ -896,7 +907,8 @@ def removeCurve(self, plot_item: BasePlotCurveItem) -> None:
# Mark it as not existing so all curves that rely on this curve get destroyed as well
plot_item.exists = False
if plot_item.y_axis_name in self.plotItem.axes:
self.plotItem.unlinkDataFromAxis(plot_item.y_axis_name)
self.plotItem.unlinkDataFromAxis(plot_item)

self.removeItem(plot_item)
self._curves.remove(plot_item)
if len(self._curves) < 1:
Expand Down
54 changes: 45 additions & 9 deletions pydm/widgets/multi_axis_plot.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import weakref
from collections import Counter
from pyqtgraph import AxisItem, PlotDataItem, PlotItem, ViewBox
from typing import List, Optional
from qtpy.QtCore import Qt
Expand Down Expand Up @@ -30,7 +29,6 @@ def __init__(self, parent=None, axisItems=None, **kargs):
viewBox.menu = MultiAxisViewBoxMenu(viewBox)
super(MultiAxisPlot, self).__init__(viewBox=viewBox, axisItems=axisItems, **kargs)

self.curvesPerAxis = Counter() # A simple mapping of AxisName to a count of curves that using that axis
self.axesOriginalRanges = {} # Dict from axis name to floats (x, y) representing original range of the axis

# A set containing view boxes which are stacked underneath the top level view. These views will be needed
Expand Down Expand Up @@ -113,7 +111,11 @@ def addAxis(

def change_axis_name(self, old_name: str, new_name: str):
"""Change the name of the axis by changing the item's key in the axes dictionary."""
axis = self.axes[old_name]["item"]
self.axes[new_name] = self.axes[old_name]
if hasattr(axis, "_curves"):
for curve in axis._curves:
curve.y_axis_name = new_name
del self.axes[old_name]

def addStackedView(self, view):
Expand Down Expand Up @@ -220,22 +222,27 @@ def linkDataToAxis(self, plotDataItem: PlotDataItem, axisName: str) -> None:
# pyqtgraph expects data items on plots to be added to both the list of curves and items to function properly
self.curves.append(plotDataItem)
self.items.append(plotDataItem)
self.curvesPerAxis[axisName] += 1
if hasattr(axisToLink, "_curves"):
axisToLink._curves.append(plotDataItem)
axisToLink.show()
for otherAxisName in self.axes.keys():
self.autoVisible(otherAxisName)

def removeAxis(self, axisName):
if axisName not in self.axes:
return

self.curvesPerAxis[axisName] = 0

oldAxis = self.axes[axisName]["item"]
self.layout.removeItem(oldAxis)
if oldAxis.scene() is not None:
oldAxis.scene().removeItem(oldAxis)
stackedView = oldAxis.linkedView()
oldAxis.unlinkFromView()
if stackedView and stackedView is not self.vb:
self.stackedViews.remove(stackedView)
del self.axes[axisName]

def unlinkDataFromAxis(self, axisName):
def unlinkDataFromAxis(self, curve: PlotDataItem):
"""
Lets the plot know that this axis is now associated with one less curve. If there are no
longer any curves linked with this axis, then removes it from the scene and cleans it up.
Expand All @@ -244,10 +251,39 @@ def unlinkDataFromAxis(self, axisName):
axisName: str
The name of the axis that a curve is being removed from
"""
if (
hasattr(curve, "y_axis_name")
and curve.y_axis_name in self.axes
and curve in self.axes[curve.y_axis_name]["item"]._curves
):
self.axes[curve.y_axis_name]["item"]._curves.remove(curve)
self.autoVisible(curve.y_axis_name)

def autoVisible(self, axisName):
"""Handle automatically hiding or showing an axis based on whether it has
visible curves attached and/or if it's the last visible axis
(don't automatically hide the last axis, even if all of it's curves are hidden)
self.curvesPerAxis[axisName] -= 1
if self.curvesPerAxis[axisName] == 0:
self.removeAxis(axisName)
Parameters
-------------
axisName: str
The name of the axis we are going to try to hide if possible, or show if not"""
# Do we have any visible curves?
axis = self.axes[axisName]["item"]
if hasattr(axis, "_curves"):
for curve in axis._curves:
if curve.isVisible():
axis.show()
return

# We don't have any visible curves, but are we the only curve being shown?
for otherAxis in self.axes.keys():
otherItem = self.axes[otherAxis]["item"]
if otherItem is not axis and otherAxis not in ["bottom", "top"] and otherItem.isVisible():
axis.hide()
return
# No other axis is visible.
axis.show()

def setXRange(self, minX, maxX, padding=0, update=True):
"""
Expand Down

0 comments on commit 9d4d377

Please sign in to comment.