0

The following code displays the following window:

import numpy as np 
import matplotlib.pylab as pl
import matplotlib.gridspec as gridspec
from matplotlib import pyplot as plt 

def plot_stuff(x,y,z):  
    gs = gridspec.GridSpec(3, 1) 
    plt.style.use('dark_background')
    pl.figure("1D Analysis")
    ax = pl.subplot(gs[0, 0]) 
    ax.set_ylabel('X VALUE')
    pl.plot(x, color="red")
    ax = pl.subplot(gs[1, 0]) 
    ax.set_ylabel('Y VALUE')
    pl.plot(y, color="green")    
    ax = pl.subplot(gs[2, :])
    ax.set_ylabel('Z VALUE')
    pl.plot(z, color="blue")
    plt.show()

How do I close the window without an explicit mouse click?

I need to visualize a LOT of data so I'm searching a way to automating the process of opening and closing windows.

I know that plt.show() is a blocking operation and I've tried using the plt.close("all") method as mentioned in the related questions but the window remains there, does not close and I have to close it manually.

I need a simple code for automating the process of opening a window, visualize the data, closing the window after a certain interval of time; and then repeat the procedure in a for loop fashion.

Employee
  • 3,109
  • 5
  • 31
  • 50
  • I mean *something* needs to happen for the window to close. What would that be? – ImportanceOfBeingErnest Dec 06 '18 at 15:36
  • Obviously an event, like a mouse click in this case. How would you go with that? – Employee Dec 06 '18 at 15:37
  • Well, the question reads "without an explicit mouse click", so I'm a bit confused. Anyways, maybe you rather show the code where `plt.close()` doesn't work? Because it should. So something must be different in your case compared to all the others and in order to find out one would need to see the code. – ImportanceOfBeingErnest Dec 06 '18 at 15:40
  • 1
    The problem is that `show()` is a *blocking* function by design. There's a question/answer here (https://stackoverflow.com/questions/11140787/closing-pyplot-windows) which might be helpful, but it isn't a trivial problem. – DatHydroGuy Dec 06 '18 at 15:43
  • @ImportanceOfBeingErnest The `main` function produces in a foor loop randomly generated x,y,z arrays and then calls for every iteration the `plot_stuff` function. This is the workflow. – Employee Dec 06 '18 at 15:45
  • I don't get why this question has been downwoted. – Employee Dec 06 '18 at 15:50
  • 1
    You want me to find out how your code looks like to then give an answer which is different from the obvious `plt.close()`? – ImportanceOfBeingErnest Dec 06 '18 at 15:50
  • I can share the full code – Employee Dec 06 '18 at 15:56
  • The simpler solution would be to *not* close the window, but instead replace the *content* of the figure at each iteration – Diziet Asahi Dec 06 '18 at 15:58
  • @DizietAsahi Thank you a lot. How? – Employee Dec 06 '18 at 16:00
  • 1
    Another potential problem is that you are mixing pylab (which is no longer supported) and pyplot. see https://matplotlib.org/faq/usage_faq.html#matplotlib-pyplot-and-pylab-how-are-they-related – Diziet Asahi Dec 06 '18 at 16:02
  • It's still unclear why `plt.close()` wouldn't work. It does. As commented already 2 months ago, you can share a [mcve] that would show the usecase and in how far you think `plt.close()` doesn't do what it should. – ImportanceOfBeingErnest Jan 30 '19 at 19:54

5 Answers5

1

Here is another solution, using an explicit close statement to close then recreate the figure at each iteration

from matplotlib import gridspec
import matplotlib.pyplot as plt
import numpy as np


def plot_stuff(x, y, z):
    gs = gridspec.GridSpec(3, 1)
    plt.style.use('dark_background')
    fig = plt.figure("1D Analysis")
    ax = plt.subplot(gs[0, 0])
    ax.set_ylabel('X VALUE')
    plt.plot(x, color="red")
    ax = plt.subplot(gs[1, 0])
    ax.set_ylabel('Y VALUE')
    plt.plot(y, color="green")
    ax = plt.subplot(gs[2, :])
    ax.set_ylabel('Z VALUE')
    plt.plot(z, color="blue")
    return fig


