I'm using matplotlib.pyplot.pause() to update a plot in a loop, mostly for debugging purposes. On my old matplotlib version (maybe 2.0.2, not sure) this worked fine, but after updating to 2.2.2 the pause function seems to have changed behaviour. Previously the figure window was left in the background, but now it is brought to the front and given focus. This makes hit hard to cancel the running script (Ctrl+C in Jupyter QtConsole), which I often use while debugging a script.
I know it is noted in the docs of pause that "This function is experimental; its behavior may be changed or extended in a future release.", but is there any way to bring back the old behaviour? Perhaps via a argument focus_window=False or something like that?
Code for reproduction
plt.figure()
# put the figure in the background, or below another windowplt.pause(2)
# now the figure will be on top, and have focus
importmatplotlibfrommatplotlibimport_pylab_helpersfrommatplotlib.rcsetupimportinteractive_bkas_interactive_bkdefpause(interval):
""" Pause for *interval* seconds. If there is an active figure it will be updated and displayed, and the GUI event loop will run during the pause. If there is no active figure, or if a non-interactive backend is in use, this executes time.sleep(interval). This can be used for crude animation. For more complex animation, see :mod:`matplotlib.animation`. This function is experimental; its behavior may be changed or extended in a future release. """backend=matplotlib.rcParams['backend']
ifbackendin_interactive_bk:
figManager=_pylab_helpers.Gcf.get_active()
iffigManagerisnotNone:
canvas=figManager.canvasifcanvas.figure.stale:
canvas.draw()
plt.show(block=False)
canvas.start_event_loop(interval)
return# No on-screen figure is active, so sleep() is all we need.importtimetime.sleep(interval)
When I use this version of pause, the figure is still brought to the front and given focus, so it seems like the change I noticed is due do some other changes deeper in the matplotlib system. Any ideas?
I don't necessarily need any changes to the pause function, I just want a simple way to update/refresh/redraw a figure in a loop without bringing the figure to the front. But it would be nice if that was possible with pause, perhaps via an argument like focus_figure like mentioned.
EDIT: I managed to not have the figure brought to the front by commenting out the line plt.show(block=False) in the snippet above. So a simple fix that works like I want is something like this:
defpause(interval, focus_figure=True):
backend=matplotlib.rcParams['backend']
ifbackendin_interactive_bk:
figManager=_pylab_helpers.Gcf.get_active()
iffigManagerisnotNone:
canvas=figManager.canvasifcanvas.figure.stale:
canvas.draw()
iffocus_figure:
plt.show(block=False)
canvas.start_event_loop(interval)
return# No on-screen figure is active, so sleep() is all we need.importtimetime.sleep(interval)
But I am no matplotlib expert, so I don't know what side effects removing the call to plt.show() might have.
The underlying problem is how to control both raise and focus behavior on each of the backends and for every platform and window manager. One might expect each gui backend to have a simple API for doing this, but it seems not be be the case at all. I think what you are running into is #6384. The problem that was addressing was the tendency for Qt windows to come up behind other windows, so there was no indication that a plot had been made. But I completely understand the opposite problem, of a plot popping up on top of something when that is not what you want.
What we might be able to do is give each backend FigureManager a raise method, which would be a no-op if there is no raise functionality available, and move the functionality from the manager's show to the new raise. Then the pyplot show could call raise by default, but this could be turned off with a kwarg.
Reacted by Filip Sund, hofingermarkus and Jules Talloen
Can you please revert the change to pause? Windows should never steal focus just to fix them popping up in the background - the reason they pop up in the background is due to the window manager choosing to keep focus on the application the user is currently working with. This change is incredibly disruptive, we also have multiple windows refreshing continuously and are unable to work with this change.
Reacted by Adam Ginsburg, Carlos Martin and lambdacoffee
Just adding support to reverting the behavior. I have found this change breaks my default development environment. I used to start a job running with interactive plots updating and then move to another Ubuntu workspace to check email or work on the script in another window, etc. Now I can't do any of this because the plot window is constantly stealing focus. For now I have reverted to 2.0.2, but please consider reverting this change.
Maybe it's worth noting that plt.pause is meant to be used in interactive mode; i.e. you usually want to use it to make some change in the plot apparent. In that sense it makes perfect sense to give it focus upon such change in the plot.
In case people want to run some continuous loop in the background, one would rather use either the provided matplotlib.animation classes, or run a loop within the GUI's event loop, possibly using a timer that matplotlib provides as well.
I use plt.pause mostly in the interactive mode for doing quick and dirty first tests before I build something more sophisticated.
And it definitely depends on the situation if stealing focus is something good or something bad.
In the worst case it updates so fast, that you can't even press ctrl+c, although you just wanted to get a quick insight without much coding effort... (I know that this is inefficient etc. but often this would just be good enough for these first test...)
I add to the request: I want to plot stuff in an interactive (zoomable etc) window but still be able to use my terminal the program is executed in. It is impossible if window is stealing the focus. This is not at all a minor problem.
Within an interactive python/IPython session or from the operating system shell? If the latter you just put the python invocation in the background. If the former, I don’t think that’s possible.
@jklymak
From the shell, but I basically want to run a curses UI as an overview and control of a measurement system and plot some values in a Real-Time chart I can zoom around. If I put the process into background I loose the curses.
In that sense it makes perfect sense to give it focus upon such change in the plot.
@ImportanceOfBeingErnest That's a false assumption. For example, I might be running a simulation in the background and want to see what it looks like every now and then. That doesn't mean I want the window popping up repeatedly in front of everything else. This behaviour is very annoying.
Reacted by James Paul Mason, George Anderson and lambdacoffee
Second @carlosgmartin statement. In my case I'm interacting with one figure and updating plots in a second figure and I don't want that second figure to steal focus every time it updates.
The draw_idlemay not be needed in some cases (it means "the next time you repaint the GUI window please re-render the figure first". If that re-rendering was scheduled anyway it is a no-op, but if you have changed the figure but not called draw_idle you may see stale output) but in will do no harm in all cases.
Activity
FSund commented on Apr 26, 2018
I have recreated an older version of pause
When I use this version of pause, the figure is still brought to the front and given focus, so it seems like the change I noticed is due do some other changes deeper in the matplotlib system. Any ideas?
I don't necessarily need any changes to the pause function, I just want a simple way to update/refresh/redraw a figure in a loop without bringing the figure to the front. But it would be nice if that was possible with pause, perhaps via an argument like focus_figure like mentioned.
EDIT: I managed to not have the figure brought to the front by commenting out the line plt.show(block=False) in the snippet above. So a simple fix that works like I want is something like this:
But I am no matplotlib expert, so I don't know what side effects removing the call to plt.show() might have.
efiring commented on Apr 27, 2018
The underlying problem is how to control both raise and focus behavior on each of the backends and for every platform and window manager. One might expect each gui backend to have a simple API for doing this, but it seems not be be the case at all. I think what you are running into is #6384. The problem that was addressing was the tendency for Qt windows to come up behind other windows, so there was no indication that a plot had been made. But I completely understand the opposite problem, of a plot popping up on top of something when that is not what you want.
What we might be able to do is give each backend FigureManager a raise method, which would be a no-op if there is no raise functionality available, and move the functionality from the manager's show to the new raise. Then the pyplot show could call raise by default, but this could be turned off with a kwarg.
CnlPepper commented on May 30, 2018
Can you please revert the change to pause? Windows should never steal focus just to fix them popping up in the background - the reason they pop up in the background is due to the window manager choosing to keep focus on the application the user is currently working with. This change is incredibly disruptive, we also have multiple windows refreshing continuously and are unable to work with this change.
mattngc commented on May 31, 2018
Just adding support to reverting the behavior. I have found this change breaks my default development environment. I used to start a job running with interactive plots updating and then move to another Ubuntu workspace to check email or work on the script in another window, etc. Now I can't do any of this because the plot window is constantly stealing focus. For now I have reverted to 2.0.2, but please consider reverting this change.
ImportanceOfBeingErnest commented on May 31, 2018
Maybe it's worth noting that plt.pause is meant to be used in interactive mode; i.e. you usually want to use it to make some change in the plot apparent. In that sense it makes perfect sense to give it focus upon such change in the plot.
In case people want to run some continuous loop in the background, one would rather use either the provided matplotlib.animation classes, or run a loop within the GUI's event loop, possibly using a timer that matplotlib provides as well.
hofingermarkus commented on Jun 14, 2018
I use plt.pause mostly in the interactive mode for doing quick and dirty first tests before I build something more sophisticated.
And it definitely depends on the situation if stealing focus is something good or something bad.
In the worst case it updates so fast, that you can't even press ctrl+c, although you just wanted to get a quick insight without much coding effort... (I know that this is inefficient etc. but often this would just be good enough for these first test...)
So please make it choose-able by the users!
evilmav commented on Aug 11, 2018
I add to the request: I want to plot stuff in an interactive (zoomable etc) window but still be able to use my terminal the program is executed in. It is impossible if window is stealing the focus. This is not at all a minor problem.
jklymak commented on Aug 11, 2018
Within an interactive python/IPython session or from the operating system shell? If the latter you just put the python invocation in the background. If the former, I don’t think that’s possible.
evilmav commented on Aug 11, 2018
@jklymak
From the shell, but I basically want to run a curses UI as an overview and control of a measurement system and plot some values in a Real-Time chart I can zoom around. If I put the process into background I loose the curses.
add pause method in Dataplot, following matplotlib/matplotlib#11131, …
carlosgmartin commented on Dec 5, 2019
@ImportanceOfBeingErnest That's a false assumption. For example, I might be running a simulation in the background and want to see what it looks like every now and then. That doesn't mean I want the window popping up repeatedly in front of everything else. This behaviour is very annoying.
starfleetjames commented on Dec 9, 2019
Second @carlosgmartin statement. In my case I'm interacting with one figure and updating plots in a second figure and I don't want that second figure to steal focus every time it updates.
anntzer commented on Dec 10, 2019
Hopefully #15440 will make this easier to control (by providing the relevant snippets for all toolkits).
starfleetjames commented on Dec 10, 2019
I did find a workaround for at least my case: let the other figure window steal focus but set it back programmatically with
where fig is the figure that I want to stay in focus.
ImportanceOfBeingErnest commented on Dec 10, 2019
I mean, you can always replace plt.pause(0.3) with
if in interactive mode (plt.ion()).
petrvokac commented on Jun 10, 2020
Bingo, thank you
timhoffm commented on Jun 17, 2020
Closed by #15440.
carlosgmartin commented on Jun 17, 2020
@ImportanceOfBeingErnest Is the first line necessary? It seems to work with the second line alone.
tacaswell commented on Jun 17, 2020
The draw_idlemay not be needed in some cases (it means "the next time you repaint the GUI window please re-render the figure first". If that re-rendering was scheduled anyway it is a no-op, but if you have changed the figure but not called draw_idle you may see stale output) but in will do no harm in all cases.