I’m making a custom component that manages a list of files the user has selected:
import param
import panel as pn
from panel.viewable import Viewer
import logging
import os
# from src.frontend.components.file_selector_modal import FileSelectorModal
pn.extension()
logger = logging.getLogger(name="app")
class FileManager(Viewer):
files = param.List(["File1.txt","File2.csv"],item_type=str,doc="A list of filenames")
def __init__(self, **params):
# not sure if init should come first, but I tried it both ways with no luck
self._refresh()
super().__init__(**params)
@param.depends('files', watch=True)
def _refresh(self):
logger.info(f"Refreshing FileManager with files: {self.files}")
new_layout = pn.Column("# Uploaded Files")
if len(self.files) == 0:
## this message refers to a button in another part of the app
new_layout.append(pn.pane.Markdown(f"**Click \"Upload file\" to upload a File**"))
else:
for filename in self.files:
new_layout.append(self._generate_file_management_widgets(filename))
self._layout = new_layout
def _generate_file_management_widgets(self,filename:str):
"""Helper method to generate the file management widgets for each file"""
## multi-select checkbox, view, and delete are not yet implemented
select_checkbox = pn.widgets.Checkbox(align='center')
view_button = pn.widgets.ButtonIcon(icon='eye', description='View the contents of the File',align='center')
delete_button = pn.widgets.ButtonIcon(icon='trash', description='Remove File from conversation',align='center')
return pn.Row(
select_checkbox,
pn.pane.Markdown(os.path.basename(filename), styles={'margin': "0px"}),
view_button,
delete_button
)
def __panel__(self):
logger.info("Rendering FileManager", level="INFO")
return self._layout
Then my app basically works like this:
from sidebar_widgets import upload_button, uploaded_files
template = pn.template.MaterialTemplate(
sidebar=[upload_button],
site = 'File Manager'
)
upload_file_button = pn.widgets.Button(name="Upload File", button_type="primary")
upload_file_button.on_click(lambda event: file_manager.files.append(uploaded_files))
file_manager = FileManager()
template.main=[pn.Row(file_manager)]
When the app starts, “Rerendering FileManager” prints to log and it renders as expected with File1.txt, File2.csv and their respective widgets. (Works as expected.)
When I click my upload button, “Refreshing FileManager with files” prints to log as expected, but “Rerendering FileManager” is not printed. It appears __panel__
is never being called after the initial render. What am I missing?
I based this code off of this example in the docs: Combine Existing Components — Panel v1.6.2