things_to_plot = [np.random.random(size=(100, 3)),
                  np.ones((100, 3)),
                  np.random.random(size=(100, 3))]
delay = 5

if __name__ == "__main__":
    plt.ion()
    for things in things_to_plot:
        fig = plot_stuff(x=things[:, 0], y=things[:, 1], z=things[:, 2])
        plt.show()
        plt.pause(delay)
        plt.close()
Employee
  • 3,109
  • 5
  • 31
  • 50
Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75
  • Traceback (most recent call last): File "18.py", line 33, in plt.close(fig=fig) TypeError: close() got an unexpected keyword argument 'fig' – Employee Feb 04 '19 at 15:35
  • Your approach works and you are the winner of the bounty. Anyway plt.close(fig=fig) does not work and I've edited your answer changing it into plt.close() which does work, so please accept the edit in order for me to mark your answer as accepted. – Employee Feb 04 '19 at 15:42
  • 1
    Curious, the code as written works just fine on my machine. Perhaps a different version of matplotlib? Anyway, glad you found my answer useful. – Diziet Asahi Feb 04 '19 at 15:53
1

Here's a different approach using animation:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

interval = 100  # in ms
rescale_axis = False

# Properties of the data
num_batches = 10
num_obs = [20, 30, 10]
feature_names = ['X VALUE', 'Y VALUE', 'Z VALUE']
feature_colors = ['red', 'green', 'blue']
num_features = len(feature_names)
data_to_plot = [np.random.rand(num_batches, num_obs[f]) for f in range(num_features)]

# Create the figure
plt.style.use('dark_background')
fig, axes = plt.subplots(num_features, 1)
fig.canvas.set_window_title('1D Analysis')
# Initial plot
lines = []
for f in range(num_features):
    line, = axes[f].plot(data_to_plot[f][0, :], c=feature_colors[f])
    lines.append(line)
    axes[f].set_ylabel(feature_names[f])
    if not rescale_axis:
        axes[f].set_ylim(0, 1)

def plot_stuff(xyz):
    x, y, z = xyz
    for f, data in enumerate([x, y, z]):
        lines[f].set_data([ix for ix in range(len(data))], data)
        if rescale_axis:
            axes[f].relim()
            axes[f].autoscale_view()
    return lines

def data_gen():
    for x, y, z in zip(*data_to_plot):
        yield x, y, z

ani = animation.FuncAnimation(fig, plot_stuff, data_gen, interval=interval)
ani.save('results.gif', dpi=80, writer='imagemagick')
plt.show()

From which you can even extract a .gif output: git output example

However, I've done a lot of visual data analysis like this, and a lot of times you'd want to go back and forth while browsing the results, taking your time for some plots while others are not as interesting and you just skip them quickly.

I know it's not what you asked for, but maybe it would be helpful to save the plots to a .pdf instead, with each plot in a different page:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages

rescale_axis = False
output_pdf_file = 'results.pdf'

# Properties of the data
num_batches = 10
num_obs = [20, 30, 10]
feature_names = ['X VALUE', 'Y VALUE', 'Z VALUE']
feature_colors = ['red', 'green', 'blue']
num_features = len(feature_names)
data_to_plot = [np.random.rand(num_batches, num_obs[f]) for f in range(num_features)]

# Create the figure
plt.style.use('dark_background')
fig, axes = plt.subplots(num_features, 1)
fig.canvas.set_window_title('1D Analysis')

# Initial plot
lines = []
for f in range(num_features):
    line, = axes[f].plot(data_to_plot[f][0, :], c=feature_colors[f])
    lines.append(line)
    axes[f].set_ylabel(feature_names[f])
    if not rescale_axis:
        axes[f].set_ylim(0, 1)

