Обработка выбора из виджетов Jupyter внутри функции

Я пишу функцию, позволяющую пользователю выбирать из ряда параметров, а затем возвращать значения на основе этих вариантов. Я используюJupyter Widgetsдля выбора и запуска в JupyterLab. Моя функция выбора отлично работает сама по себе, но как только она была встроена в другую функцию, она перестает работать. Пример:

      import ipywidgets as widgets

def get_choices():    
    selections = [
    widgets.ToggleButtons(
        options=['Not Included', 'Included', 'Favorite'],
        description=f"{choice}",
        disabled=False,
        style= {'description_width': '300px'}
    )
    for choice in ['choice1', 'choice2', 'choice3']
   ]
    
    for e in selections:
        display(e)

    ## waiting for user input
    print("\n\nPRESS ENTER WHEN FINISHED")
    input()
    
    return wiki_edges_select

choices = get_choices()

print(choices)
>> [ToggleButtons(description='choice1', index=1, options=('Not Included', 'Included', 'Favorite'), style=ToggleButtonsStyle(description_width='300px'), value='Included'),
 ToggleButtons(description='choice2', index=1, options=('Not Included', 'Included', 'Favorite'), style=ToggleButtonsStyle(description_width='300px'), value='Included'),
 ToggleButtons(description='choice3', index=2, options=('Not Included', 'Included', 'Favorite'), style=ToggleButtonsStyle(description_width='300px'), value='Favorite')]

(Обратите внимание, что значения ,Included,Favorite). Однако при внедрении в функцию-оболочку:

      def get_choices_and_process():
    choices = get_choices()
    print(choices)

get_choices_and_process()
>> [ToggleButtons(description='choice1', options=('Not Included', 'Included', 'Favorite'), style=ToggleButtonsStyle(description_width='300px'), value='Not Included'), ToggleButtons(description='choice2', options=('Not Included', 'Included', 'Favorite'), style=ToggleButtonsStyle(description_width='300px'), value='Not Included'), ToggleButtons(description='choice3', options=('Not Included', 'Included', 'Favorite'), style=ToggleButtonsStyle(description_width='300px'), value='Not Included')]

(Обратите внимание, что значения , ,Not Included)

Я хотел бы иметьchoicesвернулся в течениеget_choices_and_process()функция отражает выбор пользователя, как если бы онget_choices()вызывается вне оболочки. Как я могу заставить это работать?

2 ответа

Если я неправильно понимаю, что вы пытаетесь сделать, вы можете использовать интерактивную функцию ipywidgets , чтобы позволить ipywidgets обрабатывать настройку пользовательского интерфейса и делать его интерактивным. А также поддерживать эту интерактивность. Вы можете получить доступ к настройкам по мере их обновления, используя аргументы ключевого слова, назначенные экземпляру интерактивной функции. Например, если вы назначаете экземплярw,w.kwargsдаст вам текущие значения.

Он работает встроенный в другую функцию, как я могу показать на демонстрации. (Это было разработано в JupyterLab.)

Пример настроек

Поместите следующее в ячейку JupyterLab:

      #based on https://stackoverflow.com/q/65137656/8508004 and https://ipywidgets.readthedocs.io/en/stable/examples/Using%20Interact.html?highlight=interactive#interactive
from ipywidgets import interactive, ToggleButtons

def print_choices(choice1, choice2, choice3):
    print(choice1)
    print(choice2)
    print(choice3)

choice1Select = ToggleButtons(options=['Not Included', 'Included', 'Favorite'],description='Choice1:',disabled=False,style= {'description_width': '300px'})
choice2Select = ToggleButtons(options=['Not Included', 'Included', 'Favorite'],description='Choice2:',disabled=False,style= {'description_width': '300px'})
choice3Select = ToggleButtons(options=['Not Included', 'Included', 'Favorite'],description='Choice3:',disabled=False,style= {'description_width': '300px'})

w = interactive(print_choices, choice1=choice1Select, choice2=choice2Select, choice3=choice3Select)
w

Вы можете выбрать настройки и увидеть обновления ниже.

Пример, встроенный в функцию-оболочку

Для функции-оболочки попробуйте это в другой ячейке ниже этой:

      def get_choices_and_process(choices):
    for tag,choice in choices.items():
        print(f"{tag} is {choice}.")
    #print(f"choices are:{choices}")

get_choices_and_process(w.kwargs)

Теперь поднимитесь и измените выбор в предыдущей ячейке. А затем снова запустите эту ячейку.


Альтернативный вариант с пониманием списка, определяющий ToggleButtons больше в соответствии с OP:
      #based on https://stackoverflow.com/q/65137656/8508004 and https://ipywidgets.readthedocs.io/en/stable/examples/Using%20Interact.html?highlight=interactive#interactive
from ipywidgets import interactive, ToggleButtons

def print_choices(choice1,choice2, choice3):
    print(choice1)
    print(choice2)
    print(choice3)

choice1Select, choice2Select, choice3Select = [
    ToggleButtons(
        options=['Not Included', 'Included', 'Favorite'],
        description=f"{choice}",
        disabled=False,
        style= {'description_width': '300px'}
    )
    for choice in ['choice1', 'choice2', 'choice3']
   ]

w = interactive(print_choices, choice1=choice1Select, choice2=choice2Select, choice3=choice3Select)
w

Asyncio может быть подходящим вариантом, если его нельзя использовать

Может быть полезно указать на это решениеМожно ли получить текущее значение ползунка виджета из функции без использования многопоточности?потому что использование asyncio позволяет получать обновленную информацию от виджета, который выбирает значение, позволяя использоватьtime.sleep()который, кажется, блокируетinteractive()от работы.

Я еще не придумал, как адаптировать это к OP.

Я не смог сделать это точно, но это решение оказалось достаточно хорошим. Оставить открытым, так как это точно не отвечает на вопрос.

      %gui asyncio
import asyncio
import ipywidgets as widgets

def get_choices():    
    selections = [
    widgets.ToggleButtons(
        options=['Not Included', 'Included', 'Favorite'],
        description=f"{choice}",
        disabled=False,
        style= {'description_width': '300px'}
    )
    for choice in ['choice1', 'choice2', 'choice3']
   ]
    
    for e in selections:
        display(e)

    return wiki_edges_select

def process_choices(choices):
    included_choices, favorite_choices = [], []
    for choice in choices:
        if choice.value == "Included":
           included_choices.append(choice)
        if choice.value == "Favorite":
           favorite_choices.append(choice)
    return included_choices, favorite_choices

def wait_for_change(widget):
    """
    This function found here: https://stackoverflow.com/questions/55244865/pause-jupyter-notebook-widgets-waiting-for-user-input
    """
    future = asyncio.Future()
    def getvalue(change):
        future.set_result(change.description)
        widget.on_click(getvalue, remove=True) 
    widget.on_click(getvalue)
    return future

async def get_choices_and_process():
    choices = get_choices()
    
    ## Waiting for selections
    button = widgets.Button(description="Click to Continue")
    display(button)
    x = await wait_for_change(button)

    included_choices, favorite_choices  = process_choices(choices)

    return my_results

processed_choices = asyncio.create_task(get_choices_and_process() )

processed_choices.result()
>> ["choice1", "choice2"], ["choice3"]

Кроме того, это работает только на ноутбуке Jupyter, а не на Jupyter-lab, что подходит для моего приложения.

Другие вопросы по тегам