2017-08-02 2 views
1

Je cherche un moyen de changer la position de l'IntSlider vertical à la droite de la matplotlib fig. Voici le code:Comment changer la position par défaut d'un curseur ipywidget à côté d'un chiffre matplotlib?

from ipywidgets import interact, fixed, IntSlider 
import numpy as np 
from matplotlib import pyplot as plt 

%matplotlib notebook 

fig = plt.figure(figsize=(8,4)) 

xs = np.random.random_integers(0, 5000, 50) 
ys = np.random.random_integers(0, 5000, 50) 

ax = fig.add_subplot(111) 
scat, = ax.plot(xs, ys, 'kx', markersize=1) 
ax.grid(which='both', color='.25', lw=.1) 
ax.set_aspect('equal'), ax.set_title('Rotate') 

def rotate(theta, xs, ys): 
    new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta)) 
    new_xs -= new_xs.min() 
    new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta)) 
    new_ys -= new_ys.min() 
    return new_xs, new_ys 

def update_plot(theta, xs, ys): 
    new_xs, new_ys = rotate(theta, xs, ys) 
    scat.set_xdata(new_xs), scat.set_ydata(new_ys) 
    ax.set_xlim(new_xs.min() - 500, new_xs.max() + 500) 
    ax.set_ylim(new_ys.min() - 500, new_ys.max() + 500) 

w = interact(update_plot, 
      theta=IntSlider(min=-180, max=180, step=5,value=0, orientation='vertical'), 
      xs=fixed(xs), 
      ys=fixed(ys)) 

C'est ce que j'ai:

enter image description here

Voici ce que je veux:

enter image description here

Il pourrait y avoir un moyen très simple fais cela, mais je ne peux pas me comprendre.

J'ai essayé de placer à la fois le fig et le widget interactive dans un VBox puis envelopper le VBox avec IPython.display et cela n'a pas fonctionné.

Impossible de trouver une solution directe à cela dans les exemples.

EDIT1:

ipywidgets fournit une classe Output() qui capture la zone de sortie et l'utiliser dans le contexte du widget.

Je vais essayer de comprendre comment l'utiliser.

Tel est l'objet: https://github.com/jupyter-widgets/ipywidgets/blob/master/ipywidgets/widgets/widget_output.py

+0

Avez-vous essayé ma solution ci-dessous? –

+1

Oui, je pense que vous êtes sur la bonne voie. Si vous trouvez un moyen de le faire fonctionner en déballant la construction de la figure et l'axe de la fonction update_plot, je vais accepter votre réponse. Prenez beaucoup à l'objet Output(). Je pense que cela va résoudre notre problème. –

Répondre

1

Vous pouvez résoudre ce problème en créant un widget de interactif et le chargement de la children dans un HBox. Les widgets enfants d'un interactif suivent cette convention; (widget_0, widget_1 ..., sortie) où le dernier membre du tuple est la sortie des widgets de contrôle. Vous pouvez définir la disposition de HBox avant ou après la déclaration. Read more on the layouts available here.

La solution suivante a quelques réserves; le graphique peut ne pas apparaître au début, vous devrez peut-être modifier le contrôle avant qu'il n'apparaisse, en second lieu lorsque vous utilisez la magie %matplotlib notebook le contrôle peut provoquer beaucoup de scintillement lors de la mise à jour. À part ça je pense que ça devrait marcher comme tu veux;

from IPython.display import display 
from ipywidgets import interactive, fixed, IntSlider, HBox, Layout 
import numpy as np 
import matplotlib.pylab as plt 
%matplotlib notebook 

def rotate(theta, xs, ys): 
    new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta)) 
    new_xs -= new_xs.min() 
    new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta)) 
    new_ys -= new_ys.min() 
    return new_xs, new_ys 

def update_plot(theta, xs, ys): 
    fig = plt.figure(figsize=(8,4)) 
    ax = fig.add_subplot(111) 
    scat, = ax.plot(xs, ys, 'kx', markersize=1) 
    ax.grid(which='both', color='.25', lw=.1) 
    ax.set_aspect('equal'), ax.set_title('Rotate') 
    new_xs, new_ys = rotate(theta, xs, ys) 
    scat.set_xdata(new_xs), scat.set_ydata(new_ys) 
    ax.set_xlim(new_xs.min() - 500, new_xs.max() + 500) 
    ax.set_ylim(new_ys.min() - 500, new_ys.max() + 500) 

xs = np.random.randint(0, 5000, 50) 
ys = np.random.randint(0, 5000, 50) 
w = interactive(update_plot, 
       theta=IntSlider(min=-180, max=180, step=5, value=0,orientation='vertical'), 
       xs=fixed(xs), 
       ys=fixed(ys)) 

