Skip to content

Commit 7bfb2f4

Browse files
authored
Merge pull request #300 from NeurodataWithoutBorders/local-file-browser
Better local file component
2 parents 38df708 + 4853f87 commit 7bfb2f4

File tree

4 files changed

+19
-64
lines changed

4 files changed

+19
-64
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Upcoming
22

3+
### New features
4+
* Improved browsing and reading local NWB files with Panel, using ipyfilechooser [PR #300](https://github.com/NeurodataWithoutBorders/nwbwidgets/pull/300)
5+
36
### Fixes
47
* Fix I/O issues when streaming data on Panel [PR #295](https://github.com/NeurodataWithoutBorders/nwbwidgets/pull/295)
58
* Fix plotly Figure not showing up, pinned Plotly version [PR #297](https://github.com/NeurodataWithoutBorders/nwbwidgets/pull/297)

nwbwidgets/panel.py

Lines changed: 15 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import ipywidgets as widgets
77
from dandi.dandiapi import DandiAPIClient
88
from fsspec.implementations.cached import CachingFileSystem
9+
from ipyfilechooser import FileChooser
910
from pynwb import NWBHDF5IO
1011
from tqdm.notebook import tqdm
1112

@@ -48,7 +49,6 @@ def __init__(
4849

4950
self.source_options_names = list()
5051
if enable_local_source:
51-
self.source_options_names.append("Local dir")
5252
self.source_options_names.append("Local file")
5353
if enable_dandi_source:
5454
self.source_options_names.append("DANDI")
@@ -83,8 +83,8 @@ def __init__(
8383
self.source_options_radio.observe(self.updated_source, "value")
8484

8585
if enable_local_source:
86-
self.source_options_radio.value = "Local dir"
87-
self.create_components_local_dir_source()
86+
self.source_options_radio.value = "Local file"
87+
self.create_components_local_file_source()
8888
elif enable_dandi_source:
8989
self.source_options_radio.value = "DANDI"
9090
self.create_components_dandi_source()
@@ -95,8 +95,6 @@ def updated_source(self, args=None):
9595
self.create_components_dandi_source()
9696
elif args["new"] == "S3":
9797
self.create_components_s3_source()
98-
elif args["new"] == "Local dir":
99-
self.create_components_local_dir_source()
10098
elif args["new"] == "Local file":
10199
self.create_components_local_file_source()
102100

@@ -182,69 +180,28 @@ def create_components_s3_source(self):
182180
self.source_s3_button.on_click(self.stream_s3_file)
183181
self.cache_checkbox.observe(self.toggle_cache)
184182

185-
def create_components_local_dir_source(self):
186-
"""Create widgets components for Loca dir option"""
187-
self.local_dir_path = widgets.Text(
188-
value="",
189-
description="Dir path:",
190-
layout=widgets.Layout(width="400px", overflow=None),
191-
)
192-
self.local_dir_button = widgets.Button(description="Search")
193-
self.local_dir_top = widgets.HBox(
194-
children=[self.local_dir_path, self.local_dir_button],
195-
layout=widgets.Layout(padding="5px 0px 5px 0px"),
196-
)
197-
self.local_dir_files = widgets.Dropdown(
198-
options=[],
199-
description="Files:",
200-
layout=widgets.Layout(width="400px", overflow=None),
201-
)
202-
self.local_dir_file_button = widgets.Button(icon="check", description="Load file")
203-
self.local_dir_panel = widgets.VBox(
204-
children=[
205-
self.local_dir_top,
206-
self.local_dir_files,
207-
self.local_dir_file_button,
208-
],
209-
layout=widgets.Layout(padding="5px 0px 5px 0px"),
210-
)
211-
self.source_changing_panel.children = [self.local_dir_panel]
212-
self.local_dir_button.on_click(self.list_local_dir_files)
213-
self.local_dir_file_button.on_click(self.load_local_dir_file)
214-
215183
def create_components_local_file_source(self):
216184
"""Create widgets components for Local file option"""
217-
self.local_file_path = widgets.Text(
218-
value="",
219-
description="File path:",
220-
layout=widgets.Layout(width="400px", overflow=None),
221-
)
222-
self.local_file_button = widgets.Button(icon="check", description="Load file")
185+
self.local_file_chooser = FileChooser()
186+
self.local_file_chooser.sandbox_path = str(Path.cwd())
187+
self.local_file_chooser.filter_pattern = ["*.nwb"]
188+
self.local_file_chooser.title = "<b>Select local NWB file</b>"
189+
self.local_file_chooser.register_callback(self.load_local_file)
223190
self.local_file_panel = widgets.VBox(
224-
children=[self.local_file_path, self.local_file_button],
191+
children=[self.local_file_chooser],
225192
layout=widgets.Layout(padding="5px 0px 5px 0px"),
226193
)
227194
self.source_changing_panel.children = [self.local_file_panel]
228-
self.local_file_button.on_click(self.load_local_file)
229195

230196
def list_dandiset_files_dropdown(self, args=None):
231197
"""Populate dropdown with all files and text area with summary"""
232198
self.dandi_summary.value = "Loading dandiset info..."
233199
self.source_dandi_file_dropdown.options = []
234200
dandiset_id = self.source_dandi_id.value.split("-")[0].strip()
235201
self.source_dandi_file_dropdown.options = list_dandiset_files(dandiset_id=dandiset_id)
236-
237202
metadata = get_dandiset_metadata(dandiset_id=dandiset_id)
238203
self.dandi_summary.value = "<style>p{word-wrap: break-word}</style> <p>" + metadata.description + "</p>"
239204

240-
def list_local_dir_files(self, args=None):
241-
"""List NWB files in local dir"""
242-
if Path(self.local_dir_path.value).is_dir():
243-
all_files = [f.name for f in Path(self.local_dir_path.value).glob("*.nwb")]
244-
self.local_dir_files.options = all_files
245-
else:
246-
print("Invalid local dir path")
247-
248205
def stream_dandiset_file(self, args=None):
249206
"""Stream NWB file from DANDI"""
250207
self.widgets_panel.children = [widgets.Label("loading...")]
@@ -290,19 +247,14 @@ def _stream_s3_file(self, s3_url):
290247
self.nwbfile = self.io.read()
291248
self.widgets_panel.children = [nwb2widget(self.nwbfile)]
292249

293-
def load_local_dir_file(self, args=None):
294-
"""Load local NWB file"""
295-
full_file_path = str(Path(self.local_dir_path.value) / self.local_dir_files.value)
296-
io = NWBHDF5IO(full_file_path, mode="r", load_namespaces=True)
297-
nwb = io.read()
298-
self.widgets_panel.children = [nwb2widget(nwb)]
299-
300250
def load_local_file(self, args=None):
301251
"""Load local NWB file"""
302-
full_file_path = str(Path(self.local_file_path.value))
303-
io = NWBHDF5IO(full_file_path, mode="r", load_namespaces=True)
304-
nwb = io.read()
305-
self.widgets_panel.children = [nwb2widget(nwb)]
252+
full_file_path = str(Path(self.local_file_chooser.selected))
253+
if self.io:
254+
self.io.close()
255+
self.io = NWBHDF5IO(full_file_path, mode="r", load_namespaces=True)
256+
self.nwbfile = self.io.read()
257+
self.widgets_panel.children = [nwb2widget(self.nwbfile)]
306258

307259
def process_dandiset(self, dandiset):
308260
try:

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ fsspec
2020
requests
2121
aiohttp
2222
ipydatawidgets==4.3.2
23+
ipyfilechooser==0.6.0

test/test_file.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def test_panel():
2727
assert isinstance(panel, widgets.Widget)
2828

2929
# Change dropdown options for coverage
30-
panel.source_options_radio.value = "Local dir"
3130
panel.source_options_radio.value = "Local file"
3231
panel.source_options_radio.value = "S3"
3332
panel.source_options_radio.value = "DANDI"

0 commit comments

Comments
 (0)