jupyter-widgets / ipywidgets

Interactive Widgets for the Jupyter Notebook
https://ipywidgets.readthedocs.io
BSD 3-Clause "New" or "Revised" License
3.15k stars 950 forks source link

not clear how to transition from interact to custom layout widgets #1731

Open den-run-ai opened 7 years ago

den-run-ai commented 7 years ago

For example, this does not work:

vb1 = VBox([x, xmin,xmax])
vb2 = VBox([y, ymin, ymax])
vb3 = VBox([c, cmin, cmax])

box = HBox([vb1, vb2, vb3])
display(box)

for cols in box.children:
    for wdg in cols.children:
        wdg.observe(plot, names='value')

The intended behavior is like this:

interact(plot, 
                       x=x,
                       y=y,
                       c=c,
                       xmin=xmin,
                       xmax=xmax,
                       ymin=ymin,
                       ymax=ymax,
                       cmin=cmin,
                       cmax=cmax                  
)
den-run-ai commented 7 years ago

Related: https://github.com/jupyter-widgets/ipywidgets/issues/826

jasongrout commented 7 years ago

It would be helpful if you posted a complete working example. I'll assume that your x, y, c, xmin, etc. are widgets here.

In the observe case, the plot function will be called with the single widget's changed notification. In the interact version, the plot function is called with each widget's current value. (Change your plot function to just print out its arguments to see this).

One simple way to fix it is to make a function that calls plot with current value of each widget, and use that in the observe call

def plot_state(change):
    return plot(x=x.value, y=y.value, c=c.value, xmin=xmin.value, ...)

Now use wdg.observe(plot_state, ...).

Does that help? If not, please post a complete example we can work from.

den-run-ai commented 7 years ago

@jasongrout I found the solution based on #826

w = interactive(plot2dcolors, 
                       x=x,
                       y=y,
                       c=c,
                       xmin=xmin,
                       xmax=xmax,
                       ymin=ymin,
                       ymax=ymax,
                       cmin=cmin,
                       cmax=cmax           
)

vb=[]
for subset in np.array(w.children[3:-1]).reshape(3,2).tolist():
    vb.append(VBox(subset))
box=VBox([HBox(w.children[:3]), HBox(vb), w.children[-1]])
box
den-run-ai commented 7 years ago

I think the documentation still needs improvement, it took me literally one hour of reading before I found that solution in the issue tracker. So I suggest to keep this issue open.

jasongrout commented 7 years ago

I found the solution based on #826

That works too. Thanks for posting this here! Would you mind pointing out where in the docs we could add a helpful note or example that you would have found earlier? (Or feel free to even make a PR, if you'd like.)

den-run-ai commented 7 years ago

IMO, interact should directly support this like plt.subplots grid. So each widget can get its own location in the grid.

den-run-ai commented 7 years ago

Or I guess in CSS/HTML this is called Flexbox grid

jasongrout commented 7 years ago

Related: https://github.com/jupyter-widgets/ipywidgets/issues/1021

den-run-ai commented 7 years ago

So how about adding an arguments grid=(m,n) to interact, and location=(x1,y1,x2,y2) to all children widgets and the output? Here 0<=x1,x2<m & 0<=y1,y2<n and no overlap should be allowed in the grid.

jasongrout commented 6 years ago

I think it is probably better to build a grid container widget that can be used outside of interact as well, perhaps using the new css grid spec for layout. Between having such a container, and using tools mentioned in #1021, I think it could be relatively easy to make a grid of controls in an interact-type situation.

gioxc88 commented 4 years ago

I would like to add something because I also encountered the same problem. I will use the same example of @denfromufa

First of all you don't need to refer to the w.children but in the final HBox you can directly use the widgets x, y, c etc .. You only need w.children[-1] for the output

w = interactive(plot2dcolors, 
                       x=x,
                       y=y,
                       c=c,
                       xmin=xmin,
                       xmax=xmax,
                       ymin=ymin,
                       ymax=ymax,
                       cmin=cmin,
                       cmax=cmax           
)

box=VBox([HBox((x, y, c)), HBox((xmin, ymin, cmin)), HBox((xmax, ymax, cmax), w.children[-1]])

finally when you display your box this will only display the widgets and not the output of the function until you interact with the box

so for example if you call display(box) you will see the widgets but not the plot (only until you click on any widget) to solve this yo have to call w.update() in fact the interactive class has an event called on_display that calls the update method