0

Based on an answer elsewhere I've managed now to create a drop-down in Jupyter. On a selection change, I would like a selection-related pandas DataFrame to be displayed. This generally works, except that once a new value (from the drop-down) is selected, the already displayed DataFrame does not get cleared: the outputs (DataFrames) end up stacking on top of one another in the Output widget. I do use clear_output but it does not seem to be helping.

A full minimal snippet demonstrating the issue can be found below:

import pandas as pd
from IPython.display import display, clear_output
import ipywidgets as widgets

out = widgets.Output()

w = widgets.Dropdown(
    options=['','one', 'two', 'three', 'four', 'five'],
    value='',
    description='Select:',
)

def on_change(change):
    if change['type'] == 'change' and change['name'] == 'value':
        
        print("changed to %s" % change['new'])  # this gets logged correctly to the console

        df = pd.DataFrame([change['new']], columns=['Selected'])
        clear_output(wait=False)  # I would expect this to prevent the output DataFrames piling up
        out.append_display_data(df)

w.observe(on_change)

display(w, out)

Any help greatly appreciated.

*Edit: ipywidgets version is 7.6.3

**Edit: The issue is not related to pandas at all. The same happens, say, for a dictionary dd = {'Selected': change['new']} that one tries to output - they also end up piling up, rather then "previously selected disappearing, the newly selected showing up".

Simon Righley
  • 4,538
  • 6
  • 26
  • 33

1 Answers1

1

An option using out.outputs = [] instead of clear_output()

Based on https://stackoverflow.com/a/64103274/8508004 .

# based on use of https://stackoverflow.com/a/64103274/8508004
import pandas as pd
from IPython.display import display, clear_output
import ipywidgets as widgets

out = widgets.Output()

w = widgets.Dropdown(
    options=['','one', 'two', 'three', 'four', 'five'],
    value='',
    description='Select:',
)

def on_change(change):
    if change['type'] == 'change' and change['name'] == 'value':
        
        print("changed to %s" % change['new'])  # this gets logged correctly to the console

        df = pd.DataFrame([change['new']], columns=['Selected'])
        out.outputs = []  # instead of output.clear_output()
        out.append_display_data(df)

w.observe(on_change)

display(w, out)

Another option:

Based on https://stackoverflow.com/a/53755012/8508004 and avoiding append_display_data(). Combined with use of the output widget as a context manager to send what to display in out.

You are using append_display_data() and so it is being appended? Even if it is getting cleared showing in ouput. That's my guess at least. At any rate append_display_data is clearly listed in the documentation as being buggy:

"Note that append_display_data cannot currently be used to display widgets. The status of this bug is tracked in this issue."

And even more damning for append_display_data() is Jason Grout's comment here:

"Unfortunately, widgets won't work with append_display_data until ipywidgets 8.0."

At present, the latest version is 7.7.1.

At any rate, you don't need to append here since you just want to replace what is displaying anyway. Use of with out: can allow the displaying dataframe output to be sent to the widget display. The use of clear_output(wait=False) seems to get respected in this case.

# based on https://stackoverflow.com/a/53755012/8508004 and avoiding `append_display_data()`
import pandas as pd
from IPython.display import display, clear_output
import ipywidgets as widgets

out = widgets.Output()

w = widgets.Dropdown(
    options=['','one', 'two', 'three', 'four', 'five'],
    value='',
    description='Select:',
)


def on_change(change):
    if change['type'] == 'change' and change['name'] == 'value':
        
        print("changed to %s" % change['new'])  # this gets logged correctly to the console

        df = pd.DataFrame([change['new']], columns=['Selected'])
        with out:
            clear_output(wait=False)
            display(df)

w.observe(on_change)

display(w, out)
Wayne
  • 6,607
  • 8
  • 36
  • 93