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
PointDrawtool 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
PointDrawon 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.