-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Multicursor disappears when not moving on nbagg with useblit=False + burns CPU #19633
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
Comments
On matplotlib master nbagg supports blitting - so I also tried with that - which prevents the high cpu usage but the smearing of the image (#19116) is renders the widget unusable: so I think it's still important to fix the |
I think the CPU burning loop is happening because the multicursor attaches a callback to the draw_event that will it self trigger a draw event and then ♾️ followed by 🔥 💻 🔥 The path is: matplotlib/lib/matplotlib/widgets.py Line 1636 in 6a35abf
to matplotlib/lib/matplotlib/widgets.py Lines 1643 to 1651 in 6a35abf
and Confusingly this doesn't happen on the qt backend, but does on the nbagg backend??? You see this behavior with this: %matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import MultiCursor
import ipywidgets as widgets
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.sin(4*np.pi*t)
fig, (ax1, ax2) = plt.subplots(2, sharex=True)
ax1.plot(t, s1)
ax2.plot(t, s2)
out = widgets.Output()
display(out)
n = 0
def drawn(event):
global n
n += 1
with out:
print(f'drawn! {n}')
fig.canvas.mpl_connect('draw_event', drawn)
multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1, useblit=False)
plt.show() |
Having not looked at the implementation at all, a simple fix might be to cache the mouse position (which may already be available from the existing Line2D's current position), and then not do anything if the mouse hasn't moved? |
@QuLogic looking at this again I think this is about nbagg and the js side rather than anything with multicursor. A simpler reproduction is: %matplotlib nbagg
import matplotlib.pyplot as plt
from ipywidgets import Output
fig, ax = plt.subplots()
l, = ax.plot([0,1],[0,1])
out = Output()
display(out)
n =0
def drawn(event):
global n
n+=1
with out:
print(n)
l.set_visible(False)
fig.canvas.mpl_connect('draw_event', drawn) which may be due to the the draw message that the frontend sends back from here? matplotlib/lib/matplotlib/backends/web_backend/js/mpl.js Lines 394 to 399 in 33c3e72
|
What is going on with The double-buffering that nbagg does may also be contributing here. |
I have been testing the matplotlib 3.4.0rc1 and I confirm the high CPU usage and significant slow down when using the notebook backend. There are also issue The example of the blitting tutorial doesn't seem to be working: import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
fig, ax = plt.subplots()
# animated=True tells matplotlib to only draw the artist when we
# explicitly request it
(ln,) = ax.plot(x, np.sin(x), animated=True)
# make sure the window is raised, but the script keeps going
plt.show(block=False)
# stop to admire our empty window axes and ensure it is rendered at
# least once.
#
# We need to fully draw the figure at its final size on the screen
# before we continue on so that :
# a) we have the correctly sized and drawn background to grab
# b) we have a cached renderer so that ``ax.draw_artist`` works
# so we spin the event loop to let the backend process any pending operations
plt.pause(0.1)
# get copy of entire figure (everything inside fig.bbox) sans animated artist
bg = fig.canvas.copy_from_bbox(fig.bbox)
# draw the animated artist, this uses a cached renderer
ax.draw_artist(ln)
# show the result to the screen, this pushes the updated RGBA buffer from the
# renderer to the GUI framework so you can see it
fig.canvas.blit(fig.bbox) and the following error message: ---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-2-f625949ed20b> in <module>
26 bg = fig.canvas.copy_from_bbox(fig.bbox)
27 # draw the animated artist, this uses a cached renderer
---> 28 ax.draw_artist(ln)
29 # show the result to the screen, this pushes the updated RGBA buffer from the
30 # renderer to the GUI framework so you can see it
/opt/miniconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py in draw_artist(self, a)
2936 """
2937 if self.figure._cachedRenderer is None:
-> 2938 raise AttributeError("draw_artist can only be used after an "
2939 "initial draw which caches the renderer")
2940 a.draw(self.figure._cachedRenderer)
AttributeError: draw_artist can only be used after an initial draw which caches the renderer Using blitting is now slower than without... :( Any chance to have this fix before the 3.4.0 release? Or to have if disable, through the |
Changing to
and never changes after that. Whereas on So somehow the |
I think the issue here is that:
This goes back to at least 3.3 so is not a recent regression. I think that removing the |
But something I missed before, is that the line is actually drawn. So the stale did not trigger a re-draw in other backends. The stale handler for figures in matplotlib/lib/matplotlib/pyplot.py Lines 782 to 800 in bfa31a4
And the |
The second and subsequent File ".../matplotlib/lib/matplotlib/pyplot.py", line 138, in post_execute
draw_all()
File ".../matplotlib/lib/matplotlib/_pylab_helpers.py", line 137, in draw_all
manager.canvas.draw_idle()
File ".../matplotlib/lib/matplotlib/backends/backend_webagg_core.py", line 164, in draw_idle
traceback.print_stack(None) Didn't we have a previous issue with this? |
Based on the original PR #4091 (comment), there is |
The previous similar issue was #13971 (comment), and the fix in that case was to avoid causing the figure to get marked stale during draw. As @tacaswell had mentioned earlier, doing the same in |
This can be all handled in the mouse move event handler instead, and prevents triggering extra draws in nbAgg. Fixes matplotlib#19633.
This can be all handled in the mouse move event handler instead, and prevents triggering extra draws in nbAgg. Fixes matplotlib#19633.
The stale machinery was a bit contentious when it went in (and @mdehoon was more correct than I understood at the time 🐑 ). If we have IPython we use
I no longer remember why we went with |
This can be all handled in the mouse move event handler instead, and prevents triggering extra draws in nbAgg. Fixes matplotlib#19633.
This can be all handled in the mouse move event handler instead, and prevents triggering extra draws in nbAgg. Fixes matplotlib#19633.
This can be all handled in the mouse move event handler instead, and prevents triggering extra draws in nbAgg. Fixes matplotlib#19633.
This can be all handled in the mouse move event handler instead, and prevents triggering extra draws in nbAgg. Fixes matplotlib#19633.
Bug report
Bug summary
When on the nbagg backend if you stop moving the mouse the multicursor will disappear. The same example works fine on the qt backend.
Additionally I noticed that when I add the multicursor my cpu usage jumps and the kernel busy indicator constantly flashes on and off.
Showing the plot without the multicursor:

and with the multicursor (just displaying, not interacting with the plot):
That usage is pretty stable and my laptop's fan goes wild.
The issue with the dissappearing was originally noticed by @ipcoder in matplotlib/ipympl#306
Code for reproduction
Actual outcome
and the high CPU usage
Expected outcome
Red line doesn't disappear + my CPU doesn't get crushed.
Matplotlib version
import matplotlib; print(matplotlib.__version__)
): '3.3.4.post2456+gfd23bb238'print(matplotlib.get_backend())
): nbaggdev instlal of maptlotlib + conda-forge for the others
The text was updated successfully, but these errors were encountered: