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

Version 0.5.0+ no longer displays output after widget interaction #233

Open
jmrohwer opened this issue Jun 11, 2020 · 9 comments
Open

Version 0.5.0+ no longer displays output after widget interaction #233

jmrohwer opened this issue Jun 11, 2020 · 9 comments

Comments

@jmrohwer
Copy link

I have not labeled this as a "bug report" as I am not sure whether this is a new feature or regression.

I am developing a package for the processing of NMR spectra that makes extensive use of the ipympl backend:
https://github.com/NMRPy/nmrpy

As part of this there are a number of widgets (interactive matplotlib graphs) for phasing and peak picking of the NMR spectra. Some of these widgets print output to the cell output area (below the ipympl widget) after user interaction, using simple print() function calls in the code. In some cases further user input is required for which I have made use of an ipywidgets.FloatText() which is then also displayed in the output area.

As of version 0.5.0 this cell output area is no longer displayed. The widget interaction with the ipympl graphs still works but the text output from print() calls and the ipywidgets output are not there.

  • Is this intentional or a regression?
  • If intentional, is there a way to get the output area back, or an alternative where to direct text output after interaction with a graph (or instantiate an ipywidgets widget) so that it is displayed to the user?

This functionality is crucial to my application and currently I am thus stuck with using ipympl<0.5.0. BTW this is using notebook, not jupyterlab.

@martinRenou
Copy link
Member

martinRenou commented Jun 11, 2020

Thanks for opening an issue.

I am not really sure what could happen here. Could it be that the events that are supposed to print outputs are not triggered since 0.5.0? Are you sure the code is actually running?

If your code is actually running but nothing is displayed: A more robust way to print stuffs in the output under the plot is to use the Output widget from ipywidgets, then you can put your plot and your Output in a box:

from ipywidgets import Output, VBox

out = Output()

# Create figure and fill the output

# Display the figure and the output
display(VBox(children=(fig.canvas, out)))

This would work in classical Jupyter Notebook, in JupyterLab and in Voila. While your solution using prints only works in Jupyter Notebook, and it is quite weak, as the Jupyter Notebook interface does not really know where to put the outputs.

@thomasaarholt
Copy link
Contributor

thomasaarholt commented Jun 11, 2020

I'm not 100% sure if I've understood this correct, but I believe what you're experiencing is how unless you create a widgets.Output, then use your print function with that Output, and make sure to display() that Output widget, the output won't be created.

I've quickly made an example below, and you can read more in the docs here. It is definitely confusing at first, but quite flexible!

button = ipywidgets.Button(description='Hello there')

output = ipywidgets.Output()

def print_hello(*args):
    print('Does not work')
    with output:
        print('Does work')
button.on_click(print_hello)
display(ipywidgets.HBox([button, output]))

image

@thomasaarholt
Copy link
Contributor

If you're using jupyter lab, you may also notice outputs appearing in the "log" in the status bar at the bottom of the window.

@martinRenou
Copy link
Member

Great minds think alike ;)

@jmrohwer
Copy link
Author

Thanks for the quick replies, this has been very helpful!

Minimal example:

class MyFigure:
    
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.ax.plot([1,3,2])
        self.fig.canvas.mpl_connect('button_press_event', self.press)
    
    def press(self, event):
        x = np.round(event.xdata, 2)
        y = np.round(event.ydata, 2)
        print('button press coordinates: ', x, y)

mf = MyFigure()

This works in ipympl<0.5 but not in 0.5.0+. I could get it to work with ipywidgets.Output() and this solves my issue. I was just wondering if this change is documented somewhere and that using the Output widget is now required? I was breaking my head trying to figure out why my code was no longer working until I realized ipympl had been updated. What is the rationale? Is the idea that all output will be required to be piped through the Output widget?

Thanks!

@thomasaarholt
Copy link
Contributor

thomasaarholt commented Jun 11, 2020

Can confirm. In the old ipympl (0.4.x) version the text appears, in the new version it does not. ipywidgets version is the same (7.5.1) in both cases.

I think ipympl is mature enough to justify spending some time on proper documentation (I've said I'd do that before, let's see what I can manage this weekend). But I guess the current issue is about release notes, which is particularly justified since the API (or equivalent to that) is changing with new releases.

@martinRenou
Copy link
Member

martinRenou commented Jun 12, 2020

What might have changed is the widget extension JavaScript side. I am not sure exactly what made this change.

What is the rationale?

Actually using print without using the Output widget is not really a good practice here.
The Jupyter Notebook interface will try its best to put the result of the print somewhere that makes sense, in your case under the Plot because that's the origin of the callback that printed something. But what do you expect to happen if the plot is displayed multiple times on the Notebook? What the Notebook interface will do in this case: It will put the print result under the last created plot. But that's not really what you want, you want it under all plots.

That's the reason for JupyterLab to not print anything from callbacks, because it does not know under which cell to put the print and takes the decision to put it in the log pane instead.

Using the Output widget allows you to decide where you put the prints., if you display the output widget multiple times the print statement will be dispatched to all outputs. That is more robust than relying on what the interface does (An interface like Voila will just ignore your print statement if you don't use an Output).

I think ipympl is mature enough to justify spending some time on proper documentation

💯 I won't have time this week-end but I can definitely help later.

@jmrohwer
Copy link
Author

Thanks for the great explanation - makes sense.

It would be great to have some documentation, agreed! Even if a change like this were mentioned in the release notes it would have helped me a a lot. Having said that I realize that this is an open-source project and people's resources are limited. Unfortunately I don't know enough about ipympl internals to be able to contribute myself.

@ezatterin
Copy link

ezatterin commented Dec 11, 2020

Thanks - I also benefited from the explanations! I was also puzzled as to why this used to work in the past.

Interestingly however I cannot see the output of print() calls in callback functions even with %matplotlib notebook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants