Skip to content
34 changes: 30 additions & 4 deletions src/ipyniivue/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""Defines constants for reuse across the package."""
"""
Provides classes for reusable, readable versions of important data.

This module uses enum to enumerate important constants for slice types,
drag modes, and multiplanar types in more readable formats for use in notebooks.
"""

import enum

Expand All @@ -10,7 +15,14 @@


class SliceType(enum.Enum):
"""Maps numerical values to slice type names."""
"""
Defines the number value equivalents for each SliceType of a NiiVue instance.

Parameters
----------
enum.Enum
A new enumeration for this class to store members.
"""

AXIAL = 0
CORONAL = 1
Expand All @@ -20,15 +32,29 @@ class SliceType(enum.Enum):


class DragMode(enum.Enum):
"""Maps numerical values to types of mouse interactions."""
"""
Defines the number value equivalents for each DragMode of a NiiVue instance.

Paramters
---------
enum.Enum
A new enumeration for this class to store members.
"""

CONTRAST = 1
MEASUREMENT = 2
PAN = 3


class MuliplanarType(enum.Enum):
"""Maps numerical values to types of panel arrangements."""
"""
Defines the number value equivalents for each MultiplanarType of a NiiVue instance.

Paramters
---------
enum.Enum
A new enumeration for this class to store members.
"""

AUTO = 0
COLUMN = 1
Expand Down
21 changes: 18 additions & 3 deletions src/ipyniivue/download_dataset.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
"""Downloads all data needed for provided example images."""
"""
Downloads the data needed to run provided examples.

This module is designed to download the nii files needed to run examples
and is set to do so if another url is not provided. It can, however, download
other data, like fonts, from the NiiVue repo or elsewhere.
"""

# Import necessary libraries
import os
Expand All @@ -17,13 +23,22 @@


def download_dataset(api_url=None, dest_folder=None):
"""Download the datasets used for demos and testing."""
"""
Download the datasets used for demos and testing.

Parameters
----------
api_url
Option to provide a custom url to download data.
dest_folder
Option to provide a custom folder to store the data.
"""
if api_url is None:
api_url = BASE_API_URL
if dest_folder is None:
dest_folder = DATA_FOLDER

"""Fetch and download files recursively."""
# Fetch and download files recursively.
print(f"Fetching contents from {api_url}...")
os.makedirs(dest_folder, exist_ok=True)
response = requests.get(api_url)
Expand Down
1 change: 1 addition & 0 deletions src/ipyniivue/options_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,4 +573,5 @@ def render_overlay_blend(self) -> float:
@render_overlay_blend.setter
def render_overlay_blend(self, value: float):
"""Automatically generated property. See generate_options_mixin.py."""

self._opts = {**self._opts, "renderOverlayBlend": value}
58 changes: 51 additions & 7 deletions src/ipyniivue/utils.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,77 @@
"""Defines some utily functions for reuse across the package."""
"""
Important utilities needed to work data between Python and JavaScript code.
It includes a class to convert from snake to camel case as well as multiple
classes the serialize data to work with JS.
"""

import enum
import pathlib
import typing


def snake_to_camel(snake_str: str):
"""Convert a string from snake case to camel case."""
"""
Convert the Python typical snake case to JS typical camel case.
Parameters
----------
snake_str : str
The snake case string to be converted.
Returns
-------
camel_string : str
The parameter string converted to camel case.
"""
components = snake_str.split("_")
return components[0] + "".join(x.title() for x in components[1:])


def file_serializer(instance: typing.Union[pathlib.Path, str], widget: object):
"""Serialize file paths."""
"""
Serialize a file to be transfered and read by the JS side.
Parameters
----------
instance : typing.Union[pathLib.Path, str]
The path to a file to be serialized.
widget : object
The NiiVue widget the instance is a part of.
"""
if isinstance(instance, str):
# make sure we have a pathlib.Path instance
# Make sure we have a pathlib.Path instance
instance = pathlib.Path(instance)
return {"name": instance.name, "data": instance.read_bytes()}


def mesh_layers_serializer(instance: list, widget: object):
"""Serialize meshes."""
"""
Serialize each layer of a mesh instance.
Parameters
----------
instance : list
The mesh instance containing the layers.
widget : object
The NiiVue widget the instance is a part of.
"""
return [
{**mesh_layer, "path": file_serializer(mesh_layer["path"], widget)}
for mesh_layer in instance
]


def serialize_options(instance: dict, widget: object):
"""Serialize options."""
# serialize enums as their value
"""
Serialize the options for a NiiVue instance.
Parameters
----------
instance : dict
The list of options to be serialized.
widget : object
The NiiVue widget the instance is a part of.
"""
# Serialize enums as their value
return {k: v.value if isinstance(v, enum.Enum) else v for k, v in instance.items()}
89 changes: 76 additions & 13 deletions src/ipyniivue/widget.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
"""Defines the NiiVue and related widgets."""
"""
Widgets representing NiiVue model instances as well as volume, mesh, and drawing models.

