在python中使用pyqtgraph绘制立方体 [英] Plot cube using pyqtgraph in python

查看:85
本文介绍了在python中使用pyqtgraph绘制立方体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 python 中使用 pyqtgraph 绘制移动的立方体(根据给定的数据集更新它们的位置).我有一个示例代码如下,但我不知道如何绘制立方体而不是代码中使用的球体.另外,我很困惑为什么这段代码在第二次运行时不起作用?我必须关闭我的 spyder 然后再次打开它或重新启动内核以运行第二次运行的代码.(win10, python3.7 spyder3.3.1)

I would like to plot moving cubes using pyqtgraph in python (update their positions based on given dataset). I have a sample code as follow, but I don't know how to plot cube instead of the sphere used in the code. Also, I'm very confused that why this code doesn't work for the second time running? I must close my spyder then open it again or restart kernel to run the code for the second time running. (win10, python3.7 spyder3.3.1)

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np

app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 20
w.show()
w.setWindowTitle('pyqtgraph example: GLScatterPlotItem')

g = gl.GLGridItem()
w.addItem(g)


##
##  First example is a set of points with pxMode=False
##  These demonstrate the ability to have points with real size down to a very small scale 
## 
pos = np.empty((53, 3))
size = np.empty((53))
color = np.empty((53, 4))
pos[0] = (1,0,0); size[0] = 0.5;   color[0] = (1.0, 0.0, 0.0, 0.5)
pos[1] = (0,1,0); size[1] = 0.2;   color[1] = (0.0, 0.0, 1.0, 0.5)
pos[2] = (0,0,1); size[2] = 2./3.; color[2] = (0.0, 1.0, 0.0, 0.5)

z = 0.5
d = 6.0
for i in range(3,53):
    pos[i] = (0,0,z)
    size[i] = 2./d
    color[i] = (0.0, 1.0, 0.0, 0.5)
    z *= 0.5
    d *= 2.0

sp1 = gl.GLScatterPlotItem(pos=pos, size=size, color=color, pxMode=False)
sp1.translate(5,5,0)
w.addItem(sp1)


##
##  Second example shows a volume of points with rapidly updating color
##  and pxMode=True
##

pos = np.random.random(size=(100000,3))
pos *= [10,-10,10]
pos[0] = (0,0,0)
color = np.ones((pos.shape[0], 4))
d2 = (pos**2).sum(axis=1)**0.5
size = np.random.random(size=pos.shape[0])*10
sp2 = gl.GLScatterPlotItem(pos=pos, color=(1,1,1,1), size=size)
phase = 0.

w.addItem(sp2)


##
##  Third example shows a grid of points with rapidly updating position
##  and pxMode = False
##

pos3 = np.zeros((100,100,3))
pos3[:,:,:2] = np.mgrid[:100, :100].transpose(1,2,0) * [-0.1,0.1]
pos3 = pos3.reshape(10000,3)
d3 = (pos3**2).sum(axis=1)**0.5

sp3 = gl.GLScatterPlotItem(pos=pos3, color=(1,1,1,.3), size=0.1, pxMode=False)

w.addItem(sp3)


def update():
    ## update volume colors
    global phase, sp2, d2
    s = -np.cos(d2*2+phase)
    color = np.empty((len(d2),4), dtype=np.float32)
    color[:,3] = np.clip(s * 0.1, 0, 1)
    color[:,0] = np.clip(s * 3.0, 0, 1)
    color[:,1] = np.clip(s * 1.0, 0, 1)
    color[:,2] = np.clip(s ** 3, 0, 1)
    sp2.setData(color=color)
    phase -= 0.1

    ## update surface positions and colors
    global sp3, d3, pos3
    z = -np.cos(d3*2+phase)
    pos3[:,2] = z
    color = np.empty((len(d3),4), dtype=np.float32)
    color[:,3] = 0.3
    color[:,0] = np.clip(z * 3.0, 0, 1)
    color[:,1] = np.clip(z * 1.0, 0, 1)
    color[:,2] = np.clip(z ** 3, 0, 1)
    sp3.setData(pos=pos3, color=color)

t = QtCore.QTimer()
t.timeout.connect(update)
t.start(50)


## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

推荐答案

导入:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np

好吧首先,貌似你不能同时运行两个QApplication.所以代替

Okay first of all, seemingly you cannot run two QApplications at the same time. So instead of

app = QtGui.QApplication([])

app = QtGui.QApplication.instance()
if app is None:
    app = QtGui.QApplication([])

如果应用程序已经存在,这只会创建一个新实例而不是新应用程序.

This will only create a new instance instead of a new Application in case one already exists.

现在使用 pyqtgraph 绘制网格.您必须定义网格的顶点.对于立方体,这些是八个角:

