Skip to content
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

Print events from matplotlib event handlers get swallowed #244

Closed
fperez opened this issue Jul 30, 2015 · 23 comments
Closed

Print events from matplotlib event handlers get swallowed #244

fperez opened this issue Jul 30, 2015 · 23 comments

Comments

@fperez
Copy link
Member

fperez commented Jul 30, 2015

Consider the following code in the notebook:

%pylab notebook

fig, ax = plt.subplots()
ax.plot(np.random.rand(10))

def onclick(event):
    print ('button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%(
        event.button, event.x, event.y, event.xdata, event.ydata))

cid = fig.canvas.mpl_connect('button_press_event', onclick)

these events never print anywhere.

The code does run, as can be seen by this code from @tacaswell:

%pylab notebook

fig, ax = plt.subplots()
ax.plot(np.random.rand(10))
collector = []
def onclick(event):
    collector.append('button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%(
        event.button, event.x, event.y, event.xdata, event.ydata))

cid = fig.canvas.mpl_connect('button_press_event', onclick)

After clicking a couple of times:

In [2]: collector
['button=1, x=201, y=275, xdata=2.196853, ydata=0.615071',
 'button=1, x=305, y=360, xdata=4.100549, ydata=0.770019',
 'button=1, x=421, y=223, xdata=6.205388, ydata=0.520279']

Somehow we're swallowing that stdout completely, I'm not 100% sure why right now. But to the user, it feels very puzzling. Given the equivalent code works just fine at the terminal, we should try to make it work without jumping through additional hoops in the notebook.

@takluyver
Copy link
Member

I think it's another case of "where should the output go". The kernel is probably sending the output, but the notebook frontend doesn't know which cell to associate it with, so it discards it. I seem to recall @jdfreder spent some effort working round this for widgets. It would be good to have a more general solution, but I'm not sure what shape that would take.

@jdfreder
Copy link
Contributor

Yeah, this is exactly the kind of thing I had to tackle with the widgets. The notebook isn't smart enough to know what cell triggered the click event, so you need to tell it before sending the message to the backend.

Thanks for pinging me here @takluyver , this is one of the things I'm trying to reimagine with the output area work I'm doing.

@minrk
Copy link
Member

minrk commented Jul 30, 2015

Threaded output is associated with the most recent execution (by the kernel). With a GUI backend, I see the output in the cell (whichever is the most recently executed). With the nbagg backend, the most recent request is likely to be a comm message, and the nbagg backend is presumably dropping those messages. The nbagg backend could hook up the cell output callback for output messages it receives for its comms.

@fperez
Copy link
Member Author

fperez commented Jul 31, 2015

Mmmh... @ellisonbg, this is something that perhaps Ryan could work on as part of the MPL work.. . He might be able to make an improvement to the actual nbagg backend in MPL itself, working with @tacaswell (perhaps with some guidance from @minrk). What do you think?

@tacaswell
Copy link

@blink1073 is also working on a widget based version of nbagg

@blink1073
Copy link
Contributor

Yes, see #4582 for where I left off. matplotlib/matplotlib#4582

@blink1073
Copy link
Contributor

The issue I ran in to originally that the div created by mpl.show was being changed from under its nose during interact.

@blink1073
Copy link
Contributor

When the following cell is executed, the comm_manager creates an nbagg connection, which creates a div. Might there be a way to add the nbagg div to the list of widgets in interactive?

%matplotlib notebook
import matplotlib.pyplot as plt
from IPython.html.widgets import interactive

fig, ax = plt.subplots()
ax.plot(range(5))


vline = ax.axvline(1, color='k')
hline = ax.axhline(0.5, color='k')

def set_cursor(x, y):
    vline.set_xdata((x, x))
    hline.set_ydata((y, y))
    ax.figure.canvas.draw_idle()

interactive(set_cursor, x=ax.get_xlim(), y=ax.get_ylim())

@fperez
Copy link
Member Author

fperez commented Aug 4, 2015

@jdfreder, perhaps you have some insights that can help us here?

@jdfreder
Copy link
Contributor

jdfreder commented Aug 7, 2015

@blink1073 if you want the div to be treated as something that can be used by interact, you'll need to write a custom widget view and python widget. The value traitlet of the widget will be what interact reads/writes.

@jdfreder
Copy link
Contributor

jdfreder commented Aug 7, 2015

