How to write into a PointDraw stream?

I need to have sync between a hv.Points plot and a DataFrame, while enabling a PointDraw tool. Yes, this calls for an Annotator, but I don’t know how to extend Annotator with further callbacks.
So, I tried the following approach, where I am using PointDraw for do plot->df sync, and a separate text box to do the renaming of annotations. And… it almost works. I just need a pointer how to let the points_stream know the vdim has changed.

import param
import pandas as pd

import panel as pn
pn.extension('tabulator')
import holoviews as hv
hv.extension('bokeh')

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

_coords_column_order = ['site','x', 'y']

class SG(param.Parameterized):

    coords = param.DataFrame(columns=_coords_column_order,doc="Dataframe containing coordinates",)
    plot = param.ClassSelector(class_=hv.Points)
    points_stream = param.ClassSelector(class_=hv.streams.PointDraw,)
    sel_stream = param.ClassSelector(class_=hv.streams.Selection1D,default=hv.streams.Selection1D())


    def plot_points(self, coords=None):
        if coords is None:
            coords = self.coords
        plot = hv.Points(coords, kdims=['x','y'], vdims=['site'])
        plot.opts(size=10,tools=['hover'],active_tools = ['point_draw',"wheel_zoom"])
        return plot

    def __init__(self,coords):

        plot = self.plot_points(coords)
        ps = hv.streams.PointDraw(data=plot.columns(), source=plot, empty_value='')
        ss = hv.streams.Selection1D(source=plot)

        super().__init__(
            coords = coords,
            points_stream = ps,
            plot = plot,
            sel_stream = ss
        )


    @pn.depends("points_stream.data", watch=True)
    def _update_coordinates_from_plot(self):
        self.coords = pd.DataFrame(self.points_stream.data)[_coords_column_order]

    def rename_site(self, new_site_name):
        logger.debug("=======\ntrying to rename site with index")
        i = self.sel_stream.index
        logger.debug(f"i: {i}, new name: {new_site_name}")
        df = self.coords.copy()
        df.loc[i,'site'] = new_site_name
        self.coords = df

coords = pd.DataFrame(
    { "site":list("ABC"), "x":[0,.2,.4], "y":[0,.1,0]},
)
A = SG(coords)
rename_text = pn.widgets.TextInput(width=100)
def rename(event):
    A.rename_site(event.new)
    logger.debug("\n"+str(A.coords))
rename_text.param.watch(rename, "value")

tbl_view = pn.widgets.Tabulator.from_param(
    A.param.coords,
    show_index=False,
)
text = "To rename a site, select it on a plot by clicking, then enter the new name in the text box below, and confirm by pressing enter."
main = pn.Row(
    pn.Column(tbl_view,text,rename_text),
    A.plot
)
main.servable()

Right now, the renaming is registered at the level of the main dataframe, but the points_stream does not know about it. So, the next time, the renamed point is being dragged, the dataframe’s 'site' reverts back to what it was before renaming.

Ok, I’ve figured this one out: I can trigger the update to the stream using the event method. Essentially using self.points_stream.event(data = df.to_dict()) as the last line in rename_site.

Unfortunately, this did not help me in the end, as I’ve discovered that updating the stream does not reflect in the stream’s source plot. So, I guess I need to change the question into “How to update hv.Points’ data programmatically?”.

I’ve tried self.points_stream.source.data = df.to_dict() and self.plot.data = df.to_dict() in the rename_site with no luck. These did not throw any errors though…