Source code for src.canns.analyzer.plotting.jupyter_utils
"""Utilities for Jupyter notebook integration with matplotlib animations."""
from __future__ import annotations
[docs]
def is_jupyter_environment() -> bool:
"""
Detect if code is running in a Jupyter notebook environment.
Returns:
bool: True if running in Jupyter/IPython notebook, False otherwise.
"""
try:
# Check if IPython is available and we're in a notebook
from IPython import get_ipython
ipython = get_ipython()
if ipython is None:
return False
# Check if we're in a notebook environment (not just IPython terminal)
# ZMQInteractiveShell is used by Jupyter notebooks
# TerminalInteractiveShell is used by IPython terminal
shell_class = ipython.__class__.__name__
return shell_class == "ZMQInteractiveShell"
except (ImportError, AttributeError):
return False
[docs]
def display_animation_in_jupyter(animation, format: str = "jshtml"):
"""
Display a matplotlib animation in Jupyter notebook using HTML/JavaScript.
Args:
animation: matplotlib.animation.FuncAnimation object
format: Display format - 'jshtml' (default) or 'html5' (video tag)
Returns:
IPython.display.HTML object if successful, None otherwise
"""
try:
from IPython.display import HTML, display
if format == "html5":
# Use HTML5 video tag (requires ffmpeg or similar)
html_content = animation.to_html5_video()
else:
# Use JavaScript-based animation (default, no external dependencies)
html_content = animation.to_jshtml()
# Add autoplay functionality - automatically click the play button
autoplay_script = """
<script>
(function() {
var attemptAutoplay = function(attempts) {
if (attempts <= 0) {
console.log('Autoplay: Max attempts reached');
return;
}
try {
// Find all animation containers
var buttons = document.getElementsByClassName('anim-buttons');
if (buttons.length === 0) {
// Retry if animations not loaded yet
setTimeout(function() { attemptAutoplay(attempts - 1); }, 200);
return;
}
// Get the last animation (most recently added)
var lastButtons = buttons[buttons.length - 1];
var allButtons = lastButtons.getElementsByTagName('button');
// matplotlib jshtml controls layout (9 buttons in total):
// Button order: ... [3] = reverse play, ... [5] = forward play ...
// We want index 5 for forward playback
// Try to click the forward play button (index 5, the 6th button)
if (allButtons.length > 5) {
allButtons[5].click();
console.log('Autoplay: Clicked forward play button (index 5)');
return;
}
// Fallback: search for play button by content
for (var i = 0; i < allButtons.length; i++) {
var btn = allButtons[i];
var btnText = btn.getAttribute('title') || btn.textContent || '';
// Look for play (not reverse play)
if (btnText === 'Play' || btnText.includes('▶') && !btnText.includes('◄')) {
btn.click();
console.log('Autoplay: Found and clicked play button at index ' + i);
return;
}
}
console.log('Autoplay: Could not find play button');
} catch(e) {
console.log('Autoplay error:', e);
}
};
// Start autoplay with retries
setTimeout(function() { attemptAutoplay(5); }, 300);
})();
</script>
"""
html_content = html_content + autoplay_script
html_obj = HTML(html_content)
display(html_obj) # Actually display the animation in Jupyter
return html_obj
except ImportError as e:
print(f"Warning: Could not import IPython.display: {e}")
return None
except Exception as e:
print(f"Warning: Could not render animation in Jupyter: {e}")
return None