Updating `GridspecLayout` on click
See original GitHub issueHi, thanks to the developers for putting this thing together – it looks awesome!
I have a usage question though. I’m trying to make a layout of plots with NxM
rows and columns. Right now I’m prototyping with just buttons. The idea is to be able to expand the layout (i.e. add new panels) by clicking on one of the buttons.
Here’s a class I have so far (probably not the best way, but anyway).
import ipywidgets as ipyW
from IPython.display import display
class PlotGrid():
def __init__(self, nn):
self.panels = []
for n in range(nn):
self.createNewPanel(n=n)
self.redraw()
def createNewPanel(self, **kwargs):
button = ipyW.Button(description='Button {}'.format(kwargs['n']),
layout=ipyW.Layout(height='auto', width='auto'))
button.on_click(self.appendGrid)
self.panels.append(button)
def appendGrid(self, status):
self.createNewPanel(n=len(self.panels))
self.redraw()
def redraw(self):
ncols = int(np.sqrt(len(self.panels)))
nrows = int(np.ceil(len(self.panels) / ncols))
self.grid = ipyW.GridspecLayout(nrows, ncols)
n = 0
for i in range(self.grid.n_rows):
for j in range(self.grid.n_columns):
if (n < len(self.panels)):
self.grid[i, j] = self.panels[n]
n += 1
def show(self):
display(self.grid)
So when I run this:
plotgrid = PlotGrid(12)
plotgrid.show()
it does what it should: fills in the .grid
object with panels. When I click on the button the appendGrid
function is indeed called, and it does append the new panels to the grid as it should. But for some reason the result is not displayed in the cell: it just remains the same. If I later update the cell by running plotgrid.show()
again – it draws the new extended .grid
as it should.
So I think I’m not understanding something fundamental with how the display
function works and Jupyter handles the events.
These are the versions of the relevant packages I got through conda:
ipykernel 5.2.0 py38h23f93f0_1 conda-forge
ipython 7.13.0 py38h32f6830_2 conda-forge
jupyterlab 2.1.0 py_0 conda-forge
widgetsnbextension 3.5.1 py38_0 conda-forge
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (3 by maintainers)
Hey @haykh your solution is probably the best approach, so congrats on figuring that out! I think the issue was that
display
doesn’t work quite the way you’d expect. If you change the objects that went into a display they will not immediately update. Instead you need to explicitly update the display (you can get a reference to it by providing an id). Like so:cell 1:
Will have
a
as the output cell 2:now cell 1’s output will be
b
.You can read more about this here, here and here. I would particularly recommend implementing the
_ipython_display_
method on your class.In your working example you are circumventing having to do something like that by using the widget update system. The
Box
object listens for changes to it’schildren
and updates any views of itself whenever it’s children changes. Unfortunately that is the best solution as my example above withupdate_display
doesn’t actually work with ipywidgets, which is a known bug https://github.com/jupyter-widgets/ipywidgets/issues/1180Yup it does!
Your approach is totally reasonable, it’s the “ipywidgets” solution whereas I was suggesting the “matplotlib” solution. More about this in this unmerged PR updating ipympl examples: https://github.com/matplotlib/ipympl/pull/244
I see you’re using
display(fig)
. Beware thatdisplay(fig)
anddisplay(fig.canvas)
are actually pretty different. The former will display a static png, while the latter will create an interactive plot so you can can pan, zoom, and easily update the contents.