From d5e65e696fa9529c316bf5174447512fff259385 Mon Sep 17 00:00:00 2001 From: Christian Brodbeck Date: Mon, 8 Jun 2026 11:54:29 -0400 Subject: [PATCH 1/3] Update docs --- docs/README.rst | 2 +- docs/api_reference.rst | 51 +-- docs/conf.py | 30 +- docs/index.rst | 77 ++-- docs/installation.rst | 80 ++-- docs/requirements.txt | 2 - docs/user_guide.rst | 751 +++++++++++------------------------- src/liveneuro/_liveneuro.py | 31 +- 8 files changed, 324 insertions(+), 700 deletions(-) diff --git a/docs/README.rst b/docs/README.rst index 617cd98..7116b2a 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -30,7 +30,7 @@ Read the Docs ------------- The documentation is automatically built and hosted on Read the Docs at: -https://liveneuron.readthedocs.io +https://liveneuro.readthedocs.io The build is triggered automatically on every push to the main branch. diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 48d475a..93c4fbd 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -1,7 +1,8 @@ API Reference ============= -This page contains the complete API reference for LiveNeuro. +This page documents the public LiveNeuro API. For guided examples, start with +the :doc:`user_guide`. Main Class ---------- @@ -11,51 +12,7 @@ Main Class :exclude-members: __init__ :show-inheritance: - The main visualization class for interactive 2D brain projections with activity time-course plots. - -Sample Data Module ------------------- - -.. autofunction:: liveneuro.create_sample_brain_data - -Data Format +Sample Data ----------- -Input Data Expectations -^^^^^^^^^^^^^^^^^^^^^^^^ - -**Vector Data** (with direction and magnitude): - -* Eelbrain NDVar with dimensions: ``([case,] time, source, space)`` -* MNE ``VolVectorSourceEstimate`` with matching ``src`` SourceSpaces object -* Built-in MNE sample data object returned by ``create_sample_brain_data()`` - -**Scalar Data** (magnitude only): - -* Eelbrain NDVar with dimensions: ``([case,] time, source)`` -* Single value per source at each time point -* Sample-data object returned by ``create_sample_brain_data(has_vector_data=False)`` - -**Built-in Sample Data:** - -* 1589 sources in volumetric source space -* 76 time points (-100ms to 400ms) -* Vector data (3D current dipoles) - -Exceptions ----------- - -The library may raise the following exceptions: - -**ValueError** - * Invalid ``display_mode`` string - * Invalid ``layout_mode`` (not "vertical" or "horizontal" and not registered in LAYOUTS) - * Invalid parameter values - -**ImportError** - * Missing required dependencies - * Eelbrain not installed when using eelbrain-specific features - -**RuntimeError** - * Data processing errors - * Visualization rendering errors +.. autofunction:: liveneuro.create_sample_brain_data diff --git a/docs/conf.py b/docs/conf.py index db0f621..981d56f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,6 @@ "sphinx.ext.viewcode", "sphinx.ext.intersphinx", "sphinx.ext.autosummary", - "myst_parser", ] autodoc_mock_imports = [ @@ -44,24 +43,28 @@ ] templates_path = ["_templates"] -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +exclude_patterns = [ + "_build", + ".ipynb_checkpoints", + "README.rst", + "Thumbs.db", + ".DS_Store", +] # The suffix(es) of source filenames. source_suffix = { ".rst": "restructuredtext", - ".md": "markdown", } # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinx_rtd_theme" -html_static_path = ["_static"] +html_static_path = [] # Theme options html_theme_options = { "logo_only": False, - "display_version": True, "prev_next_buttons_location": "bottom", "style_external_links": True, "collapse_navigation": False, @@ -99,19 +102,6 @@ "numpy": ("https://numpy.org/doc/stable/", None), "matplotlib": ("https://matplotlib.org/stable/", None), "plotly": ("https://plotly.com/python-api-reference/", None), + "mne": ("https://mne.tools/stable/", None), + "eelbrain": ("https://eelbrain.readthedocs.io/en/stable/", None), } - -# MyST-Parser settings -myst_enable_extensions = [ - "colon_fence", - "deflist", - "dollarmath", - "fieldlist", - "html_admonition", - "html_image", - "replacements", - "smartquotes", - "strikethrough", - "substitution", - "tasklist", -] diff --git a/docs/index.rst b/docs/index.rst index 3961368..bf69b66 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,62 +1,41 @@ -Welcome to LiveNeuro's documentation! -====================================== +.. raw:: html -**LiveNeuro** is an interactive 2D brain visualization library using Plotly and Dash, designed for real-time exploration of neural activity data. +

+ Python Version + License + Documentation +

