I have a simulation which calculates surface data for each iteration of the simulation. I would like to continuously plot that data as a surface plot to the same window (updating the plot in each iteration) in order to see how it evolves and to check the algorithm.
My Idea was to create a class that would initialize the window/plot and then redraw to that window from inside the simulation loop. Here is the class I came up with:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter
import matplotlib
matplotlib.interactive( False )
class plot3dClass( object ):
def __init__( self, systemSideLength, lowerCutoffLength ):
self.systemSideLength = systemSideLength
self.lowerCutoffLength = lowerCutoffLength
self.fig = plt.figure()
self.ax = self.fig.add_subplot( 111, projection='3d' )
self.ax.set_zlim3d( -10e-9, 10e9 )
X = np.arange( 0, self.systemSideLength, self.lowerCutoffLength )
Y = X
self.X, self.Y = np.meshgrid(X, Y)
self.ax.w_zaxis.set_major_locator( LinearLocator( 10 ) )
self.ax.w_zaxis.set_major_formatter( FormatStrFormatter( '%.03f' ) )
heightR = np.zeros( self.X.shape )
self.surf = self.ax.plot_surface( self.X, self.Y, heightR, rstride=1, cstride=1, cmap=cm.jet, linewidth=0, antialiased=False )
#~ self.fig.colorbar( self.surf, shrink=0.5, aspect=5 )
plt.show()
def drawNow( self, heightR ):
self.surf = self.ax.plot_surface( self.X, self.Y, heightR, rstride=1, cstride=1, cmap=cm.jet, linewidth=0, antialiased=False )
plt.draw() # redraw the canvas
time.sleep(1)
The problem I have with this code, is that the code stops at the 'plt.show()' and only continues, when I close the plot-window. Also I am not sure if the calls of 'self.ax.plot_surface( ... )' and 'plt.draw()' would update the figure as I would like it.
So is this class the right direction?
If yes: What modifications are needed?
If not: Could somebody please give me advice how to achieve what I want?
I realize that this problem might seem trivial to others, but I (honestly) did spend the whole day yesterday on Google and trying and I'm at a loss...
Any help would greatly appreciated, so that I can get back to my actual work.
Tanks alot in advance.
As a reference:
I also found the following code which does, what I want, but it is in 2D, so it does not help me directly:
from pylab import *
import time
ion()
tstart = time.time() # for profiling
x = arange(0,2*pi,0.01) # x-array
line, = plot(x,sin(x))
for i in arange(1,200):
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
print 'FPS:' , 200/(time.time()-tstart)
You do not need to plt.show()
if it is an animated (interactive) plot. You also want interactive set to True, not False which is the same as calling ion()
in your 2d example. Also, you need to remove()
the surface plots from previous frames if you do not want to see them all.
Otherwise you were pretty close.
This works for me:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter
import matplotlib, time
class plot3dClass( object ):
def __init__( self, systemSideLength, lowerCutoffLength ):
self.systemSideLength = systemSideLength
self.lowerCutoffLength = lowerCutoffLength
self.fig = plt.figure()
self.ax = self.fig.add_subplot( 111, projection='3d' )
self.ax.set_zlim3d( -10e-9, 10e9 )
rng = np.arange( 0, self.systemSideLength, self.lowerCutoffLength )
self.X, self.Y = np.meshgrid(rng,rng)
self.ax.w_zaxis.set_major_locator( LinearLocator( 10 ) )
self.ax.w_zaxis.set_major_formatter( FormatStrFormatter( '%.03f' ) )
heightR = np.zeros( self.X.shape )
self.surf = self.ax.plot_surface(
self.X, self.Y, heightR, rstride=1, cstride=1,
cmap=cm.jet, linewidth=0, antialiased=False )
# plt.draw() maybe you want to see this frame?
def drawNow( self, heightR ):
self.surf.remove()
self.surf = self.ax.plot_surface(
self.X, self.Y, heightR, rstride=1, cstride=1,
cmap=cm.jet, linewidth=0, antialiased=False )
plt.draw() # redraw the canvas
time.sleep(1)
matplotlib.interactive(True)
p = plot3dClass(5,1)
for i in range(2):
p.drawNow(np.random.random(p.X.shape))
I am grateful for the answer from Paul, although I haven't to tried it out yet.
In the meantime I had found another solution that works and renders with OpenGL using MayaVI, which is OK as I only need real-time quick visual feedback. However I had to install the following packages under Ubuntu: python-enthoughtbase and mayavi2
Here's the code:
import numpy as np
import time
from enthought.mayavi import mlab
from enthought.tvtk.tools import visual
class plot3dClass( object ):
def __init__( self, systemSideLength, lowerCutoffLength ):
self.systemSideLength = systemSideLength
self.lowerCutoffLength = lowerCutoffLength
rangeMax = self.systemSideLength
X = np.arange( 0, self.systemSideLength, self.lowerCutoffLength )
Y = X
matrixSize = int( round( self.systemSideLength / self.lowerCutoffLength ) )
heightR = np.zeros( ( matrixSize, matrixSize ) )
fig = mlab.figure(size=(500,500))
visual.set_viewer(fig)
self.surf = mlab.surf( X, Y, heightR, warp_scale = 1e1 ) # NOTE: the warp_scale factor is relative to the scale of the x- and y-axes
box_extent = ( 0,rangeMax, 0,rangeMax, -1e-7,1e-7 ) # NOTE: the extent options refers to the size and position in the 3D space relative to the origin
mlab.outline(self.surf, color=(0.7, .7, .7), extent = box_extent )
def drawNow( self, heightR ):
self.surf.mlab_source.scalars = heightR
time.sleep(0.033)
This class is not quite where I would like it to be and I have two immediate issues with it:
I will try getting these answered in another thread.
EDIT: Ok. I have just tried the code as suggested by Paul and it also works for me. However, trying it I have become aware that MatPlotLib probably is not the best choice for doing animations in real-time. At least for me it is extremely slow (perhaps only in 3D?).
So in the end I will stick with the MayaVI implementation from above, which except for the two points mentioned, works great.
EDIT: If you go with the MatPlotLib solution, I have found that you can put the line matplotlib.interactive(True)
inside the declaration of the plotting class. That way you can have MatPlotLib only defined within the plotting class.
I had a similar problem and this worked for me:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for k in xrange(0,X_range):
ax.plot(x_input, y_input, z_input)
plt.draw()
plt.pause(0.02)
ax.cla()
For you, I'd imagine the solution be something similar to the top answer except replacing time.sleep()
with plt.pause()
, which would finish the drawing the figure before sleeping.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.