user3553031
user3553031

Reputation: 6224

new widgets get written to logs rather than drawn to display

I have a Jupyter script similar to the following:

import ipywidgets
import IPython.display
import functools

def _handler(text: ipywidgets.Text, button: ipywidgets.Button) -> None:
    label = ipywidgets.Label(f"Doing stuff with input '{text.value}'.")
    IPython.display.display(label)

    # Do a bunch more stuff, including drawing new UI elements.

prompt = ipywidgets.Text(description="Input: ")
button = ipywidgets.Button(description="Run")
button.on_click(functools.partial(_handler, prompt))
prompt_box = ipywidgets.HBox([prompt, button])
IPython.display.display(prompt_box)

I prompt the user for some input, then resume execution when the user clicks "Run". The further execution involves reading a bunch of data and drawing various new UI controls (Label, IntProgress, etc.). Eventually, I'll draw some more buttons and ask the user to select an operation to perform on the data. However, rather than drawing my Label (and all of the other stuff that I omitted from this simplified case), Jupyter just writes them to the log:

12:00:00 AM  Label(value="Doing stuff with input 'asdf'.")

What's going on here? Google's search AI hinted that there are ways that I could have messed up Jupyter's UI framework but didn't give me any further information, and all of the search results that I found seemed to be solved by writing to an Output that was defined outside of the click handler -- but since I need to do more than just display text here, that is not a solution. Unless Jupyter doesn't allow UI elements to be drawn from within event handlers, I don't see how I could have broken its UI framework here, and if that is the case, I'm at a loss as to how I can return to a context where I can draw UI elements again.

Upvotes: 0

Views: 63

Answers (1)

Wayne
Wayne

Reputation: 9984

Before I get to addressing some points made in the text of the post, for those ending up here because of the title 'new widgets get written to logs rather than drawn to display'...
I encourage you to see the third paragraph here on the Jupyter Discourse Forum in reply to 'Matplotlib draw in console instead of cell output when call in widget event callback'. The main point "is that now with ipywidgets explicitly handling the output is necessary to control what goes to output vs. the Log console."

In other words if you see things going to JupyterLab's Log console, you didn't fully implement that yet. And if you aren't using JupyterLab but are using current Jupyter Notebook (version 7+) and things aren't working as you expect, you may wish to see my answer here to 'ipywidgets buttons not responding'.


Now for specifics to this post...

"What's going on here? ....and all of the search results that I found seemed to be solved by writing to an Output that was defined outside of the click handler -- but since I need to do more than just display text here, that is not a solution." The Output handler should handle everything, not just text. You can completely update the output area of the cell, including with rich media and other widgets. (Or App/Dashboard if you are using Voila with essentially a single cell or so.) See ipywidget's documentation on 'Output widgets: leveraging Jupyter’s display system'.

And so the option you dismissed as not being a solution should indeed be the solution.
Try this variation on your code below. It adds the output widget and removes some of the odd complexity you had added in.

import ipywidgets
import functools

# Create an Output widget to which the output can be directed 
output = ipywidgets.Output()

def _handler(text: ipywidgets.Text, button: ipywidgets.Button) -> None:
    with output:
        label = ipywidgets.Label(f"Doing stuff with input '{text.value}'.")
        display(label)
        # Do a bunch more stuff, including drawing new UI elements.

prompt = ipywidgets.Text(description="Input: ")
button = ipywidgets.Button(description="Run")
button.on_click(functools.partial(_handler, prompt))
prompt_box = ipywidgets.HBox([prompt, button])

display(prompt_box, output)

You'd have to provide more specifics about the type of 'a bunch more stuff' if there is indeed something this doesn't handle?

More support that the output widget can handle a lot of rich output and widgets:

  • See here and references therein. You'll especially want to read ipywidget's documentation on 'Output widgets: leveraging Jupyter’s display system'.
  • Also see my complex example here. You can get that in an active Jupyter session without log in or account generation by going here clicking 'launch binder ' and looking at the last three available notebooks listed there when the session starts. '3D scatter plots customizable via widgets' , '3D scatter plot using data in a file and Voila interface', and 'streamlined 3D scatter plot in Voila interface'

Upvotes: 0

Related Questions