def plot_stuff(x, y, z):
    for f, data in enumerate([x, y, z]):
        lines[f].set_data([ix for ix in range(len(data))], data)
        if rescale_axis:
            axes[f].relim()
            axes[f].autoscale_view()
    return lines

with PdfPages(output_pdf_file) as pdf:
    for x, y, z in zip(*data_to_plot):
        plot_stuff(x, y, z)
        pdf.savefig()
Pasa
  • 652
  • 6
  • 13
0

You can embed your matplotlib plot in a window created with a GUI library for python and use the GUI library API to handle the window.

The matplotlib user_interfaces Examples provide many examples using various GUI libraries.

I would go for Qt5 using PySide2 (see embedding_in_qt5), Qt4 using PySide (see embedding_in_qt4 or embedding_in_qt4_wtoolbar) or Tkinter (see embedding_in_tk or embedding_in_tk_canvas).

jpeg
  • 2,372
  • 4
  • 18
  • 31
0

I have tested the below solution and which is working perfectly. I have used only pylab module.

import numpy as np 
import matplotlib.pylab as pl
import matplotlib.gridspec as gridspec

def plot_stuff(x,y,z):
    pl.ion() # interactive mode on
    gs = gridspec.GridSpec(3, 1) 
    pl.style.use('dark_background')
    pl.figure("1D Analysis")
    ax = pl.subplot(gs[0, 0]) 
    ax.set_ylabel('X VALUE')
    pl.plot(x, color="red")
    ax = pl.subplot(gs[1, 0]) 
    ax.set_ylabel('Y VALUE')
    pl.plot(y, color="green")    
    ax = pl.subplot(gs[2, :])
    ax.set_ylabel('Z VALUE')
    pl.plot(z, color="blue")
    pl.show()
    pl.pause(3) # pause for 3 sec
    pl.close()  # close the window

items = [np.random.rand(100, 3),
            np.random.randint(10, size=(100, 3)),
            np.random.rand(100, 3)]


for item in items:
    plot_stuff(x=item[:, 0], y=item[:, 1], z=item[:, 2])
sbodd
  • 321
  • 2
  • 7
-1

I would tackle the problem differently and create only one figure, and update the content at each iteration.

import matplotlib.pyplot as plt
from matplotlib import gridspec
from matplotlib.axes import Axes
import numpy as np
from matplotlib.figure import Figure


def plot_stuff(x, y, z, fig: Figure = None):
    print(f"plotting x[{x.shape}],y[{y.shape}],z[{z.shape}] in fig[{fig.__repr__()}]")
    if fig is None:
        fig = plt.gcf()
    fig.clf()
    gs = gridspec.GridSpec(3, 1)
    fig.canvas.set_window_title("1D Analysis")
    ax1: Axes = plt.subplot(gs[0, 0])
    ax1.set_ylabel('X VALUE')
    ax1.plot(x, color="red")
    ax2: Axes = plt.subplot(gs[1, 0])
    ax2.set_ylabel('Y VALUE')
    ax2.plot(y, color="green")
    ax3: Axes = plt.subplot(gs[2, :])
    ax3.set_ylabel('Z VALUE')
    ax3.plot(z, color="blue")
    fig.canvas.draw_idle()


things_to_plot = [np.random.random(size=(100, 3)),
                  np.ones((100, 3)),
                  np.random.random(size=(100, 3))]
delay = 5

if __name__ == "__main__":
    plt.ion()
    plt.show()
    fig = plt.figure()
    for things in things_to_plot:
        plot_stuff(x=things[:, 0], y=things[:, 1], z=things[:, 2], fig=fig)
        plt.draw()
        plt.pause(delay)
Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75
  • I tried this approach and nothing happens. The window remains there still and the figure does not change leading me to need to close it manually. – Employee Jan 30 '19 at 09:13
  • @Lossofhumanidentity I've edited my answer with complete code that does what I was proposing – Diziet Asahi Jan 30 '19 at 20:59