Remove user-drawn PolyDraw polygons programmatically

I would like to have a Panel application with a PolyDraw tool where the user can draw an unlimited number of polygons, and the console will print out the data of the polygons. I would also like to add a “clear all” button, which will clear all of the polygon data, effectively resetting the plot back to the start. The following is the code I am using to do this.

import holoviews as hv
import panel as pn
from holoviews import opts, streams

hv.extension('bokeh')


def myfunc(data):
    print(data)

poly = hv.Polygons([]).opts(opts.Polygons(fill_alpha=0.3, fill_color='black', active_tools=['poly_draw']))
poly_stream = streams.PolyDraw(source=poly, drag=False, num_objects=0, show_vertices=True,
                                styles={'fill_color' : 'black'}, vertex_style={'size': 5, 'fill_color': 'white', 'line_color' : 'black'})
poly_stream.add_subscriber(myfunc)

def clearPolys(button):
    global poly_stream
    print("CLEAR BUTTON PUSHED!!!!!")
    poly_stream.event(data={'xs': [], 'ys': []})

clearBtn = pn.widgets.Button(name='Clear', button_type='primary')
pn.bind(clearPolys, clearBtn, watch=True)
pn.Row(clearBtn, poly).servable()

However, this doesn’t really work. Below is the output of me drawing a single line, then clearing, then drawing another line.

2024-01-13 16:06:47,914 Starting Bokeh server version 3.2.2 (running on Tornado 6.3.3)
2024-01-13 16:06:47,916 User authentication hooks NOT provided (default user enabled)
2024-01-13 16:06:47,917 Bokeh app running at: http://localhost:5006/panelclearpolys
2024-01-13 16:06:47,917 Starting Bokeh server with process id: 87141
2024-01-13 16:06:53,602 WebSocket connection opened
2024-01-13 16:06:53,603 ServerConnection created
{'xs': [[0.047305045871559634, 0.047305045871559634]], 'ys': [[0.8122448979591836, 0.8122448979591836]], 'fill_color': [0]}
{'xs': [[0.047305045871559634, 0.7858371559633027]], 'ys': [[0.8122448979591836, 0.8653061224489796]], 'fill_color': ['black']}
CLEAR BUTTON PUSHED!!!!!
{'xs': [], 'ys': []}
{'xs': [[0.047305045871559634, 0.7858371559633027], [0.11611238532110092, 0.11611238532110092]], 'ys': [[0.8122448979591836, 0.8653061224489796], [0.39183673469387753, 0.39183673469387753]], 'fill_color': ['black', 0]}
{'xs': [[0.047305045871559634, 0.7858371559633027], [0.11611238532110092, 0.5794151376146789]], 'ys': [[0.8122448979591836, 0.8653061224489796], [0.39183673469387753, 0.636734693877551]], 'fill_color': ['black', 'black']}

You can see that the data is empty for one update, but somehow, the old vertices come back, and the plot is never cleared. Is there any way I can get the polygons to disappear from the plot, and have their vertices removed from the stream data?

Thanks!
(I am working on a much more complex project than this, but this demonstrates what I want to do)

I sort of figured this one out… it was much more complicated than it needs to be and ended up requiring me to re-render everything on the plot.

In the above example, I don’t think it would be possible. You need to write a class and store the axes as properties… like so: all that just to remove some polygons · wx4stg/pyxlma-explorer@0c8acba · GitHub

Hi @wx4stg

Try opening a specific feature request if you believe something could/ should be improved. Or alternatively try starting a discussion on Discord HoloViz. Some of the HoloViews core developers monitor those forums more closely.

I know it’s almost 2 years late… but here is a solution using DynamicMap and Pipe:

import holoviews as hv
import panel as pn
from holoviews import opts, streams

hv.extension('bokeh')


def myfunc(data):
    print(data)

# changes are here
def polys(data):
    return hv.Polygons(data, kdims=['xs', 'ys']).opts(opts.Polygons(fill_alpha=0.3, fill_color='black', active_tools=['poly_draw']))
poly_pipe = streams.Pipe(data={'xs': [], 'ys': []})
poly = hv.DynamicMap(polys, streams=[poly_pipe])
# til here

poly_stream = streams.PolyDraw(source=poly, drag=False, num_objects=0, show_vertices=True,
                                styles={'fill_color' : 'black'}, vertex_style={'size': 5, 'fill_color': 'white', 'line_color' : 'black'})
poly_stream.add_subscriber(myfunc)

def clearPolys(button):
    global poly_stream
    print("CLEAR BUTTON PUSHED!!!!!")
    poly_stream.event(data={'xs': [], 'ys': []})
    poly_pipe.send({'xs': [], 'ys': []})

clearBtn = pn.widgets.Button(name='Clear', button_type='primary')
pn.bind(clearPolys, clearBtn, watch=True)
pn.Row(clearBtn, poly).servable()

I’m reviving this conversation because I’m having a somewhat similar issue with PolyEdit (initially with PolyDraw too, but I found the Pipe workaround above, which unfortunately doesn’t work for the PolyEdit case; see PolyDraw and PolyEdit Tools' Vertices Not Hiding When Switching to Points in Holoviews). I am curious to learn how you re-render stuff on the plot and what is the importance of storing the axes? I tried following your code but it was definitely too complex for me to pinpoint those details. I know it’s been ages but any pointers will be appreciated!

The method I used to re-render stuff was to create new hv Points objects, then in the the old pn.holoviews panes, set the “object” property to point to the new hv Points. See the re-render function here: pyxlma-explorer/lma_data_explorer.py at 2ffdb3c5fe78230a6553419e59d317ba84b22f5c · wx4stg/pyxlma-explorer · GitHub

However, I’ve linked an old commit as this was extremely slow and would occasionally result in the integrated graphics of my laptop becoming so locked up that I was unable to even move my mouse. As such I removed this functionality (if you look at the commit history, there are several commits referencing doing things “without re-rendering” and the last of those removed the re-render function entirely.)

The project I was working on was sort of a spare time thing and I eventually lost interest in dealing with weird edge cases so it’s kind of “hibernating” right now.