19

Today I upgraded matplotlib to version 2.0.2, after not upgrading for possibly 3 years.

Now I have the problem that in interactive plots the window always comes to the front, as if this here make matplotlib plotting window pop up as the active one had come to be the default behaviour.

How can I deactivate it? I don't want the window to come to front every 5 seconds and raise over my text editor, browser, ...

I want it to stay in the z-ordering where I've placed it, be it to front or behind an active window.

I believe that the following commit from 31 Jan 2016 is responsible for this problematic behaviour: tkagg: raise each new window; partially addresses #596

Found a related comment on Github https://github.com/matplotlib/matplotlib/issues/596#issuecomment-305298577

it appears that it is my call to plt.pause which is causing this issue, and not the plt.plot call.

Daniel F
  • 13,684
  • 11
  • 87
  • 116
  • I think it is pretty natural that if you issue a command doing something in a window you would like to have the window come on top. It does not steal the focus though (which would indeed be annoying). If you are thinking of the matplotlib window more like an application that should simply stay where it is, no matter what happens inside (maybe like a video player which continues playing the movie even if minimized) you may want to do exactly that: Create an independent application. If you update your question to show the actual problem (or rather a [mcve] of it), one might help you doing so. – ImportanceOfBeingErnest Aug 17 '17 at 10:37
  • 3
    @ImportanceOfBeingErnest The issue is with `plt.pause()`. It performs something similar to `time.sleep()`, but does not block the UI, ie you can drag the window and resize it. With `plt.show()` it is perfectly understandable that the window is fully brought to the front, but `plt.pause()`? There's no reason for a command that has the functionality of entering an OS-friendly idle state to do anything regarding window management, like moving a window in front of your browser. I solved this by using the `matplotlib.animation`, but this "feature" has seriously broken the `plt.pause()` function. – Daniel F Aug 17 '17 at 11:20
  • `plt.pause` does three main things. (1) It redraws the canvas. (2) It calls `plt.show` (3) It starts the event queue. So if it is valid for `show` to make the window come in front, it will surely be for any command that calls `show` as well. However, I'm not sure what this question is actually about. Is it a complaint about some feature? Do you want help with something? – ImportanceOfBeingErnest Aug 17 '17 at 11:42
  • @ImportanceOfBeingErnest It was a question. I didn't know where this behavior came from. Instead of just waiting for an answer I searched deeper, I assume that this is ok. And the Info that surfaced got added to the question. I assume that this is also ok. Yes, `plt.pause()` calls `plt.show()`, but having it then act this way is a regression. I no longer want help with this question. Do you want me to delete it, or to leave it for others who may encounter the same problem? – Daniel F Aug 17 '17 at 11:56
  • 1
    The fact that you have found a solution which works for you somehow tells us that there is a possible workaround for this issue. It might thus be helpful for others as well and you may answer your own question with it. I also added another workaround, in case changing the backend is an option. – ImportanceOfBeingErnest Aug 17 '17 at 11:58

4 Answers4

25

Changing the backend

The issue seems only present using the Tk backend. Using the Qt backend, the window would stay where it was while updating with plt.pause.

To change the backend use those lines at the beginning of your script.

import matplotlib
matplotlib.use("Qt4agg") # or "Qt5agg" depending on you version of Qt

Modifying plt.pause

If changing the backend is not an option, the following might help. The cause of the window constantly popping up to the front comes from plt.pause calling plt.show() internally. You therefore implement you own pause function, without calling show. This requires to be in interactive mode plt.ion() first and then at least once call plt.show(). Afterwards you may update the plot with the custom mypause function as shown below.

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from time import time
from random import random

plt.ion()
# set up the figure
fig = plt.figure()
plt.xlabel('Time')
plt.ylabel('Value')

plt.show(block=False)

def mypause(interval):
    backend = plt.rcParams['backend']
    if backend in matplotlib.rcsetup.interactive_bk:
        figManager = matplotlib._pylab_helpers.Gcf.get_active()
        if figManager is not None:
            canvas = figManager.canvas
            if canvas.figure.stale:
                canvas.draw()
            canvas.start_event_loop(interval)
            return


t0 = time()
t = []
y = []
while True:
    t.append( time()-t0 )
    y.append( random() )
    plt.gca().clear()
    plt.plot( t , y )
    mypause(1)

