使用python-matplotlib连续的3D绘图(例如,图形更新)?

时间:2021-08-16 06:01:11

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.

我对这段代码的问题是,代码在“pl .show()”处停止,只在我关闭plot-window时继续。我也不确定是不是“self。ax”plot_surface(…)”而“pl .draw()”则会按我的意愿更新数据。

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:

我也找到了下面的代码,它是我想要的,但是它是2D的,所以不能直接帮助我:

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)

3 个解决方案

#1


7  

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.

如果它是一个动画(交互式)的情节,那么你就不需要使用plt.show()。您还希望交互式设置为True,而不是False,这与在2d示例中调用ion()相同。此外,如果不希望全部看到,还需要从以前的帧中删除()表面图。

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))

#2


1  

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

同时我找到了另一种使用MayaVI的OpenGL进行渲染和工作的解决方案,这是可以的,因为我只需要实时快速的视觉反馈。然而,我不得不在Ubuntu下安装以下包:python-enthoughtbase和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:

这门课不是我想要的,我现在有两个问题:

  1. After a short will the window is grayed by Ubuntu as (I suppose) Ubuntu thinks the application is not responding. Perhaps rather a an Ubuntu issue, but annoying.
  2. 在一段时间之后,Ubuntu的窗口就会变灰(我猜),Ubuntu认为应用程序没有响应。也许更像是Ubuntu的问题,但是很烦人。
  3. I have been trying to find out how I can be able to rotate the plot with the mouse while animating.
  4. 我一直在试图找出如何在动画的同时,用鼠标旋转情节。

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?).

编辑:好的。我刚刚试用了Paul建议的代码,它也适用于我。然而,尝试它我已经意识到,MatPlotLib可能不是实时做动画的最佳选择。至少对我来说,它是非常慢的(也许只有3D版本?)

So in the end I will stick with the MayaVI implementation from above, which except for the two points mentioned, works great.

因此,最后我将坚持上面提到的MayaVI实现,它除了上面提到的两点之外,非常有用。

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.

编辑:如果您使用MatPlotLib解决方案,我发现您可以将线MatPlotLib .interactive(True)放在plot类的声明中。这样,就可以只在plot类中定义MatPlotLib。

#3


0  

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.

对于您,我认为解决方案类似于上面的答案,除了用pl .pause()替换time.sleep(),它将在睡觉前完成图形。

#1


7  

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.

如果它是一个动画(交互式)的情节,那么你就不需要使用plt.show()。您还希望交互式设置为True,而不是False,这与在2d示例中调用ion()相同。此外,如果不希望全部看到,还需要从以前的帧中删除()表面图。

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))

#2


1  

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

同时我找到了另一种使用MayaVI的OpenGL进行渲染和工作的解决方案,这是可以的,因为我只需要实时快速的视觉反馈。然而,我不得不在Ubuntu下安装以下包:python-enthoughtbase和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:

这门课不是我想要的,我现在有两个问题:

  1. After a short will the window is grayed by Ubuntu as (I suppose) Ubuntu thinks the application is not responding. Perhaps rather a an Ubuntu issue, but annoying.
  2. 在一段时间之后,Ubuntu的窗口就会变灰(我猜),Ubuntu认为应用程序没有响应。也许更像是Ubuntu的问题,但是很烦人。
  3. I have been trying to find out how I can be able to rotate the plot with the mouse while animating.
  4. 我一直在试图找出如何在动画的同时,用鼠标旋转情节。

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?).

编辑:好的。我刚刚试用了Paul建议的代码,它也适用于我。然而,尝试它我已经意识到,MatPlotLib可能不是实时做动画的最佳选择。至少对我来说,它是非常慢的(也许只有3D版本?)

So in the end I will stick with the MayaVI implementation from above, which except for the two points mentioned, works great.

因此,最后我将坚持上面提到的MayaVI实现,它除了上面提到的两点之外,非常有用。

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.

编辑:如果您使用MatPlotLib解决方案,我发现您可以将线MatPlotLib .interactive(True)放在plot类的声明中。这样,就可以只在plot类中定义MatPlotLib。

#3


0  

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.

对于您,我认为解决方案类似于上面的答案,除了用pl .pause()替换time.sleep(),它将在睡觉前完成图形。