Inside a class, how to watch for changes in a global variable?

I have the following code:

import panel as pn
import param
import pandas as pd
import io

pn.extension()

file_upload = pn.widgets.FileInput(accept=".xlsx")

class DataSelector(param.Parameterized):
    select = param.Selector(["Please upload data"])
    unwantedButton = param.Event(label="Unwanted button")

    @param.depends('unwantedButton', watch=True)
    def refreshData(self):
        if file_upload.value:
            data = io.BytesIO()
            file_upload.save(data)
            data.seek(0)
            df = pd.read_excel(data)
            
            self.param.select.objects = sorted(df.columns.to_list(), key=str.lower)

data_selector = DataSelector()

pn.Column(
    file_upload,
    data_selector.param.select,
    data_selector.param.unwantedButton
).servable()

which produces a file selection widget, a dropdown menu and a button:

afbeelding

I now want to modify the code such that the button can be removed. I want updates to be triggered automatically when a file is uploaded. That is, I want to watch for changes of a global variable (file_upload) inside a param.Parameterized class. The function refreshData needs to be called every time a file is uploaded.

Ideally I would want to replace @param.depends('unwantedButton', watch=True) with @param.depends('file_upload', watch=True)

maybe this

import panel as pn
import param
import pandas as pd 
import io

pn.extension()

file_upload = pn.widgets.FileInput(accept=".xlsx")

class DataSelector(param.Parameterized):

    select = param.Selector(["Please upload data"])
    
    def __init__(self, file_upload, **params):
        super().__init__(**params)
        self.file_upload = file_upload
        
    def file_callback(self, event):
        if event.new:
            data = io.BytesIO()
            self.file_upload.save(data)
            data.seek(0)
            df = pd.read_excel(data)
            self.param.select.objects = sorted(df.columns.to_list(), key=str.lower)
            
# Pass in file_upload   
data_selector = DataSelector(file_upload=file_upload) 

file_upload.param.watch(data_selector.file_callback, 'value')

pn.Column(
    file_upload, 
    data_selector.param.select
).servable()
2 Likes

Well, there actually does seem to be a minor issue. I need to first select a different value in the dropdown menu, because otherwise it returns “Please upload data”

1 Like

Ok I’m not 100% I think there was another issue I read in the forum with something similar been looking but haven’t found. I’ve tried to but been unsuccessful to trigger an initial select

It might be a running issue, there’s an open github about it in the below link if it’s the same issue

Updating selected value in widget after changing param.Selector objects - Param - HoloViz Discourse

A second problem is the following inconsistency, where I use {"A": 1, "B": 2, "C": 3} as an example of selected data:

CASE 1:

select = param.Selector({"A": 1, "B": 2, "C": 3}, label="Select a letter")


CASE 2:
select = param.Selector(["Please upload data"], label="Select a letter")
self.param.select.objects = {"A": 1, "B": 2, "C": 3}

The result of self.select is a number for case 1, but a letter for case 2. That is, if in both cases the first entry is selected, case 1 returns 1 and case 2 returns A. How do I get the number for case 2?




I tried the code below, but it did not work:

select = param.Selector({"Please upload data": ""}, label="Select a letter")
self.param.select.objects = {"A": 1, "B": 2, "C": 3}

This code section returns "" if no data has been selected yet, but otherwise still returns a letter.

Ok, for future reference I “fixed” this by using self.select = df.columns[0] after changing self.param.select.objects to select the first column in df.

One comment to assigning a dict to param.selector.object.
That only works OK at init. Later you’ll need to assign the dict to param.selector.names and the dict.values() to param.selector.objects.
Will flow through to widgets correctly as well.