0

I'm having problems creating widgets from a function -- specifically using observe to perform the function callback. It must be related to the observe, because a simple test case works fine:

from ipyfilechooser import FileChooser
import sys
import os
sys.path.append("shared/heminger/lib/dcdat")
sys.path.append("shared/heminger/lib/combustion_functions")
import converter
import DataViz
import ipywidgets as widgets
from ipywidgets import TwoByTwoLayout, Layout

def create_sensors_list_widget():
    sensor_list = widgets.Textarea(
        value='tag,tag2',
        placeholder='Type something',
        description='List of Sensors (separated by comma)',
        disabled=False
    )
    display(sensor_list)

create_sensors_list_widget()

This code cell will properly display the textarea widget. However, when a similar function is called by function which is called by widget.RadioButtons.observe it all falls apart. Here is what I'm working with at the moment. The intention is that an appropriate widget will be displayed depending on the selection of the ratio button (note, I do not have code to remove/delete widgets if the choice changes, but will add later).

It seems that the functions are being called... if I open my log, I can see the output showing up there (I know that in order to output normally via ipwidgets, I need to create an Output object). So, the code is entering the functions

from ipyfilechooser import FileChooser
import sys
import os
import ipywidgets as widgets
from ipywidgets import TwoByTwoLayout, Layout

output_radio_selected = widgets.Text() 
button_all_sensors = widgets.RadioButtons(
    options=['Read all sensors', 'Sensors from File', 'Manual Entry'],
    value='Read all sensors', 
    description='Read all sensors?:',
    disabled=False
)

def create_sensor_option_widgets():
    print('test')
    create_sensors_list_widget()
    if selected_option == 'Sensors from File':
        print(' from file' )
        create_sensors_from_file_widget()
    elif selected_option == 'Manual Entry':
        create_sensors_list_widget()
        print(' selected manual entry')
    else:
        print('oops')

def create_sensors_from_file_widget():
    sensor_file = FileChooser('/')
    sensor_file.title = '<b>File List Containing Sensors</b>'
    sensor_file
    

def create_sensors_list_widget():
    sensor_list = widgets.Textarea(
        value='test1, test2',
        placeholder='Type something',
        description='List of Sensors (separated by comma)',
        disabled=False
    )
    display(sensor_list)

def bind_selected_to_output(sender):
    global selected_option # Global variable to hold the user input for reuse in your code
    output_radio_selected.value = button_all_sensors.value
    selected_option = output_radio_selected.value # Example variable assigned the selected value
    print('Selected option set to: ' + selected_option) # For test purposes
    create_sensor_option_widgets()

button_all_sensors.observe(bind_selected_to_output, names=['value'])
display(button_all_sensors)

Note that the original function to pass the value of the radio button into a global variable and call a function on state change was pulled from Why is my ipywidget observe being call multiple times on a single state change?

Mike H
  • 29
  • 5
  • There's an example of making widgets with a function where observer works [here](https://stackoverflow.com/a/75440619/8508004). I'm not following what you are trying to trigger to happen? Additionally your code snippet doesn't approach being a minimal reproducible example, see more about that [here](https://stackoverflow.com/help/how-to-ask) and [here](https://stackoverflow.com/help/minimal-reproducible-example), making it hard for others to take your current work and build it into something addressing your issue. – Wayne Aug 31 '23 at 19:35
  • 1
    Sorry, copied the cell, but not the imports. Edited such to make it fully reproduceable. Basically, I want to create a different widgets based on the input from a radio selection. Creating a widget from a generic function seems to be no problem -- so it seems as though I'm struggling with the observe function creating the new widget. – Mike H Sep 01 '23 at 12:16

1 Answers1

0

I was able to solve this by just creating all the widgets, and the hide/showing them depending on the state of the radio.

from ipyfilechooser import FileChooser
import sys
import os
import ipywidgets as widgets
from ipywidgets import TwoByTwoLayout, Layout

output_radio_selected = widgets.Text() 
button_all_sensors = widgets.RadioButtons(
    options=['Read all sensors', 'Sensors from File', 'Manual Entry'],
    value='Read all sensors', 
    description='Read all sensors?:',
    disabled=False
)

sensor_file = FileChooser('/')
sensor_file.title = '<b>File List Containing Sensors</b>'

sensor_list = widgets.Textarea(
        value='test1, test2',
        placeholder='Type something',
        description='List of Sensors (separated by comma)',
        disabled=True,
    )


def bind_selected_to_output(sender): # Connect the input from the user to the output so we can access it
    #print(sender)
    global selected_option # Global variable to hold the user input for reuse in your code
    output_radio_selected.value = button_all_sensors.value
    selected_option = output_radio_selected.value # Example variable assigned the selected value
    #print('Selected option set to: ' + selected_option) # For test purposes
    if selected_option == 'Manual Entry':
        sensor_file.layout.display = "none"
        sensor_list.layout.display = "block"
    elif selected_option == 'Sensors from File':
        sensor_list.layout.display = "none"
        sensor_file.layout.display = "block"
    elif selected_option == 'Read all sensors':
        sensor_list.layout.display = "none"
        sensor_file.layout.display = "none"
        

button_all_sensors.observe(bind_selected_to_output, names=['value'])
display(button_all_sensors)

sensor_list.layout.display = "none"
sensor_file.layout.display = "none"
display(sensor_file)
display(sensor_list)

Note that there are options for "how" to hide. The option I chose: widget.layout.display will move the remaining contents to fill in the space. The other option is widget.layout.visibility which does not affect the page layout.

Mike H
  • 29
  • 5
  • I was just coming to see if you had it handling this type of thing in your code. You can also use `widgets.VBox()` or `Hbox()` or related containers and then update the children of that, see an example of that [here](https://nbviewer.org/github/fomightez/3Dscatter_plot-binder/blob/master/3D_scatter_Voila_matplotlibADJUSTABLE.ipynb). And a much more detailed example of the various approaches [here](https://stackoverflow.com/a/73978799/8508004). – Wayne Sep 01 '23 at 14:51