如何保存带有 "Save As" 提示的 interactive() 显示的 .svg 输出?

How can I save the .svg output from an interactive() display with a "Save As" prompt?

我是一名艺术家,正在尝试使用 NumPy 和 jupyter notebook 中的各种工具进行 generative/procedural 设计。

我有一些代码 https://github.com/GreySoulX/Circle-generator/blob/main/BrokenCircles.ipynb(见下文)将生成一些随机半径的同心圆并将它们输出为 SVG 代码。我可以让它显示,我什至可以用基本代码得到 SVG 输出,但是当把它全部放在一个函数中并用 interactive() 调用它时,我保存的文件是空的,而不是我的笔记本中显示的widgets.VBox() .

我在哪里可以解决这个问题?我只是错过了一百万英里吗?

import numpy as np
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt
from IPython.display import display, Markdown, clear_output
from ipywidgets import widgets
from ipywidgets import interact, interact_manual, interactive, Button
def make_circles(n_circles=100,min_radius=0.05, max_radius=9.0, debug=False, Refresh=False ):

    x_bounds = [-10, 10]
    y_bounds = [-10, 10]
    circles = []

    for i in range(n_circles):
        c = np.array([0, 0])
        r = np.unique(np.sort(np.round(np.random.uniform(min_radius,max_radius,1),2)))
        circles.append((c, r))
    
    circles
    circle_patches = []
    for c, r in circles:
        circle_patches.append(mpatches.Circle(c, r, fill=None, edgecolor='black'))

    fig, ax = plt.subplots(figsize=(20, 20))
 
    if not debug:
        plt.grid(False)
        plt.axis('off')

    ax.set_aspect('equal')
    ax.set_xlim(x_bounds)
    ax.set_ylim(y_bounds)

    collection = PatchCollection(circle_patches, match_original=True)
    ax.add_collection(collection)

    return fig, ax
    
w = interactive(make_circles,
                n_circles=(1,300,1),
                min_radius=(0.00, 2.0, 0.1),
                max_radius=(2.0, 20, 0.5))

#------Button----------
button = widgets.Button(description='Save as...')
out = widgets.Output()

def on_button_clicked(_):
    #link function with output
    with out:
        #what happens when we hit button
        #clear_output()
        print('Saving Files...')
        plt.savefig('SomeFile.svg', bbox_inches = 'tight', pad_inches = 0)
        plt.savefig('SomeFile.png', bbox_inches = 'tight', pad_inches = 0)


# linking button and function together using a button's method
button.on_click(on_button_clicked)
# displaying Circles and button
widgets.VBox([button,out,w])
#------End Button------------

发生了什么事

这是内联后端如何处理结账数字以及 plt.savefig 内部处理的问题。静态图形在笔记本中的显示方式(即不使用 ipympl 时)是在单元格执行结束时(或在本例中为滑块回调结束时)当前图形关闭并显示。

然而,plt.savefig 期望有一个当前打开的图形,因为它在内部调用 plt.gcf(获取当前图形),它要么获取最近活动的图形,要么在没有图形时创建一个新的空图形活跃。

因此,当您不在函数中执行此操作时,直到单元格执行完毕,图形才会关闭,因此 plt.savefig 能够找到图形。但是,当您转到功能时,它不再能够找到当前图形。

对此有两种基本解决方案。

解决方案

1。全球 fig

您可以将图形提升到全局范围并使用 fig.savefig - 这确保绘图更新方法和保存方法都引用相同的 fig.

def make_circles(n_circles=100,min_radius=0.05, max_radius=9.0, debug=False, Refresh=False ):
    global fig
    ...
    fig, ax = plt.subplots()
    ....

def on_button_clicked(_):
    global fig
    ...
        fig.savefig('SomeFile.svg', bbox_inches = 'tight', pad_inches = 0)

2 - 交互式后端

使用交互式后端之一,例如 %matplotlib qt%matplotlib ipympl。如果你在笔记本或 jupyterlab 上工作,那么我推荐你可以使用 pip install ipympl.

安装的 ipympl

有了这些后端,图形的相同关闭不会自动发生,因此您可以像这样构建代码:

%matplotlib ipympl

fig, ax = plt.subplots()
def make_circles(n_circles=100,min_radius=0.05, max_radius=9.0, debug=False, Refresh=False ):
    ax.cla() # clear all the artists off the axes
    ...
    # use the same axes for `add_collection`

def on_button_clicked(_):
    # you can now use `plt.savefig` but I would still recommend using `fig.savefig`