Different behaviour of ipywidgets.interactive_output() on Windows and macOS - matplotlib

I wanted to learn how to use interactive plots in Jupyter Notebook and created a script that updates a figure based on the current values of two sliders. The idea is simply that when the value of one of the sliders changes the figure should be updated.
This worked fine when I first ran the code on my Windows PC (using Windows 10). However, when I then ran the same code on my MacBook (using macOS Monterey 12.6.1), the figure is not updated but a new figure is created every time I change the value of one of my sliders.
Please find a MWE below:
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
%matplotlib widget
# Create two sliders
slider1 = widgets.IntSlider(value=4, min=0, max=10, step=1,
description='Slider1', continuous_update = False)
slider2 = widgets.IntSlider(value=8, min=0, max=10, step=1,
description='Slider2', continuous_update = False)
# Some function that plots the result based on the current values of the sliders
def plotResult(a, b):
plt.close('all')
plt.figure()
plt.hlines(a+b, xmin=0, xmax=10)
# Create interactive plot
interactive_plot = widgets.interactive_output(plotResult, {"a":slider1, "b":slider2})
ui = widgets.HBox([slider1, slider2])
display(ui, interactive_plot)
When I run the above code in a cell in my Jupyter Notebook, I get the following output:
(https://i.stack.imgur.com/NX02T.png).
When I now change any of the sliders, on Windows my figure is "updated in-place", while on macOS a new figure is created everytime (every figure newly created figure is labeled as 'Figure 1'). I don't understand why this is happening as I explicitly close all currently opening figures at the beginning of plotResults(a,b) by using the command plt.close('all').
I thought that since every newly created figure on macOS is labeled 'Figure 1' the problem could be that on macOS the cells are not refreshed. Thus, I additionally imported the module IPython using import IPython and then added the command IPython.display.clear_output(wait=True) after the first line of plotResults(a,b). However, this did not work and the problem persisted.
I also tried to use a different Matplotlib backend such as %matplotlib ipympl or %matplotlib nbagg instead of %matplotlib widget but this also did not help anything.

Related

matplotlib chart construction to suit use flask? (working example uses matplotlib.figure FigureCanvasAgg) can I construct errorbars?

I have a matplotlib chart working nicely as a python script. I need to create this chart style in flask. Can't use this method within flask as flask thread management doesn't play with matplotlib.
Oddly, the current method will run once successfully, subsequent runs will produce this error.
RuntimeError: main thread is not in main loop
So this is my desired chart format to produce in flask.
the code I'm using currently.
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.29, top=.91)
ax.set_title(title)
ax.set_ylabel("y label text")
ax.set_xlabel('x label text')
ax.tick_params(axis='x', labelrotation = -80)
l = ax.plot(df_output['column1'])
y_error = df_output['column2']
plt.errorbar(list(df_output.index), \
list(df_output['column1']), \
yerr = y_error,fmt='o',ecolor = 'blue',color='blue')
fig.legend(l, loc=8, labels=labels)
#loc=2 = top left corner, loc=8 = 'lower center'
#plt.show()
plt.savefig(output_path+"/"+title+'_errorbars.png')
I found this example that works with flask
https://gist.github.com/illume/1f19a2cf9f26425b1761b63d9506331f
it uses this matplotlib charting syntax. Need to convert my old matplotlib format to suit the flask compatible format. Is this chart format possible via FigureCanvasAgg?
fig = Figure()
axis = fig.add_subplot(1, 1, 1)
print("type(axis):", type(axis))
x_points = data.iloc[:, 0]
y_points = data['mean minus sterility control mean']
axis.plot(x_points, y_points)
output = io.BytesIO()
FigureCanvasAgg(fig).print_png(output)
return Response(output.getvalue(), mimetype="image/png")
I'll admit to not being strong in building matpotlib charts. changing between chart building methods throws me.
I'm digging around the docs at moment.
https://matplotlib.org/stable/gallery/user_interfaces/canvasagg.html
I did find this Q&A (RuntimeError: main thread is not in main loop with Matplotlib and Flask)
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
while this appears to run for me. I want to move away from creating charts as files on the server, too much potential for file mismanagement, creating the chart as a io.BytesIO() output (or some format within the flask http response to user) is a much better solution.
(I'd like to keep at an image output, rather than change architecture to (say) a json output and constructing chart in client using javascript libraries)

Is it possible to store values from ipywidgets to cell code like Google colab Form

When I use Google Colab Form and change field value, new value stored in cell code. So next time I open notebook I see new value in cell.
How can I use same functionality for ipywidgets field?
For example.
Cell with Colab Form:
slices = 4 ##param {type:"slider", min:1, max:10, step:0}
Cell with ipywidget:
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
# interact() syntax
def f(x):
return x
interact(f, x=10);
# classic syntax
widgets.IntSlider(
value=7,
min=0,
max=10,
step=1,
description='Test:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='d'
)
In Google Colab Form changed slider value injects into cell code.
But I didn't find same functionality for ipywidgets. If I move slider - its new value will be lost on notebook reload.
Maybe some tricks exists for this?

Jupyter-lab pyplot cell re-evaluation breaks interactive widget display

This code works as expected on a cleanly started kernel, but on re-executing the second cell, instead of an interactive widget (ipypml per https://matplotlib.org/3.3.0/users/interactive.html ), I get just the text output as in the image.
How are jupyter, jupyter-lab, widgets, pyplot, and matplotlib interacting to cause this problem?
And how do I properly do a plot so that I can re-execute the cell without re-starting the kernel?
Cell 0:
%matplotlib widget
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
Cell 1:
fig,ax = plt.subplots(num=0)
ax.plot(np.arange(4))
Cell 1 output:
[<matplotlib.lines.Line2D at 0x161b913a0>]
I found that I need to make sure the figure gets cleanly opened (or closed and re-opened) in each update. I had used the plt.subplots(num=0) option to keep repeated updates from creating new, non-displayed figures. (note that the 2nd cell in the screenshot has a been re-executed 5 times and has a [6] prefix)
plt.close(0)
fig,ax = plt.subplots(num=0)
ax.plot(np.arange(4))

How to add plot commands to a figure in more than one cell, but display it only in the end?

I want to do the following in a Jupyter Notebook:
Create a pyplot.figure in a cell;
For each subsequent cells, calculate values and plot them to that same figure without displaying anything;
At the end, in another cell, display the figure with the result of every previous plot command.
Currently, while using %matplotlib notebook, the figure is always displayed after the same cell it's been created, and I don't even call plt.show().
This is not the behavior I desire. Instead I would like to postpone the display of the figure for the last cell only, but the figure of course should contain the results of the sequential plot commands called in the cells in between.
You can capture the content of a cell of a jupyter notebook using the magic command %%capture. You can also hide any output of a specific line by putting a ; at the end of it.
Showing the figure can be done by simply typing the variable in which the figure is stored, e.g. fig.
Combining those techniques gives you
import matplotlib.pyplot as plt
%matplotlib notebook
%%capture captured
fig, ax=plt.subplots()
ax.plot([1,2,3]);
fig # now show the figure
which is probably more understandable in the acutal notebook like this:
Also see How to overlay plots from different cells?

IPython plotting inline not showing

I have the following code in IPython running IPython QT Console on Linux.
%pylab inline
Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
For more information, type 'help(pylab)'.
fig = figure()
ax = fig.add_axes()
ax = fig.add_axes([0,500, 0, 5000])
ax.plot([1,2,3,44], [4,4,55,55])
Out[5]: [<matplotlib.lines.Line2D at 0x3d8e7d0>]
fig
Out[6]: <matplotlib.figure.Figure at 0x3d25fd0>
fig.show()
/usr/lib/pymodules/python2.7/matplotlib/figure.py:362: UserWarning: matplotlib is currently using a non-GUI backend, so cannot show the figure
"matplotlib is currently using a non-GUI backend, "
I've been struggling to make this work for some time, I've tried changing the backend manually with matplotlib.use() to Qt4Agg, GTK etc with no luck. This also happens in IPython notebook even when I call display().
Any ideas how to get the inline plotting working?
Marked Jakob's answer as the answer, but both are true actually. I had to replace the matploblibrc file with a new copy, started IPython QT Console with --pylab=None then manually entered %pylab inline in the console. Somehow this fixed the problem.
The axis object is defined incorrectly, this prevents matplotlib from rendering.
Remove the first ax = fig.add_axes(), and replace the second line with
ax = fig.add_axes([0, 0, 1, 1]).
The add_axes method requests the size of the axis in relative coordinates, in the form left, bottom, width, height with values between 0 and 1, see e.g. matplotlib tutorial.
You may also try fig.add_subplot(111) instead of fig.add_axes() or fig,ax = subplots() to create your figure and axis objects. The latter assumes that you have populated the interactive namespace matplotlib (%pylab) call in IPython.
It looks like your matplotlib build was compiled without a gui backend.
This is done when either a) it's explicitly specified (handy for webservers), or b) the required libraries for at least one gui backend aren't present (e.g. no Tk, Gtk, Qt, etc).
How did you install matplotlib?
If you compiled it from source, make sure that you have the development libraries for at least Tk installed and that your python install was compiled with Tk support (it is by default). If you installed it from your distro's repositories, whoever built the package built it without gui support, and you'll need to install it from another source.