1

I am trying to create a button from ipywidgets.Button instance that will save a specific figure once it's pressed. But using the following Ipython notebook leads to multiple file savings once the button has been hit:

import numpy as np
from ipywidgets import interact, interactive,FloatSlider,IntSlider,Button
from IPython.display import display
import matplotlib.pylab as plt
%matplotlib inline
button = Button(description="Savefig")
display(button)
def plotfields(p,a):
    fig,ax = plt.subplots(1,2,figsize=(4*2,4),sharey=True)
    X,Y=np.meshgrid(np.linspace(0,100,100),np.linspace(0,100,100))
    b1 = np.sin(p*X)*np.cos(a*Y)
    b2 = np.sin(a*X)*np.cos(p*Y)
    ax[0].imshow(b1,aspect='auto')
    ax[1].imshow(b2,aspect='auto')
    ax[0].set_ylabel(r'$t$')
    ax[0].set_xlabel(r'$x$')
    ax[1].set_xlabel(r'$x$')
    plt.tight_layout()
    def on_button_clicked(b):
        fig.savefig("test_p{:.3s}_a{:.3s}.eps".format(str(int(100*p)),str(int(100*a))))
        fig.savefig("test_p{:.3s}_a{:.3s}.png".format(str(int(100*p)),str(int(100*a))))
    button.on_click(on_button_clicked)

p_w  = FloatSlider(min=0.01, max=1, step=0.01, value=0.01)
a_w  = FloatSlider(min=0.01, max=1, step=0.01, value=0.01)
interact(plotfields,p=p_w,a=a_w)

1 Answer 1

3

The problem is that you create one callback for each time the plotfields function is called; and it is called a lot of times, everytime one of the sliders is moved.
It would therefore make sense to move the button.on_click(on_button_clicked) outside of the repeatedly called function.
I would then use display to display the figure.

%%capture
import numpy as np
from ipywidgets import interact, interactive,FloatSlider,IntSlider,Button
from IPython.display import display
import matplotlib.pylab as plt
%matplotlib inline
button = Button(description="Savefig")
display(button)

params = [0.01,0.01]

def init():
    fig,ax = plt.subplots(1,2,figsize=(4*2,4),sharey=True)
    X,Y=np.meshgrid(np.linspace(0,100,100),np.linspace(0,100,100))
    b1 = np.sin(params[0]*X)*np.cos(params[1]*Y)
    b2 = np.sin(params[1]*X)*np.cos(params[0]*Y)
    im1 = ax[0].imshow(b1,aspect='auto')
    im2 = ax[1].imshow(b2,aspect='auto')
    ax[0].set_ylabel(r'$t$')
    ax[0].set_xlabel(r'$x$')
    ax[1].set_xlabel(r'$x$')
    plt.tight_layout()
    return fig, im1, im2

fig, im1, im2 = init();

def plotfields(p,a):
    params[0] = p; params[1] = a
    b1 = np.sin(params[0]*X)*np.cos(params[1]*Y)
    b2 = np.sin(params[1]*X)*np.cos(params[0]*Y)
    im1.set_data(b1)
    im2.set_data(b2)
    display(fig)

def on_button_clicked(b):
    fig.savefig("test_p{:.3s}_a{:.3s}.eps".format(str(int(100*params[0])),str(int(100*params[1]))))
    fig.savefig("test_p{:.3s}_a{:.3s}.png".format(str(int(100*params[0])),str(int(100*params[1]))))
button.on_click(on_button_clicked)

p_w  = FloatSlider(min=0.01, max=1, step=0.01, value=0.01)
a_w  = FloatSlider(min=0.01, max=1, step=0.01, value=0.01)
interact(plotfields,p=p_w,a=a_w)
Sign up to request clarification or add additional context in comments.

1 Comment

@kommandoking: Saw your addition of X,Y inside method plotfields(p,a): Well intended, but that code-correction should be fist put as comment. You don't know about author's intent. Give them a chance to fix it their own or explain it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.