Seeking help to sync sidebar tools

Hello Community!

I have recently started using Panel for one of my projects and have been enjoying it.

What I am trying to do with Panel is develop a dashboard where users provide input according to a prespecified format, and then the dashboard runs an algorithm, such as optimization, and returns results.

I am currently using “FastListTemplate” and its sidebar feature. In the sidebar, I have several tools, including an input tool where users provide input, and algorithm tools that return outputs based on the provided input.

My goal is to have the input and algorithm tools connected such that the algorithm tools run and return correct outputs based on the input provided by the input tool. I also need to ensure that the algorithm tools run only when users have provided input (i.e., users have gone through the input tool) and return an error if users have not provided input (i.e., users haven’t gone through the input tool). That is, users are supposed to go through the tools sequentially, visiting the input tool before the algorithm tools.

What I have tried is exporting all input data from the input tool and then having the algorithm tools read the exported data. However, the input and algorithm tools seem to be running independently in parallel, and even if the input tool exports the user’s input, the algorithm tools return an error saying they cannot find the exported data.

So what I think should be done is to have these tools synced to run in a sequential manner: once the input tool has been fully completed, the algorithm tools can run.

What would be the easiest (or smartest) way to sync these sidebar tools in a sequential manner?

I have attached a picture of the current version of my dashboard.

  • The Input dashboard is what I referred to as the “input tool,” where users provide input.
  • Optimization and Decision Comparison tools are the aforementioned algorithm tools.

I am not sure if my question is clear. Please let me know if you have any questions or need clarification.

It’d help to have a minimal example of code.

Hello @ahuang11

Thank you for your reply. I was trying to upload a simplified version of my original code here, but it seems like I am not allowed to upload any attachments. Let me share it through Google Drive: test.py - Google Drive

In the code, I am trying to sync the functions “Input_Page” and “test_algorithm” as described above.

Please let me know if you have any questions.

Thanks for posting it. It’s still a little too complex / long. Are you able to boil it down to the fundamentals?

1 Like

Thank @ahuang11 for your prompt reply.

Can you try this?:test.py - Google Drive

Basically, what I want to do is have the two functions (Input_Page and test_algorithm) in the sidebar run sequentially.

In the “Input_Page”, when we click the “submit” button, it should automatically export the data into a pre-specified directory in the “.pickle” format. Once the data is exported, my understanding is that the “test_algorithm” function should be able to read it and return a message saying, “Users have provided input,” which does not happen. I guess these two functions are not synced properly, so I would like to ask for help in addressing this.

Please let me know if you need more clarification

