Skip to content

Commit

Permalink
Merge pull request #406 from BCDA-APS/401-trim_plot
Browse files Browse the repository at this point in the history
replace plot_prune_fifo() with trim_plot()
  • Loading branch information
prjemian authored Aug 26, 2020
2 parents b845a11 + 6b6d1bf commit 71e387d
Showing 1 changed file with 197 additions and 3 deletions.
200 changes: 197 additions & 3 deletions apstools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@
~replay
~run_in_thread
~safe_ophyd_name
~select_live_plot
~select_mpl_figure
~show_ophyd_symbols
~split_quoted_line
~summarize_runs
~text_encode
~to_unicode_or_bust
~trim_plot_by_name
~trim_plot_lines
~trim_string_for_EPICS
~unix
Expand Down Expand Up @@ -1276,7 +1280,16 @@ def plot_prune_fifo(bec, n, y, x):
*object* :
instance of ophyd.Signal (or subclass),
independent (x) axis
DEPRECATED: Will be removed by end of 2020-12.
Use :func:`trim_plot_lines` instead.
Note the order of parameters is different in :func:`trim_plot_lines`.
"""
warnings.warn(
"DEPRECATED: plot_prune_fifo() will be removed"
" in a future release. Use trim_plot_lines() instead."
" Note the order of parameters is different in trim_plot_lines()."
)

if n < 0:
raise ValueError(
Expand Down Expand Up @@ -1310,6 +1323,187 @@ def plot_prune_fifo(bec, n, y, x):
return lp


def select_mpl_figure(x, y):
"""
get the MatPlotLib Figure window for y vs x
PARAMETERS
x
*object*:
X axis object (an ``ophyd.Signal``)
y
ophyd object:
X axis object (an ``ophyd.Signal``)
RETURNS
object or ``None``:
Instance of ``matplotlib.pyplot.Figure()``
"""
import matplotlib.pyplot as plt
figure_name = f"{y.name} vs {x.name}"
if figure_name in plt.get_figlabels():
return plt.figure(figure_name)


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


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, replaces :func:`plot_prune_fifo`)
"""
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"):
logger.warning(
"%s vs %s: mpl remove() error: %s",
y.name, x.name, str(exc))
ax.legend()
liveplot.update_plot()
logger.debug("trim complete")


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.
Note: :func:`trim_plot_by_name` is being tested as an alternative to
:func:`trim_plot_lines` and is not guaranteed
to remain in apstools in its present implementation, if kept at all.
This function may not appear to trim all lines properly
when run from a plan (such as the linger example below).
This is because the plots are generated from a RunEngine callback.
That callback executes, scheduling the plots for drawing
*after* ``bp.scan()`` completes and likely after
:func:`trim_plot_by_name` is run.
Q: How to block the RunEngine until the plots are drawn by the callback?
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()
trim_plot_by_name(5)
trim_plot_by_name(5, "noisy_det vs motor")
trim_plot_by_name(5, ["noisy_det vs motor", "det noisy_det vs motor"]])
# longer example (with simulators from ophyd)
from ophyd.sim import *
snooze = 0.25
def the_scans():
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)
trim_plot_by_name(3)
# repeat the_scans 15 times
uids = RE(bps.repeat(the_scans, 15))
trim_plot_by_name()
def the_scans():
yield from bp.scan([noisy_det], motor, -1, 1, 5)
yield from bp.scan([noisy_det], motor2, 3, 1, 4)
yield from bps.sleep(1)
trim_plot_by_name(3, "noisy_det vs motor")
trim_plot_by_name(5, "noisy_det vs motor2")
(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()


def print_snapshot_list(db, printing=True, **search_criteria):
"""
print (stdout) a list of all snapshots in the databroker
Expand Down Expand Up @@ -1433,13 +1627,13 @@ def json_export(headers, filename, zipfilename=None):
EXAMPLE: READ THE ZIP FILE:
using :meth:`~json_import`::
using :func:`~json_import`::
datasets = json_import("data.json", zipfilename="bluesky_data.zip")
EXAMPLE: READ THE JSON TEXT FILE
using :meth:`~json_import`::
using :func:`~json_import`::
datasets = json_import("data.json)
Expand All @@ -1457,7 +1651,7 @@ def json_export(headers, filename, zipfilename=None):

def json_import(filename, zipfilename=None):
"""
read the file exported by :meth:`~json_export()`
read the file exported by :func:`~json_export()`
RETURNS
Expand Down

0 comments on commit 71e387d

Please sign in to comment.