Skip to content

Make pyplot.pause not give focus to the figure window #11131

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

Closed
FSund opened this issue Apr 26, 2018 · 18 comments
Closed

Make pyplot.pause not give focus to the figure window #11131

FSund opened this issue Apr 26, 2018 · 18 comments
Milestone

Comments

@FSund
Copy link

FSund commented Apr 26, 2018

Bug report

Bug summary

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 window
plt.pause(2)
# now the figure will be on top, and have focus

Matplotlib version

  • Operating system: Windows 10 Pro build 1709
  • Matplotlib version: 2.2.2 (via Anaconda)
  • Matplotlib backend: Qt5Agg
  • Python version: 3.5.3
@FSund
Copy link
Author

FSund commented Apr 26, 2018

I have recreated an older version of pause

import matplotlib
from matplotlib import _pylab_helpers
from matplotlib.rcsetup import interactive_bk as _interactive_bk
def pause(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']
    if backend in _interactive_bk:
        figManager = _pylab_helpers.Gcf.get_active()
        if figManager is not None:
            canvas = figManager.canvas
            if canvas.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.
    import time
    time.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:

def pause(interval, focus_figure=True):
    backend = matplotlib.rcParams['backend']
    if backend in _interactive_bk:
        figManager = _pylab_helpers.Gcf.get_active()
        if figManager is not None:
            canvas = figManager.canvas
            if canvas.figure.stale:
                canvas.draw()
            if focus_figure:
                plt.show(block=False)
            canvas.start_event_loop(interval)
            return

    # No on-screen figure is active, so sleep() is all we need.
    import time
    time.sleep(interval)

But I am no matplotlib expert, so I don't know what side effects removing the call to plt.show() might have.

@efiring
Copy link
Member

efiring commented 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
Copy link

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
Copy link

mattngc commented 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
Copy link
Member

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
Copy link

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
Copy link

evilmav commented 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
Copy link
Member

jklymak commented 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
Copy link

evilmav commented 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.

pengfe2017 added a commit to pengfe2017/DataPlot that referenced this issue Oct 6, 2019
…Make pyplot.pause not give focus to the figure window
@carlosgmartin
Copy link

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.

@starfleetjames
Copy link

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
Copy link
Contributor

anntzer commented Dec 10, 2019

Hopefully #15440 will make this easier to control (by providing the relevant snippets for all toolkits).

@starfleetjames
Copy link

I did find a workaround for at least my case: let the other figure window steal focus but set it back programmatically with

plt.figure(fig.number)
plt.get_current_fig_manager().show()

where fig is the figure that I want to stay in focus.

@ImportanceOfBeingErnest
Copy link
Member

I mean, you can always replace plt.pause(0.3) with

plt.gcf().canvas.draw_idle()
plt.gcf().canvas.start_event_loop(0.3)

if in interactive mode (plt.ion()).

@petrvokac
Copy link

I mean, you can always replace plt.pause(0.3) with

plt.gcf().canvas.draw_idle()
plt.gcf().canvas.start_event_loop(0.3)

if in interactive mode (plt.ion()).

Bingo, thank you

@timhoffm
Copy link
Member

Closed by #15440.

@QuLogic QuLogic added this to the v3.3.0 milestone Jun 17, 2020
@carlosgmartin
Copy link

@ImportanceOfBeingErnest Is the first line necessary? It seems to work with the second line alone.

@tacaswell
Copy link
Member

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.

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