Using an animation.

Finally, using a matplotlib.animation class would render all of the above obsolete. An example for matplotlib.animation.FuncAnimation is shown on the matplotlib page.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • I tried had that, but `Tk` was the only one which had no dependencies which my machine couldn't fulfill, so I opted for the "use `matplotlib.animation`"-route – Daniel F Aug 17 '17 at 12:00
  • Ok, so the solution is a bit more involved. You may use you own version of `plt.pause` which does not call `show` as in the updated answer above. – ImportanceOfBeingErnest Aug 17 '17 at 12:08
  • This works very well. I applied it ("Modifying `plt.pause`") to another script which also made use of `plt.pause()` and it was a quick fix. Thanks for your effort! – Daniel F Aug 17 '17 at 12:20
  • Using `draw_idle` is a bit better here, it will defer the actually rendering until the GUI repaints. – tacaswell Aug 20 '17 at 16:23
  • 1
    @tacaswell `canvas.draw()` is the code that [`pause` uses internally](https://github.com/matplotlib/matplotlib/blob/225a4a0bdbf24e767ce2491a1580080e0fa2a10e/lib/matplotlib/pyplot.py#L297). So I guess if you think that `canvas.draw_idle()` should be used instead, why not change it in the matplotlib code? – ImportanceOfBeingErnest Aug 20 '17 at 21:22
  • Because no one has noticed and changed it yet. Could you put an PR to do that? – tacaswell Aug 20 '17 at 21:46
  • @tacaswell I did so [here](https://github.com/matplotlib/matplotlib/pull/9064). Hope this will not break anything ;-) – ImportanceOfBeingErnest Aug 21 '17 at 10:14
  • For me the line `canvas.start_event_loop(interval)` is actually what causes the plot window to come to the front. I don't have a solution yet – JBSnorro Oct 29 '17 at 14:12
  • @JBSnorro Use `matplotlib.animation` instead. – ImportanceOfBeingErnest Oct 29 '17 at 14:25
11

It is April 2019 and the mypause() function (copied from the upto date pyplot implementation) for Matplotlib 3.0.3 should look more like

import time
import matplotlib.pyplot as plt
def mypause(interval):
    manager = plt._pylab_helpers.Gcf.get_active()
    if manager is not None:
        canvas = manager.canvas
        if canvas.figure.stale:
            canvas.draw_idle()        
        #plt.show(block=False)
        canvas.start_event_loop(interval)
    else:
        time.sleep(interval)

After some testing (Qt5 backend/Spyder/Windows 7 64bit) the calls that do the trick for me are:

#plt.pause(0.001) #Brings plot to foreground
#fig.canvas.draw() #Does not work
#plt.draw_all() #Does not work
#plt.draw() #Does not work
#fig.canvas.flush_events() #Updates only if I click the figure 
#import time; time.sleep(0.001) #Does not help flush_events()
#fig.canvas.draw_idle() #Does not work by itself
#fig.canvas.start_event_loop(0.001) #Does not work by itself
#mypause(0.001) #Works!

#Works!
fig.canvas.draw_idle()
fig.canvas.start_event_loop(0.001)

Were fig is your figure object. Either of both alone didn't work in my case. According to the animation documentation this is essentially what FuncAnimation does.

neurosock
  • 496
  • 6
  • 13
10

I had the same problem on the Mac. I'm not sure if this is the best approach, but instead of using plt.pause(0.001), I switched to fig.canvas.start_event_loop(0.001) to update each frame on my animation. This allows the windows to stay in the background.

DanHickstein
  • 6,588
  • 13
  • 54
  • 90
  • 1
    This worked for me on Gnome/Ubuntu, but I also had to add a call to `fig.canvas.draw_idle()` as mentioned by @pepgma . – Jeff Learman Feb 11 '21 at 16:56
0

I rewrote the code to use matplotlib.animation with the help from this documentation page https://matplotlib.org/devdocs/api/animation_api.html#module-matplotlib.animation

I got to that page through this one https://matplotlib.org/devdocs/api/_as_gen/matplotlib.pyplot.pause.html

which states the following about matplotlib.pyplot.pause

This can be used for crude animation. For more complex animation, see matplotlib.animation.

This function is experimental; its behavior may be changed or extended in a future release.

Daniel F
  • 13,684
  • 11
  • 87
  • 116