Jupyter Notebook,配合 ipywidgets 的 interact是什么?

5 2025-11-29 11:18

在 Jupyter Notebook 里,「ipywidgets 的 interact」可以一句话把任何 Python 函数变成“可交互小面板”:
函数里有哪些参数,它就自动给你生成一组滑动条 / 输入框;
你拖动滑动条,函数会立即被重新调用,输出(打印值、画图、甚至 3D 曲面)实时更新。
整个过程不需要写 HTML/JS,也无需 Dash 后端,只要浏览器里跑着 Notebook 就能用。
下面给出最小可运行示例,并把它套到刚才的“三次多项式黎曼曲面”任务上,让你 30 秒内就能看到滑动条生效。

一、5 分钟上手 ipywidgets

  1. 安装(一次性)
    pip install ipywidgets

    JupyterLab 用户再执行:

    jupyter labextension install @jupyter-widgets/jupyterlab-manager
  2. 在 Notebook 新建一个 cell,输入:
Python
 
复制
import ipywidgets as wg
from IPython.display import display
import numpy as np
import matplotlib.pyplot as plt

def foo(a, b):
    x = np.linspace(0, 2*np.pi, 200)
    y = np.sin(a*x) * np.exp(-b*x)
    plt.figure(figsize=(4,2))
    plt.plot(x, y)
    plt.show()

wg.interact(foo, a=wg.FloatSlider(1, 0, 5, 0.1),
               b=wg.FloatSlider(0.3, 0, 1, 0.05));
 
运行后立刻得到两条滑动条:拖动 a/b,图实时重绘——这就是 interact。

二、把“三次黎曼曲面”搬进来

我们把前面的 build_riemann_surface 原样拿过来,再用 interact 把 4 个系数映射成滑动条即可。
下面代码直接粘进一个 Notebook cell 就能跑:
Python
 
复制
# Cell 1:导入
import numpy as np
import plotly.graph_objects as go
import ipywidgets as wg
from IPython.display import display

# 参数范围
RE_Z_RANGE = [-2, 2]
IM_Z_RANGE = [-2, 2]
N_GRID = 100          # 网格密度,笔记本上 100 足够流畅
HEIGHT_SCALE = 0.4

# Cell 2:求解函数(同前)
def solve_cubic(a3, a2, a1, a0, z):
    if abs(a3) < 1e-14:
        raise ValueError("a3==0")
    p = np.array([1, a2/a3, a1/a3, a0/a3], dtype=complex)
    roots = np.roots(p)
    idx = np.argsort(np.angle(roots))
    return roots[idx]

def build_riemann_surface(a3, a2, a1, a0):
    x = np.linspace(*RE_Z_RANGE, N_GRID)
    y = np.linspace(*IM_Z_RANGE, N_GRID)
    X, Y = np.meshgrid(x, y)
    Z = X + 1j*Y
    W1, W2, W3 = [np.zeros_like(Z) for _ in range(3)]
    for idx, z in np.ndenumerate(Z):
        try:
            w123 = solve_cubic(a3, a2, a1, a0, z)
            W1[idx], W2[idx], W3[idx] = w123
        except:
            W1[idx], W2[idx], W3[idx] = np.nan, np.nan, np.nan
    Z1 = W1.real + HEIGHT_SCALE*0
    Z2 = W2.real + HEIGHT_SCALE*1
    Z3 = W3.real + HEIGHT_SCALE*2
    return X, Y, Z1, Z2, Z3

# Cell 3:绘图函数
surface_fig = None   # 全局句柄,用来刷新
def plot_cubic_riemann(a3_r, a3_i, a2_r, a2_i,
                       a1_r, a1_i, a0_r, a0_i):
    global surface_fig
    a3 = complex(a3_r, a3_i)
    a2 = complex(a2_r, a2_i)
    a1 = complex(a1_r, a1_i)
    a0 = complex(a0_r, a0_i)
    X, Y, Z1, Z2, Z3 = build_riemann_surface(a3, a2, a1, a0)
    traces = []
    for k, Z in enumerate([Z1, Z2, Z3], 1):
        traces.append(
            go.Surface(x=X, y=Y, z=Z,
                       colorscale='Viridis',
                       showscale=False,
                       name=f'Leaf {k}')
        )
    if surface_fig is None:               # 第一次画
        surface_fig = go.FigureWidget(traces)
        surface_fig.update_layout(
            title='Cubic Riemann Surface',
            scene=dict(xaxis_title='Re(z)',
                       yaxis_title='Im(z)',
                       zaxis_title='Re(w)')
        )
        display(surface_fig)
    else:                                 # 更新
        with surface_fig.batch_update():
            for i, tr in enumerate(traces):
                surface_fig.data[i].x = tr.x
                surface_fig.data[i].y = tr.y
                surface_fig.data[i].z = tr.z

# Cell 4:一行 interact 搞定
wg.interact(
    plot_cubic_riemann,
    a3_r=wg.FloatSlider(1.0, -2, 2, 0.05, description='a3.r'),
    a3_i=wg.FloatSlider(0.0, -2, 2, 0.05, description='a3.i'),
    a2_r=wg.FloatSlider(0.0, -2, 2, 0.05, description='a2.r'),
    a2_i=wg.FloatSlider(0.0, -2, 2, 0.05, description='a2.i'),
    a1_r=wg.FloatSlider(0.0, -2, 2, 0.05, description='a1.r'),
    a1_i=wg.FloatSlider(0.0, -2, 2, 0.05, description='a1.i'),
    a0_r=wg.FloatSlider(0.0, -2, 2, 0.05, description='a0.r'),
    a0_i=wg.FloatSlider(0.0, -2, 2, 0.05, description='a0.i')
);
 
运行 Cell 4 后,Notebook 会弹出 8 条滑动条(4 个系数各自的实部/虚部),
第一次自动绘制 Plotly 3D 曲面,以后每拖一下滑动条,Python 后台会重新跑
build_riemann_surface → 更新 FigureWidget.data,曲面实时刷新,完成真正的“占位演示”。

三、常见坑

  1. 经典 Jupyter Notebook 与 JupyterLab 的 widgets 插件版本要对齐;
    如果滑动条没出来,按提示执行:
    jupyter nbextension enable --py widgetsnbextension
  2. 网格密度 N_GRID 太大(>150)时,每次重算可能 0.3~0.5 s,体感会卡;
    想更丝滑,可改用 continuous_update=False,让滑动条松手后才刷新:
    wg.FloatSlider(..., continuous_update=False)
  3. 如果更喜欢 Matplotlib 的 3D 曲面,只要把 plot_cubic_riemann 里的
    go.Surface 换成 ax.plot_surface 即可,interact 用法完全一样。
至此,你已经在 Jupyter Notebook 里用 ipywidgets 的 interact 实现了“拖动系数 → 黎曼曲面实时更新”的完整占位演示。

全部评论

·