dshefman
dshefman

Reputation: 1017

How to link opposite values of two buttons using ipywidgets in Python 3?

How do I link the value of two buttons to be opposite of one another? The widgets.jslink() function only seems to link the value to be the same, not the opposite. I know that I could use widgets.ToggleButtons() to link two buttons, but I want the success button to be green and the fail button to be red. ToggleButtons() does not appear to allow different coloring for each button. If it does, I'm open to that as a solution, as well. Here is the code I have so far (FYI: I'm running this code in JupyterLab using ipywidgets and node.js):

button_y= widgets.Button(
    description='Success',
    disabled=False,
    button_style='success'
    tooltip='Click me',
    icon='check'
)

button_n= widgets.Button(
    description='Failure',
    disabled=False,
    button_style='danger'
    tooltip='Click me',
    icon='check'
)

display(widgets.HBox((button_y, button_n)))

Output:

two buttons output

Upvotes: 2

Views: 446

Answers (2)

DougR
DougR

Reputation: 3479

So this method is a bit more complicated but.... as you mentioned in the OP, to sync the inverse of the widgets value using jslink and without relying on the Python kernel, then this solution does this with the use of an ipyvuetify template.

import traitlets
import ipyvuetify as v
import ipywidgets as widgets

class InvertCheckbox(v.VuetifyTemplate):
    """
    A class that creates a checkbox with an inverse value.
    Used with inverted_jslink -  A function that links two widgets with an inverse relationship. See function below

    """
    val = traitlets.Bool(True).tag(sync=True)
    invert_val = traitlets.Bool(False).tag(sync=True)

    template = traitlets.Unicode('''
        <template>
            <div>
                <v-checkbox v-model="val" />
            </div>
        </template>
        <script>
            export default {
                watch: {
                    val: function(newVal, oldVal) {
                        if (newVal !== oldVal) {
                            this.invert_val = !newVal;
                        }
                    },
                    invert_val: function(newVal, oldVal) {
                        if (newVal !== oldVal) {
                            this.val = !newVal;
                        }
                    }
                    }
            }
        </script>
        ''').tag(sync=True) 


def inverted_jslink(widget1,widget2,not_widget):
    """
    A function that links two widgets with an inverse relationship."""

    widgets.jslink(widget1, (not_widget, "val"))
    widgets.jslink((not_widget, "invert_val"), widget2)
    
button_y= widgets.Button(
    description='Success',
    disabled=True,
    button_style='success',
    tooltip='Click me',
    icon='check'
)

button_n= widgets.Button(
    description='Failure',
    disabled=False,
    button_style='danger',
    tooltip='Click me',
    icon='check'
)

not_widget = InvertCheckbox()
inverted_jslink(
    (button_y, "disabled"), (button_n, "disabled")
                 ,not_widget)

display(widgets.HBox((button_y, button_n)))

Not try these commands in Jupyter...


button_n.disabled = True

button_n.disabled = False

button_y.disabled

button_y.disabled = True

Upvotes: 0

Matthias Ossadnik
Matthias Ossadnik

Reputation: 911

Guessing that you want to link the disabled attributes. You can use the observe method in the backend to add logic to links:

def toggle_button_n(value):
    button_n.disabled = not value.new

def toggle_button_y(value):
    button_y.disabled = not value.new

button_n.observe(toggle_button_y, names=['disabled'])
button_y.observe(toggle_button_n, names=['disabled'])

You also need to change the initialisation so that only one of the buttons is enabled.

Upvotes: 1

Related Questions