Aside from setting up the Mesh, Volume, Drawing, and NiiVue widgets, this module
contains many of the classes needed to make NiiVue instances work, such as classes
to load objects in, change attributes of the instance, and more.
"""

import pathlib

Expand All @@ -19,7 +25,13 @@


class Mesh(ipywidgets.Widget):
"""Defines a mesh widget."""
"""
Represents a Mesh model.

Parameters
----------
ipywidgets.Widget : A widget representing a mesh model and its data.
"""

path = t.Union([t.Instance(pathlib.Path), t.Unicode()]).tag(
sync=True, to_json=file_serializer
Expand All @@ -31,7 +43,13 @@ class Mesh(ipywidgets.Widget):


class Volume(ipywidgets.Widget):
"""Defines a volume widget."""
"""
Represents a Volume model.

Parameters
----------
ipywidgets.Widget : A widget representing a volume model and its data.
"""

path = t.Union([t.Instance(pathlib.Path), t.Unicode()]).tag(
sync=True, to_json=file_serializer
Expand All @@ -44,7 +62,13 @@ class Volume(ipywidgets.Widget):


class Drawing(ipywidgets.Widget):
"""Defines a drawing widget."""
"""
Represents a Drawing model.

Parameters
----------
ipywidgets.Widget : A widget representing a drawing model and its data.
"""

path = t.Union([t.Instance(pathlib.Path), t.Unicode()]).tag(
sync=True, to_json=file_serializer
Expand All @@ -55,7 +79,15 @@ class Drawing(ipywidgets.Widget):


class NiiVue(OptionsMixin, anywidget.AnyWidget):
"""Represents a Niivue instance."""
"""
Represents a Niivue instance.

Parameters
----------
OptionsMixin : The list of default options for a NiiVue instance.
anywidget.Anywidget : An AnyWidget model representing a NiiVue
instance and its data.
"""

_esm = pathlib.Path(__file__).parent / "static" / "widget.js"

Expand All @@ -77,7 +109,8 @@ def __init__(self, height: int = 300, **options):
super().__init__(height=height, _opts=_opts, _volumes=[], _meshes=[])

def load_volumes(self, volumes: list):
"""Load a list of volumes into the widget.
"""
Load a list of volumes into the widget.

Parameters
----------
Expand All @@ -99,21 +132,43 @@ def add_volume(self, volume: dict):

@property
def volumes(self):
"""Returns the list of volumes."""
"""
Returns the list of volumes.

Returns
-------
list
A list of dictionairies containing the volume information.
"""
return list(self._volumes)

def load_drawings(self, drawings: list):
"""Create and load the drawings passed as an argument."""
"""
Load a list of drawings into the widget.

Parameters
----------
drawings : list
A list of dictionaries containing the drawing information.
"""
drawings = [Drawing(**item) for item in drawings]
self._drawings = drawings

@property
def drawings(self):
"""Returns a list of the loaded drawings."""
"""
Returns the list of drawings.

Returns
-------
list
A list of dictionairies containing the drawing information.
"""
return list(self._drawings)

def load_meshes(self, meshes: list):
"""Load a list of meshes into the widget.
"""
Load a list of meshes into the widget.

Parameters
----------
Expand All @@ -124,7 +179,8 @@ def load_meshes(self, meshes: list):
self._meshes = meshes

def add_mesh(self, mesh: Mesh):
"""Add a single mesh to the widget.
"""
Add a single mesh to the widget.

Parameters
----------
Expand All @@ -135,12 +191,19 @@ def add_mesh(self, mesh: Mesh):

@property
def meshes(self):
"""Returns the list of meshes."""
"""
Returns the list of meshes.

Returns
-------
list
A list of dictionairies containing the mesh information.
"""
return list(self._meshes)


class WidgetObserver:
"""Sets an observed for `widget` on the `attribute` of `object`."""
"""Creates an observer on the `attribute` of `object` for a `widget`."""

def __init__(self, widget, obj, attribute):
self.widget = widget
Expand Down
Loading