How to create multiple instances of object and display methods

Hello, I am glad to have joined this community and am hoping to seek for some help:
I am working on creating a fiber photometry GUI that (should) eventually be able to read in multiple (at least 2) CSVs or pickled files and create object instances based on each file read in, then be able to call certain function methods/display plots etc.
The code below currently allows me to upload a CSV file containing the necessary information, read it and create a new object, and call some methods that display some Plotly visuals.
I am wondering how to go about being able to instantiate more objects (whilst keeping the first one created) and having a sort of dropdown menu where I can switch between views of each object and its called upon function methods/visuals.

class FiberGui(param.Parameterized):
    #Check: Are all these attributes necessary?
    data = pd.DataFrame()
    fpho_data_df = pd.DataFrame()
    file_input = param.Parameter()
    input_1 = param.Parameter()
    input_2 = param.Parameter()
    input_3 = param.Parameter()
    input_4 = param.Parameter()
    input_5 = param.Parameter()
    test_obj = param.Parameter()
    plot_pane = param.Parameter()
    plot_pane_2 = param.Parameter()
    plot_panes = param.Parameter()
    fitted_plot = param.Parameter()
    raw_plot = param.Parameter()
    # update_plot = param.Action(lambda x: x.param.trigger('update_plot'), label = 'Read CSV')

    
    def __init__(self, **params):
        self.param.file_input.default = pn.widgets.FileInput(accept = '.csv')
        super().__init__(**params)

        #Buttons
        self.raw_signal_plot_button = pn.widgets.Button(name = "Plot Raw Signal Graph", button_type = 'primary')
        self.raw_signal_plot_button.on_click(self.get_raw_signal_plot)
        self.fitted_exp_plot_button = pn.widgets.Button(name = "Plot Fitted Exp Graph", button_type = 'success')
        self.fitted_exp_plot_button.on_click(self.get_fitted_exp_plot)
        self.read_input_button = pn.widgets.Button(name = "Read CSV and Input Params")
        self.read_input_button.on_click(self.save_input_params)
        #Buttons
        
        #Plot panes
        self.plot_pane = pn.pane.Plotly(height = 400, sizing_mode = "stretch_width")
        self.plot_pane_2 = pn.pane.Plotly(height = 600, sizing_mode = "stretch_width")
        self.plot_panes = pn.Column(self.plot_pane, self.raw_signal_plot_button, self.plot_pane_2, self.fitted_exp_plot_button)
        #Plot panes
        
        #Entry/Input params
        self.input_1 = pn.widgets.TextInput(name = 'Object Name', width = 90, placeholder = 'String')
        self.input_2 = pn.widgets.IntInput(name = 'Fiber Number', width = 90, placeholder = 'Int')
        self.input_3 = pn.widgets.IntInput(name = 'Animal Number', width = 90, placeholder = 'Int')
        self.input_4 = pn.widgets.TextInput(name = 'Exp Date', width = 90, placeholder = 'Date')
        self.input_5 = pn.widgets.TextInput(name = 'Exp Time', width = 90, placeholder = 'Time')

        self.entry_rows = pn.Column('FiberOBJ input', self.input_1, self.input_2, self.input_3, self.input_4, self.input_5, background = 'WhiteSmoke')
        #Entry/Input params
        
        #Views
        self.side_view = None
        self.main_view = None
    
    #Adding raw csv input
    @pn.depends("file_input.value", watch = True)
    def _parse_file_input(self):
        value = self.file_input.value
        if value:
            self.data = read_csv(self.file_input)
            print("read data")
            if not self.data.empty:
                self.side_view.append(self.entry_rows)
                self.side_view.append(self.read_input_button)
            else:
                print("Dataframe empty")
        else:
            print("Error reading file")

            
    # @pn.depends("file_input.value", watch = True)
    def save_input_params(self, *args):
        obj_name = self.input_1.value
        fiber_num = self.input_2.value
        animal_num = self.input_3.value
        exp_date = self.input_4.value
        exp_time = self.input_5.value

        if not self.data.empty:
            self.test_obj = createObj(self.data, obj_name, fiber_num, animal_num, exp_date, exp_time)
            self.raw_plot = self.test_obj.raw_signal_trace()
            self.fitted_plot, self.fpho_data_df = self.test_obj.plot_fitted_exp()
            print("OBJ CREATED")
        else:
            print("Didnt create obj")

    
    def _side_view(self):
        self.side_view = pn.Column(
            pn.panel(self.file_input, width = 600)
            # pn.Row(self.entry_rows, width = 500)  
        )
        return self.side_view
    
    @pn.depends('data', watch = True)
    def _main_view(self):
        self.main_view = pn.Column(
            pn.panel(self.plot_panes, width = 400)
        )
        return self.main_view
    
    def get_raw_signal_plot(self, *args):
        #Adds plot pane to window
        self.plot_pane.object = self.raw_plot
        self.plot_pane.param.trigger('object')
        self.main_view.append(self.plot_pane)
        
        return self.plot_pane
    
    def get_fitted_exp_plot(self, *args):
        fitted_btn = pn.Row(self.fitted_exp_plot_button, width = 400)
        self.main_view.append(fitted_btn)
        self.plot_pane_2.object = self.fitted_plot
        self.plot_pane_2.param.trigger('object')
        self.main_view.append(self.plot_pane_2)
        
        return self.plot_pane_2
            

fiber_examp = FiberGui()
fiber_side = fiber_examp._side_view()
fiber_main = fiber_examp._main_view()

# component = pn.Column(
#     description,
#     fiber_view
#     # sizing_mode='stretch_width'
# )
# component

template = pn.template.MaterialTemplate(site = "Fiber Photometry", title = 'FiberPho GUI',
                                       sidebar = ["**Upload CSV** and set **Input Parameters** for your fiber object here",
                                                  fiber_side],
                                       main = [
                                           pn.Column(pn.panel(fiber_main, height = 700))
                                       ])


server = pn.serve(template, start = False, show = False, loop = loop, websocket_max_message_size = 10485760000, 
                  verbose = True,
                  threaded = False
                 )
server.start()

Any help/guidance in the right direction would be appreciated!
Thank you

Hi @yobae

Welcome to the community :+1:

You can use a Selector parameter to keep track of your datasets and the current dataset.

from io import StringIO

import pandas as pd
import panel as pn
import param

pn.extension("tabulator", sizing_mode="stretch_width")

ACCENT_COLOR="#0072B5"

class Dataset(param.Parameterized):
    value = param.DataFrame()

class FiberGui(param.Parameterized):
    new_dataset_input = param.ClassSelector(class_=pn.widgets.FileInput, constant=True)
    dataset=param.Selector()

    def __init__(self, **params):
        params["new_dataset_input"]=pn.widgets.FileInput(accept = '.csv')
        super().__init__(**params)
        self.param.dataset.objects=[]


    @pn.depends("new_dataset_input.value", watch = True)
    def _parse_new_data_set(self):
        value = self.new_dataset_input.value
        if not value:
            return

        string_io = StringIO(value.decode("utf8"))
        dataset = Dataset(value=pd.read_csv(string_io), name=self.new_dataset_input.filename)
        existing_datasets = self.param.dataset.objects
        self.param.dataset.objects=[*existing_datasets, dataset]
        self.dataset = dataset

    @pn.depends("dataset")
    def output(self):
        if self.dataset:
            return pn.widgets.Tabulator(self.dataset.value.head(100), theme="fast", height=800, pagination="remote", page_size=25)
        else:
            return "No Datasets Loaded"

gui = FiberGui()

template = pn.template.FastListTemplate(
    site = "Awesome Panel", title = 'Fiber Photometry App with File Upload',
    sidebar = ["**Upload a new dataset** here", gui.new_dataset_input, pn.Param(gui, parameters=["dataset"], show_name=False)],
    main = [pn.panel(gui.output, loading_indicator=True)],
    accent_base_color=ACCENT_COLOR, header_background=ACCENT_COLOR
)
template.servable()

If you like this example please share on twitter https://twitter.com/MarcSkovMadsen/status/1469588373912395776?s=20 or Linked In Panel on LinkedIn: #dataviz #python #datascience. Thanks

2 Likes

Just a small note: You should NOT use pickle as a way to upload data. The reason is it is not secure, see pickle — Python object serialization — Python 3.10.1 documentation and Exploiting Python pickles - David Hamann.

2 Likes

I appreciate the quick response. I have not gotten time to properly implement this into my current code, but I will be sure to update you and share on twitter/linkedin when I get the chance.
Thank you so much!

1 Like