Individual grouped Toggle Buttons

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:

grafik

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)
3 Likes