1

I am trying to write a set of functions that allow me to use a Jupyter Notebook to 'interactively' create a list (based on values from a different, predefined list). At the highest level, these functions would print an item (e.g., a type of animal), I would then provide an annotation for this item (e.g., 'bird' or 'fish') using an interactive textbox, and then my annotation would be stored in a new list.

Something like this is what I want to do (but it's obviously not quite there):

from ipywidgets import widgets  
import asyncio

#function that waits for an input into the textbox
def wait_for_change(widget):
    future = asyncio.Future()
    def getvalue(change):
        future.set_result(change.description)
        widget #display the textbox (and then wait for user input using the 'return' button?)
        widget.value #get the value in the textbox
    return future

#the textbox widget
input_text = widgets.Text()

#list of animals to classify
list_to_tag = ["seagull", "flounder", "trout", "albatross"]
tagged_animals = []

#function to display the textbox and list of animals to classify, return list of annotations
async def f():
    for i in list_to_tag:
        print("going to tag {}".format(i))
        x = await wait_for_change(input_text)
        print("tagged {} with {}".format(i, x))
        tagged_animals.append(x)
    print("All animals tagged")
    return tagged_animals

#start the annotation task
asyncio.create_task(f())
input_text

The script currently crashes with the warning:

task: <Task pending coro=<f() done, defined at <ipython-input-31-d698dc6b74bc>:17> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x109a71f10>()]>>

Something to do with asyncio.Future()?

Thanks in advance for the help.

arranjdavis
  • 657
  • 8
  • 16

1 Answers1

2

Okay, I've figured out how to do this - the code below is based off this answer.

from ipywidgets import Button, HBox
import asyncio

#function for waiting for button clicks
def wait_for_change(widget1, widget2):
    future = asyncio.Future()
    def getvalue(change):
        future.set_result(change.description)
        widget1.on_click(getvalue, remove=True)
        widget2.on_click(getvalue, remove=True)
    widget1.on_click(getvalue)
    widget2.on_click(getvalue)
    return future

#annotation buttons
button1=Button(description="bird")
button2=Button(description="fish")

#terms to annotate
list_to_tag = ["seagull", "flounder", "trout", "albatross"]
tagged_animals = []

This next block of code uses a coroutine and asyncio (this answer explains what is going on here).

#use a coroutine function to iterate through all items to annotate
async def f():
    for i in list_to_tag:
        print(i)
        x = await wait_for_change(button1,button2) 
        tagged_animals.append(x)
    return tagged_animals

Then call the functions and do the annotations.

#iterate through annotations, save results
annotations = asyncio.create_task(f())

#display buttons
HBox([button1,button2])

Finally, once all items are annotated, access the annotations.

#get the results
annotations.result()
['bird', 'fish', 'fish', 'bird']
arranjdavis
  • 657
  • 8
  • 16