Using Philipp’s example in Custom component that is both a Viewer and Reactive, I was able to make a group of Toggle buttons with individual design, what the current version of ToggleGroup cannot do.
You can then also manipulate each button disabled
parameter, see methods _cannot_be_ok
and _can_only_be_ok
.
I just wanted to share my work in case someone requires something similar:
import panel as pn
pn.extension()
import param
OK = 0
NOK = 1
AVG = 2
ASSESSMENTLIST = ('Good!', 'Unsatisfying', 'Average')
class ToggleAssessmmentGroup(pn.widgets.base.CompositeWidget):
# see https://discourse.holoviz.org/t/custom-component-that-is-both-a-viewer-and-reactive/3673/2
assessment = param.ObjectSelector(default=ASSESSMENTLIST[OK], objects=ASSESSMENTLIST)
_composite_type = pn.Row
def __init__(self, **params):
self.assessment_ok_btn = pn.widgets.Toggle(
name=ASSESSMENTLIST[OK],
button_type = 'success',
)
self.assessment_nok_btn = pn.widgets.Toggle(
name=ASSESSMENTLIST[NOK],
button_type = 'danger',
)
self.assessment_avg_btn = pn.widgets.Toggle(
name=ASSESSMENTLIST[AVG],
button_type = 'warning',
)
js_callback_check_assessments = """
if (cb_obj.active) {
btn1.active = false;
btn2.active = false;
};
"""
self.assessment_ok_btn.jscallback(
value=js_callback_check_assessments,
args={'btn1':self.assessment_nok_btn,
'btn2':self.assessment_avg_btn})
self.assessment_nok_btn.jscallback(
value=js_callback_check_assessments,
args={'btn1':self.assessment_ok_btn,
'btn2':self.assessment_avg_btn})
self.assessment_avg_btn.jscallback(
value=js_callback_check_assessments,
args={'btn1':self.assessment_nok_btn,
'btn2':self.assessment_ok_btn})
def set_assessment_status_cb(event):
if (event.name == 'value') and (event.new):
self.assessment = event.obj.name
self.assessment_ok_btn.param.watch(
set_assessment_status_cb,
'value')
self.assessment_nok_btn.param.watch(
set_assessment_status_cb,
'value')
self.assessment_avg_btn.param.watch(
set_assessment_status_cb,
'value')
super().__init__(**params)
self._composite[:] = [self.assessment_ok_btn, self.assessment_nok_btn, self.assessment_avg_btn]
self._widget_dict = {obj.name: obj for obj in self._composite}
self._sync_disabled_param()
self.param.watch(self._sync_widgets,'assessment')
def _sync_widgets(self, event):
if self._widget_dict[event.new].disabled:
err_msg = f'Button "{self.assessment}" is disabled. Previous value is reset: "{event.old}"'
self.assessment = event.old
raise ValueError(err_msg)
else:
self._widget_dict[self.assessment].value = True
def _cannot_be_ok(self):
self.assessment_ok_btn.disabled = True
self.assessment_nok_btn.disabled = False
self.assessment_avg_btn.disabled = False
self.assessment_nok_btn.value = True
def _can_only_be_ok(self):
self.assessment_ok_btn.disabled = False
self.assessment_nok_btn.disabled = True
self.assessment_avg_btn.disabled = True
self.assessment_ok_btn.value = True
@param.depends('disabled', watch=True)
def _sync_disabled_param(self):
self.assessment_ok_btn.disabled = self.disabled
self.assessment_nok_btn.disabled = self.disabled
self.assessment_avg_btn.disabled = self.disabled
a = ToggleAssessmmentGroup(width=300, background='black')
b = pn.pane.Markdown(a.assessment)
a.link(b, assessment='object')
pn.Column("# How did you find our service?", a,b)