Custom plotting¶
During a BLISS session, users may create data (other than scan data) that needs to be displayed graphically. Flint offers a collection of different type of plots (curve, scatter, image…) so user can select the one that fit the with the data to be displayed.
Basic plot display¶
A generic display is provided through the BLISS plot()
command.
It checks the data dimensionality to select the kind of charts.
This can be convenient for basic display.
# Display a list (as a single curve using index as x-axis)
plot([1, 2, 3, 1, 2, 3])
import numpy
# Display a 1D data (as a single curve using index as x-axis)
array = numpy.array([1, 2, 3, 1, 2, 3])
plot(array)
# Display a 2D data (as an image)
image = numpy.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
plot(image)
# Display a 3D data (as a stack of images)
cube = numpy.arange(1000)
cube.shape = 10, 10, 10
plot(cube)
Create a plot¶
To use more features another way is provided.
First a plot has to be created from Flint.
The first argument (here curve
) is used to select the kind of expected plot.
See the following documentation for example of each kind.
f = flint()
p = f.get_plot("curve")
Other arguments are available to edit some behavior of this plot.
A title can be specified, a unique name can be set to reuse plots instead of creating a new one. The plot can be closeable (default is true) or selected by default at the creation (default is false).
p = f.get_plot("curve",
name="My plot title",
unique_name="myplot42",
closeable=True,
selected=True)
1D plot / curve plot¶
The main plot is a curve plot.
It can be used to display several 1D data as curves.
import numpy
# Create the plot
f = flint()
p = f.get_plot("curve", name="My plot")
# Create data
t = numpy.linspace(0, 10 * numpy.pi, 100)
s = numpy.sin(t)
c = numpy.cos(t)
# Update the plot
p.add_curve(t, c, legend="aaa") # the legend have to be unique
p.add_curve(t, s, legend="bbb")
# Clean up the plot
p.clean_data()
This also can be used to display parametric functions
import numpy
t = numpy.linspace(-3, 3, 50)
x = 16 * numpy.sin(t)**3
y = 13 * numpy.cos(t) - 5 * numpy.cos(2*t) - 2 * numpy.cos(3*t) - numpy.cos(4*t)
f = flint()
p = f.get_plot("curve", name="My heart")
p.add_curve(x, y, legend="heart")
Few functions are available to tune the axes
p.xlabel = "It's the time"
p.yscale = "log"
p.xscale = "linear"
Descriptive 1D plot¶
The 1D plot also provides a descriptive mode. In this mode, there is a split between the data and the way it is rendered.
The description of the rendering is done by a layer of items. First this description have to be provided, then the data can be set.
When data for axis are shared, it’s also a convenient way to only transfer data once.
import numpy
# Create the plot
f = flint()
p = f.get_plot("curve", name="My plot")
# Create the data
t = numpy.linspace(0, 10 * numpy.pi, 100)
s1 = numpy.sin(t)
s2 = numpy.sin(t + numpy.pi * 0.5)
s3 = numpy.sin(t + numpy.pi * 1.0)
s4 = numpy.sin(t + numpy.pi * 1.5)
# Describe the plot
p.add_curve_item("t", "s1")
p.add_curve_item("t", "s2")
p.add_curve_item("t", "s3")
p.add_curve_item("t", "s4")
# Transfer the data / and update the plot
p.set_data(t=t, s1=s1, s2=s2, s3=s3, s4=s4)
# As the plot description is already known
# The plot can be updated the same way again
p.set_data(t=t+1, s1=s1+1, s2=s2+1, s3=s3+1, s4=s4+1)
# Clean up the plot
p.clean_data()
For complex plot, if you want to handle properly the refresh of the plot a transaction can be used. It will enforce a signle update of the plot at the end of the transaction.
with p.transaction():
p.clear_items()
p.add_curve_item("x", "y", legend="item1")
p.add_curve_item("x", "w", legend="item2")
p.remove_item(legend="item2")
p.set_data(x=[1])
p.append_data(x=[2, 3, 4, 5])
p.set_data(y=[6, 5, 6, 5, 6])
Plot3D¶
A 3D plot is provided.
For now it supports scatters and meshes.
# create the plot
f = flint()
p = f.get_plot("plot3d", name="My plot")
# describe the items to display
p.add_scatter_item("x", "y", "z", "value", legend="item1",
symbol="o", symbol_size=50, lut="viridis")
p.add_mesh_item("vertexes", "faces", legend="item2")
# update the data
p.set_data(
x=[0, 1, 2], y=[0, 1, 0], z=[0, 0, 1], value=[0, 1, 2],
vertexes=[[0, 0, 0], [1, 1, 0], [2, 0, 1]],
faces=[[0, 1, 2]],
)
Scatter plot¶
A 2D scatter plot is provided.
A scatter is a group of of three 1D data of the same length. Each of them respectively contains x-locations, y-locations and values.
The widget provides different visualization mode to display the data as points, or with a solid rendering, including 2D histogram rendering.
import numpy
# Create the plot
f = flint()
p = f.get_plot("scatter", name="My plot")
# Create the data and setup the plot
y, x = numpy.ogrid[:10, :10]
x, y = numpy.zeros_like(y) + x, numpy.zeros_like(x) + y
x, y = x.flatten(), y.flatten()
v = numpy.sin(numpy.sqrt((x-5)**2 + (y-5)**2))
p.set_data(x, y, v)
# The colormap can be updated
p.set_colormap(lut="red")
Image plot¶
A plot is provided to display a specific image.
It provides a dedicated view to display a single image, with tools to provides vertical and horizontal histograms.
import numpy
# Create the plot
f = flint()
p = f.get_plot("image", name="My plot")
# Create the data and setup the plot
y, x = numpy.ogrid[:10, :10]
image = numpy.sin(numpy.sqrt((x-5)**2 + (y-5)**2))
p.set_data(image)
# The colormap can be updated
p.set_colormap(lut="red")
# The direction of the y-axis can be specified
p.yaxis_direction = "down"
In case the side histograms are not needed and take space for nothing, this can be hidden the following way.
p.side_histogram_displayed = False
2D plot¶
Another 2D plot is provided to allow to compose a view with many images. It provides less tools than the dedicated image plot.
import numpy
# Create the plot
f = flint()
p = f.get_plot("plot2d", name="My plot")
# Create the data and setup the plot
y, x = numpy.ogrid[:10, :10]
image1 = numpy.sin(numpy.sqrt((x-5)**2 + (y-5)**2))
image2 = numpy.cos(numpy.sqrt((x-5)**2 + (y-5)**2))
p.add_image(image1, origin=(0, 0), scale=(0.5, 0.5), legend="img1") # legend have to be unique
p.add_image(image2, origin=(5, 0), scale=(0.5, 0.5), legend="img2")
# Clean up the plot
p.clean_data()
Curve stack¶
This plot displays a single curve from a selectable list of curves.
The data have to be provided as a 2D array. The slow axis is used as the axis of the curve.
The selection is done with a slider.
import numpy
# Create the plot
f = flint()
p = f.get_plot(plot_class="curvestack", name="My curve stack")
# Create the data and setup the plot
curves = numpy.empty((10, 100))
for i in range(10):
curves[i] = numpy.sin(numpy.arange(100) / 30 + i * 6)
x = numpy.arange(100) * 10
p.set_data(curves=curves, x=x)
Image stack¶
This plot displays a single image from a stack of image.
The data have to be provided as a 3D array. The 2 first slow axis are used as the image axes.
A slider is provided to browse the images.
import numpy
# Create the plot
f = flint()
p = f.get_plot(plot_class="stackview", name="My image stack")
# Create the data and setup the plot
cube = numpy.arange(10 * 10 * 10)
cube.shape = 10, 10, 10
cube = numpy.sin(cube)
p.set_data(cube)
# The colormap can be updated
p.set_colormap(lut="red")
Time curve plot¶
A dedicated plot is provided to display curve data with time as x-axis.
It’s the plot used by the regulation framework.
This is useful to simplify the display of a fixed period of time (the last x seconds), and to update the data using the new known data only.
The GUI allow the user to change the last displayed period of time displayed.
# Create the plot
f = flint()
p = f.get_plot(plot_class="timecurveplot", name="My plot")
# Setup the plot to display a dedicated data name
# The data will be provided later
# the `time` data name is used as x-axis
p.add_time_curve_item("diode1")
# The curve style can be specified
p.add_time_curve_item("diode2", color="red")
# The data can be set
# The time have to be provided in epoch second UTC (see python API `time.time()`)
p.set_data(time=[0, 1, 2], diode1=[0, 1, 1], diode2=[1, 5, 1])
# The data also can be appended
p.append_data(time=[3], diode1=[2], diode2=[6])
# Old data is dropped automatically
# This can be setup programatically
p.select_x_duration(second=5)
# And data can be cleared
p.clear_data()
Grid container¶
A grid container can be created to structure multiple data plots inside.
The container provides a get_plot
method. Extra argument can be passed like
row
, col
, row_span
, col_span
in order to localize the plot to the grid.
# Create the container
f = flint()
container = f.get_plot("grid", name="Grid", selected=True)
# Create the sub plots
p1 = container.get_plot("curve", row=0, col=0)
p2 = container.get_plot("curve", row=0, col=1)
p3 = container.get_plot("curve", row=1, col=0)
p4 = container.get_plot("curve", row=1, col=1)
# Now you can add data to each plots
p1.add_curve([0, 1, 2, 3], [1, 2, 1, 2])
p2.add_curve([0, 1, 2, 3], [2, 1, 2, 1])
p3.add_curve([0, 1, 2, 3], [3, 2, 1, 0])
p4.add_curve([0, 1, 2, 3], [0, 1, 2, 3])
Extra commands¶
Plots provide few extra commands.
# Create a plot
f = flint()
p = f.get_plot(plot_class="plot1d", name="My plot")
The plot life cycle can be checked and changed with this commands:
if p.is_open():
p.close()
Set the focus on a specific plot can be set the following way:
p.focus()
A plot can be exported to the logbook this way:
p.export_to_logbook()
From scripts¶
From a scripts Flint also can be used to display plot.
The plot()
command can be imported the following way:
from bliss.common.plot import plot
plot([1, 2, 3, 4, 5, 6])
Flint and it’s plot API can be imported the following way:
from bliss.common.plot import get_flint
f = get_flint()
p = f.get_plot("plot1d", name="My plot")
Custom plot class¶
If none of this charts fit your needs there is still a way to write your own plot embedded inside Flint.
See the custom plot class documentation.