initial commit

This commit is contained in:
2025-08-19 09:13:22 -07:00
parent 28464811d6
commit 0977a3e14d
820 changed files with 1003358 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
"""Jinja2 HTML templates."""
import lazy_loader as lazy
(__getattr__, __dir__, __all__) = lazy.attach_stub(__name__, __file__)

View File

@@ -0,0 +1,2 @@
__all__ = ["_get_html_template"]
from ._templates import _get_html_template

View File

@@ -0,0 +1,171 @@
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
from __future__ import annotations # only needed for Python ≤ 3.9
import datetime
import functools
import uuid
from dataclasses import dataclass
from typing import Any, Literal
from .._fiff.pick import channel_type
from ..defaults import _handle_default
_COLLAPSED = False # will override in doc build
def _format_number(value: int | float) -> str:
"""Insert thousand separators."""
return f"{value:,}"
def _append_uuid(string: str, sep: str = "-") -> str:
"""Append a UUID to a string."""
return f"{string}{sep}{uuid.uuid4()}"
def _data_type(obj) -> str:
"""Return the qualified name of a class."""
return obj.__class__.__qualname__
def _dt_to_str(dt: datetime.datetime) -> str:
"""Convert a datetime object to a human-readable string representation."""
return dt.strftime("%Y-%m-%d at %H:%M:%S %Z")
def _format_baseline(inst) -> str:
"""Format the baseline time period."""
if inst.baseline is None:
baseline = "off"
else:
baseline = (
f"{round(inst.baseline[0], 3):.3f} {round(inst.baseline[1], 3):.3f} s"
)
return baseline
def _format_metadata(inst) -> str:
"""Format metadata representation."""
if inst.metadata is None:
metadata = "No metadata set"
else:
metadata = f"{inst.metadata.shape[0]} rows × {inst.metadata.shape[1]} columns"
return metadata
def _format_time_range(inst) -> str:
"""Format evoked and epochs time range."""
tr = f"{round(inst.tmin, 3):.3f} {round(inst.tmax, 3):.3f} s"
return tr
def _format_projs(info) -> list[str]:
"""Format projectors."""
projs = [f'{p["desc"]} ({"on" if p["active"] else "off"})' for p in info["projs"]]
return projs
@dataclass
class _Channel:
"""A channel in a recording."""
index: int
name_html: str
type: str
type_pretty: str
status: Literal["good", "bad"]
def _format_channels(info) -> dict[str, dict[Literal["good", "bad"], list[str]]]:
"""Format channel names."""
ch_types_pretty: dict[str, str] = _handle_default("titles")
channels = []
if info.ch_names:
for ch_index, ch_name in enumerate(info.ch_names):
ch_type = channel_type(info, ch_index)
ch_type_pretty = ch_types_pretty.get(ch_type, ch_type.upper())
ch_status = "bad" if ch_name in info["bads"] else "good"
channel = _Channel(
index=ch_index,
name_html=ch_name.replace(" ", " "),
type=ch_type,
type_pretty=ch_type_pretty,
status=ch_status,
)
channels.append(channel)
# Extract unique channel types and put them in the desired order.
ch_types = list(set([c.type_pretty for c in channels]))
ch_types = [c for c in ch_types_pretty.values() if c in ch_types]
channels_formatted = {}
for ch_type in ch_types:
goods = [c for c in channels if c.type_pretty == ch_type and c.status == "good"]
bads = [c for c in channels if c.type_pretty == ch_type and c.status == "bad"]
if ch_type not in channels_formatted:
channels_formatted[ch_type] = {"good": [], "bad": []}
channels_formatted[ch_type]["good"] = goods
channels_formatted[ch_type]["bad"] = bads
return channels_formatted
def _has_attr(obj: Any, attr: str) -> bool:
"""Check if an object has an attribute `obj.attr`.
This is needed because on dict-like objects, Jinja2's `obj.attr is defined` would
check for `obj["attr"]`, which may not be what we want.
"""
return hasattr(obj, attr)
@functools.lru_cache(maxsize=2)
def _get_html_templates_env(kind):
# For _html_repr_() and mne.Report
assert kind in ("repr", "report"), kind
import jinja2
templates_env = jinja2.Environment(
loader=jinja2.PackageLoader(
package_name="mne.html_templates", package_path=kind
),
autoescape=jinja2.select_autoescape(default=True, default_for_string=True),
)
if kind == "report":
templates_env.filters["zip"] = zip
templates_env.filters["format_number"] = _format_number
templates_env.filters["append_uuid"] = _append_uuid
templates_env.filters["data_type"] = _data_type
templates_env.filters["dt_to_str"] = _dt_to_str
templates_env.filters["format_baseline"] = _format_baseline
templates_env.filters["format_metadata"] = _format_metadata
templates_env.filters["format_time_range"] = _format_time_range
templates_env.filters["format_projs"] = _format_projs
templates_env.filters["format_channels"] = _format_channels
templates_env.filters["has_attr"] = _has_attr
return templates_env
def _get_html_template(kind, name):
return _RenderWrap(
_get_html_templates_env(kind).get_template(name),
collapsed=_COLLAPSED,
)
class _RenderWrap:
"""Class that allows functools.partial-like wrapping of jinja2 Template.render()."""
def __init__(self, template, **kwargs):
self._template = template
self._kwargs = kwargs
def render(self, *args, **kwargs):
return self._template.render(*args, **kwargs, **self._kwargs)

