在 python 中使用 pyqtgraph 绘制立方体
Plot cube using pyqtgraph in python
我想在 python 中使用 pyqtgraph 绘制移动立方体(根据给定数据集更新它们的位置)。我有一个示例代码如下,但我不知道如何绘制立方体而不是代码中使用的球体。
另外,我很困惑为什么这段代码在第二次 运行ning 时不起作用?我必须关闭我的 spyder 然后再次打开它或重新启动内核以 运行 代码第二次 运行ning。 (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
。所以而不是
app = QtGui.QApplication([])
写
app = QtGui.QApplication.instance()
if app is None:
app = QtGui.QApplication([])
这只会创建一个新实例,而不是一个新的应用程序,以防已经存在。
现在用 pyqtgraph
绘制网格。您必须定义网格的顶点。对于立方体,这些是八个角:
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
这些只是角点在笛卡尔坐标系中的位置。
现在我们必须定义面孔。这些是三角形,所以我们需要 12 个这样的立方体。我们在应跨越三角形的顶点的数组位置中给出三角形的角:
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]])
很可能有一个算法;现在我只是手写了它;也许我稍后会更新。
现在我们必须定义面部的颜色。我们需要每张脸的 RGBA 值;我刚把它设为红色:
colors = np.array([[1,0,0,1] for i in range(12)])
有了这个我们可以创建一个GLMeshItem
。我用黑色画边,这样你就可以看到三角形了:
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_()
您可以通过以下方式移动立方体:
cube.translate(x,y,z)
会自动更新。当然,您可以在一个列表中包含多个立方体实例。
EDIT 我实际上发现了一个丑陋的算法。它使用的想法是,如果三角形位于立方体的表面上,则至少有一个坐标必须始终相同。新代码是:
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
语句更改为:
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
现在它不会抛出任何错误。这是因为:
- 我不使用看似已弃用的
/=
- 我只是
continue
如果向量是零向量
我想在 python 中使用 pyqtgraph 绘制移动立方体(根据给定数据集更新它们的位置)。我有一个示例代码如下,但我不知道如何绘制立方体而不是代码中使用的球体。 另外,我很困惑为什么这段代码在第二次 运行ning 时不起作用?我必须关闭我的 spyder 然后再次打开它或重新启动内核以 运行 代码第二次 运行ning。 (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
。所以而不是
app = QtGui.QApplication([])
写
app = QtGui.QApplication.instance()
if app is None:
app = QtGui.QApplication([])
这只会创建一个新实例,而不是一个新的应用程序,以防已经存在。
现在用 pyqtgraph
绘制网格。您必须定义网格的顶点。对于立方体,这些是八个角:
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
这些只是角点在笛卡尔坐标系中的位置。
现在我们必须定义面孔。这些是三角形,所以我们需要 12 个这样的立方体。我们在应跨越三角形的顶点的数组位置中给出三角形的角:
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]])
很可能有一个算法;现在我只是手写了它;也许我稍后会更新。
现在我们必须定义面部的颜色。我们需要每张脸的 RGBA 值;我刚把它设为红色:
colors = np.array([[1,0,0,1] for i in range(12)])
有了这个我们可以创建一个GLMeshItem
。我用黑色画边,这样你就可以看到三角形了:
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_()
您可以通过以下方式移动立方体:
cube.translate(x,y,z)
会自动更新。当然,您可以在一个列表中包含多个立方体实例。
EDIT 我实际上发现了一个丑陋的算法。它使用的想法是,如果三角形位于立方体的表面上,则至少有一个坐标必须始终相同。新代码是:
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
语句更改为:
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
现在它不会抛出任何错误。这是因为:
- 我不使用看似已弃用的
/=
- 我只是
continue
如果向量是零向量