126 lines
3.3 KiB
Python
126 lines
3.3 KiB
Python
from abc import ABC, abstractmethod
|
|
import plotext as plt
|
|
|
|
import plotly.express as px
|
|
from plotly.subplots import make_subplots
|
|
|
|
from nicegui import ui, context
|
|
|
|
|
|
class AbstractPlot(ABC):
|
|
def __init__(
|
|
self,
|
|
channels,
|
|
in_subplots=False,
|
|
) -> None:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def setup_plot(self):
|
|
"""Set up the plot."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update_plot(self, channels, start_time=0, **kwargs):
|
|
"""Update the plot with new data from the given channels."""
|
|
pass
|
|
|
|
|
|
class PlotlyPlot(AbstractPlot):
|
|
def __init__(
|
|
self,
|
|
channels,
|
|
start_time=0,
|
|
in_subplots=False,
|
|
) -> None:
|
|
self.channels = channels
|
|
self.start_time = start_time
|
|
self.in_subplots = in_subplots
|
|
|
|
self.plot, self.fig = self.setup_plot()
|
|
|
|
def setup_plot(self):
|
|
|
|
num_traces = len(self.channels)
|
|
labels = [channel.name for channel in self.channels.values()]
|
|
|
|
context.client.content.classes("h-[100vh]") # full height and width of the container
|
|
|
|
colors = px.colors.qualitative.D3
|
|
fig = make_subplots(rows=num_traces, cols=1, shared_xaxes=True, vertical_spacing=0.05)
|
|
|
|
subfigures = [
|
|
px.line(
|
|
markers=True,
|
|
)
|
|
for _ in range(num_traces)
|
|
]
|
|
for i, subfig in enumerate(subfigures):
|
|
|
|
trace = subfig["data"][0]
|
|
|
|
subfig["data"][0]["showlegend"] = True
|
|
subfig["data"][0]["name"] = f"{labels[i]}"
|
|
subfig["data"][0]["line"]["color"] = colors[i % len(colors)]
|
|
|
|
fig.add_trace(trace, row=i + 1, col=1)
|
|
|
|
fig.update_layout(
|
|
autosize=True,
|
|
margin=dict(l=10, r=10, b=10, t=10, pad=5),
|
|
# paper_bgcolor="LightSteelBlue",
|
|
)
|
|
|
|
# set xaxis title for last subplot at the bottom
|
|
fig["layout"][f"xaxis{num_traces}"]["title"] = "time (s)"
|
|
|
|
plot = ui.plotly(fig).classes("w-full h-full") # full height and width inside the container
|
|
|
|
return plot, fig
|
|
|
|
def update_plot(self, channels, start_time=0, **kwargs) -> None:
|
|
|
|
for i, channel in enumerate(channels.values()):
|
|
self.fig["data"][i]["x"] = channel.x - start_time
|
|
self.fig["data"][i]["y"] = channel.y
|
|
|
|
self.plot.update_figure(self.fig)
|
|
|
|
|
|
class ConsolePlot(AbstractPlot):
|
|
def __init__(self, channels, start_time=0, in_subplots=False, marker="hd"):
|
|
self.channels = channels
|
|
self.start_time = start_time
|
|
self.in_subplots = in_subplots
|
|
self.marker = marker
|
|
|
|
self.setup_plot()
|
|
|
|
def setup_plot(self):
|
|
"""Set up the plot."""
|
|
plt.clf()
|
|
plt.limit_size(False, False)
|
|
plt.theme("pro")
|
|
|
|
plt.xlabel("Timestamp")
|
|
plt.ylabel("Value")
|
|
|
|
def update_plot(self, channels, start_time=0, in_subplots=False, marker="hd"):
|
|
"""Plots channels on the console."""
|
|
|
|
num_channels = len(channels)
|
|
|
|
if in_subplots:
|
|
plt.subplots(num_channels, 1)
|
|
|
|
plt.main().plot_size(plt.tw(), plt.th() - 1)
|
|
plt.clear_data()
|
|
|
|
for i, channel in enumerate(channels.values()):
|
|
if in_subplots:
|
|
plt.subplot(i + 1, 1)
|
|
|
|
plt.plot(channel.x - start_time, channel.y, label=channel.name, marker=marker)
|
|
|
|
plt.show()
|