View File

@@ -0,0 +1,8 @@
{% extends "section.html.jinja" %}
{% block html_content %}
<div class="row">
{{ html_slider_axial | safe }}
{{ html_slider_sagittal | safe }}
{{ html_slider_coronal | safe }}
</div>
{% endblock html_content %}

View File

@@ -0,0 +1,6 @@
{% extends "section.html.jinja" %}
{% block html_content %}
<pre class="my-0">
<code class="language-{{ language }}">{{ code }}</code>
</pre>
{% endblock html_content %}

View File

@@ -0,0 +1,10 @@
</div>
</div>
<footer>
<nav class="navbar fixed-bottom navbar-light bg-light border-top justify-content-center pt-0 pb-0 small">
<span>Created on {{ date }} via <a href="https://mne.tools" target="_blank">MNE-Python</a> {{ mne_version }}</span>
</nav>
</footer>
</body>
</html>

View File

@@ -0,0 +1,5 @@
{% extends "section.html.jinja" %}
{% block html_content %}
{{repr | safe}}
{{sensitivity_maps | safe}}
{% endblock html_content %}

View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="{{ lang | safe }}">
<head>
<meta charset="UTF-8">
{{include | safe }}
<script type="text/javascript">
{{ js | safe }}
</script>
<style type="text/css">
{{ css | safe }}
</style>
<title>{{ title }}</title>
</head>
<body data-bs-spy="scroll" data-bs-target="#toc-navbar" data-bs-offset="150">
<nav class="navbar fixed-top navbar-light bg-light shadow-sm" id="top-bar">
<div class="container-fluid">
<a class="navbar-brand d-flex align-items-center" href="#">
<img src="data:image/png;base64,{{ mne_logo_img }}" alt="MNE" width="80" class="d-inline-block">
<span class="mx-2 fs-3">{{ title }}</span>
</a>
<div class="btn-group" role="group" aria-label="Filter by tags" id="filter-by-tags-dropdown-menu">
<button class="btn btn-primary dropdown-toggle" type="button" id="show-hide-tags" data-bs-toggle="dropdown"
aria-expanded="false">
Filter by tags
</button>
<ul class="dropdown-menu dropdown-menu-end shadow-sm vh-100 overflow-auto" aria-labelledby="show-hide-tags">
<li>
<label class="dropdown-item" id="selectAllTagsCheckboxLabel">
<input class="form-check-input me-1" type="checkbox" value="" checked>
Select all
</label>
</li>
<li>
<hr class="dropdown-divider">
</li>
{% for tag in tags|sort %}
<li>
<label class="tag dropdown-item me-5" data-mne-tag="{{tag}}">
<input class="form-check-input me-1" type="checkbox" value="" checked>
{{ tag }}
<span class="badge bg-primary rounded-pill float-end me-1" data-mne-tag="{{tag}}"></span>
</label>
</li>
{% endfor %}
</ul>
</div>
</div>
</nav>

View File