# Define the layout here. 
box_layout = Layout(display='flex', flex_flow='row', justify_content='space-between', align_items='center') 

display(HBox([w.children[1],w.children[0]], layout=box_layout)) 

Mise à jour:

C'est la solution de Jason Coulis de la ipywidgets gitter.

from IPython.display import display, clear_output 
from ipywidgets import interact, fixed, IntSlider, HBox, Layout, Output, VBox 
import numpy as np 
import matplotlib.pyplot as plt 
%matplotlib inline 

def rotate(theta, xs, ys): 
    new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta)) 
    new_xs -= new_xs.min() 
    new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta)) 
    new_ys -= new_ys.min() 
    return new_xs, new_ys 

out = Output(layout={'width': '300px', 'height': '300px'}) 

def update_plot(change): 
    theta = change['new'] # new slider value 
    with out: 
     clear_output(wait=True) 
     fig = plt.figure(figsize=(4,4)) 
     ax = fig.add_subplot(111) 
     scat, = ax.plot(xs, ys, 'kx', markersize=1) 
     ax.grid(which='both', color='.25', lw=.1) 
     ax.set_aspect('equal'), ax.set_title('Rotate') 
     new_xs, new_ys = rotate(theta, xs, ys) 
     scat.set_xdata(new_xs), scat.set_ydata(new_ys) 
     ax.set_xlim(new_xs.min() - 500, new_xs.max() + 500) 
     ax.set_ylim(new_ys.min() - 500, new_ys.max() + 500) 
     plt.show() 

xs = np.random.randint(0, 5000, 50) 
ys = np.random.randint(0, 5000, 50) 

slider = IntSlider(min=-180, max=180, step=5, value=0, orientation='vertical') 
slider.observe(update_plot, 'value') 
update_plot({'new': slider.value}) 
display(HBox([out, slider])) 
+1

Vous avez partiellement résolu le problème, mais l'enroulement de la construction de la figure matplotlib dans la fonction update_plot a entraîné ce mauvais comportement de rafraîchissement et, également, la figure est créée lors de la première mise à jour du curseur. Il pourrait y avoir un moyen de capturer la sortie avec le matplotlib à l'intérieur. Vérifiez ceci: https://github.com/jupyter-widgets/ipywidgets/blob/master/ipywidgets/widgets/widget_output.py –

+0

@BrunoRuasDePinho le scintillement peut avoir quelque chose à voir avec le navigateur que vous êtes ou quelle version de ipywidgets que vous travaillez en utilisant comme indiqué dans [ce numéro] (https://github.com/jupyter-widgets/ipywidgets/issues/1532). Vous pouvez voir des améliorations marginales en utilisant '% matplotlib inline' au lieu de'% matplotlib notebook' mais vous utilisez certaines fonctionnalités interactives. Donc, IDK cela peut être aussi bon qu'il obtient pour le moment. Je vais continuer à regarder mais au moins me jeter un upvote pour le soln partiel :) –

+1

Sure James! Merci!! –

2

j'ai décidé d'essayer cet exemple en utilisant bqplot au lieu de matplotlib et il est avéré être beaucoup plus simple.

import numpy as np 
from bqplot import pyplot as plt 
from IPython.display import display 
from ipywidgets import interactive, fixed, IntSlider, HBox, Layout 

plt.figure(min_aspect_ratio=1, max_aspect_ratio=1) 

xs = np.random.randint(0, 5000 + 1, 100) 
ys = np.random.randint(0, 5000 + 1, 100) 

scat = plt.scatter(xs, ys) 

def rotate(theta, xs, ys): 
    new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta)) 
    new_xs -= new_xs.min() 
    new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta)) 
    new_ys -= new_ys.min() 
    return new_xs, new_ys 

def update_plot(theta, xs, ys): 
    new_xs, new_ys = rotate(theta, xs, ys) 
    scat.x, scat.y = new_xs, new_ys 

w = interactive(update_plot, 
      theta=IntSlider(min=-180, max=180, step=5,value=0, orientation='vertical'), 
      xs=fixed(xs), 
      ys=fixed(ys)) 

box_layout = Layout(display='flex', flex_flow='row', justify_content='center', align_items='center') 
display(HBox([plt.current_figure(), w], layout=box_layout)) 

bqplot est conçu pour être un widget interactif. De cette façon, il pourrait être simplement ajouté à la sortie sans avoir à l'intégrer dans la fonction update_plot.

De la documentation bqplot:

Dans bqplot, tout seul attribut de la parcelle est un widget de interactive. Cela permet à l'utilisateur d'intégrer n'importe quel intrigue avec des widgets IPython pour créer une interface graphique complexe et riche en fonctionnalités à partir de quelques lignes simples de code Python .

Je conserverai la réponse acceptée de James parce qu'elle a répondu à la question initiale.