Are all of these needed to make a minimal example? If not, can you simplify further; see

    onsite_div_1 = pn.widgets.FloatInput(name='General_Conditions', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_1 = pn.widgets.FloatInput(name='General_Conditions', value=879740 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_2 = pn.widgets.FloatInput(name='Site_Work', value=4684133 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_2 = pn.widgets.FloatInput(name='Site_Work', value=2100096 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_3 = pn.widgets.FloatInput(name='Concrete_Material_Labor', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_3 = pn.widgets.FloatInput(name='Concrete_Material_Labor', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_4 = pn.widgets.FloatInput(name='Masonry', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_4 = pn.widgets.FloatInput(name='Masonry', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_5 = pn.widgets.FloatInput(name='Metals', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_5 = pn.widgets.FloatInput(name='Metals', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_6 = pn.widgets.FloatInput(name='Wood & Plastics', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_6 = pn.widgets.FloatInput(name='Wood & Plastics', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_7 = pn.widgets.FloatInput(name='Thermal & Moisture', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_7 = pn.widgets.FloatInput(name='Thermal & Moisture', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_8 = pn.widgets.FloatInput(name='Doors & Windows', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_8 = pn.widgets.FloatInput(name='Doors & Windows', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_9 = pn.widgets.FloatInput(name='Finishes', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_9 = pn.widgets.FloatInput(name='Finishes', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_10 = pn.widgets.FloatInput(name='Specialities', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_10 = pn.widgets.FloatInput(name='Specialities', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_11 = pn.widgets.FloatInput(name='Equipment (Commercial & Appliances', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_11 = pn.widgets.FloatInput(name='Equipment (Commercial & Appliances', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_12 = pn.widgets.FloatInput(name='Furnishings (FF&E)', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_12 = pn.widgets.FloatInput(name='Furnishings (FF&E)', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_13 = pn.widgets.FloatInput(name='Special Construction', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_13 = pn.widgets.FloatInput(name='Special Construction', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_14 = pn.widgets.FloatInput(name='Conveying Systems', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_14 = pn.widgets.FloatInput(name='Conveying Systems', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_15 = pn.widgets.FloatInput(name='Mechanical', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_15 = pn.widgets.FloatInput(name='Mechanical', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

    onsite_div_16 = pn.widgets.FloatInput(name='Electrical', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))
    offsite_div_16 = pn.widgets.FloatInput(name='Electrical', value=0 , step=1, start=0, end=10000000, format=PrintfTickFormatter(format='$%f'))

@ahuang11 I apologize for any inconvenience I might have caused. I have tried to simplify my code further, hoping to make it more convenient to work with.

#%%
import panel as pn
import pickle
import os
import pandas as pd
#%%



#%%
def Test_Cost_Export(clicked):
    
    ''' "Submit" Button function to export input provided by users '''
    
    if clicked:
        building_df = {}
        building_df['Test Input'] = {building_box[1].name: building_box[1].value}   
        path =  os.path.abspath(os.path.dirname(__file__))
        with open(path+'\Building_Cost_Data.pkl', 'wb') as f:
            pickle.dump(building_df, f)                    

        return "Building cost has been updated"
    return "Click Submit"
#%%


#%%
def Test_Input_Page():
    
    ''' Users are asked to provide data '''
    ''' '''

    global building_box
    onsite_div_1 = pn.widgets.FloatInput(name='General_Conditions', value=0 , step=1, start=0, end=10000000)
    
    building_box = pn.Column(pn.pane.Markdown("#### Test Input",  height=50, margin=(0,0,0,0),align='center'), onsite_div_1)

    submit = pn.widgets.Button(name="Submit", button_type="primary")        

                                    
    result = pn.pane.Markdown(pn.bind(Test_Cost_Export, submit)) # Linking user input widget and "submit" button

    tabs = pn.Tabs(('Test Cost', pn.Column(building_box, submit, result)))
    return tabs
#%%




#%%
def show_page(page_key):
    main_area.clear()
    main_area.append(mapping[page_key])
#%%



#%%
def test_algorithm():
    
    ''' This function is used to read the input generated by "Test_Input_Page". '''
    ''' Issue: 
        - This function returns "Users have not provided input yet" if there is no "Building_Cost_Data.pkl" in the directory when the code is run.
        - Even if the "submit" button in the "Input Dashboard" tool is clicked to generate data, this function is not updated accordingly unless you refresh the app.
        - Refreshing the app in my original code takes some time, so we want to avoid it.
    '''

    try:
        path =  os.path.abspath(os.path.dirname(__file__))
        with open(path+ '\Building_Cost_Data.pkl', 'rb') as f:
            var_dict_building = pickle.load(f)   
            
        ''' Displaying the data to check whether updates made in the "Input Dashboard" have been automatically reflected in this function:
            - Not reflected at all
        '''
        data = pd.DataFrame.from_dict(var_dict_building)
        return pn.Column(pn.pane.Markdown(f"## Users have provided input"), pn.panel(data))
    except:
        return pn.pane.Markdown(f"## Users have not provided input yet")

#%%


#%%

def Panel_Dashboard():



    pn.extension('tabulator', 'plotly')
    global main_area, mapping
 
    
    ''' Side buttons  '''
    button1 = pn.widgets.Button(name="Introduction", button_type="warning", icon="file-info", styles={"width": "100%"})
    button2 = pn.widgets.Button(name="Input Dashboard", button_type="warning", icon="file-info", styles={"width": "100%"})
    button3 = pn.widgets.Button(name="Optimization", button_type="warning", icon="clipboard-data", styles={"width": "100%"})

    button1.on_click(lambda event: show_page("Introduction"))
    button2.on_click(lambda event: show_page("Input Dashboard"))
    button3.on_click(lambda event: show_page("Optimization"))
    
    

    
    mapping = {"Introduction": pn.Column(pn.pane.Markdown("Test Interactive Configurator", width=550),align="center"),
               "Input Dashboard": Test_Input_Page(),
               "Optimization": test_algorithm(),
               }
    
    main_area = pn.Column(mapping["Introduction"], styles={"width":"100%"})
    
    
    sidebar_buttons = pn.Column(pn.pane.Markdown("## Tool Selection"), button1, button2, button3,
     					styles={"width": "100%", "padding": "15px"})
    
    
    template = pn.template.FastListTemplate(
        site="Random_",
        title="Test Interactive Configurator",
        sidebar=[sidebar_buttons],
        main = [main_area],
        sidebar_width =350,
        theme="default",
        theme_toggle=False,
        accent="#F08080"
    )

    return template
#%%


template = Panel_Dashboard()
template.servable()

# # http://localhost:5006/test
# # panel serve test.py --autoreload

Here, I have provided my code as text.

As I mentioned earlier, in the “Input_Page”, when we click the “submit” button, it should automatically export the data into a pre-specified directory in the “.pickle” format. Once the data is exported, my understanding is that the “test_algorithm” function should be able to read it and return correct outputs, which does not happen.

For our project, I have no choice but to separate the input dashboard and algorithm tools as I did in the code. Is there an easy and efficient way to sync these two functions in a sequential and dynamic manner, such that any updates made in the input dashboard function are directly reflected in the algorithm tool function?

Thanks.

Thanks for simplifying it.

Can you call test_algorithm from within Test_Cost_Export?

Also, I don’t think you’re using button1.on_click correctly; it should accept an event if you use on_click.

Here’s how I’d approach it.

import panel as pn

pn.extension()

pretend_file = {}

def download(clicks):
    pretend_file["inputs"] = clicks
    process(clicks)
    main.objects = [f"Clicked {clicks}"]

def process(clicks):
    if "inputs" not in pretend_file:
        print("No file to process")
        return
    inputs = pretend_file["inputs"]  # you can read the pickle file here
    main.objects = [f"Processed {inputs}"]  # you can process the data here


download_button = pn.widgets.Button(name="Download")
process_button = pn.widgets.Button(name="Process")
pn.bind(download, download_button.param.clicks, watch=True)
pn.bind(process, process_button.param.clicks, watch=True)

main = pn.Column()
sidebar = pn.Column(download_button, process_button)
pn.Row(sidebar, main).show()

Hello @ahuang11 !

I apologize for the late reply and sincerely appreciate your help. It took me some time to apply your suggestion to my original project, but now everything seems to be working fine!

Thanks!!

1 Like