You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Qt5Agg backend is extremely slow when panning a plot. When continuously moving the mouse the refresh rates go down further. At some point it takes multiple seconds for the plot to redraw, leaving the user interface completely unresponsive. On the same machine the Qt4Agg backend works fine in comparison.
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
plt.plot(np.range(5))
plt.show()
The Qt5Agg backend is extremely slow when panning a plot. When
continuously moving the mouse the refresh rates go down further. At some
point it takes multiple seconds for the plot to redraw, leaving the user
interface completely unresponsive. On the same machine the Qt4Agg backend
works fine in comparison.
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
plt.plot(np.range(5))
plt.show()
—
Reply to this email directly or view it on GitHub #4604.
Tested on matplotlib master, but I've experienced this problem since the introduction of Qt5Agg. I just didn't depend on Qt5 back then and decided to wait until things might get more stable.
Also the slow panning behaviour applies to imshow plots and simple line plots alike.
Tested on matplotlib master, but I've experienced this problem since the
introduction of Qt5Agg. I just didn't depend on Qt5 back then and decided
to wait until things might get more stable.
Also the slow panning behaviour applies to imshow plots and simple line
plots alike.
—
Reply to this email directly or view it on GitHub #4604 (comment)
.
From a quick glance (if I'm getting the cProfile output right), the runtime of backend_bases.py:2989(drag_pan) on Qt4 is 1.695s, while Qt5 takes 5.690s and is called 4 times as much. Same proportions with backend_qt5.py:668(dynamic_update) and backend_qt5agg.py:140(draw).
However, I'm still missing a measure of how many frames are actually seen on display. It looks like on Qt5Agg the screen was updated no more than 3 times at all.
Just to show you how desperate I am, this is (another run) to show how these 200+ total redraws look like: http://youtu.be/1WAJp5ett9M
That is 8 seconds until the last frame is drawn. Also Ubuntu, but different machine and different graphics driver.
Thats definitely bad. I still can't really reproduce it (OSX and Ubuntu 15.04 virtual machine). Does anyone have any idea what controls the event rate?
It seems that (on systems I tested) Qt5 emits much more events leading to more FigureCanvasQt draw() calls. Ideally, a received event should notify Qt to do a deferred (costly) update the scene/figure. The current implementation however redraws the Agg canvas for every single event.
def draw(self):
"""
Draw the figure with Agg, and queue a request
for a Qt draw.
"""
# The Agg draw is done here; delaying it until the paintEvent
# causes problems with code that uses the result of the
# draw() to update plot elements.
FigureCanvasAgg.draw(self)
self._priv_update()
When I move the FigureCanvasAgg.draw() call to the paintEvent I'm getting excellent performance. @mfitzp states that matplotlib requires a draw call at this time since other code might rely on the fact that draw() implicitly updates plot elements too, like tick label positions. When writing the PGF backend I experienced a similar dilemma and ended up implementing a dummy flag for ignoring any drawing calls to the renderer when calling draw() isn't actually used for drawing. Maybe it is possible to add something like this to FigureCanvasAgg as well.
The way draw() is designed/used in matplotlib leads to very cumbersome handling within the backends, especially when the actual drawing process is very expensive and spurious redraws must be avoided at all costs. Matplotlib should have a method like recalculate() instead of using draw() for updating plot elements.
So apparently the draw_idle and dynamic_update canvas methods are supposed to be called for notifying a GUI to repaint itself. For GUI event handlers I replaced all the synchronous draw calls with the asynchronous draw_idle methods (proposed fix: b87bd20). As far as I can see this completely fixes the problem for me. Any regressions on other platforms or comments concerning the modifications?
So apparently the draw_idle and dynamic_update canvas methods are
supposed to be called for notifying a GUI to repaint itself. For GUI event
handlers I replaced all the synchronous draw calls with the asynchronous
draw_idle methods (proposed fix: b87bd20 b87bd20).
As far as I can see this completely fixes the problem for me. Any
regressions on other platforms or comments concerning the modifications?
—
Reply to this email directly or view it on GitHub #4604 (comment)
.
Yea, I must confess I don't understand why draw_idle was made that way. If I got the idea right, draw_idle should post a single redraw request to the event loop and prevent flooding the event loop with further requests as long as the outstanding draw wasn't dealt with. That is exactly what QWidget.update() is for, no need to reinvent it.
By the way, it would be interesting to check if this _priv_update() hack is still necessary on Qt5/Win. Did draw_idle emulate the intended behaviour of update() because it did (does?) not work on Windows?
I'm currently on holiday in Greece so won't be able to look at this til
next Wed.
For what it's worth I suspect it may be occurring due to Qt5 support for
high-resolution trackpads (and multi touch). This means Qt5 produces a lot
more events per scroll than Qt4.
Activity
WeatherGod commentedon Jul 8, 2015
Just to help work this out, what version of matplotlib? Also, is this
problem irrespective of whether one is plotting an image versus a line plot?
On Wed, Jul 8, 2015 at 11:13 AM, Peter Würtz notifications@github.com
wrote:
pwuertz commentedon Jul 8, 2015
Tested on matplotlib master, but I've experienced this problem since the introduction of Qt5Agg. I just didn't depend on Qt5 back then and decided to wait until things might get more stable.
Also the slow panning behaviour applies to imshow plots and simple line plots alike.
jenshnielsen commentedon Jul 8, 2015
I cannot reproduce this with:
PyQt 5.4.2 (Homebrew)
Qt 5.4.2 (Homebrew)
on OS X 10.10.3
pwuertz commentedon Jul 8, 2015
I think I saw this on every Ubuntu system so far. Someone who can confirm this?
WeatherGod commentedon Jul 8, 2015
It would be interesting to do some profiling to see if there are some
extraneous draw calls happening. It happened once to me in mplot3d.
On Wed, Jul 8, 2015 at 11:56 AM, Peter Würtz notifications@github.com
wrote:
jenshnielsen commentedon Jul 8, 2015
I will try to test it out on a Ubuntu system when I have a chance
pwuertz commentedon Jul 8, 2015
Here is a quick cProfile for panning a plot by moving the mouse for roughly 2 seconds, then stop and trying to close the window.
https://gist.github.com/pwuertz/8046f7092871046f3e50#file-profile_qt4agg-txt
https://gist.github.com/pwuertz/8046f7092871046f3e50#file-profile_qt5agg-txt
From a quick glance (if I'm getting the cProfile output right), the runtime of backend_bases.py:2989(drag_pan) on Qt4 is 1.695s, while Qt5 takes 5.690s and is called 4 times as much. Same proportions with backend_qt5.py:668(dynamic_update) and backend_qt5agg.py:140(draw).
jenshnielsen commentedon Jul 8, 2015
From my reading it looks like drag_pan is indeed called 4 times as often in the qt5 backend. The per_call time is almost the same. (0.024 vs 0.025 )
pwuertz commentedon Jul 8, 2015
However, I'm still missing a measure of how many frames are actually seen on display. It looks like on Qt5Agg the screen was updated no more than 3 times at all.
jenshnielsen commentedon Jul 8, 2015
As far as I can se it's calling figure draw 69 vs 242 times
pwuertz commentedon Jul 8, 2015
Just to show you how desperate I am, this is (another run) to show how these 200+ total redraws look like: http://youtu.be/1WAJp5ett9M
That is 8 seconds until the last frame is drawn. Also Ubuntu, but different machine and different graphics driver.
WeatherGod commentedon Jul 8, 2015
runsnakerun is awesome for visualizing cProfile output.
http://www.vrplumber.com/programming/runsnakerun/
On Wed, Jul 8, 2015 at 2:25 PM, Peter Würtz notifications@github.com
wrote:
jenshnielsen commentedon Jul 8, 2015
Thats definitely bad. I still can't really reproduce it (OSX and Ubuntu 15.04 virtual machine). Does anyone have any idea what controls the event rate?
tacaswell commentedon Jul 8, 2015
I wonder if the event selector rules are different and that the draws are being allowed to interrupt the event loop too often in Qt5
tacaswell commentedon Jul 8, 2015
attn @mfitzp
pwuertz commentedon Jul 8, 2015
It seems that (on systems I tested) Qt5 emits much more events leading to more FigureCanvasQt draw() calls. Ideally, a received event should notify Qt to do a deferred (costly) update the scene/figure. The current implementation however redraws the Agg canvas for every single event.
When I move the
FigureCanvasAgg.draw()call to thepaintEventI'm getting excellent performance.@mfitzp states that matplotlib requires a draw call at this time since other code might rely on the fact that
draw()implicitly updates plot elements too, like tick label positions. When writing the PGF backend I experienced a similar dilemma and ended up implementing adummyflag for ignoring any drawing calls to the renderer when callingdraw()isn't actually used for drawing. Maybe it is possible to add something like this to FigureCanvasAgg as well.The way
draw()is designed/used in matplotlib leads to very cumbersome handling within the backends, especially when the actual drawing process is very expensive and spurious redraws must be avoided at all costs. Matplotlib should have a method likerecalculate()instead of usingdraw()for updating plot elements.pwuertz commentedon Jul 9, 2015
So apparently the
draw_idleanddynamic_updatecanvas methods are supposed to be called for notifying a GUI to repaint itself. For GUI event handlers I replaced all the synchronousdrawcalls with the asynchronousdraw_idlemethods (proposed fix: b87bd20). As far as I can see this completely fixes the problem for me. Any regressions on other platforms or comments concerning the modifications?tacaswell commentedon Jul 9, 2015
I am a bit concerned about losing the single-shot time logic in draw_idle,
but I may be misunderstanding how that is working.
On Thu, Jul 9, 2015 at 11:00 AM Peter Würtz notifications@github.com
wrote:
pwuertz commentedon Jul 9, 2015
Yea, I must confess I don't understand why
draw_idlewas made that way. If I got the idea right,draw_idleshould post a single redraw request to the event loop and prevent flooding the event loop with further requests as long as the outstanding draw wasn't dealt with. That is exactly whatQWidget.update()is for, no need to reinvent it.By the way, it would be interesting to check if this
_priv_update()hack is still necessary on Qt5/Win. Diddraw_idleemulate the intended behaviour ofupdate()because it did (does?) not work on Windows?tacaswell commentedon Jul 10, 2015
I do not either and it is probably not worth the forensics to sort it out.
Can you make that branch into a PR?
mfitzp commentedon Jul 10, 2015
I'm currently on holiday in Greece so won't be able to look at this til
next Wed.
For what it's worth I suspect it may be occurring due to Qt5 support for
high-resolution trackpads (and multi touch). This means Qt5 produces a lot
more events per scroll than Qt4.
attn @mfitzp https://github.com/mfitzp
—
Reply to this email directly or view it on GitHub
#4604 (comment)
.
Merge pull request #4612 from pwuertz/fix_4604