-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
display print
in callbacks in the notebook
#116
Comments
This is (unfortunately) the expected behavior in JupyterLab. JupyterLab does not show outputs from callbacks triggered by a widget change. The use of the output widget is the way to go. |
Note: there is ongoing work to add a "global output area" for display events that are not tied to a specific cell, which may be useful for debugging. |
A very hacky way to make this work is: # make sure prints in callbacks make it to the notebook
def subplots(*args, **kwargs):
from IPython.display import display
import ipywidgets as widgets
import weakref
import functools
fig, ax = plt.subplots(*args, **kwargs)
fig._output = output = widgets.Output()
display(output)
orig_mpl_connect = fig.canvas.mpl_connect
@functools.wraps(orig_mpl_connect)
def mpl_connect(key, cb, **kwargs):
try:
r = weakref.WeakMethod(cb)
except TypeError:
r = lambda: cb
def wrapper(*args, **kw):
cb = r()
with output:
if cb is None:
...
else:
cb(*args, **kw)
orig_mpl_connect(key, wrapper, **kwargs)
fig.canvas.mpl_connect = mpl_connect
return fig, ax which relies on |
A slightly more thought out version: '''
make sure prints in callbacks make it to the notebook
See https://ipywidgets.readthedocs.io/en/stable/examples/Output%20Widget.html
By default, calling `print` in a ipywidgets callback results in the output
being lost (because it is not clear _where_ it should go). You can explictily
capture that the text to a given output area using at Output widget.
This is a wrapper for `plt.subplots` that makes sure
a) an ipywidgets.widgets.Output is created with each Figure
b) the `mpl_connect` on the canvas is monkey-patched such that all
user callbacks run in a context where the stdout is captured and sent
to that output area.
'''
import matplotlib.pyplot as plt
def _monkey_patch_pyplot():
import matplotlib.pyplot as plt
import functools
from IPython.display import display
import ipywidgets as widgets
import weakref
@functools.wraps(plt.figure)
def figure(*args, **kwargs):
fig = figure._figure(*args, **kwargs)
fig._output = output = widgets.Output()
display(output)
orig_mpl_connect = fig.canvas.mpl_connect
@functools.wraps(orig_mpl_connect)
def mpl_connect(key, cb, **kwargs):
# try to use a WeakMethod to make sure we don't keep objects alive
# to match the behavior of the base mpl_connect
try:
r = weakref.WeakMethod(cb)
except TypeError:
def r():
return cb
def wrapper(*args, **kw):
cb = r()
with output:
if cb is not None:
cb(*args, **kw)
orig_mpl_connect(key, wrapper, **kwargs)
# mokeny patch the canvas
fig.canvas.mpl_connect = mpl_connect
return fig
# stash the orginal
figure._figure = plt.figure
# monkey patch pyplot (!?)
plt.figure = figure
plt._print_hacked = True
# make sure we only do this once!
if getattr(plt, '_print_hacked', False):
...
else:
_monkey_patch_pyplot()
# clean up after our selves and do not polute the user's namespace
del _monkey_patch_pyplot
del plt |
We're running into similar issues and I'm playing with the suggestion above. An interesting case that I run into, that may be of interest: if an |
@almarklein do you have a short example you could post? |
Yes. So this works fine, printing "hello 2" in the cell output. import asyncio
ran = 0
def print_some():
global ran
ran += 1
print("hello 2")
loop = asyncio.get_event_loop()
loop.call_soon(print_some) But this does not. It shows "hello 1" in the global log widget. But "hello 2" is never shown. import asyncio
import ipywidgets
button = ipywidgets.Button(description="click me")
ran = 0
def print_some():
global ran
ran += 1
print("hello 2")
@button.on_click
def on_click(event):
print("hello 1")
loop = asyncio.get_event_loop()
loop.call_soon(print_some)
button |
It would be interesting to see if "hello 2" gets transmitted to the frontend over the websocket connection. If it does, there's probably a change in jupyterlab we could make to get it into the log. In general, running code outside of the framework of the kernel execute or comm messages (like in this async case) messes with the output capturing and redirection that the ipython kernel does. |
when run in IPython prints out to stdout on every mouse click. In jlab + ipympl the prints don't show up anywhere I can find...
From https://ipywidgets.readthedocs.io/en/stable/examples/Output%20Widget.html it looks like this can work via:
but I suspect there is a way for this to work automatically....
The text was updated successfully, but these errors were encountered: