|
@@ -28,6 +28,9 @@ try:
|
|
|
except ImportError:
|
|
|
_BOKEH_FOUND = False
|
|
|
|
|
|
+__all__ = ['Backend', 'PlotKind', 'PlotHandler', 'PlotManager', 'plot', 'follow', 'set_xlim', 'set_ylim',
|
|
|
+ 'Arrow', 'StepPlot', 'ScatterPlot', 'LinePlot']
|
|
|
+
|
|
|
# TODO: Bokeh (see TODOs)
|
|
|
# TODO: More Plot Kinds
|
|
|
|
|
@@ -146,13 +149,14 @@ class PlotHandler:
|
|
|
interval (int): Amount of milliseconds between plot refreshes.
|
|
|
frames (int): The amount of frames for the animation. Only used if the
|
|
|
animation needs to be saved.
|
|
|
+ static (bool): When :code:`True`, no animation will be created.
|
|
|
|
|
|
See Also:
|
|
|
- :class:`Backend`
|
|
|
- :class:`PlotKind`
|
|
|
- :class:`PlotManager`
|
|
|
"""
|
|
|
- def __init__(self, object, figure, kind, backend=Backend.MPL, interval=100, frames=None):
|
|
|
+ def __init__(self, object, figure, kind, backend=Backend.MPL, interval=100, frames=None, static=False):
|
|
|
assert Backend.exists(backend), "Invalid backend."
|
|
|
self.object = object
|
|
|
self.kind = kind
|
|
@@ -173,14 +177,15 @@ class PlotHandler:
|
|
|
}
|
|
|
|
|
|
# backend info:
|
|
|
- if Backend.compare("MPL", backend) or Backend.compare("SNS", backend):
|
|
|
- self.__ani = animation.FuncAnimation(figure[0], lambda _: self.update(),
|
|
|
- interval=interval, frames=frames)
|
|
|
- figure[0].canvas.mpl_connect('close_event', lambda evt: self.__close_event())
|
|
|
- elif Backend.compare("BOKEH", backend):
|
|
|
- curdoc().add_periodic_callback(lambda: self.update(), interval)
|
|
|
- # TODO (is this even possible?):
|
|
|
- curdoc().on_session_destroyed(lambda ctx: self.__close_event())
|
|
|
+ if not static:
|
|
|
+ if Backend.compare("MPL", backend) or Backend.compare("SNS", backend):
|
|
|
+ self.__ani = animation.FuncAnimation(figure[0], lambda _: self.update(),
|
|
|
+ interval=interval, frames=frames)
|
|
|
+ figure[0].canvas.mpl_connect('close_event', lambda evt: self.__close_event())
|
|
|
+ elif Backend.compare("BOKEH", backend):
|
|
|
+ curdoc().add_periodic_callback(lambda: self.update(), interval)
|
|
|
+ # TODO (is this even possible?):
|
|
|
+ curdoc().on_session_destroyed(lambda ctx: self.__close_event())
|
|
|
|
|
|
def signal(self, name, *args):
|
|
|
"""
|
|
@@ -792,6 +797,74 @@ class PlotManager:
|
|
|
handler.stop()
|
|
|
|
|
|
|
|
|
+def plot(object, figure, kind, backend=Backend.get("MPL"), margin=(0.02, 0.02)):
|
|
|
+ """
|
|
|
+ Plot data on a figure after the simulation has finished.
|
|
|
+ This will automatically plot the full graph, so all axis scaling needs to be done afterwards.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ object (Any): The object from which data needs to be polled. By default,
|
|
|
+ the :code:`data_xy` attribute will be used on the object,
|
|
|
+ which should result in a 2xN array in the form of
|
|
|
+ :code:`([x1, x2, x3...], [y1, y2, y3...])`.
|
|
|
+ figure: The figure object required for plotting. This has been made
|
|
|
+ externally to allow for full manipulation of the plotting
|
|
|
+ framework itself.
|
|
|
+ kind (PlotKind): What needs to be plotted. See :class:`PlotKind` for more
|
|
|
+ info.
|
|
|
+ backend (Backend): The backend to use.
|
|
|
+ margin (tuple): A margin for the limits of the axes.
|
|
|
+
|
|
|
+ See Also:
|
|
|
+ :class:`PlotHandler`
|
|
|
+ """
|
|
|
+ assert Backend.exists(backend), "Invalid backend."
|
|
|
+ ph = PlotHandler(object, figure, kind, backend, static=True)
|
|
|
+ ph.update()
|
|
|
+
|
|
|
+ x, y = ph.get_data()
|
|
|
+ xlim = min(x), max(x)
|
|
|
+ ylim = min(y), max(y)
|
|
|
+ xlim = xlim[0] - margin[0], xlim[1] + margin[0]
|
|
|
+ ylim = ylim[0] - margin[1], ylim[1] + margin[1]
|
|
|
+
|
|
|
+ set_xlim(figure, backend, xlim)
|
|
|
+ set_ylim(figure, backend, ylim)
|
|
|
+
|
|
|
+
|
|
|
+def set_xlim(figure, backend, values):
|
|
|
+ """
|
|
|
+ Shorthand method for setting the x limits of the figure.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ figure: The figure to alter.
|
|
|
+ backend (Backend): The backend to use.
|
|
|
+ values (tuple): The new limits for the x-axis.
|
|
|
+ """
|
|
|
+ if Backend.compare("MPL", backend) or Backend.compare("SNS", backend):
|
|
|
+ figure[1].set_xlim(values)
|
|
|
+ elif Backend.compare("BKH", backend):
|
|
|
+ lower, upper = values
|
|
|
+ figure.x_range.start = lower
|
|
|
+ figure.x_range.end = upper
|
|
|
+
|
|
|
+
|
|
|
+def set_ylim(figure, backend, values):
|
|
|
+ """
|
|
|
+ Shorthand method for setting the y limits of the figure.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ figure: The figure to alter.
|
|
|
+ backend (Backend): The backend to use.
|
|
|
+ values (tuple): The new limits for the y-axis.
|
|
|
+ """
|
|
|
+ if Backend.compare("MPL", backend) or Backend.compare("SNS"):
|
|
|
+ figure[1].set_ylim(values)
|
|
|
+ elif Backend.compare("BKH", backend):
|
|
|
+ lower, upper = values
|
|
|
+ figure.y_range.start = lower
|
|
|
+ figure.y_range.end = upper
|
|
|
+
|
|
|
# TEMPORARY TEST OBJECT:
|
|
|
import math
|
|
|
class __Block:
|