Testing ipywidgets and Music21

These are tests of widgets within Music21

import ipywidgets as widgets
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
@widgets.interact
def f(x=5):
    print(x)
 interactive(children=(IntSlider(value=5, description='x', max=15, min=-5), Output()), _dom_classes=('widget-in…
@widgets.interact(x=(0, 5))
def ff(x=5):
    print(x)
 interactive(children=(IntSlider(value=5, description='x', max=5), Output()), _dom_classes=('widget-interact',)…
@widgets.interact_manual(
    color=['blue', 'red', 'green'], lw=(1.0, 10.0))
def plot(freq=1.0, color='blue', lw=2, grid=True):
    t = np.linspace(-1.0, 1.0, 1000)
    fig, ax = plt.subplots(1, 1, figsize=(8, 6))
    ax.plot(t, np.sin(2 * np.pi * freq * t),
            lw=lw, color=color)
    ax.grid(grid)
 interactive(children=(FloatSlider(value=1.0, description='freq', max=3.0, min=-1.0), Dropdown(description='col…
freq_slider = widgets.FloatSlider(
    value=2.0,
    min=1.0,
    max=10.0,
    step=0.1,
    description='Frequency:',
    readout_format='.1f',
)
freq_slider
 FloatSlider(value=2.0, description='Frequency:', max=10.0, min=1.0, readout_format='.1f')

Create a Custom Widget

Older Jupyter provided require/define from RequireJS which gave access to Jupyter widgets. This is no longer the case, so we first need to install anywidget (since it’s not part of the music21 ecosystem):

%pip install anywidget
 Requirement already satisfied: anywidget in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (0.9.21)
 Requirement already satisfied: ipywidgets>=7.6.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from anywidget) (8.1.5)
 Requirement already satisfied: psygnal>=0.8.1 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from anywidget) (0.15.1)
 Requirement already satisfied: typing-extensions>=4.2.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from anywidget) (4.12.2)
 Requirement already satisfied: comm>=0.1.3 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipywidgets>=7.6.0->anywidget) (0.2.2)
 Requirement already satisfied: ipython>=6.1.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipywidgets>=7.6.0->anywidget) (8.29.0)
 Requirement already satisfied: traitlets>=4.3.1 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipywidgets>=7.6.0->anywidget) (5.14.3)
 Requirement already satisfied: widgetsnbextension~=4.0.12 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipywidgets>=7.6.0->anywidget) (4.0.13)
 Requirement already satisfied: jupyterlab-widgets~=3.0.12 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipywidgets>=7.6.0->anywidget) (3.0.13)
 Requirement already satisfied: decorator in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (5.1.1)
 Requirement already satisfied: jedi>=0.16 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.19.2)
 Requirement already satisfied: matplotlib-inline in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.1.7)
 Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (3.0.48)
 Requirement already satisfied: pygments>=2.4.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (2.18.0)
 Requirement already satisfied: stack-data in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.6.3)
 Requirement already satisfied: pexpect>4.3 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (4.9.0)
 Requirement already satisfied: parso<0.9.0,>=0.8.4 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.8.4)
 Requirement already satisfied: ptyprocess>=0.5 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.7.0)
 Requirement already satisfied: wcwidth in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.2.13)
 Requirement already satisfied: executing>=1.2.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from stack-data->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (2.1.0)
 Requirement already satisfied: asttokens>=2.1.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from stack-data->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (2.4.1)
 Requirement already satisfied: pure-eval in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from stack-data->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (0.2.3)
 Requirement already satisfied: six>=1.12.0 in /Users/cuthbert/Git/music21base/venv/lib/python3.13/site-packages (from asttokens>=2.1.0->stack-data->ipython>=6.1.0->ipywidgets>=7.6.0->anywidget) (1.16.0)

 [notice] A new release of pip is available: 24.3.1 -> 25.3
 [notice] To update, run: pip install --upgrade pip
 Note: you may need to restart the kernel to use updated packages.

Now create a button that synchronizes Counter value with JS value

import anywidget
import traitlets


class Counter(anywidget.AnyWidget):
    _esm = r'''
    export function render({ model, el }) {
        const bm = document.createElement('button');
        bm.textContent = '-';

        const bp = document.createElement('button');
        bp.textContent = '+';

        const span = document.createElement('span');
        span.style.marginLeft = '10px';
        span.style.marginRight = '10px';

        const set_span = () => {
            span.textContent = String(model.get('value'));
        };

        bm.addEventListener('click', () => {
            const x = model.get('value');
            model.set('value', x - 1);
            model.save_changes();
        });

        bp.addEventListener('click', () => {
            const x = model.get('value');
            model.set('value', x + 1);
            model.save_changes();
        });

        model.on('change:value', set_span);

        el.appendChild(bm);
        el.appendChild(span);
        el.appendChild(bp);

        set_span();
    }
    '''
    value = traitlets.Int(0).tag(sync=True)


c = Counter()
c
 <__main__.Counter object at 0x109c752b0>

This number here will change (when reevaluated) with Counter’s value changing above

c.value
 4
def a_w(min=0, max=100):
    @widgets.interact(x=(min,max))
    def f(x=50):
        print(x)
a_w(20, 60)
 interactive(children=(IntSlider(value=50, description='x', max=60, min=20), Output()), _dom_classes=('widget-i…
def a_w(min=0, max=100):
    @widgets.interact(x=(min,max))
    def f(x=(min+max)//2):
        print(x)
a_w(10, 20)
 interactive(children=(IntSlider(value=15, description='x', max=20, min=10), Output()), _dom_classes=('widget-i…