pythonbokehpanelholoviewsholoviz

Dynamically updating a Holoviz Panel layout


Without going into the motivational details, I want to perform the following sequence of step using Panel:

  1. Display a panel.widgets.FileInput element.
  2. Once a file has been selected, then display a panel.widgets.MultiSelect element with items from the file, the first few of which are selected. The MultiSelect element appears below the FileInput element.
  3. On the basis of what is selected (both initially and through user interaction with the panel.widgets.MultiSelect element), display some items in a column to the right of the FileInput element.

Steps 1 and 2 work fine, but I can't get the 3rd thing to happen. Here is a minimum example that illustrates the problem when it is run:

import panel as pn
pn.extension()

# Create and organize basic elements to show where things go
file_input = pn.widgets.FileInput()
item_selector = pn.widgets.MultiSelect()
controls = pn.Column(file_input, "layout[0][1]")
layout = pn.Row(controls, pn.Column("layout[1][0]"))
print(layout)

# Set up selector object
@pn.depends(file_contents=file_input, watch=True)
def _item_selector(file_contents):
    # Dummy items for illustration purpose
    items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']    
    item_selector = pn.widgets.MultiSelect(
        name="Choose items", 
        options=items,
        size=12,
        width=120,
        value=items[:2],
    )
    layout[0][1] = item_selector

# Define interactivity based on selector object
@pn.depends(selected_items=item_selector, watch=True)
def _col(selected_items):
    col = pn.Column()
    for item in selected_items:
        col.append(item)
    layout[1] = col

layout

Any pointers to help clear up my misunderstandings would be much appreciated.


Solution

  • I figured out where I went wrong. In the function _item_selector() I set layout[0][1] to a new MultiSelect object, and the function _col() is affected by the original MultiSelect object set up on line 6. The code below fixes the problem

    import panel as pn
    pn.extension()
    
    # Create and organize basic elements to show where things go
    file_input = pn.widgets.FileInput()
    item_selector = pn.widgets.MultiSelect()
    controls = pn.Column(file_input, "")
    layout = pn.Row(controls, pn.Column(""))
    
    # Set up selector object
    @pn.depends(file_contents=file_input, watch=True)
    def _item_selector(file_contents):
        # Dummy file contents
        items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 
        item_selector.options = items
        item_selector.value = ['a', 'c']
        item_selector.size = 12
        item_selector.width = 120
        layout[0][1] = item_selector
    
    # Define interactivity based on selector object
    @pn.depends(selected_items=item_selector, watch=True)
    def _col(selected_items):
        col = pn.Column()
        for item in selected_items:
            col.append(item)
        layout[1] = col
    
    layout
    

    I'm sure this is not the most elegant approach to the problem I am trying to solve, and I would be interested in any feedback. In particular, as a newbie to Panel, I don't know that it is necessary to set up the layout beforehand on lines 5-8, or if this can be done dynamically as well.