首页 > 解决方案 > 如何使用“另存为”提示保存来自 interactive() 显示的 .svg 输出?

问题描述

我是一名艺术家,试图使用 NumPy 和 jupyter notebook 中的各种工具来围绕生成/程序设计。

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

标签: pythonmatplotlibsvgjupyter-notebookipywidgets

解决方案


发生了什么

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

但是plt.savefig,当它在内部调用(获取当前图形)时,期望有一个当前打开的图形,它plt.gcf要么抓取最近活动的图形,要么在没有活动图形的情况下创建一个新的空图形。

因此,当您不在函数中执行此操作时,该图在单元格完成执行之前不会关闭,因此plt.savefig能够找到该图。但是,当您移动到函数时,它不再能够找到当前图形。

对此有两个基本的解决方案。

解决方案

1. 全球fig

您可以将 figure 提升到全局范围并使用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 中工作,那么我建议您使用 ipympl 安装它pip install 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`


推荐阅读