Search code examples
pythonjupyter-notebookdatabricksipywidgetsdatabricks-notebook

Dynamically create and display ipywidgets, failing in databricks notebook


Goal: An array of ipywidgets, that can be extended through a button-click on the UI.

import ipywidgets as widgets
from IPython.display import display

# Keeps track of default and dynamically added widgets
widgets_list = [widgets.Text(value='Give me')]

w_out_widgets_list = widgets.Output()
# display defaults
w_out_widgets_list.append_display_data(widgets.HBox(widgets_list))  

def add_new_widget(b):
    with w_out_widgets_list:
        widgets_list.append(widgets.Text(value='more and '))
        w_out_widgets_list.clear_output()
        display(widgets.HBox(widgets_list))

w_new_widget = widgets.Button(description='Add Widget')
w_new_widget.on_click(add_new_widget)

display(widgets.VBox([w_out_widgets_list, w_new_widget]))

This is working as expected in my locally running jupyter notebook.

Within the databricks notebook, a strange behaviour is observable:

  • Default widget creation works fine
  • Button click appends a widget to the widget_list, but does not update the displayed widgets.
  • Upon a second click, the view updates by displaying the now three widgets below the default one.

Not understood behavior within databricks

ipywidgets are on version 7.7.2 there.

Any ideas on what the cause of this behavior is?


Solution

  • My guess is, that the identification of currently active widget Output varied from databricks to local Jupyter Notebook.

    Wrapping the functionality in a class allows to recreate the expected behavior within databricks, too:

    import ipywidgets as widgets
    
    
    class App:
        def __init__(self):
            self.widgets_list = [widgets.Text(value="Give me")]
            self.w_new_widget = widgets.Button(description="Add Widget")
            self.w_new_widget.on_click(self.add_new_widget)
            self.app_out = widgets.Output()
            self.generate_layout()
    
        def generate_layout(self):
            self.app_out.clear_output()
            with self.app_out:
                display(widgets.VBox([widgets.HBox(self.widgets_list),
                                      self.w_new_widget]))
    
        def add_new_widget(self, *args):
            self.widgets_list.append(widgets.Text(value="more and "))
            self.generate_layout()
    
    
    app = App()
    display(app.app_out)