Wait for specific user input from widgets

Hi all,
I am running the following code in a notebook:

from ipywidgets import Box, Button, Text
import asyncio
from IPython.core.display import display


loop = asyncio.get_running_loop()

def wait_for_change(widget):
    future = loop.create_future()
    def getvalue(change):
        if user_input.value != 'hi':
            future.set_result(False)
        else:
            future.set_result(True)
        widget.on_click(getvalue, remove=True) 
    widget.on_click(getvalue)
    return future

button = Button(description='Submit')
user_input = Text()

async def f():
    valid = False
    while not valid:
        valid = await wait_for_change(button)
    print('done')

display(Box([user_input, button]))

loop.create_task(f())
print('continue...')

My goal is to stop the program until a specific user input is registered. For my given example this means that the print(‘continue…’) statement is printed after the user submitted ‘hi’. Does someone has an idea what I have to change so that this works?

create_task, in this case, returns immediately. The obejct you get back, a Task, could be awaited, but this would then block your main thread, and the widget messages wouldn’t go through.

So, unfortunately the answer today is pretty much “no,” you can’t block the main execution loop and get back user input (about the only option is the input()) method, which has special treatment, and would remove the async stuff

If you’re already on the widget bus, you could fake it by chaining everything off certain traits changing values, which could appear to be executing cells: they’d just be empty Box instances, and you’d fill in the children later, i guess. This would have the advantage (perhaps) of also not requiring any async functions, though if you do end up needing to call async stuff, it can be more predictable to use some of the tornado machinery, e.g. tornado.ioloop.IOLoop.get_current().add_callback, which will ensure it happens in the right place (especially for error reporting).

Yes, following on this:

Maybe you can use the example code I have already to adapt it get the user input and then look like it changes when ‘Submit’ is pressed. (My current button equivalent is ‘Plot Selected’.) To see my example and code, go to here and click on the ‘launch binder’ badge . Then under ‘Available Demonstration Notebooks’, select ‘3D scatter plot using data in a file and Voila interface’. The code you want to play with is in the only code cell on the page.

Thank you for your inputs!

I think I need to adapt my idea because I am trying to write a package which is imported and executed in only one cell. So I need to wait for valid user input to continue with further calculations. But unfortunately it seems like this doesn’t work.

I could use input() but then I can’t create a frontend which looks nicely.

Ah, well, if you just have exactly one output, everything’s way easier! In the past, I’ve even used a state machine library like transitions which lets you very predictably specify the state… and should probably be a standalone package :blush: ! (Note: i did try, a bit, but there’s a lot to do!)

Basically, all the widgets get created up-front, and observed or added to children as needed. A handy thing from this is you can even get a nice state machine diagram for documentation purposes out of the end, which is better than the “trait soup” it’s easy to achieve with doing everything imperatively.

1 Like