"""
Plot Support
+++++++++++++++++++++++++++++++++++++++
.. autosummary::
~select_mpl_figure
~select_live_plot
~trim_plot_lines
~trim_plot_by_name
"""
import logging
logger = logging.getLogger(__name__)
[docs]def select_live_plot(bec, signal):
"""
Get the *first* live plot that matches ``signal``.
PARAMETERS
bec
*object*:
instance of ``bluesky.callbacks.best_effort.BestEffortCallback``
signal
*object*:
The Y axis object (an ``ophyd.Signal``)
RETURNS
*object*:
Instance of ``bluesky.callbacks.best_effort.LivePlotPlusPeaks()``
or ``None``
"""
for live_plot_dict in bec._live_plots.values():
live_plot = live_plot_dict.get(signal.name)
if live_plot is not None:
return live_plot
[docs]def trim_plot_lines(bec, n, x, y):
"""
Find the plot with axes x and y and replot with at most the last *n* lines.
Note: :func:`trim_plot_lines` is not a bluesky plan. Call it as
normal Python function.
EXAMPLE::
trim_plot_lines(bec, 1, m1, noisy)
PARAMETERS
bec
*object* :
instance of BestEffortCallback
n
*int* :
number of plots to keep
x
*object* :
instance of ophyd.Signal (or subclass),
independent (x) axis
y
*object* :
instance of ophyd.Signal (or subclass),
dependent (y) axis
(new in release 1.3.5)
"""
liveplot = select_live_plot(bec, y)
if liveplot is None:
logger.debug("no live plot found with signal '%s'", y.name)
return
fig = select_mpl_figure(x, y)
if fig is None:
logger.debug("no figure found with '%s vs %s'", y.name, x.name)
return
if len(fig.axes) == 0:
logger.debug("no plots on figure: '%s vs %s'", y.name, x.name)
return
ax = fig.axes[0]
while len(ax.lines) > n:
try:
ax.lines[0].remove()
except ValueError as exc:
if not str(exc).endswith("x not in list"):
# fmt: off
logger.warning(
"%s vs %s: mpl remove() error: %s",
y.name, x.name, str(exc),
)
# fmt: on
ax.legend()
liveplot.update_plot()
logger.debug("trim complete")
[docs]def trim_plot_by_name(n=3, plots=None):
"""
Find the plot(s) by name and replot with at most the last *n* lines.
Note: this is not a bluesky plan. Call it as normal Python function.
It is recommended to call :func:`~trim_plot_by_name()` *before* the
scan(s) that generate plots. Plots are generated from a RunEngine
callback, executed *after* the scan completes.
PARAMETERS
n
*int* :
number of plots to keep
plots
*str*, [*str*], or *None* :
name(s) of plot windows to trim
(default: all plot windows)
EXAMPLES::
trim_plot_by_name() # default of n=3, apply to all plots
trim_plot_by_name(5) # change from default of n=3
trim_plot_by_name(5, "noisy_det vs motor") # just this plot
trim_plot_by_name(
5,
["noisy_det vs motor", "det noisy_det vs motor"]]
)
EXAMPLE::
# use simulators from ophyd
from bluesky import plans as bp
from bluesky import plan_stubs as bps
from ophyd.sim import *
snooze = 0.25
def scan_set():
trim_plot_by_name()
yield from bp.scan([noisy_det], motor, -1, 1, 5)
yield from bp.scan([noisy_det, det], motor, -2, 1, motor2, 3, 1, 6)
yield from bps.sleep(snooze)
# repeat the_scans 15 times
uids = RE(bps.repeat(scan_set, 15))
(new in release 1.3.5)
"""
import matplotlib.pyplot as plt
if isinstance(plots, str):
plots = [plots]
for fig_name in plt.get_figlabels():
if plots is None or fig_name in plots:
fig = plt.figure(fig_name)
for ax in fig.axes:
while len(ax.lines) > n:
ax.lines[0].remove()
# update the plot legend
ax.legend()