10

I am using Matplotlib on MacOS with Sulime Text. I use Python 3.5 and Matplotlib 2.0.

When I work on a figure, I usually have a script that plot the data, and save the figure in a .pdf file with plt.savefig(). Then I use Skim (a pdf viewer) in order to refresh the file each time I modify and run the script. This allows me to set my working layout as clean as: there is one window for the script, and one window for the figure which is automatically refreshing.

I would like to do keep the same layout, but using the Matplotlib figures (because they are interactive). I am looking for a way to use plt.show() but always in the same figure that has been created the first time I've run the script.

For instance:

1. First run

import matplotlib.pyplot as plt
import numpy as np 

fig, ax = plt.figure()    

noise = np.random.rand(1, 100)
ax(noise)
plt.show()

2. Following runs

import matplotlib.pyplot as plt
import numpy as np 

# This is the super command I am looking for
fig = plt.get_previous_run_figure()
ax = fig.axes

noise = np.random.rand(1, 100)
ax.plot(noise)
plt.draw()

In that case of course, I would have to do a first-run script separately from the main script. Does anyone know if it is possible ?

Leonard
  • 2,510
  • 18
  • 37
  • Do I understand correctly that fig = plt.figure(0); fig.clf() would not work for you because you want to re-use the window from another python instance? – Daan Feb 21 '17 at 12:46
  • Possible duplicate of [Store and reload matplotlib.pyplot object](http://stackoverflow.com/questions/7290370/store-and-reload-matplotlib-pyplot-object) – tmdavison Feb 21 '17 at 13:28
  • The "super command" your looking for is called pickling in python. See the duplicate question I suggested for an example – tmdavison Feb 21 '17 at 13:29
  • @tom Will pickling keep the window open between runs? I think that is what the OP asked for... – MB-F Feb 21 '17 at 13:53
  • 1
    @kazemakase Ah, no probably not. I may have misunderstood the question. I doubt you can keep a window open if you are closing your python session, but pickling should at least allow you to reopen a figure from a previous session. – tmdavison Feb 21 '17 at 13:56
  • Maybe it's possible to create a figure in one script, pickle it, and leave the script running. Meanwhile unpickle and use the figure in other scripts. However, this is prone to fail hilariously - too bad I lack the time to try it right now. – MB-F Feb 21 '17 at 14:00
  • @Daan: you understand well, I want to re-use the window from another Python instance. – Leonard Feb 21 '17 at 14:02
  • @tom, @kazemakase, I also tried to pickle the matplotlib figure, but `plt.show()` is still opening the figure in a new window instance – Leonard Feb 21 '17 at 14:04

3 Answers3

4

You want to have multiple consecutive python sessions share a common Matplotlib window. I see no way to share this windows from separate processes, especially when the original owner may terminate at any point in time.

However, you could do something similar to your current workflow in which you have an external pdf viewer to view a output file which you update from multiple python instances.

See this question/answer on how to pickle a matplotlib figure: Store and reload matplotlib.pyplot object

In every script, output your matplotlib figure as a pickled object, rather than calling plt.show():

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

ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
pickle.dump(ax, file('myplot.pickle', 'w'))

Then, start a dedicated python session which loads this pickled object and calls plt.show(). Have this script run in a loop, checking for updates of the pickled file on disk, and reloading when necessary:

import matplotlib.pyplot as plt
import pickle

while True:
   ax = pickle.load(file('myplot.pickle'))
   plt.show()

Alternative

Instead of having separate python sessions, I usually have a single Ipython session in which I run different script. By selecting the same figure windows, I end up with a mostly similar setup as you describe, in which the same figure window is reused throughout the day.

import matplotlib.pyplot as plt

fig = plt.figure(0)
fig.clf()

plt.show()
Community
  • 1
  • 1
Daan
  • 2,049
  • 14
  • 21
  • Thank for this answer. I tried your code, but `plt.show()` anyway open a new figure to show the freshly-loaded pickled axes in the loop (even if you assume that `plt.ion()` is activated to prevent `plt.show()` from blocking the loop). I also tried creating a figure first and showing it with `plt.show(block=False)` before the loop, and it still gives the same result. – Leonard Feb 21 '17 at 16:32
  • If the new window were to open in the same spot as the old window, would that work for you? – Daan Feb 22 '17 at 15:21
  • I moved from MacOSX backend to TkAgg, it made your solution work great ! – Leonard Feb 23 '17 at 08:05
  • Another quick solution is to use WebAgg, and to set the default port (for ex: 8888). Each time the script is stopped and re-launched, the port is updated with the new figure, and the web browser refreshes automatically. – Leonard Feb 23 '17 at 08:22
0

In principle establishing a connection between two different scripts could be done using a system-wide clipboard. (As far as I know the clipboard in windows and macos are system-wide.)

So the idea can be to set up an application using tk or pyqt, and implement a generic FigureCanvas. This application could have an event listener for changes in the clipboard.

The other main workflow script would then call some function that wraps the current figure into a pickle object and sends it to the clipboard, from where it gets caught by the GUI application, is unpickled and shown in the canvas.

This sounds like a little bit of work, but should meet the very restrictive requirements from the question.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
0

The alternative from Daan worked for me. Here's a bit more code. I used this in a Tkinter interactive GUI for reusing/updating a matplotlib figure window:

fig1 = None
if fig1:
    #if exists, clear figure 1
    plt.figure(1).clf() 
    plt.suptitle("New Fig Title", fontsize=18, fontweight='bold')
    #reuse window of figure 1 for new figure
    fig1 = plt.scatter(points.T[0], points.T[1], color='red', **plot_kwds)      
else:
    #initialize
    fig1 = plt.figure(num=1,figsize=(7, int(7*imgRatio)), dpi=80)
    plt.tick_params(axis='both', which='major', labelsize=14)
    plt.tick_params(axis='both', which='minor', labelsize=14)           
    plt.suptitle("Title", fontsize=18, fontweight='bold')
    fig1 = plt.scatter(points.T[0], points.T[1], color='red', **plot_kwds)

The figure is reusing the (interactive) plt window. For this to work, I had to set interactive : True in the matplotlibrc file (see my comment here)

Alex
  • 2,784
  • 2
  • 32
  • 46