dharhas
February 12, 2025, 10:23pm
1
I’m trying to build a custom widget that makes a popup modal out of FileSelector. I have code that works if I use the class once, but if I try and use it twice it breaks. Doesn’t filter correctly and shows me a dual window when I try and drill down one folder.
pn.Column(csv_file).servable()
- Works correctly, single window, shows csv files
pn.Column(csv_file, txt_file).servable()
- double window after drilling down does not show csv files
I assume it is something to do with global state of the widget.
Reproducer below for panel 1.6.
import panel as pn
from panel.viewable import Viewer
class FileSelectorModal(Viewer):
def __init__(self, FileSelectorParams={}, **params):
super().__init__(**params)
# Create the FileSelector widget
self.file_selector = pn.widgets.FileSelector(
name="Select a File",
**FileSelectorParams,
)
# Create buttons
self.open_modal_button = pn.widgets.Button(
name="Select File", button_type="primary"
)
self.close_modal_button = pn.widgets.Button(name="Close", button_type="primary")
# Create a TextInput widget to display the selected file
self.selected_file = pn.widgets.TextInput(disabled=True)
# Create the modal content
self.modal_content = pn.Column(
self.file_selector,
self.close_modal_button,
)
# Create the modal
self.modal = pn.Modal(self.modal_content)
# Attach the button callbacks
self.open_modal_button.on_click(self.open_modal)
self.close_modal_button.on_click(self.close_modal)
# Create the layout
self.layout = pn.Row(
f"**{self.name}:**",
f"{FileSelectorParams}",
self.selected_file,
self.open_modal_button,
self.modal,
)
def open_modal(self, event):
self.modal.open = True
def close_modal(self, event):
if self.file_selector.value:
self.selected_file.value = self.file_selector.value[0]
self.modal.open = False
def __panel__(self):
return self.layout
csv_file = FileSelectorModal(
name="Enter CSV File",
FileSelectorParams={"directory": "~/", "file_pattern": "*.csv"},
)
txt_file = FileSelectorModal(
name="Enter TXT File",
FileSelectorParams={"directory": "~/", "file_pattern": "*.txt"},
)
pn.Column(csv_file).servable()
# pn.Column(csv_file, txt_file).servable()
For this to work, the modal must be instantiated on demand and discarded on close. This seems to work for me (panel 1.6.0) :
import panel as pn
from panel.viewable import Viewer
class FileSelectorModal(Viewer):
def __init__(self, FileSelectorParams={}, **params):
super().__init__(**params)
# Create the FileSelector widget
self.file_selector = pn.widgets.FileSelector(
name="Select a File",
**FileSelectorParams,
)
# Create buttons
self.open_modal_button = pn.widgets.Button(
name="Select File", button_type="primary"
)
self.close_modal_button = pn.widgets.Button(name="Close", button_type="primary")
# Create a TextInput widget to display the selected file
self.selected_file = pn.widgets.TextInput(disabled=True)
# Create the modal content
self.modal_content = pn.Column(
self.file_selector,
self.close_modal_button,
)
# Attach the button callbacks
self.open_modal_button.on_click(self.open_modal)
self.close_modal_button.on_click(self.close_modal)
# Create the layout
self.layout = pn.Row(
f"**{self.name}:**",
f"{FileSelectorParams}",
self.selected_file,
self.open_modal_button,
)
def open_modal(self, event):
self.modal = pn.Modal(self.modal_content)
# Add a param watcher to detect when the modal is closed
self.modal.param.watch(self.modal_open_did_change, "open")
self.layout.append(self.modal)
self.modal.open = True
def close_modal(self, event):
self.modal.open = False
def modal_open_did_change(self, event):
if self.modal.open == False:
if self.file_selector.value:
self.selected_file.value = self.file_selector.value[0]
self.layout.remove(self.modal)
self.modal = None
def __panel__(self):
return self.layout
csv_file = FileSelectorModal(
name="Enter CSV File",
FileSelectorParams={"directory": "~/", "file_pattern": "*.csv"},
)
txt_file = FileSelectorModal(
name="Enter TXT File",
FileSelectorParams={"directory": "~/", "file_pattern": "*.txt"},
)
#pn.Column(csv_file).servable()
pn.Column(csv_file, txt_file).servable()
dharhas
February 13, 2025, 1:38pm
3
pierrotsmnrd:
class FileSelectorModal(Viewer):
def __init__(self, FileSelectorParams={}, **params):
super().__init__(**params)
# Create the FileSelector widget
self.file_selector = pn.widgets.FileSelector(
name="Select a File",
**FileSelectorParams,
)
# Create buttons
self.open_modal_button = pn.widgets.Button(
name="Select File", button_type="primary"
)
self.close_modal_button = pn.widgets.Button(name="Close", button_type="primary")
# Create a TextInput widget to display the selected file
self.selected_file = pn.widgets.TextInput(disabled=True)
# Create the modal content
self.modal_content = pn.Column(
self.file_selector,
self.close_modal_button,
)
# Attach the button callbacks
self.open_modal_button.on_click(self.open_modal)
self.close_modal_button.on_click(self.close_modal)
# Create the layout
self.layout = pn.Row(
f"**{self.name}:**",
f"{FileSelectorParams}",
self.selected_file,
self.open_modal_button,
)
def open_modal(self, event):
self.modal = pn.Modal(self.modal_content)
# Add a param watcher to detect when the modal is closed
self.modal.param.watch(self.modal_open_did_change, "open")
self.layout.append(self.modal)
self.modal.open = True
def close_modal(self, event):
self.modal.open = False
def modal_open_did_change(self, event):
if self.modal.open == False:
if self.file_selector.value:
self.selected_file.value = self.file_selector.value[0]
self.layout.remove(self.modal)
self.modal = None
def __panel__(self):
return self.layout
csv_file = FileSelectorModal(
name="Enter CSV File",
FileSelectorParams={"directory": "~/", "file_pattern": "*.csv"},
)
txt_file = FileSelectorModal(
name="Enter TXT File",
FileSelectorParams={"directory": "~/", "file_pattern": "*.txt"},
)
#pn.Column(csv_file).servable()
pn.Column(csv_file, txt_file).servable()
This works after adding pn.extension("modal")
at the top. Otherwise I get a warning and the modal does not appear. In the original code, for some reason I didn’t need it.