@@ -0,0 +1,19 @@
<div class="accordion-item {{ div_klass }}" id="{{ id }}" data-mne-tags="{% for tag in tags %} {{ tag }} {% endfor %}">
<div class="accordion-header" id="accordion-header-{{id}}">
<button class="accordion-button pt-1 pb-1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-collapse-{{ id }}" aria-expanded="true" aria-controls="accordion-collapse-{{id}}">
<div class="w-100">
<span class="me-auto"><a href="#{{ id }}" class="text-decoration-none">{{ title }}</a></span>
{% for tag in tags %}
<span class="badge bg-primary rounded-pill float-end me-1" data-mne-tag="{{ tag }}">
{{ tag }}
</span>
{% endfor %}
</div>
</button>
</div>
<div id="accordion-collapse-{{ id }}" class="accordion-collapse collapse {{ show }}" aria-labelledby="accordion-header-{{ id }}">
<div class="accordion-body">
{{ html | safe }}
</div>
</div>
</div>

View File

@@ -0,0 +1,17 @@
{% extends "section.html.jinja" %}
{% block html_content %}
<figure class="figure mx-auto d-block">
{% if image_format == 'svg' %}
<div class="d-flex justify-content-center">
{{ img|safe }}
</div>
{% else %}
<img class="figure-img img-fluid rounded mx-auto my-0 d-block" alt="{{ title }}"
src="data:image/{{ image_format }};base64,{{ img }}">
{% endif %}
{% if caption is not none %}
<figcaption class="figure-caption text-center">{{ caption }}</figcaption>
{% endif %}
</figure>
{% endblock html_content %}

View File

@@ -0,0 +1,5 @@
{% extends "section.html.jinja" %}
{% block html_content %}
{{repr | safe}}
{{source_space | safe}}
{% endblock html_content %}

View File

@@ -0,0 +1,23 @@
<div class="accordion-item {{ div_klass }}" id="{{ id }}" data-mne-tags="{% for tag in tags %} {{ tag }} {% endfor %}">
<div class="accordion-header" id="accordion-header-{{id}}">
<button class="accordion-button pt-1 pb-1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-collapse-{{ id }}" aria-expanded="true" aria-controls="accordion-collapse-{{id}}">
<div class="w-100">
<span class="me-auto"><a href="#{{ id }}" class="text-decoration-none">{{ title }}</a></span>
{% for tag in tags %}
<span class="badge bg-primary rounded-pill float-end me-1" data-mne-tag="{{ tag }}">
{{ tag }}
</span>
{% endfor %}
</div>
</button>
</div>
<div id="accordion-collapse-{{ id }}" class="accordion-collapse collapse {{ show }}" aria-labelledby="accordion-header-{{ id }}">
<div class="accordion-body">
{% block html_content %}
{% for html in htmls %}
{{ html | safe }}
{% endfor %}
{% endblock %}
</div>
</div>
</div>

View File