Now to mesh drawing with pyqtgraph. You have to define the vertexes of your mesh. For a cube these are the eight corners:

vertexes = np.array([[1, 0, 0], #0
                     [0, 0, 0], #1
                     [0, 1, 0], #2
                     [0, 0, 1], #3
                     [1, 1, 0], #4
                     [1, 1, 1], #5
                     [0, 1, 1], #6
                     [1, 0, 1]])#7

这些只是笛卡尔坐标中角点的位置.

These are just the positions of the corners in cartesian coordinates.

现在我们必须定义面.这些是三角形,所以我们需要 12 个三角形来制作一个立方体.我们在应跨越三角形的顶点的数组位置中给出三角形的角点:

Now we have to define faces. These are triangles so we need 12 of those for a cube. We give the corners of the triangles in the array positions of the vertexes that shall span the triangle:

faces = np.array([[1,0,7], [1,3,7],
                  [1,2,4], [1,0,4],
                  [1,2,6], [1,3,6],
                  [0,4,5], [0,7,5],
                  [2,4,5], [2,6,5],
                  [3,6,5], [3,7,5]])

很可能有一个算法;现在我只是手写;也许我会稍后更新.

There may very well be an algorithm to it; for now I just wrote it by hand; maybe I will update later.

现在我们必须定义人脸的颜色.我们需要每张脸的 RGBA 值;我只是让它变红了:

Now we have to define colors of the faces. We need RGBA values for every face; I just made it red:

colors = np.array([[1,0,0,1] for i in range(12)])

有了这个,我们可以创建一个GLMeshItem.我用黑色绘制边缘,以便您可以看到三角形:

With this we can create a GLMeshItem. I draw the edges in black so you can see the triangles:

cube = gl.GLMeshItem(vertexes=vertexes, faces=faces, faceColors=colors,
                     drawEdges=True, edgeColor=(0, 0, 0, 1))

现在我们只需要添加项目并启动主循环:

Now we just need to add the item and start the mainloop:

w.addItem(cube)

if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

您可以使用以下命令移动立方体:

You can move the cube with:

cube.translate(x,y,z)

它会自动更新.当然,您可以在一个列表中包含多个多维数据集实例.

It will automatically update. And of course you can have several cube instances in a list.

EDIT 我实际上发现了一个丑陋的算法.它使用的想法是,如果三角形位于立方体的表面上,则至少一个坐标必须始终相同.新代码是:

EDIT I actually found an ugly algorithm. It uses the idea, that at least one coordinate must always be the same if the triangles are on the surface of the cube. New code is:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np
import itertools

app = QtGui.QApplication.instance()
if app is None:
    app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 20
w.show()
w.setWindowTitle('A cube')

vertexes = np.array(list(itertools.product(range(2),repeat=3)))

faces = []

for i in range(2):
    temp = np.where(vertexes==i)
    for j in range(3):
        temp2 = temp[0][np.where(temp[1]==j)]
        for k in range(2):
            faces.append([temp2[0],temp2[1+k],temp2[3]])

faces = np.array(faces)

colors = np.array([[1,0,0,1] for i in range(12)])


cube = gl.GLMeshItem(vertexes=vertexes, faces=faces, faceColors=colors,
                     drawEdges=True, edgeColor=(0, 0, 0, 1))

w.addItem(cube)

if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

EDIT 2 pyqtgraph 中的错误是由于内部零除法.如果您转到文件 MeshData.py,您将找到一个函数 vertexNormals.我将第一个 if 语句改为:

EDIT 2 The error in pyqtgraph is because of internal zero division. If you go to the file MeshData.py you will find a function vertexNormals. I changed the first if statement to:

if self._vertexNormals is None:
    faceNorms = self.faceNormals()
    vertFaces = self.vertexFaces()
    self._vertexNormals = np.empty(self._vertexes.shape, dtype=float)
    for vindex in xrange(self._vertexes.shape[0]):
        faces = vertFaces[vindex]
        if len(faces) == 0:
            self._vertexNormals[vindex] = (0,0,0)
            continue
        norms = faceNorms[faces]  ## get all face normals
        norm = norms.sum(axis=0)       ## sum normals
        if all(norm==0):
            self._vertexNormals[vindex] = norm
            continue
        #norm /= (norm**2).sum()**0.5  ## and re-normalize
        np.true_divide(norm, (norm**2).sum()**0.5, out=norm, casting='unsafe')
        self._vertexNormals[vindex] = norm

现在它不会抛出任何错误.这是因为:

Now it doesn't throw any errors. This is because:

  • 我不使用看似已弃用的 /=
  • 如果向量是零向量,我只是continue

这篇关于在python中使用pyqtgraph绘制立方体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