I’m playing with Panel and Param from Holoviz, and I’m looking for a way to temporarily disable the button while the triggered callback is running.
From the documentation (https://param.holoviz.org/_modules/param/__init__.html#Event) it does read like it is possible. But I can’t seem to make it work…
A follow-up question: Why can’t we – or why shouldn’t we – move the toggle_loading function inside the TestButton class?
Is it because disabling the button only takes place on the widget event_button level, and the parameterized class only handles the parameters and their relationships?
What are Parameterized classes designed to do, in general?
(As you can probably tell from my question I’m new to Holoviz and UI-related concepts)
I’ll try to answer my own questions above (just to share with other newbies like me, since I feel the documentation/examples for the package is a bit inadequate).
Is it because disabling the button only takes place on the widget event_button level, and the parameterized class only handles the parameters and their relationships?
Yes. The UI button widget is external to the Parameterized class, and it has not been defined when class or instance were created. Therefore it is not possible to disable the button inside the class or with the instance.
What are Parameterized classes designed to do, in general?
To the best of my understanding a Parameterized class has two uses:
To provide attribute validation. E.g. if we have a class called Book, with a price attribute, it is a good idea to perform validation whenever user tries to set the attribute value. Specifically, actions like a_book.price = 'abc' on a_book = Book() object should be prevented. This can be achieved with a Parameterized class by defining price = param.Number().
To define relationships among parameters. E.g. if we want to keep track of both the book price in USD and GBP with two attributes Book.price_USD and Book.price_GBP, then it is a good idea to alter the other price whenever one price changes. Specifically, setting a_book.price_USD = 10 should automatically update the a_book.price_GBP attribute to the corresponding numerical value accounting for exchange rates. This can be achieved in a Parameterized class by defining callbacks via the @param.depends decorator.
@Marc Please correct me if I said anything crazy. And please add to the list if you see other important/interesting uses of the param package.
THANKS so much for sharing your thoughts and insights. It helps other users in the same situation and it helps the developers of Panel to improve it.
Panel can be a mouthful. It’s still young, it already has so much powerful functionality and the documentation do need some iterations before its here.
If you ever feel like contributing to improving the documentation its just a git clone of https://github.com/holoviz/panel away. The documentation is developed in Jupyter Notebooks that are straightforward to create or update. There is a community of contributors on https://gitter.im/pyviz/pyviz that loves to help other contributors get started. So reach out there.
The validation (1) you get from Parameterized classes is one advantage. The encapsulation of the domain or business knowledge (2) into a Parameterized class is another advantage. Personally I like the Parameterized approach because
It helps med define, model, encapsulate and focus on the domain or problem to solve.
You end up with a self contained component that can be used and shared as such.
You get a more reactive approach. You have some parameters and can react when they change.
But I experience a lot of friends and colleagues who have difficulties with that approach. Either because the don’t have a lot experience with Classes or because they actually favor a functional approach without classes. Most data science coding I see is functional and thus it is a change of mindset to suddenly start writing classes.
The below is an attempt to solve your problem @Pill-GZ using a functional approach.
For me I really lack having for example the updating parameter to store whether or not my app is updating. I think that is simpler and more general to think about.
Combining the state of updating with the checkbox widget that is shown to the user is not a good idea I think. The updating value could just as well be shown to the user via a CheckButton, String, an Indicator or something else.
import time
import panel as pn
pn.extension()
# Create Domain Functions. I.e. the functions that solves your problem.
# For example extracting, transforming or loading data
# For example creating visualizations
def print_something():
print('something')
time.sleep(1)
# Create Widgets and wire them together
updating_checkbox = pn.widgets.Checkbox(name="Updating", disabled=True, )
action_button = pn.widgets.Button(name="Action", button_type="primary")
event_button = pn.widgets.Button(name="Event", button_type="success")
def start_updating():
updating_checkbox.value = True
action_button.disabled = True
event_button.disabled = True
def stop_updating():
action_button.disabled = False
event_button.disabled = False
updating_checkbox.value = False
def click_handler(event):
if updating_checkbox.value:
return
start_updating()
print_something()
stop_updating()
action_button.on_click(click_handler)
event_button.on_click(click_handler)
# Create the Layout and serve the app
template = pn.template.FastListTemplate(
title="How to disable buttons in Panel? Functional Approach", theme="dark"
)
template.main[:]=[
pn.Column(
action_button,
event_button,
updating_checkbox
)
]
template.servable()
For me the below is an attempt to solve your use case @Pill-GZ using a Parameterized approach.
This is one attempt. There are other ways of doing this that would also work. And you could also mix the two approaches.
But for me this is better. I can model the problem using a Parameterized class, lay it out in a view and end up with a class/ component that makes sense to encapsulate in a library/ package and share with other users.
Thank you, @Marc, for this example. I wish examples in #panel would present in two forms - functional and class(ic).
Personally I love functional approach but I’m mathematician by background so biased a bit.
P.S. I do read your comments and posts for inspiration - and so far you’ve been a great help! Thank you!
However, I wonder if there’s a way to speed up disabling it with Javascript because I can spam press the “Action” button a few times before it gets disabled, triggering it multiple times.