How to trigger `on_click` event of the button widget?

Hello, Dear Holoviz people.

I would like to trigger button click event programmatically.
Ideally once at the very beginning - but there might be other scenarios when I will have to trigger it in some other way.

So here is the code using Panel widgets (which is my favorite way of using Panel at this point):

import panel as pn
pn.extension()
from random import randint
from panel import widgets as pnw
from matplotlib.figure import Figure

pbutton = pnw.Button(name='Click me', button_type='primary')
plot = pn.pane.Matplotlib(name='plot')

def b(event):
    fig = Figure()
    ax = fig.add_subplot()
    ax.plot([randint(0,10) for i in range(10)])
    plot.object = fig
    
pbutton.on_click(b)
#pbutton.click() <--- I would put my trigger here
pn.Column(pbutton, plot)

It works, but I don’t know how to trigger button click programmatically.
In ipywidgets it would be something like pbutton.click().

Instead I had to use watch and replace on_click call like below:

pbutton = pnw.Button(name='Click me', button_type='primary')
plot = pn.pane.Matplotlib(name='plot')

def b(event):
    fig = Figure()
    ax = fig.add_subplot()
    ax.plot([randint(0,10) for i in range(10)])
    plot.object = fig
    
#pbutton.on_click(b)
pbutton.param.watch(b, 'value') # <---- Added this one
pbutton.value = True            # <---- And this one to trigger at first initialization

pn.Column(pbutton, plot)

This works just fine, but I would prefer doing one click() call instead of value=True especially given that button widget claims not to have value parameter (look at the very bottom of this page in the other section).

And learning from the best, I share parameterized version of this code:

import param

class ActionPlot(param.Parameterized):
    plot = pn.pane.Matplotlib(name='plot')
    draw = param.Action(lambda x: x.param.trigger('draw'), label='ClickMe')

    @param.depends('draw', watch=True)
    def handler(self):
        fig = Figure()
        ax = fig.add_subplot()
        ax.plot([randint(0,10) for i in range(10)])
        self.plot.object = fig

actionPlot = ActionPlot()
actionPlot.param.trigger('draw') # <--- This is what triggers click
pn.Column(pn.panel(actionPlot.param, show_labels=False, show_name=False, margin=0), actionPlot.plot)

This works as expected as well (even, though, I find it counter intuitive that actionPlot.param doesn’t pull plot pane automatically)

So the real question is whether you plan to implement something like .click() as adjoint for on_click() for API completeness, since it’s implemented for watch and param versions of the code.

Here is a little illustration of the above (first example is what I’m talking about):

1 Like

the button have a .clicks attribute. If you increase it with pbutton.clicks+=1, it will trigger a click programaticallly.

import panel as pn
pn.extension()
from random import randint
from panel import widgets as pnw
from matplotlib.figure import Figure

pbutton = pnw.Button(name='Click me', button_type='primary')
plot = pn.pane.Matplotlib(name='plot')


other_button = pnw.Button(name='Other button', button_type='warning')

def b(event):
    fig = Figure()
    ax = fig.add_subplot()
    ax.plot([randint(0,10) for i in range(10)])
    plot.object = fig
    
pbutton.on_click(b)

def c(e):
    pbutton.clicks += 1

other_button.on_click(c)
#pbutton.click() <--- I would put my trigger here
pbutton.clicks += 1

pn.Column(pbutton, plot, other_button).servable()
4 Likes

Wait, what?
@nghenzi, that was totally eye opening to me…
Thank you!

Indeed simply typing pbutton.clicks+=1 made it working!
I actually don’t know what to think here - it’s probably indeed an equivalent of .click() method (if not better than it).

While I’m thinking about it - and letting some others to reply - I’ll mark your answer as solution.

Thank you a bunch!

2 Likes

How would one set all this up utilizing Bokeh backend? I am still learning Holoviz and thought tinkering around with the provided code would help, but all I get is an AttributeError

" Exception: The Matplotlib backend is not configured. Try adding matplotlib.use('agg')"

Which the suggestion does not work.

Hi @chrisrichardsondev

Great question. Please open a new post containing a minimum, reproducible example if possible. It will make it much easier to try to help. Thanks.

1 Like