20

I have a python / matplotlib application that frequently updates a plot with new data coming in from a measurement instrument. The plot window should not change from background to foreground (or vice versa) with respect to other windows on my desktop when the plot is updated with new data.

This worked as desired with Python 3 on a machine running Ubuntu 16.10 with matplotlib 1.5.2rc. However, on a different machine with Ubuntu 17.04 and matplotlib 2.0.0, the figure window pops to the front every time the plot is updated with new data.

How can I control the window foreground/background behavior and keep the window focus when updating the plot with new data?

Here's a code example illustrating my plotting routine:

import matplotlib import matplotlib.pyplot as plt from time import time from random import random print ( matplotlib.__version__ ) # set up the figure fig = plt.figure() plt.xlabel('Time') plt.ylabel('Value') plt.ion() # plot things while new data is generated: t0 = time() t = [] y = [] while True: t.append( time()-t0 ) y.append( random() ) fig.clear() plt.plot( t , y ) plt.pause(1) 
6
  • As I cannot test this in any way, just a suggestion what to try: instead of plt.ion(), what happens if you use plt.show(block=False) and then in your while loop add plt.draw() right after the plt.plot() call? Commented May 31, 2017 at 7:50
  • @Thomas Kühn: Thanks for your suggestion. However, that did not change anything on the Ubuntu 17.04 / matplotlib 2.0.0 environment. Commented May 31, 2017 at 7:57
  • What backend are you using? It may be worth changing this to see if this solves the problem. Commented May 31, 2017 at 13:14
  • @Ed Smith: I don't know which backend is used by default with my example code shown above. But just to be sure I tried it with explicitly selecting the TkAgg backend. This did not change anything. Commented May 31, 2017 at 19:58
  • Here's a workaround stackoverflow.com/a/45734500/277267 Commented Aug 19, 2017 at 8:58

2 Answers 2

22

matplotlib was changed somewhere from version 1.5.2rc to 2.0.0 such that pyplot.show() brings the window to the foreground (see here). The key is therefore to avoid calling pyplot.show() in the loop. The same goes for pyplot.pause().

Below is a working example. This will still bring the window to the foreground at the beginning. But the user may move the window to the background, and the window will stay there when the figure is updated with new data.

Note that the matplotlib animation module might be a good choice to produce the plot shown in this example. However, I couldn't make the animation work with interactive plot, so it blocks further execution of other code. That's why I could not use the animation module in my real-life application.

import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt import time from random import random print ( matplotlib.__version__ ) # set up the figure plt.ion() fig = plt.figure() ax = plt.subplot(1,1,1) ax.set_xlabel('Time') ax.set_ylabel('Value') t = [] y = [] ax.plot( t , y , 'ko-' , markersize = 10 ) # add an empty line to the plot fig.show() # show the window (figure will be in foreground, but the user may move it to background) # plot things while new data is generated: # (avoid calling plt.show() and plt.pause() to prevent window popping to foreground) t0 = time.time() while True: t.append( time.time()-t0 ) # add new x data value y.append( random() ) # add new y data value ax.lines[0].set_data( t,y ) # set plot data ax.relim() # recompute the data limits ax.autoscale_view() # automatic axis scaling fig.canvas.flush_events() # update the plot and take care of window events (like resizing etc.) time.sleep(1) # wait for next loop iteration 
Sign up to request clarification or add additional context in comments.

4 Comments

If you have UI problems due to using time.sleep, like being unable to move/resize the window, take a look at this answer stackoverflow.com/a/45734500/277267 which is somewhat of a re-implementation of the plt.pause method.
if you're just updating lines and don't need other rescaling, etc., you can just replace plt.pause() with fig.canvas.flush_events(). this is the key call difference
If I could mark this answer as useful a hundred time, I'd certainly do it. Thanks a lot for your code snippet, it works wonderfuly :)
matplotlib is giving me such headaches... when using Qt as a backend (matplotlib.use('Qt5Agg')), you need to insert a fig.canvas.draw_idle() before the flush_events call...
1

For the tkinter backend (matplotlib.use("TkAgg")), using flush_events is not sufficient: you also need to call fig.canvas.draw_idle() before each fig.canvas.flush_events(). As @samlaf wrote, the same holds for the Qt5Agg backend.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.