If instead you want something that doesn't display in the widget area, you may be able to fool it by having a Python class that inherits from HasTraits, having a traitlet called value. Interact would read/write that value like it was a widget.

@minrk minrk added this to the no action milestone Sep 11, 2015
@tacaswell
Copy link

Not sure this should be closed. We also have an open mpl issue related to exceptions from event callbacks also being swallowed matplotlib/matplotlib#5111

@ellisonbg ellisonbg modified the milestones: 5.0, no action Oct 6, 2015
@ellisonbg
Copy link
Contributor

OK, marking as 5.0

@minrk minrk reopened this Oct 6, 2015
@tacaswell
Copy link

It could be argued that this should only be a bug on the mpl side, but I suspect that we will need some support from IPython on this one.

@blink1073
Copy link
Contributor

Fixed by matplotlib/matplotlib#5754.

screen shot 2016-05-04 at 2 03 12 pm

@fperez
Copy link
Member Author

fperez commented May 5, 2016

Wonderful, thanks folks. Closing here (obviously users will need a newer mpl to get the fix, but that's fine).

@fperez fperez closed this as completed May 5, 2016
@hakim89
Copy link

hakim89 commented Jul 25, 2016

I would greatly appreciate a working example... in this code I can't even see the button
jupyer == 4.1.0
matplotlib == 1.5.1
python 2.7

%matplotlib notebook
from matplotlib.widgets import Button
import matplotlib.pyplot as plt
def callback(event):
    plt.text(event.xdata, event.ydata, 'clicked')

f,a = plt.subplots(1)
b1 = Button(a,'Button1')
b1.on_clicked(callback)
plt.show()

@amnezzia
Copy link

the original example still does not work, I don't see any prints under the plot
ipython == 6.1.0
notebook == 5.0.0
matplotlib == 2.0.2

did recent changes revert the fix?

@tacaswell
Copy link

The changes referenced above were never released (it was targetted for 2.1) but in the mean time the widgets code has move through 3 major versions so the fixed nbagg did not work. The upcoming 2.1 release (the first rc will be announced as soon as the mac wheels finish building on travis!) will still use the old js.

For a backend which is integrated with widgets see https://github.com/matplotlib/jupyter-matplotlib / ipympl

@mannyglover
Copy link

I used the example posted by @hakim89:

%matplotlib notebook
from matplotlib.widgets import Button
import matplotlib.pyplot as plt
def callback(event):
    plt.text(event.xdata, event.ydata, 'clicked')

f,a = plt.subplots(1)
b1 = Button(a,'Button1')
b1.on_clicked(callback)
plt.show()

It works as designed in my jupyter notebook: produces a big button, and whenever I click in the button, the word "click" is plotted on the button. However, I wanted to test out if the std out would print out in the cell, so I added print(event.xdata, event.ydata) to the callback function. Nothing prints out. Then I tried ipympl per the link in @tacaswell's post above. I did pip3 install ipyml and then changed %matplotlib notebook to %matplotlib widget. Now, no plot is generated.

Versions of relevant packages are included in the screenshot below (from Jupyter Notebook -> Help -> About). Also, matplotlib version is 2.2.2

screen shot 2018-09-18 at 3 12 09 pm

@tacaswell
Copy link

Did you try restarting the kernel after switching to %matplotlib kernel? I think they may interfere with each other...

The no plot for ipympl should be a bug report against https://github.com/matplotlib/jupyter-matplotlib

@nvaytet
Copy link

nvaytet commented Aug 28, 2020

If the print statements are for debugging purposes, you can use os.write instead:

import matplotlib.pyplot as plt
import os
fig, ax = plt.subplots()
ax.plot(np.random.rand(10))

def onclick(event):
    msg = 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f\n'%(
        event.button, event.x, event.y, event.xdata, event.ydata)
    os.write(1, msg.encode())

cid = fig.canvas.mpl_connect('button_press_event', onclick)

The messages will show up in the terminal that is running the jupyter notebook.

@nvaytet
Copy link

nvaytet commented Dec 11, 2020

This is still an issue for me. Using the example in the first message, I do not see any print statements anywhere in the notebook.
This is both for %matplotlib notebook and %matplotlib widget backends.

I would like to see this issue re-opened.

Versions (conda-forge):

  • matplotlib: 3.3.2
  • jupyter_client: 6.1.7
  • jupyter_core: 4.6.3
  • notebook: 6.1.5
  • ipympl: 0.5.8

Thanks

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests