Hi,
I’m having issues making a two-way sync between a Holoviews plot and a Panel Tabulator widget.
The following code only work from Plot to Table, not the other way around I cannot find a way to make it.
The reasons I’m doing this are:
- Process the data added via the
PointDraw
tool so that the list of points are sorted by column A and rounded to the second decimal. This is important that they are sorted (and relying on the table sorting functionality) in this case; - Use the customisation capability of Tabulator (as opposed to use Annotators for example);
- Allow the User to enter data both way: via a
PointDraw
on the graph or via the table directly.
import param
import panel as pn
import pandas as pd
import holoviews as hv
from holoviews.streams import PointDraw
hv.extension("bokeh")
pn.extension("tabulator")
class MyPlot(param.Parameterized):
df = param.DataFrame(columns=["A", "B"])
stream = param.ClassSelector(
default=PointDraw(num_objects=10), class_=(PointDraw,), precedence=-1
)
@pn.depends("stream.data", watch=True)
def _update_dataframe(self):
"""Function to reflect the change in the PointDraw data to the original data"""
df = pd.DataFrame(self.stream.data).round(2).sort_values("A").reset_index()
if "index" in df.columns:
df = df.drop(["index"], axis=1)
self.df = df
def table(self):
return pn.widgets.Tabulator(self.df, width=500, layout="fit_columns")
def plot(self):
line = hv.Curve(self.df, "A", "B")
points = hv.Scatter(self.df, "A", "B")
points.opts(active_tools=["point_draw"], width=500, height=500, size=20)
self.stream.source = points
return points * line
def view(self):
return pn.Row(self.plot, self.table, width_policy="max").servable()
df = pd.DataFrame(data={"A": [0, 1], "B": [0, 1]})
MyPlot(df=df).view()
If I click on the graph, a point is added, and the change is reflected in the table.
plot --> self.df --> table
How can I achieve the reverse: I modify a value in the table and the graph updates accordingly?
table --> self.df --> plot
Any help would be great. Thanks.