Hello,
I would like to build a GUI where users can upload the CSV files (FileInput), select columns (MultiChoice), and generate a plot.
The corresponding ColorPicker widget will be generated or deleted when adding or deleting a column.
The challenge I’m facing is related to updating the plot color.
When users pick a new color from the ‘ColorPicker’ widget, the plot does not immediately reflect the new color choice. (But when adding a new column, the color changes are applied correctly.)
The following are my codes,
import param
import pandas as pd
import hvplot.pandas
import panel as pn
import holoviews as hv
from io import StringIO
class DataViewer(param.Parameterized):
file_input = param.Parameter(pn.widgets.FileInput(accept='.csv', multiple=True, filename=[]))
trial = param.Parameter(pn.widgets.MultiChoice(name='Trial'))
plot = param.Parameter()
color_pickers = param.Dict({})
def __init__(self, **params):
super().__init__(**params)
self.file_input.param.watch(self._load_file, 'filename')
self.trial.param.watch(self._update_plot, 'value')
self.plot = hv.Curve([])
self.dfs = {}
def _load_file(self, event):
if self.file_input.value is None:
return
self.trial.value = [] # reset MultiChoice
options = []
for filename, content in zip(self.file_input.filename, self.file_input.value):
df = pd.read_csv(StringIO(content.decode()))
filename_without_extension = filename.rsplit('.', 1)[0] # remove file extension
self.dfs[filename_without_extension] = df
options += [f'{filename_without_extension}-{col}' for col in df.columns[1:]]
self.trial.options = options
def _update_color(self, event):
self._update_plot() # regenerate plot
def _update_plot(self, event=None):
if not self.trial.value: # exit if no trial selected
return
# update ColorPicker widgets when adding or delecting a trial from MultiChoice
for trial in self.trial.value:
if trial not in self.color_pickers:
color_picker = pn.widgets.ColorPicker(name=trial, value="#000000")
color_picker.param.watch(self._update_plot, 'value')
self.color_pickers[trial] = color_picker
for trial in list(self.color_pickers):
if trial not in self.trial.value:
del self.color_pickers[trial]
self.plot = hv.Overlay([self.dfs[trial.split('-')[0]].hvplot.line(x='Time', y=trial.split('-')[1], color=self.color_pickers[trial].value, label=trial) for trial in self.trial.value])
@pn.depends('plot')
def plot_view(self):
return self.plot
def color_picker_view(self):
return pn.Row(*self.color_pickers.values())
def panel(self):
return pn.Column(self.file_input, self.trial, self.color_picker_view, self.plot_view)
viewer = DataViewer()
viewer.panel().servable()
Here is the example data:
Time,Trial_1,Trial_2
0,10,20
1,20,30
2,20,40
3,20,90
4,60,10
The GUI:
Sorry for not providing a minimal code example to replicate this issue.
Because if I just create a ColorPicker and connect it to a _update_plot function, it works well.
I am struggling to identify the part of the process that might be causing the problem.
Any thoughts or suggestions are greatly appreciated.
Thank you in advance!