+ +LiveNeuro +========= .. image:: liveNeuron.png :alt: LiveNeuro visualization overview :align: center :width: 720px -.. image:: https://img.shields.io/badge/python-3.10%2B-blue - :target: https://www.python.org/downloads/ - :alt: Python Version - -.. image:: https://img.shields.io/badge/license-MIT-green - :target: https://opensource.org/licenses/MIT - :alt: License - -.. image:: https://img.shields.io/badge/docs-ReadTheDocs-blue - :target: https://liveneuron.readthedocs.io/en/latest/index.html - :alt: Documentation - -Key Features ------------- - -* **Interactive 2D brain projections** - axial, sagittal, coronal, and hemisphere views -* **Activity time-course plots** for time series visualization -* **Optimized arrow rendering** for smoother interaction with dense vector fields -* **Real-time controls** for time navigation and interaction -* **Jupyter notebook support** for interactive development -* **Customizable colormaps** and visualization options -* **Export capabilities** for static images - -Quick Start ------------ - -Installation -^^^^^^^^^^^^ - -**Install from GitHub** - -.. code-block:: bash - - pip install https://github.com/liang-bo96/LiveNeuro/archive/refs/heads/main.zip - -Basic Usage -^^^^^^^^^^^ +**LiveNeuro** helps MNE-Python and Eelbrain users inspect volume source estimates +as interactive 2D brain projections with linked time-course plots. +It is built on Plotly and Dash, works in notebooks or a browser, +and accepts MNE volume vector source estimates and :class:`eelbrain.NDVar` objects. -.. code-block:: python +Start Here +---------- - from liveneuro import LiveNeuro +* :ref:`installation`: create an environment and install LiveNeuro. +* :ref:`quick-start`: open the built-in sample visualization. +* :ref:`eelbrain-data-input`: plot an :class:`eelbrain.NDVar`. +* :ref:`mne-data-input`: plot an :class:`mne.VolVectorSourceEstimate`. +* :doc:`api_reference`: look up constructor parameters and public methods. - # Create visualization with built-in MNE sample data - viz = LiveNeuro() - - # Launch in interactive plot - viz.run() +Highlights +---------- -For a full walkthrough of layouts and controls, start with the :doc:`user_guide`. +* Linked activity time course with click-to-navigate updates. +* Interactive sagittal, coronal, axial, and hemisphere projections. +* Vector-field arrows with scale and threshold controls. +* Notebook, JupyterLab, and external browser display modes. +* Static export for figures and presentations. .. toctree:: :maxdepth: 2 @@ -67,7 +46,7 @@ For a full walkthrough of layouts and controls, start with the :doc:`user_guide` api_reference Indices and tables -================== +------------------ * :ref:`genindex` * :ref:`modindex` diff --git a/docs/installation.rst b/docs/installation.rst index 9e98f2b..dcf7a26 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,65 +1,75 @@ +.. _installation: + Installation ============ -Requirements ------------- +LiveNeuro requires Python 3.10 or higher and the scientific Python stack used by +MNE-Python and Eelbrain. For the most reliable setup, create an environment with +``mamba`` or ``conda`` first, then install LiveNeuro with ``pip``. + +Create An Environment +--------------------- + +Follow the Eelbrain installation guide for platform-specific dependencies: -LiveNeuro requires Python 3.10 or higher and an environment with Eelbrain -available. The recommended procedure is to create an environment through -``mamba`` following the official Eelbrain installation guide, then install -LiveNeuro with ``pip``: +* `Eelbrain installation guide `_ + +For an existing conda-style environment, the minimum Eelbrain install is: + +.. code-block:: bash -https://eelbrain.readthedocs.io/en/stable/installing.html + mamba install -c conda-forge eelbrain -Install from GitHub -------------------- +Install LiveNeuro +----------------- -Install directly from GitHub: +Install the current development version directly from GitHub: .. code-block:: bash - pip install https://github.com/liang-bo96/LiveNeuro/archive/refs/heads/main.zip + pip install https://github.com/Eelbrain/LiveNeuro/archive/refs/heads/main.zip -Verify Installation -------------------- +Verify The Install +------------------ -To verify the installation, run: +Run a short import check: .. code-block:: python from liveneuro import LiveNeuro - print("LiveNeuro installed successfully!") - -Troubleshooting ---------------- -Common Issues -^^^^^^^^^^^^^ + viz = LiveNeuro() + print("LiveNeuro is ready.") -**ImportError: No module named 'liveneuro'** +Then continue with :ref:`quick-start`. -Make sure you have installed the package correctly. Try: +.. _installation-troubleshooting: -.. code-block:: bash +Installation Troubleshooting +---------------------------- - pip install --upgrade "https://github.com/liang-bo96/LiveNeuro/archive/refs/heads/main.zip" +``ImportError: No module named 'liveneuro'`` + Confirm that the environment running Python is the same one where LiveNeuro + was installed, then reinstall or upgrade: -**Plotly/Dash version conflicts** + .. code-block:: bash -If you encounter version conflicts, try: + pip install --upgrade "https://github.com/Eelbrain/LiveNeuro/archive/refs/heads/main.zip" -.. code-block:: bash +Missing Eelbrain or scientific dependencies + Revisit the Eelbrain installation guide, or install Eelbrain from + conda-forge: - pip install --upgrade plotly dash + .. code-block:: bash -**Missing dependencies** + mamba install -c conda-forge eelbrain -If Eelbrain is missing or not importable, revisit the Eelbrain installation -guide for platform-specific environment setup. +Plotly or Dash version conflicts + Upgrade the interactive plotting dependencies inside the active environment: -.. code-block:: bash + .. code-block:: bash - mamba install -c conda-forge eelbrain + pip install --upgrade plotly dash -For editable installs, test commands, and repository structure, see the -repository ``README.md``. +For editable installs, tests, and repository structure, see the repository +``README.md``. diff --git a/docs/requirements.txt b/docs/requirements.txt index 79e7c9c..3f3cda0 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,7 +2,5 @@ sphinx>=7.0.0 sphinx-rtd-theme>=1.3.0 sphinx-autodoc-typehints>=1.24.0 -myst-parser>=2.0.0 - diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 5193a9b..c61437f 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -1,406 +1,44 @@ User Guide ========== -This comprehensive guide covers all features and configuration options of LiveNeuro based on the actual implementation. - -Getting Started ---------------- - -Basic Workflow -^^^^^^^^^^^^^^ - -1. **Create** visualization object with desired parameters -2. **Launch** with ``run()`` -3. **Interact** with hover, click, and zoom -4. **Export** static images if needed - -.. code-block:: python - - from liveneuro import LiveNeuro - - # Step 1: Create - viz = LiveNeuro(display_mode="lyr") - - # Step 2: Launch - viz.run() # Auto inline in Jupyter; external browser otherwise - - # Step 3: Interact (in browser/notebook) - # Step 4: Export - viz.export_images(output_dir="./plots", time_idx=30) - -Run Modes ---------- +LiveNeuro turns source-space time series into an interactive Dash application: +a time-course plot controls one or more 2D brain projections, and each +projection updates as you move through time. .. note:: - In IPython shells (outside notebooks), explicitly pass ``mode="external"`` to - ``run()`` to start a visualization that can be accessed in a browser. -* **Interpreter / shell**: uses built-in MNE sample data if ``y`` is omitted; launches a server on a random port (explicit ``mode="external"`` recommended). - - .. code-block:: python - - viz = LiveNeuro() - viz.run(mode="external") - -* **Notebook**: auto-selects ``mode="inline"`` (embedded IFrame). Choose explicitly if you want a browser or a Lab tab. - - .. code-block:: python - - viz = LiveNeuro() - viz.run() # inline by default in notebooks; use mode="external" for shells - # viz.run(mode="jupyterlab") # open in JupyterLab tab - # viz.run(mode="external") # force external browser - -Understanding Display Modes ----------------------------- - -Display modes control which anatomical views are shown and in what order. - -Anatomical Coordinate System -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* **X-axis (Sagittal)**: Side view, left-right through brain -* **Y-axis (Coronal)**: Front view, front-back through brain -* **Z-axis (Axial)**: Top view, top-bottom through brain -* **L**: Left hemisphere lateral view -* **R**: Right hemisphere lateral view - -Single View Modes -^^^^^^^^^^^^^^^^^ - -Use single letters for individual views: - -.. code-block:: python - - viz = LiveNeuro(display_mode="x") # Sagittal only - viz = LiveNeuro(display_mode="y") # Coronal only - viz = LiveNeuro(display_mode="z") # Axial only - viz = LiveNeuro(display_mode="l") # Left hemisphere only - viz = LiveNeuro(display_mode="r") # Right hemisphere only - -Multi-View Modes -^^^^^^^^^^^^^^^^ - -Combine letters for multiple views: - -.. code-block:: python - - # Orthogonal views (special keyword) - viz = LiveNeuro(display_mode="ortho") # x + y + z - - # Hemisphere combinations - viz = LiveNeuro(display_mode="lr") # Left + Right - viz = LiveNeuro(display_mode="lyr") # Left + Coronal + Right (default) - viz = LiveNeuro(display_mode="lzr") # Left + Axial + Right - - # Axis combinations - viz = LiveNeuro(display_mode="xz") # Sagittal + Axial - viz = LiveNeuro(display_mode="yx") # Coronal + Sagittal - viz = LiveNeuro(display_mode="yz") # Coronal + Axial - -Four-View Modes -^^^^^^^^^^^^^^^ - -For comprehensive anatomical coverage: - -.. code-block:: python - - # Four views in different orders - viz = LiveNeuro(display_mode="lyrz") # L + Coronal + R + Axial - viz = LiveNeuro(display_mode="lzry") # L + Axial + R + Coronal - -**Note:** Order of letters determines display order of anatomical projections (left-to-right). - -Layout Modes ------------- - -Vertical Layout (Default) -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -**When to use:** - -* General use, including 1–4 projections -* When the detailed activity time course should be prominent -* When you want more horizontal space per projection - -**Characteristics:** - -* Detailed activity time course on top -* Brain projections arranged beneath -* Good for multiple projections while preserving horizontal space - -.. code-block:: python - - viz = LiveNeuro( - display_mode="lyr", - layout_mode="vertical" # Default - ) - -Horizontal Layout -^^^^^^^^^^^^^^^^^ - -**When to use:** - -* Wide screens or presentations -* When spatial layout (left-to-right comparison) is the focus - -**Characteristics:** - -* Detailed activity time course on the left -* Brain projections arranged to the right - - -.. code-block:: python - - viz = LiveNeuro( - display_mode="lyrz", - layout_mode="horizontal" - ) - -Arrow Visualization -------------------- - -Understanding Arrows -^^^^^^^^^^^^^^^^^^^^ - -Arrows represent vector fields in brain data: - -* **Direction**: Shows orientation of activity (e.g., current flow) -* **Length**: Indicates magnitude (controlled by ``arrow_scale``) -* **Visibility**: Filtered by ``arrow_threshold`` - -Arrow Scale Parameter -^^^^^^^^^^^^^^^^^^^^^ - -The ``arrow_scale`` parameter (default: 1.0) controls arrow length: - -.. code-block:: python - - # Short arrows for dense data - viz = LiveNeuro(arrow_scale=0.5) - - # Default balanced length - viz = LiveNeuro(arrow_scale=1.0) - - # Long arrows for sparse data - viz = LiveNeuro(arrow_scale=2.0) - -**Recommendations:** - -* Dense vector fields: 0.5 - 0.8 -* Moderate density: 1.0 (default) -* Sparse fields: 1.2 - 2.0 - -Arrow Threshold Parameter -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``arrow_threshold`` parameter filters arrows by magnitude: - -.. code-block:: python - - # Show all arrows - viz = LiveNeuro(arrow_threshold=None) - - # Auto threshold (10% of maximum magnitude) - viz = LiveNeuro(arrow_threshold='auto') - - # Custom threshold - viz = LiveNeuro(arrow_threshold=0.15) - -**When to use:** - -* ``None``: Small datasets or when all vectors are important -* ``'auto'``: Good default for most cases -* *float*: Fine-tune based on data characteristics - -Combined Arrow Optimization -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -For best results, combine both parameters: - -.. code-block:: python - - # Dense data with many small vectors - viz = LiveNeuro( - arrow_scale=0.7, - arrow_threshold='auto' - ) - - # Sparse data with clear patterns - viz = LiveNeuro( - arrow_scale=1.5, - arrow_threshold=0.05 - ) - -Color Mapping -------------- - -Built-in Colormaps -^^^^^^^^^^^^^^^^^^ - -LiveNeuro supports Plotly's built-in colorscales: - -.. code-block:: python - - # Sequential colormaps - viz = LiveNeuro(cmap='YlOrRd') # Yellow-Orange-Red (default) - viz = LiveNeuro(cmap='Hot') # Hot (red/orange/yellow) - viz = LiveNeuro(cmap='Viridis') # Perceptually uniform - viz = LiveNeuro(cmap='OrRd') # Orange-Red - viz = LiveNeuro(cmap='Reds') # Red scale - -See https://plotly.com/python/builtin-colorscales/ for all options. - -Custom Colormaps -^^^^^^^^^^^^^^^^ - -Create custom colorscales with transparency: - -.. code-block:: python - - # Custom gradient with transparency - custom_cmap = [ - [0, 'rgba(255,255,0,0.5)'], # Yellow, 50% transparent - [0.5, 'rgba(255,165,0,0.8)'], # Orange, 80% opaque - [1, 'rgba(255,0,0,1.0)'] # Red, fully opaque - ] - - viz = LiveNeuro(cmap=custom_cmap) - -**Color Range:** - -* Automatically scaled to data range (min to max) -* Consistent across all views and time points -* Unified colorbar shows current scaling -* Override manually with ``vmin`` / ``vmax`` to lock the range - -.. code-block:: python - - viz = LiveNeuro(vmin=-2.0, vmax=2.0) + Examples in this guide assume notebook use, where + :meth:`liveneuro.LiveNeuro.run` displays the app inline. See + :ref:`run-modes` for browser, JupyterLab, and fixed-port options. -Activity Time Course Plot Modes --------------------- +.. _quick-start: -Full Mode (Default) -^^^^^^^^^^^^^^^^^^^ +Quick Start +----------- -Shows a sampled set of source traces plus statistics (random subset for readability): +Start with the built-in sample data to check that the app opens correctly: .. code-block:: python - viz = LiveNeuro(show_max_only=False) - -**Displays:** - -* Random subset of source activity traces -* Mean activity -* Maximum activity across all sources - -Simplified Mode -^^^^^^^^^^^^^^^ - -Shows only summary statistics: - -.. code-block:: python - - viz = LiveNeuro(show_max_only=True) - -**Displays:** - -* Mean activity -* Maximum activity - -**When to use:** - -* You want a clean summary without individual traces -* Presentations or screenshots where mean/max is enough - -Interactive Features --------------------- - -Hover Information -^^^^^^^^^^^^^^^^^ - -**Brain Plots:** - -* Hover over any voxel to see activity value -* Shows magnitude at current time point - -**Detailed activity time course:** - -* Hover over time axis to see max and min activity -* Displays values for all visible traces - -Time Navigation -^^^^^^^^^^^^^^^ - -**Click to Navigate:** - -* Click anywhere on the time-course axis -* Brain views instantly update to that time point -* Precise time selection with spike indicator - - -Zoom and Pan -^^^^^^^^^^^^ - -**Mouse Controls:** - -* **Mouse Wheel**: Zoom in/out -* **Click + Drag**: Box zoom (default drag mode) -* **Pan**: Select the pan tool in the toolbar, then drag to move the view -* **Double-click**: Reset to original view - - -Data Input ----------- - -Using Built-in MNE Sample Data -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Simplest option for testing and learning: - -.. code-block:: python - - # Use built-in MNE sample data - viz = LiveNeuro(y=None) - -**Characteristics:** - -* 1589 sources in volumetric source space -* 76 time points (-100ms to 400ms) -* Vector data (3D current dipoles) -* Requires Eelbrain installed + from liveneuro import LiveNeuro -Using Eelbrain NDVar -^^^^^^^^^^^^^^^^^^^^^ + viz = LiveNeuro() + viz.run() -For your own data: +Use :meth:`liveneuro.LiveNeuro.export_images` when you want static output: .. code-block:: python - from eelbrain import datasets - from liveneuro import LiveNeuro - - # Load Eelbrain data - data_ds = datasets.get_mne_sample(src='vol', ori='vector') - y = data_ds['src'] # NDVar with dimensions (case, time, source, space) - - # Visualize - viz = LiveNeuro(y=y) - -**Expected Dimensions:** + viz.export_images(output_dir="./plots", time_idx=30) -* Vector data: ``([case,] time, source, space)`` -* Scalar data: ``([case,] time, source)`` -* If case dimension present: mean is computed automatically +.. _mne-data-input: -Using MNE VolVectorSourceEstimate -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Using MNE Data +-------------- -For MNE volume vector source estimates, pass the source estimate together with -the matching source space. The source estimate provides vector data and time -values; the source space provides the 3D coordinates needed for projections. +For MNE users, the most direct input is a volume vector source estimate together +with the matching source space. The source estimate supplies the data and time +values; the source space supplies the 3D coordinates needed for projection. .. code-block:: python @@ -421,7 +59,6 @@ values; the source space provides the 3D coordinates needed for projections. baseline=(None, 0), proj=True, ) - inverse_operator = read_inverse_operator( data_dir / "sample_audvis-meg-vol-7-meg-inv.fif" ) @@ -437,239 +74,287 @@ values; the source space provides the 3D coordinates needed for projections. ) viz = LiveNeuro(y=stc, src=src) + viz.run() +``src`` is required for MNE source estimates. LiveNeuro needs source-space +coordinates for the 2D projections, while the MNE source estimate stores vertex +ids and data values. Scalar MNE :class:`mne.VolSourceEstimate` objects are not +currently accepted directly; use a vector +:class:`mne.VolVectorSourceEstimate` or convert scalar source data to an +Eelbrain :class:`eelbrain.NDVar`. -Running the Application ------------------------ +.. _eelbrain-data-input: -Browser Mode -^^^^^^^^^^^^^^^^^^^^^^^ +Using Eelbrain Data +------------------- -Opens in external browser: +LiveNeuro also accepts Eelbrain :class:`eelbrain.NDVar` objects: .. code-block:: python - viz = LiveNeuro() - viz.run(mode="external") # Random port; defaults to inline in notebooks, external browser otherwise - - # Or specify port - viz.run(port=8888, mode="external") # Force browser if inline is detected - - -Jupyter Modes (Default) -^^^^^^^^^^^^^ + from eelbrain import datasets + from liveneuro import LiveNeuro -Multiple options for Jupyter notebooks: + data = datasets.get_mne_sample(src="vol", ori="vector") + y = data["src"] -.. code-block:: python - - # Inline display (embedded in notebook) - auto size + viz = LiveNeuro(y=y) viz.run() - # JupyterLab tab (opens in separate tab) - viz.run(mode='jupyterlab') +Expected dimensions are ``([case,] time, source, space)`` for vector data and +``([case,] time, source)`` for scalar data. If a case dimension is present, +LiveNeuro plots the case average. +Concepts +-------- -Exporting Images ----------------- +Source estimates + LiveNeuro visualizes source activity over time. For MNE inputs, pass a + volume vector source estimate such as + :class:`mne.VolVectorSourceEstimate`. -Basic Export -^^^^^^^^^^^^ +Source spaces + The matching MNE :class:`mne.SourceSpaces` object provides the source + coordinates. This is why MNE inputs use ``LiveNeuro(y=stc, src=src)``. -Export current view as static image: +Vector and scalar data + Vector data has a 3D orientation at each source and time point. LiveNeuro + shows its magnitude as color and its orientation as arrows. Supported scalar + :class:`eelbrain.NDVar` data is shown as magnitude only. -.. code-block:: python +Time course and projections + The time-course plot summarizes activity across sources. Clicking a time + point updates each brain projection to that time. - result = viz.export_images( - output_dir="./images", - time_idx=30, - format="png" - ) +.. _run-modes: - if result["status"] == "success": - for plot_type, filepath in result["files"].items(): - print(f"{plot_type}: {filepath}") +Running The Application +----------------------- -**Exports:** +:meth:`liveneuro.LiveNeuro.run` starts the Dash application. The common modes are: -* All brain projection views -* Activity Time Course plot -* Separate files for each +.. list-table:: + :header-rows: 1 -Supported Formats -^^^^^^^^^^^^^^^^^ + * - Mode + - Use + - Example + * - ``external`` + - Browser tab from a script, shell, or notebook. + - ``viz.run(mode="external")`` + * - ``inline`` + - Embedded output in a notebook. + - ``viz.run(mode="inline")`` + * - ``jupyterlab`` + - Separate JupyterLab tab. + - ``viz.run(mode="jupyterlab")`` -.. code-block:: python +Use a fixed port when you need a predictable URL: - # PNG (default, best for presentations) - viz.export_images(format="png") +.. code-block:: python - # JPEG (smaller file size) - viz.export_images(format="jpg") + viz.run(port=8888, mode="external") - # SVG (vector, scalable) - viz.export_images(format="svg") +.. _display-modes: - # PDF (publication quality) - viz.export_images(format="pdf") +Display Modes +------------- -Performance Optimization ------------------------- +``display_mode`` controls which anatomical projections are shown and in what +order. The default is ``"lyr"``: left hemisphere, coronal, right hemisphere. + +.. list-table:: + :header-rows: 1 + + * - Value + - Views + - Typical use + * - ``"lyr"`` + - Left, coronal, right + - Default hemisphere comparison. + * - ``"lr"`` + - Left and right + - Compact hemisphere view. + * - ``"ortho"`` + - Sagittal, coronal, axial + - MNE-style orthogonal overview. + * - ``"x"``, ``"y"``, ``"z"`` + - One sagittal, coronal, or axial view + - Focused inspection. + * - ``"lyrz"`` or ``"lzry"`` + - Four views + - Broader anatomical coverage. + +Letters determine display order, so ``"xz"`` and ``"zx"`` use the same two +views in a different order. + +.. _layout-modes: -For Large Datasets -^^^^^^^^^^^^^^^^^^ +Layout Modes +------------ -1. **Use arrow threshold**: +``layout_mode`` controls where the time course appears relative to the brain +views. The implementation default is ``"horizontal"``. - .. code-block:: python +.. list-table:: + :header-rows: 1 - viz = LiveNeuro(arrow_threshold='auto') + * - Value + - Layout + - Good for + * - ``"horizontal"`` + - Time course on the left, brain views on the right. + - Wide screens, notebooks, and side-by-side comparison. + * - ``"vertical"`` + - Time course above the brain views. + - More horizontal space per brain projection. -2. **Simplify detailed activity time course**: +Example: - .. code-block:: python +.. code-block:: python - viz = LiveNeuro(show_max_only=True) + viz = LiveNeuro(display_mode="lyrz", layout_mode="horizontal") -3. **Use focused display modes**: +.. _visual-controls: - .. code-block:: python +Visual Controls +--------------- - viz = LiveNeuro(display_mode="lr") # Fewer views +Arrow controls +^^^^^^^^^^^^^^ -Combined Optimization -^^^^^^^^^^^^^^^^^^^^^ +For vector data, arrows show orientation and color shows magnitude. +``arrow_scale`` changes arrow length; ``arrow_threshold`` hides lower-magnitude +vectors. .. code-block:: python - # Optimized for large dataset viz = LiveNeuro( - display_mode="lr", # Only 2 views - layout_mode="horizontal", # Better layout - arrow_scale=0.7, # Smaller arrows - arrow_threshold='auto', # Filter weak vectors - show_max_only=True, # Simplified time course - cmap='Hot' + arrow_scale=0.7, + arrow_threshold="auto", ) -Best Practices --------------- +Use ``arrow_threshold="auto"`` to hide vectors below 10% of the maximum +magnitude. Use ``None`` when you want to see every vector. -For Presentations -^^^^^^^^^^^^^^^^^ +Color mapping +^^^^^^^^^^^^^ -.. code-block:: python +``cmap`` accepts Plotly colorscale names or custom colorscale lists: - viz = LiveNeuro( - display_mode="lyr", # Standard comparison view - layout_mode="horizontal", # Wide screen friendly - arrow_scale=1.2, # Slightly larger arrows - arrow_threshold='auto', # Clean visualization - show_max_only=True, # Focus on patterns - cmap='Hot' # High contrast - ) +.. code-block:: python - viz.run(mode='external') # Full screen + viz = LiveNeuro(cmap="Viridis") -For Publications -^^^^^^^^^^^^^^^^ +LiveNeuro uses a consistent color range across views and time points. Set +``vmin`` and ``vmax`` when you need fixed limits across figures: .. code-block:: python - viz = LiveNeuro( - display_mode="lyr", - arrow_scale=1.0, - arrow_threshold='auto', - cmap='YlOrRd' # Publication-friendly - ) + viz = LiveNeuro(vmin=-2.0, vmax=2.0) - # Export high-quality images - viz.export_images( - output_dir="./publication_figures", - time_idx=30, - format="pdf" # Vector format for publications - ) +See `Plotly built-in colorscales `_ +for available color names. + +Time-course detail +^^^^^^^^^^^^^^^^^^ -For Interactive Exploration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +By default, the time-course plot shows a sampled set of source traces plus mean +and maximum activity. Use ``show_max_only=True`` for a cleaner summary: .. code-block:: python - viz = LiveNeuro( - display_mode="lyrz", # Comprehensive views - layout_mode="horizontal", # Better for exploration - arrow_scale=1.0, - arrow_threshold=None, # See all vectors initially - show_max_only=False, # Full time-course information - cmap='Viridis' - ) + viz = LiveNeuro(show_max_only=True) - viz.run() # Enable debug with debug=True when troubleshooting +Interacting With The Visualization +---------------------------------- -Troubleshooting ---------------- +* Hover over brain projections to inspect activity at the current time point. +* Click the time-course plot to update all brain views. +* Use mouse wheel zoom, box zoom, pan, and double-click reset from the Plotly + toolbar. -Common Issues -^^^^^^^^^^^^^ +.. _export-images: -**Issue: Random port is inconvenient** +Exporting Images +---------------- -Solution: +:meth:`liveneuro.LiveNeuro.export_images` saves each brain projection and the +time-course plot as separate image files: .. code-block:: python - viz.run(port=8888) # Use fixed port + result = viz.export_images( + output_dir="./images", + time_idx=30, + format="png", + ) -**Issue: Arrows too dense or unclear** +Supported formats include ``"png"``, ``"jpg"``, ``"svg"``, and ``"pdf"``. +Kaleido is required for static export and is included in the package +dependencies. -Solution: +.. _performance: + +Performance +----------- + +For larger datasets, reduce the number of rendered traces and arrows before +reducing the data itself: .. code-block:: python viz = LiveNeuro( - arrow_scale=0.7, # Reduce size - arrow_threshold='auto' # Filter weak ones + display_mode="lr", + arrow_scale=0.7, + arrow_threshold="auto", + show_max_only=True, ) -**Issue: Detailed activity time course too cluttered** - -Solution: +These settings render fewer brain views, hide low-magnitude arrows, and keep the +time course focused on summary traces. -.. code-block:: python - - viz = LiveNeuro(show_max_only=True) +.. _troubleshooting: -**Issue: Slow performance** +Troubleshooting +--------------- -Solution: +Installation problems + See :ref:`installation-troubleshooting`. -.. code-block:: python +Missing or invalid ``src`` with MNE data + MNE source estimates store data and vertex ids, but LiveNeuro also needs the + matching source-space coordinates. Pass the source space used to create the + estimate: ``LiveNeuro(y=stc, src=src)``. See :ref:`mne-data-input`. - viz = LiveNeuro( - display_mode="lr", # Fewer views - arrow_threshold='auto', # Fewer arrows - show_max_only=True # Simpler time course - ) +Scalar MNE :class:`mne.VolSourceEstimate` input fails + LiveNeuro currently accepts MNE :class:`mne.VolVectorSourceEstimate` objects + directly, not scalar :class:`mne.VolSourceEstimate` objects. Convert scalar + source data to an Eelbrain :class:`eelbrain.NDVar` or use a vector source + estimate. -**Issue: Export fails** +Invalid ``display_mode`` or ``layout_mode`` + Check :ref:`display-modes` and :ref:`layout-modes` for supported values. -Check: +The browser URL changes on each run + Pass a fixed port, for example ``viz.run(port=8888, mode="external")``. -1. Output directory exists or can be created -2. Kaleido is installed (included in dependencies, upgrade if needed): ``pip install -U kaleido`` -3. Sufficient disk space +Arrows are too dense + Use ``arrow_threshold="auto"`` and reduce ``arrow_scale``. -Debug Mode -^^^^^^^^^^ +The time-course plot is too cluttered + Use ``show_max_only=True``. -Debug mode is off by default for a clean UI. Enable it when troubleshooting: +Export fails + Confirm that the output directory is writable and that Kaleido is installed + in the active environment. -.. code-block:: python +More diagnostic output + Run with ``debug=True``: - viz.run(debug=True) + .. code-block:: python -This provides: + viz.run(debug=True) -* Detailed console output -* Error tracebacks -* Performance information +For constructor parameters and method signatures, see +:class:`liveneuro.LiveNeuro` in the :doc:`api_reference`. diff --git a/src/liveneuro/_liveneuro.py b/src/liveneuro/_liveneuro.py index 0b9c47e..8b02248 100644 --- a/src/liveneuro/_liveneuro.py +++ b/src/liveneuro/_liveneuro.py @@ -2,8 +2,9 @@ LiveNeuro core module. This module provides the core LiveNeuro class, an interactive 2D visualization -interface for Eelbrain's NDVar and MNE source estimate data structures. It -transforms neuroscience data into explorable brain maps and time-series plots. +interface for Eelbrain's :class:`eelbrain.NDVar` and MNE source estimate data +structures. It transforms neuroscience data into explorable brain maps and +time-series plots. """ from __future__ import annotations @@ -33,8 +34,9 @@ class LiveNeuro: If ``y`` has a case dimension, the mean is plotted. If ``y`` has a space dimension, the norm is plotted. If None, uses MNE sample data for demonstration. - Pass an Eelbrain NDVar, an MNE ``VolVectorSourceEstimate`` with - ``src``, or the sample data object returned by + Pass an Eelbrain :class:`eelbrain.NDVar`, an MNE + :class:`mne.VolVectorSourceEstimate` with ``src``, or the sample data + object returned by :func:`liveneuro.create_sample_brain_data`. cmap Plotly colorscale for heatmaps. Can be: @@ -91,22 +93,25 @@ class LiveNeuro: 'Source 0', 'Source 1', etc.). If False, hides all titles and legends for a cleaner visualization. Default is False. src - Matching MNE SourceSpaces object when ``y`` is an - ``mne.VolVectorSourceEstimate``. Required for MNE source estimates because - the source estimate stores vertex ids while LiveNeuro needs 3D source - coordinates. + Matching MNE :class:`mne.SourceSpaces` object when ``y`` is an + :class:`mne.VolVectorSourceEstimate`. Required for MNE source estimates + because the source estimate stores vertex ids while LiveNeuro needs 3D + source coordinates. Notes ----- Expected input format - - For vector data: NDVar with dimensions ([case,] time, source, space) - - For scalar data: NDVar with dimensions ([case,] time, source) - - For MNE vector volume data: VolVectorSourceEstimate plus matching ``src`` + - For vector data: :class:`eelbrain.NDVar` with dimensions + ([case,] time, source, space) + - For scalar data: :class:`eelbrain.NDVar` with dimensions + ([case,] time, source) + - For MNE vector volume data: :class:`mne.VolVectorSourceEstimate` plus + matching ``src`` - If case dimension present: mean across cases is plotted - If space dimension present: norm across space is plotted for butterfly plot - - ``create_sample_brain_data`` returns a minimal NDVar-like object compatible - with the ``y`` parameter for quick demos + - :func:`liveneuro.create_sample_brain_data` returns a minimal NDVar-like + object compatible with the ``y`` parameter for quick demos """ def __init__( From 32269641b81d1d2325af6973cfeb59f8e3c40c60 Mon Sep 17 00:00:00 2001 From: Christian Brodbeck Date: Mon, 8 Jun 2026 13:53:14 -0400 Subject: [PATCH 2/3] CI: Cache mne_data for tests --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c4c1346..79e5ecb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -51,6 +51,12 @@ jobs: # Test that the package can be imported after installation python -c "from liveneuro import LiveNeuro, create_sample_brain_data; print('Package imports successful!')" + - name: Cache MNE datasets + uses: actions/cache@v3 + with: + path: ~/mne_data + key: mne-data + - name: Run fast tests (Linux) if: runner.os == 'Linux' run: | From fb9fe337d219447c004d62e5fbd90fc358c9a8da Mon Sep 17 00:00:00 2001 From: Christian Brodbeck Date: Mon, 8 Jun 2026 13:53:54 -0400 Subject: [PATCH 3/3] CI: bump action versions --- .github/workflows/test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79e5ecb..dd5297a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,12 +18,12 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Cache pip dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt', '**/pyproject.toml') }} @@ -52,7 +52,7 @@ jobs: python -c "from liveneuro import LiveNeuro, create_sample_brain_data; print('Package imports successful!')" - name: Cache MNE datasets - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/mne_data key: mne-data @@ -72,7 +72,7 @@ jobs: - name: Upload coverage reports to Codecov if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10' - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: unittests @@ -87,7 +87,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10"