@@ -0,0 +1,49 @@
<div class="accordion-item slider {{ klass }}" id="{{ id }}"
data-mne-tags="{% for tag in tags %} {{ tag }} {% endfor %}">
<div class="accordion-header" id="accordion-header-{{ id }}">
<button class="accordion-button pt-1 pb-1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-collapse-{{ id }}" aria-expanded="true" aria-controls="accordion-collapse-{{ id }}">
<div class="w-100">
<span class="me-auto"><a href="#{{ id }}" class="text-decoration-none">{{ title }}</a></span>
{% for tag in tags %}
<span class="badge bg-primary rounded-pill float-end me-1" data-mne-tag="{{ tag }}">
{{ tag }}
</span>
{% endfor %}
</div>
</button>
</div>
<div id="accordion-collapse-{{ id }}" class="accordion-collapse collapse {{ show }}" aria-labelledby="accordion-header-{{ id }}">
<div class=" accordion-body">
<div class="mx-auto d-block w-75">
<label for="slider-{{ id }}" class="form-label small">
Move the slider or click on the image and use the <kbd>←</kbd> and <kbd>→</kbd> keys to browse.
</label>
<input type="range" class="form-range" min="0" max="{{ images|length - 1 }}" value="{{ start_idx }}"
id="slider-{{id}}">
</div>
<div id="corousel-{{ id }}" class="carousel carousel-dark" data-bs-interval="false" data-bs-wrap="false" data-bs-keyboard="true">
<div class="carousel-inner">
{% for idx, img, caption in range(images|length)|zip(images, captions) %}
<div class="carousel-item {% if idx == start_idx %}active{% endif %}">
<figure class="figure mx-auto d-block ">
<img class="figure-img img-fluid rounded mx-auto my-0 d-block" alt="{{title}}"
src="data:image/{{ image_format }};base64,{{ img }}">
<figcaption class="figure-caption text-center">
{{ caption }}
</figcaption>
</figure>
</div>
{% endfor %}
</div>
{# <button class="carousel-control-prev" type="button" data-bs-target="#corousel-{{id}}" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#corousel-{{id}}" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button> #}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,12 @@
<div class="container-fluid" id="container">
<div class="row">
<div class="col-2 px-1 position-fixed vh-100 overflow-auto" id="toc">
<h5 class="px-1">Table of contents</h5>
<nav class="nav nav-pills flex-column lh-sm" id="toc-navbar">
{% for title, dom_id, tags_ in titles|zip(dom_ids, tags) %}
<a class="nav-link list-group-item list-group-item-action text-break" href="#{{ dom_id }}"
data-mne-tags="{% for tag in tags_ %} {{ tag }} {% endfor %}">{{ title }}</a>
{% endfor %}
</nav>
</div>
<div id="content" class="accordion col-10 offset-2">

View File

@@ -0,0 +1,97 @@
{% set section = "Acquisition" %}
{% set section_class_name = section | lower | append_uuid %}
{# Collapse content during documentation build. #}
{% if collapsed %}
{% set collapsed_row_class = "mne-repr-collapsed" %}
{% else %}
{% set collapsed_row_class = "" %}
{% endif %}
{%include 'static/_section_header_row.html.jinja' %}
{% if duration %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Duration</td>
<td>{{ duration }} (HH:MM:SS)</td>
</tr>
{% endif %}
{% if inst is defined and inst | has_attr("kind") and inst | has_attr("nave") %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Aggregation</td>
{% if inst.kind == "average" %}
<td>average of {{ inst.nave }} epochs</td>
{% elif inst.kind == "standard_error" %}
<td>standard error of {{ inst.nave }} epochs</td>
{% else %}
<td>{{ inst.kind }} ({{ inst.nave }} epochs)</td>
{% endif %}
</tr>
{% endif %}
{% if inst is defined and inst | has_attr("comment") %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Condition</td>
<td>{{inst.comment}}</td>
</tr>
{% endif %}
{% if inst is defined and inst | has_attr("events") %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Total number of events</td>
<td>{{ inst.events | length }}</td>
</tr>
{% endif %}
{% if event_counts is defined %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Events counts</td>
{% if events is not none %}
<td>
{% for e in event_counts %}
{{ e }}
{% if not loop.last %}<br />{% endif %}
{% endfor %}
</td>
{% else %}
<td>Not available</td>
{% endif %}
</tr>
{% endif %}
{% if inst is defined and inst | has_attr("tmin") and inst | has_attr("tmax") %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Time range</td>
<td>{{ inst | format_time_range }}</td>
</tr>
{% endif %}
{% if inst is defined and inst | has_attr("baseline") %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Baseline</td>
<td>{{ inst | format_baseline }}</td>
</tr>
{% endif %}
{% if info["sfreq"] is defined and info["sfreq"] is not none %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Sampling frequency</td>
<td>{{ "%0.2f" | format(info["sfreq"]) }} Hz</td>
</tr>
{% endif %}
{% if inst is defined and inst.times is defined %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Time points</td>
<td>{{ inst.times | length | format_number }}</td>
</tr>
{% endif %}
{% if inst is defined and inst | has_attr("metadata") %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Metadata</td>
<td>{{ inst | format_metadata }}</td>
</tr>
{% endif %}

View File

@@ -0,0 +1,25 @@
{% set section = "Channels" %}
{% set section_class_name = section | lower | append_uuid %}
{# Collapse content during documentation build. #}
{% if collapsed %}
{% set collapsed_row_class = "mne-repr-collapsed" %}
{% else %}
{% set collapsed_row_class = "" %}
{% endif %}
{%include 'static/_section_header_row.html.jinja' %}
{% for channel_type, channels in (info | format_channels).items() %}
{% include 'static/_channels.html.jinja' %}
{% endfor %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Head & sensor digitization</td>
{% if info["dig"] is not none %}
<td>{{ info["dig"] | length }} points</td>
{% else %}
<td>Not available</td>
{% endif %}
</tr>

View File

@@ -0,0 +1,38 @@
{% set section = "Filters" %}
{% set section_class_name = section | lower | append_uuid %}
{# Collapse content during documentation build. #}
{% if collapsed %}
{% set collapsed_row_class = "mne-repr-collapsed" %}
{% else %}
{% set collapsed_row_class = "" %}
{% endif %}
{%include 'static/_section_header_row.html.jinja' %}
{% if info["highpass"] is defined and info["highpass"] is not none %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Highpass</td>
<td>{{ "%0.2f" | format(info["highpass"]) }} Hz</td>
</tr>
{% endif %}
{% if info["lowpass"] is defined and info["lowpass"] is not none %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Lowpass</td>
<td>{{ "%0.2f" | format(info["lowpass"]) }} Hz</td>
</tr>
{% endif %}
{% if info.projs is defined and info.projs %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Projections</td>
<td>
{% for p in (info | format_projs) %}
{{ p }}
{% if not loop.last %}<br />{% endif %}
{% endfor %}
</td>
</tr>
{% endif %}

View File

@@ -0,0 +1,58 @@
{% set section = "General" %}
{% set section_class_name = section | lower | append_uuid %}
{# Collapse content during documentation build. #}
{% if collapsed %}
{% set collapsed_row_class = "mne-repr-collapsed" %}
{% else %}
{% set collapsed_row_class = "" %}
{% endif %}
{%include 'static/_section_header_row.html.jinja' %}
{% if filenames %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Filename(s)</td>
<td>
{% for f in filenames %}
{{ f }}
{% if not loop.last %}<br />{% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>MNE object type</td>
<td>{{ inst | data_type }}</td>
</tr>
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Measurement date</td>
{% if info["meas_date"] is defined and info["meas_date"] is not none %}
<td>{{ info["meas_date"] | dt_to_str }}</td>
{% else %}
<td>Unknown</td>
{% endif %}
</tr>
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Participant</td>
{% if info["subject_info"] is defined and info["subject_info"] is not none %}
{% if info["subject_info"]["his_id"] is defined %}
<td>{{ info["subject_info"]["his_id"] }}</td>
{% endif %}
{% else %}
<td>Unknown</td>
{% endif %}
</tr>
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Experimenter</td>
{% if info["experimenter"] is defined and info["experimenter"] is not none %}
<td>{{ info["experimenter"] }}</td>
{% else %}
<td>Unknown</td>
{% endif %}
</tr>

View File

@@ -0,0 +1,7 @@
<script type="text/javascript">
{% include 'static/repr.js' %}
</script>
<style type="text/css">
{% include 'static/repr.css' %}
</style>

View File

@@ -0,0 +1,10 @@
{%include '_js_and_css.html.jinja' %}
{% set info = inst.info %}
<table class="table mne-repr-table">
{%include '_general.html.jinja' %}
{%include '_acquisition.html.jinja' %}
{%include '_channels.html.jinja' %}
{%include '_filters.html.jinja' %}
</table>

View File

@@ -0,0 +1,10 @@
{%include '_js_and_css.html.jinja' %}
{% set info = inst.info %}
<table class="table mne-repr-table">
{%include '_general.html.jinja' %}
{%include '_acquisition.html.jinja' %}
{%include '_channels.html.jinja' %}
{%include '_filters.html.jinja' %}
</table>

View File

@@ -0,0 +1,29 @@
{%include '_js_and_css.html.jinja' %}
{% set section = "Forward" %}
{% set section_class_name = section | lower | append_uuid %}
{# Collapse content during documentation build. #}
{% if collapsed %}
{% set collapsed_row_class = "mne-repr-collapsed" %}
{% else %}
{% set collapsed_row_class = "" %}
{% endif %}
<table class="table mne-repr-table">
{%include 'static/_section_header_row.html.jinja' %}
{% for channel_type, channels in (info | format_channels).items() %}
{% include 'static/_channels.html.jinja' %}
{% endfor %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Source space</td>
<td>{{ source_space_descr }}</td>
</tr>
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>Source orientation</td>
<td>{{ source_orientation }}</td>
</tr>
</table>

View File

@@ -0,0 +1,32 @@
<table class="table mne-repr-table">
<tr>
<th>Method</th>
<td>{{ method }}</td>
</tr>
<tr>
<th>Fit parameters</th>
<td>{% if fit_params %}{% for key, value in fit_params.items() %}{{ key }}={{ value }}<br />{% endfor %}{% else %}&mdash;{% endif %}</td>
</tr>
<tr>
<th>Fit</th>
<td>{% if fit_on %}{{ n_iter }} iterations on {{ fit_on }} ({{ n_samples }} samples){% else %}no{% endif %}</td>
</tr>
{% if fit_on %}
<tr>
<th>ICA components</th>
<td>{{ n_components }}</td>
</tr>
<tr>
<th>Available PCA components</th>
<td>{{ n_pca_components }}</td>
</tr>
<tr>
<th>Channel types</th>
<td>{{ ch_types|join(', ') }}</td>
</tr>
<tr>
<th>ICA components marked for exclusion</th>
<td>{% if excludes %}{{ excludes|join('<br />' | safe) }}{% else %}&mdash;{% endif %}</td>
</tr>
{% endif %}
</table>

View File

@@ -0,0 +1,10 @@
{%include '_js_and_css.html.jinja' %}
{%set inst = info %}
<table class="table mne-repr-table">
{%include '_general.html.jinja' %}
{%include '_acquisition.html.jinja' %}
{%include '_channels.html.jinja' %}
{%include '_filters.html.jinja' %}
</table>

View File

@@ -0,0 +1,14 @@
<table class="table mne-repr-table">
<tr>
<th>Channels</th>
<td>{{ channels }}</td>
</tr>
<tr>
<th>Source space</th>
<td>{{ source_space_descr }}</td>
</tr>
<tr>
<th>Source orientation</th>
<td>{{ source_orientation }}</td>
</tr>
</table>

View File

@@ -0,0 +1,10 @@
{%include '_js_and_css.html.jinja' %}
{% set info = inst.info %}
<table class="table mne-repr-table">
{%include '_general.html.jinja' %}
{%include '_acquisition.html.jinja' %}
{%include '_channels.html.jinja' %}
{%include '_filters.html.jinja' %}
</table>

View File

@@ -0,0 +1,50 @@
<table class="table mne-repr-table">
<tr>
<th>Data type</th>
<td>{{ spectrum._data_type }}</td>
</tr>
{%- for unit in units %}
<tr>
{%- if loop.index == 1 %}
<th rowspan={{ units | length }}>Units</th>
{%- endif %}
<td class="justify">{{ unit }}</td>
</tr>
{%- endfor %}
<tr>
<th>Data source</th>
<td>{{ inst_type }}</td>
</tr>
{%- if inst_type == "Epochs" %}
<tr>
<th>Number of epochs</th>
<td>{{ spectrum.shape[0] }}</td>
</tr>
{% endif -%}
<tr>
<th>Dims</th>
<td>{{ spectrum._dims | join(", ") }}</td>
</tr>
<tr>
<th>Estimation method</th>
<td>{{ spectrum.method }}</td>
</tr>
{% if "taper" in spectrum._dims %}
<tr>
<th>Number of tapers</th>
<td>{{ spectrum._mt_weights.size }}</td>
</tr>
{% endif %}
<tr>
<th>Number of channels</th>
<td>{{ spectrum.ch_names|length }}</td>
</tr>
<tr>
<th>Number of frequency bins</th>
<td>{{ spectrum.freqs|length }}</td>
</tr>
<tr>
<th>Frequency range</th>
<td>{{ '%.2f'|format(spectrum.freqs[0]) }} {{ '%.2f'|format(spectrum.freqs[-1]) }} Hz</td>
</tr>
</table>

View File

@@ -0,0 +1,17 @@
{% set channel_names_good = channels["good"] | map(attribute='name_html') | join(', ') %}
<tr class="repr-element {{ section_class_name }} {{ collapsed_row_class }}">
<td class="mne-repr-section-toggle"></td>
<td>{{ channel_type }}</td>
<td>
<button class="mne-ch-names-btn sd-sphinx-override sd-btn sd-btn-info sd-text-wrap sd-shadow-sm" onclick="alert('Good {{ channel_type}}:\n\n{{ channel_names_good | safe }}')" title="(Click to open in popup)&#13;&#13;{{ channel_names_good | safe }}">
{{ channels["good"] | length}}
</button>
{% if channels["bad"] %}
{% set channel_names_bad = channels["bad"] | map(attribute='name_html') | join(', ') %}
and <button class="mne-ch-names-btn sd-sphinx-override sd-btn sd-btn-info sd-text-wrap sd-shadow-sm" onclick="alert('Bad {{ channel_type}}:\n\n{{ channel_names_bad | safe }}')" title="(Click to open in popup)&#13;&#13;{{ channel_names_bad | safe }}">
{{ channels["bad"] | length}} bad
</button>
{% endif %}
</td>
</tr>

View File

@@ -0,0 +1,12 @@
<tr class="mne-repr-section-header {{ section_class_name }}"
{% if collapsed %} title="Show section" {% else %} title="Hide section" {% endif %}
onclick="toggleVisibility('{{ section_class_name }}')">
<th class="mne-repr-section-toggle">
<button {% if collapsed %}class="collapsed"{% endif %}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
</button>
</th>
<th colspan="2">
<strong>{{ section }}</strong>
</th>
</tr>

View File

@@ -0,0 +1,107 @@
/*
Styles in this section apply both to the sphinx-built website docs and to notebooks
rendered in an IDE or in Jupyter. In our web docs, styles here are complemented by
doc/_static/styles.css and other CSS files (e.g. from the sphinx theme, sphinx-gallery,
or bootstrap). In IDEs/Jupyter, those style files are unavailable, so only the rules in
this file apply (plus whatever default styling the IDE applies).
*/
.mne-repr-table {
display: inline; /* prevent using full container width */
}
.mne-repr-table tr.mne-repr-section-header > th {
padding-top: 1rem;
text-align: left;
vertical-align: middle;
}
.mne-repr-section-toggle > button {
all: unset;
display: block;
height: 1rem;
width: 1rem;
}
.mne-repr-section-toggle > button > svg {
height: 60%;
}
/* transition (rotation) effects on the collapser button */
.mne-repr-section-toggle > button.collapsed > svg {
transition: 0.1s ease-out;
transform: rotate(-90deg);
}
.mne-repr-section-toggle > button:not(.collapsed) > svg {
transition: 0.1s ease-out;
transform: rotate(0deg);
}
/* hide collapsed table rows */
.mne-repr-collapsed {
display: none;
}
@layer {
/*
Selectors in a `@layer` will always be lower-precedence than selectors outside the
layer. So even though e.g. `div.output_html` is present in the sphinx-rendered
website docs, the styles here won't take effect there as long as some other rule
somewhere in the page's CSS targets the same element.
In IDEs or Jupyter notebooks, though, the CSS files from the sphinx theme,
sphinx-gallery, and bootstrap are unavailable, so these styles will apply.
Notes:
- the selector `.accordion-body` is for MNE Reports
- the selector `.output_html` is for VSCode's notebook interface
- the selector `.jp-RenderedHTML` is for Jupyter notebook
- variables starting with `--theme-` are VSCode-specific.
- variables starting with `--jp-` are Jupyter styles, *some of which* are also
available in VSCode. Here we try the `--theme-` variable first, then fall back to
the `--jp-` ones.
*/
.mne-repr-table {
--mne-toggle-color: var(--theme-foreground, var(--jp-ui-font-color1));
--mne-button-bg-color: var(--theme-button-background, var(--jp-info-color0, var(--jp-content-link-color)));
--mne-button-fg-color: var(--theme-button-foreground, var(--jp-ui-inverse-font-color0, var(--jp-editor-background)));
--mne-button-hover-bg-color: var(--theme-button-hover-background, var(--jp-info-color1));
--mne-button-radius: var(--jp-border-radius, 0.25rem);
}
/* chevron position/alignment; in VSCode it looks ok without adjusting */
.accordion-body .mne-repr-section-toggle > button,
.jp-RenderedHTML .mne-repr-section-toggle > button {
padding: 0 0 45% 25% !important;
}
/* chevron color; MNE Report doesn't have light/dark mode */
div.output_html .mne-repr-section-toggle > button > svg > path,
.jp-RenderedHTML .mne-repr-section-toggle > button > svg > path {
fill: var(--mne-toggle-color);
}
.accordion-body .mne-ch-names-btn,
div.output_html .mne-ch-names-btn,
.jp-RenderedHTML .mne-ch-names-btn {
-webkit-border-radius: var(--mne-button-radius);
-moz-border-radius: var(--mne-button-radius);
border-radius: var(--mne-button-radius);
border: none;
background-image: none;
background-color: var(--mne-button-bg-color);
color: var(--mne-button-fg-color);
font-size: inherit;
min-width: 1.5rem;
padding: 0.25rem;
text-align: center;
text-decoration: none;
}
.accordion-body .mne-ch-names-btn:hover,
div.output_html .mne.ch-names-btn:hover,
.jp-RenderedHTML .mne-ch-names-btn:hover {
background-color: var(--mne-button-hover-bg-color);
text-decoration: underline;
}
.accordion-body .mne-ch-names-btn:focus-visible,
div.output_html .mne-ch-names-btn:focus-visible,
.jp-RenderedHTML .mne-ch-names-btn:focus-visible {
outline: 0.1875rem solid var(--mne-button-bg-color) !important;
outline-offset: 0.1875rem !important;
}
}

View File

@@ -0,0 +1,23 @@
// must be `var` (not `const`) because this can get embedded multiple times on a page
var toggleVisibility = (className) => {
const elements = document.querySelectorAll(`.${className}`);
elements.forEach(element => {
if (element.classList.contains("mne-repr-section-header")) {
return // Don't collapse the section header row
}
element.classList.toggle("mne-repr-collapsed");
});
// trigger caret to rotate
var sel = `.mne-repr-section-header.${className} > th.mne-repr-section-toggle > button`;
const button = document.querySelector(sel);
button.classList.toggle("collapsed");
// adjust tooltip
sel = `tr.mne-repr-section-header.${className}`;
const secHeadRow = document.querySelector(sel);
secHeadRow.classList.toggle("collapsed");
secHeadRow.title = secHeadRow.title === "Hide section" ? "Show section" : "Hide section";
}

View File

@@ -0,0 +1,60 @@
<table class="table mne-repr-table">
<tr>
<th>Data type</th>
<td>{{ tfr._data_type }}</td>
</tr>
{%- for unit in units %}
<tr>
{%- if loop.index == 1 %}
<th rowspan={{ units | length }}>Units</th>
{%- endif %}
<td class="justify">{{ unit }}</td>
</tr>
{%- endfor %}
<tr>
<th>Data source</th>
<td>{{ inst_type }}</td>
</tr>
{%- if inst_type == "Epochs" %}
<tr>
<th>Number of epochs</th>
<td>{{ tfr.shape[0] }}</td>
</tr>
{% endif -%}
{%- if inst_type == "Evoked" %}
<tr>
<th>Number of averaged trials</th>
<td>{{ nave }}</td>
</tr>
{% endif -%}
<tr>
<th>Dims</th>
<td>{{ tfr._dims | join(", ") }}</td>
</tr>
<tr>
<th>Estimation method</th>
<td>{{ tfr.method }}</td>
</tr>
{% if "taper" in tfr._dims %}
<tr>
<th>Number of tapers</th>
<td>{{ tfr._mt_weights.size }}</td>
</tr>
{% endif %}
<tr>
<th>Number of channels</th>
<td>{{ tfr.ch_names|length }}</td>
</tr>
<tr>
<th>Number of timepoints</th>
<td>{{ tfr.times|length }}</td>
</tr>
<tr>
<th>Number of frequency bins</th>
<td>{{ tfr.freqs|length }}</td>
</tr>
<tr>
<th>Frequency range</th>
<td>{{ '%.2f'|format(tfr.freqs[0]) }} {{ '%.2f'|format(tfr.freqs[-1]) }} Hz</td>
</tr>
</table>