Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Yu HU
hdf-viewer
Commits
5caacdbd
Commit
5caacdbd
authored
Feb 19, 2019
by
Eric Pellegrini
Browse files
added json file support for HDF dumped data
refactored HDFViewer class updated demo notebook accordingly
parent
39e94c4f
Changes
2
Hide whitespace changes
Inline
Side-by-side
notebooks/HDFViewerDemo.ipynb
View file @
5caacdbd
...
...
@@ -5,26 +5,26 @@
```
%% Cell type:code id: tags:
```
python
import
h5py
from
hdfviewer.widgets.HDFViewer
import
HDFViewerWidget
from
hdfviewer.widgets.PathSelector
import
PathSelector
```
%% Cell type:code id: tags:
```
python
p
ath
=
PathSelector
(
extensions
=
[
".hdf"
,
".h5"
,
".nxs"
])
p
ath
.
widget
selectedP
ath
=
PathSelector
(
extensions
=
[
".hdf"
,
".h5"
,
".nxs"
])
selectedP
ath
.
widget
```
%% Cell type:code id: tags:
```
python
if
path
.
file
:
hdf
=
h5py
.
File
(
path
.
file
,
"r"
)
widget
=
HDFViewerWidget
(
hdf
)
if
selectedPath
.
path
:
widget
=
HDFViewerWidget
(
selectedPath
.
path
)
display
(
widget
)
```
%%%% Output: display_data
...
...
src/hdfviewer/widgets/HDFViewer.py
View file @
5caacdbd
import
binascii
import
io
import
json
import
os
import
webbrowser
...
...
@@ -10,26 +12,90 @@ import matplotlib.pyplot as plt
import
ipywidgets
as
widgets
from
IPython.core.display
import
display
from
hdfviewer
import
__version__
from
hdfviewer.viewers.MplDataViewer
import
MplDataViewer
,
MplDataViewerError
from
hdfviewer.widgets.MplOutput
import
MplOutput
def
HDFViewerWidget
(
hdf
,
startPath
=
None
):
class
HDFViewerError
(
Exception
):
"""Error handler for :mod:`HDFViewer` related exceptions.
"""
pass
def
_openHDFFile
(
filename
):
"""Open a HDF file.
The file can be a *true* HDF file or a json file in which a HDF has been dumped into.
:param hdfSource: the path to the file storing the HDF contents
:type hdfSource: str
The file can be a path to a *true* HDF file or to a file in which the HDF contents has been dumped into.
:raises: :class:`TypeError`: if the input is not a str
:raises: :class:`IOError`: if the path is not valid or could not be opened through h5py API
"""
hdf
=
None
if
not
isinstance
(
filename
,
str
):
raise
TypeError
(
"invalid input type"
)
if
not
os
.
path
.
isfile
(
filename
):
raise
IOError
(
"the input is not a file"
)
# First try to open the file as a HDF5 file
try
:
hdf
=
h5py
.
File
(
filename
,
"r"
)
# Any exception should be caught at this level
except
:
# Try to open it as a json dumped HDF file
# The JSON must contain the str version
with
open
(
filename
,
"r"
)
as
f
:
d
=
json
.
load
(
f
)
byt
=
binascii
.
a2b_base64
(
d
[
"data"
])
try
:
hdf
=
h5py
.
File
(
io
.
BytesIO
(
byt
),
"r"
)
# Any exception should be caught at this level
except
:
pass
finally
:
return
hdf
def
HDFViewerWidget
(
filename
,
startingPath
=
None
):
"""Helper function that displays a :class:`HDFViewer` widget from a file.
The file can be a *true* HDF file or a json file in which a HDF has been dumped into.
:param hdf: the path to the file storing the HDF contents
:type hdf: str
:param startPath: the hdf path from where the HDF data will be inspected.
If not set, the starting path will be the root of the HDF data
:type startPath: str or None
"""
vbox
=
widgets
.
VBox
()
button
=
widgets
.
Button
(
description
=
"documentation"
,
tooltip
=
"open documentation for release {0}"
.
format
(
__version__
))
button
=
widgets
.
Button
(
description
=
"documentation"
,
tooltip
=
"open documentation for release {0}"
.
format
(
__version__
))
button
.
on_click
(
lambda
event
:
HDFViewer
.
info
())
button
.
on_click
(
lambda
event
:
HDFViewer
.
info
())
vbox
.
children
=
[
button
,
HDFViewer
(
hdf
,
startPath
)]
hdf
=
_openHDFFile
(
filename
)
if
hdf
is
None
:
raise
HDFViewerError
(
"An error occured when reading {!r} file"
.
format
(
filename
))
return
vbox
vbox
.
children
=
[
button
,
HDFViewer
(
hdf
,
startingPath
)]
class
HDFViewerError
(
Exception
):
""":class:`HDFViewer` specific exception"""
return
vbox
pass
class
HDFViewer
(
widgets
.
Accordion
):
"""This class allows to inspect HDF data in the context of **Jupyter Lab**
...
...
@@ -42,108 +108,101 @@ class HDFViewer(widgets.Accordion):
:start-after: usage-begin
:end-before: usage-end
:param hdf: the hdf Data to be inspected.
- for `str` input type, this will be the path to the HDF datafile
- for `File`, this will be the output of a prior HDF file opening in read mode
- for `bytes`, this will the byte representation of athe HDF data
:type hdf: str or bytes or :class:`h5py.File`
:param hdf: the hdf data file to be inspected.
:type hdf: :class:`h5py.File`
:param startPath: the hdf path from where the HDF data will be inspected.
If not set, the starting path will be the root of the HDF data
:type startPath: str or None
:raises: :class:`HDFViewerError`: if the HDF data could not be set properly
"""
def
__init__
(
self
,
hdf
,
startPath
=
None
):
widgets
.
Accordion
.
__init__
(
self
)
try
:
if
isinstance
(
hdf
,
h5py
.
File
)
and
hdf
.
mode
==
"r"
:
self
.
_hdf
=
hdf
elif
isinstance
(
hdf
,
str
):
self
.
_hdf
=
h5py
.
File
(
hdf
,
"r"
)
elif
isinstance
(
hdf
,
bytes
):
self
.
_hdf
=
h5py
.
File
(
io
.
BytesIO
(
hdf
),
"r"
)
else
:
raise
IOError
(
"Invalid HDF stream"
)
except
Exception
as
e
:
raise
HDFViewerError
(
str
(
e
))
self
.
_hdf
=
hdf
if
startPath
is
None
:
self
.
_startPath
=
"/"
self
.
children
=
[
HDFViewer
(
hdf
,
self
.
_startPath
)]
self
.
set_title
(
0
,
self
.
_startPath
)
self
.
children
=
[
HDFViewer
(
self
.
_
hdf
,
self
.
_startPath
)]
self
.
set_title
(
0
,
self
.
_startPath
)
else
:
self
.
_startPath
=
startPath
attributesAccordion
=
widgets
.
Accordion
()
for
idx
,(
key
,
value
)
in
enumerate
(
self
.
_hdf
[
self
.
_startPath
].
attrs
.
items
()):
attributesAccordion
.
children
=
list
(
attributesAccordion
.
children
)
+
[
widgets
.
HTML
(
value
)]
attributesAccordion
.
set_title
(
idx
,
key
)
for
idx
,
(
key
,
value
)
in
enumerate
(
self
.
_hdf
[
self
.
_startPath
].
attrs
.
items
()):
attributesAccordion
.
children
=
list
(
attributesAccordion
.
children
)
+
[
widgets
.
HTML
(
value
)]
attributesAccordion
.
set_title
(
idx
,
key
)
# Setup the groups and datasets accordion
groupsAccordion
=
widgets
.
Accordion
()
datasetsAccordion
=
widgets
.
Accordion
()
for
value
in
list
(
self
.
_hdf
[
self
.
_startPath
].
values
()):
if
isinstance
(
value
,
h5py
.
Group
):
groupsAccordion
.
children
=
list
(
groupsAccordion
.
children
)
+
[
HDFViewer
(
hdf
,
value
.
name
)]
groupsAccordion
.
set_title
(
len
(
groupsAccordion
.
children
)
-
1
,
value
.
name
)
elif
isinstance
(
value
,
h5py
.
Dataset
):
for
value
in
list
(
self
.
_hdf
[
self
.
_startPath
].
values
()):
if
isinstance
(
value
,
h5py
.
Group
):
groupsAccordion
.
children
=
list
(
groupsAccordion
.
children
)
+
[
HDFViewer
(
hdf
,
value
.
name
)]
groupsAccordion
.
set_title
(
len
(
groupsAccordion
.
children
)
-
1
,
value
.
name
)
elif
isinstance
(
value
,
h5py
.
Dataset
):
datasetInfo
=
[]
shape
=
value
.
shape
# Set some informations about the current hdf value
datasetInfo
.
append
(
"<i>Dimension: %s</i>"
%
str
(
shape
))
datasetInfo
.
append
(
"<i>Reduced dimension: %s</i>"
%
str
(
tuple
([
s
for
s
in
shape
if
s
!=
1
])))
datasetInfo
.
append
(
"<i>Reduced dimension: %s</i>"
%
str
(
tuple
([
s
for
s
in
shape
if
s
!=
1
])))
datasetInfo
.
append
(
"<i>Type: %s</i>"
%
value
.
dtype
.
name
)
datasetInfo
=
"<br>"
.
join
(
datasetInfo
)
vbox
=
widgets
.
VBox
()
vbox
.
children
=
[
widgets
.
HTML
(
datasetInfo
),
MplOutput
()]
datasetsAccordion
.
children
=
list
(
datasetsAccordion
.
children
)
+
[
vbox
]
datasetsAccordion
.
set_title
(
len
(
datasetsAccordion
.
children
)
-
1
,
value
.
name
)
datasetsAccordion
.
observe
(
self
.
_onSelectDataset
,
names
=
"selected_index"
)
vbox
.
children
=
[
widgets
.
HTML
(
datasetInfo
),
MplOutput
()]
datasetsAccordion
.
children
=
list
(
datasetsAccordion
.
children
)
+
[
vbox
]
datasetsAccordion
.
set_title
(
len
(
datasetsAccordion
.
children
)
-
1
,
value
.
name
)
datasetsAccordion
.
observe
(
self
.
_onSelectDataset
,
names
=
"selected_index"
)
# Display only the accordions which have children
nestedAccordions
=
[(
"attributes"
,
attributesAccordion
),(
"groups"
,
groupsAccordion
),(
"datasets"
,
datasetsAccordion
)]
for
title
,
acc
in
nestedAccordions
:
nestedAccordions
=
[(
"attributes"
,
attributesAccordion
),
(
"groups"
,
groupsAccordion
),
(
"datasets"
,
datasetsAccordion
)]
for
title
,
acc
in
nestedAccordions
:
if
not
acc
.
children
:
continue
acc
.
selected_index
=
None
self
.
children
=
list
(
self
.
children
)
+
[
acc
]
self
.
set_title
(
len
(
self
.
children
)
-
1
,
title
)
self
.
set_title
(
len
(
self
.
children
)
-
1
,
title
)
# By default, the accordion is closed at start-up
self
.
selected_index
=
None
def
_onSelectDataset
(
self
,
change
):
def
_onSelectDataset
(
self
,
change
):
"""A callable that is called when a new dataset is selected
See `here <https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Events.html#Traitlet-events>`__ for more information
#Traitlet-events>`__ for more information
See `here <https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Events.html
"""
idx
=
change
[
"new"
]
# If the accordions is closed does nothing
if
idx
is
None
:
return
accordion
=
change
[
"owner"
]
path
=
accordion
.
get_title
(
idx
)
vbox
=
accordion
.
children
[
idx
]
output
=
vbox
.
children
[
1
]
output
.
clear_output
(
wait
=
False
)
with
output
:
try
:
self
.
_viewer
=
MplDataViewer
(
self
.
_hdf
[
path
],
standAlone
=
False
)
self
.
_viewer
=
MplDataViewer
(
self
.
_hdf
[
path
],
standAlone
=
False
)
except
MplDataViewerError
as
e
:
label
=
widgets
.
Label
(
value
=
str
(
e
))
display
(
label
)
display
(
label
)
else
:
# Bind the DataViewer figure to the MplOutput widget for allowing a "clean" output clearing (i.e. release the figure from plt)
output
.
figure
=
self
.
_viewer
.
viewer
.
figure
...
...
@@ -153,7 +212,7 @@ class HDFViewer(widgets.Accordion):
"""Open the url of a given release of the package documentation.
:param release: the release of the package.
If None, the current release will be selected.
:type release: str
"""
...
...
@@ -161,6 +220,6 @@ class HDFViewer(widgets.Accordion):
version
=
version
if
version
else
__version__
# This will open the package documentation stored on readthedocs
url
=
os
.
path
.
join
(
"https://hdf-viewer.readthedocs.io/en/{0}"
.
format
(
version
))
url
=
os
.
path
.
join
(
"https://hdf-viewer.readthedocs.io/en/{0}"
.
format
(
version
))
webbrowser
.
open
(
url
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment