项目前提:
本文前提是是在已有数据更新线程以及各类基础功能摁扭实现后进行的显示修改
主要分成几个步骤
- 方法的引入,需要把pycharm中的环境下载并引入包括import pyqtgraph.opengl as gl,import numpy as np等
- 我这里是将写好的动态三维散点图放置在ui的布局中的
- 用一些基础方法先将要使用的控件放到我们的总控件中,比如将点、文本等控件放到gl.GLViewWidget()中去
- 由于pycharm中的pyqtgraph的功能有限,所以光修改我的代码部分是达不成我的目的的,所以本文还会对GLGridItem这个类进行修改
- 写方法刷新点、刷新文本。 这里的文本主要是点的坐标
图的创建:
这里有了总的视图,并且我们加入了坐标对象以及三维网格对象
我是对我的代码进行了删改放在这里的,旨在一步一步来,所以我没有现在的效果图
def paint3D(self):
self.guiplt = gl.GLViewWidget() # 总view
self.guiplt.setBackgroundColor('8B8682') # 总背景色
self.grid = gl.GLGridItem() # 网格控件
self.grid.setSpacing(x=1, y=1, z=1) # 刻度
self.grid.setColor('FFFAFA') # 红色 这里的颜色选择可以参考网上:py颜色查询
self.guiplt.addItem(self.grid)
# 坐标轴,我认为他不太美观我没加
# ax = gl.GLAxisItem()
# ax.setSize(10, 10, 10)
# self.guiplt.addItem(ax)
self.scatter = gl.GLScatterPlotItem() # 散点图控件
self.guiplt.addItem(self.scatter)
return self.guiplt
点与坐标添加:
在我做点与坐标的时候浪费了不少时间
在这里我们需要知道几个前提:
- 这个是重设坐标点的方法self.scatter.setData(pos=pos, size=size, color=color, pxMode=False)
它的pos参数我们要知道是个(N,3) N行三列的数组所以开始设为空
pos = np.empty((N, 3))
它的color参数是要(N,4)N行四列的数组,我们开始也设为空
color = np.empty((N, 4))
它的size参数是一维数组或者是N行1列的数组,我们开始也设为空
size = np.empty((N))
我们做动态散点,点的数量肯定是变化的,所以这里的N我们可以设置为我们的点的数目
对坐标来说的话,这里提供的textitem每次只能对应一个点显示一个信息所以我们要创建多个变量
pos = np.empty((num_in_tlv, 3))
size = np.empty((num_in_tlv))
color = np.empty((num_in_tlv, 4))
for i in range(self.points_num):
pos[i] = ('%.2f' % x, '%.2f' % y, '%.2f' % z)
size[i] = 0.1
if z > 0:
color[i] = (0.0, 0.0, 1.0, 1.0) # 颜色(3) 加 透明度(1)
else:
color[i] = (1.0, 0.0, 0.0, 1.0) # 颜色(3) 加 透明度(1) 这里的颜色是RGB可以上网搜
# textitem
str1 = '%.2f' % x
str2 = '%.2f' % y
str3 = '%.2f' % z
exec(
"self.text3d%s.setData(pos=np.array([x, y, z]), text='('+str1+','+str2+','+str3+')', "
"color='EEEED1', "
"font=QtGui.QFont('Helvetica', 10)) " % i)
# 这样的话会出现一些问题 如果创建了20个点第二次只有3个点的话那其余的是不是还一直存在
#我们要删除多余的,在我的程序开头我创建了60个文本控件并加到了主view中
for k in range(60):
exec("self.text3d%s = gl.GLTextItem()" % k)
exec("self.guiplt.addItem(self.text3d%s)" % k)
# 这里是删除多余点
for k in range(60 - self.points_num):
exec("self.text3d%s.setData(text='')" % (self.points_num + k))
修改GLGridItem类:
我这里直接粘贴了 因为解释太多比较累 里面有一些注释
if self.antialias:
glEnable(GL_LINE_SMOOTH)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
glEnable(GL_LINE_STIPPLE) # 可以虚线
glLineStipple(2, 0x00FF) # 虚线格式
glBegin(GL_LINES) # 开始画
x, y, z = self.size()
xs, ys, zs = self.spacing()
xvals = np.arange(-x / 2., x / 2. + xs * 0.001, xs)
yvals = np.arange(0, y / 1. + ys * 0.001, ys)
zvals = np.arange(-z / 2., z / 2. + zs * 0.001, zs)
glColor4f(1.0, 1.0, 1.0, 0.3) # 颜色和透明度
for x in xvals:
glVertex3f(x, yvals[0], 0)
glVertex3f(x, yvals[-1], 0) # 从(0,0,0)来看 向x轴方向看 像波浪线往y方向一条一条
for y in yvals:
glVertex3f(xvals[0], y, 0)
glVertex3f(xvals[-1], y, 0) # 从 y方向上是波浪线
glEnd() # 因为我的虚线只想让xy那个平面虚所以
glDisable(GL_LINE_STIPPLE) # 禁止虚线
glBegin(GL_LINES)
glColor4f(*self.color().getRgbF())
for zxx in xvals:
glVertex3f(zxx, y, zvals[0]) # 因为当我们从z轴方向上看的时候 x y 就已经分不清了 没法在二维面看出三维变化
glVertex3f(zxx, y, zvals[-1]) # 我们需要从z的x方向上看是 波浪的 y值是这个竖直网格的位置在y的哪个值上
for zxy in zvals:
glVertex3f(xvals[0], y, zxy) # 我们需要从z的y轴方向上看是波浪的 波浪就是一条一条的像波 像是:三
glVertex3f(xvals[-1], y, zxy)
for zyx in yvals:
glVertex3f(-x, zyx, zvals[0])
glVertex3f(-x, zyx, zvals[-1])
for zyy in zvals:
glVertex3f(-x, yvals[0], zyy)
glVertex3f(-x, yvals[-1], zyy)
glEnd()
# 以上代码需要注意的是我修改了xvals = np.arange(-x / 2., x / 2. + xs * 0.001, xs)
# yvals = np.arange(0, y / 1. + ys * 0.001, ys)
# zvals = np.arange(-z / 2., z / 2. + zs * 0.001, zs)
# 因为y代表距离没有负的 所以改了他的起始值
这个设计大概是这样 如果想让z轴上去 只要改zvals就行了
zvals = np.arange(0., z / 1. + zs * 0.001, zs)
修改过后的大概是这样
一些小操作:
- 首先就是想让坐标轴的轴显示 其次是我在(0, 0, 0)所做的一个模型
def addradar(self, data): # 添加原点的模型
vertices = np.array([
[-1, -0.5, 0.9], # bottom vertices
[1, -0.5, 0.9],
[1, 0.5, 0.9],
[-1, 0.5, 0.9],
[-1, -0.5, -0.9], # top vertices
[1, -0.5, -0.9],
[1, 0.5, -0.9],
[-1, 0.5, -0.9]
])
faces = np.array([
[0, 1, 2], # bottom faces
[0, 2, 3],
[4, 6, 5], # top faces
[4, 7, 6],
[0, 4, 5], # side faces
[0, 5, 1],
[1, 5, 6],
[1, 6, 2],
[2, 6, 7],
[2, 7, 3],
[3, 7, 4],
[3, 4, 0]
])
colors = np.array([
[0, 1, 0, 1], # green red[1, 0, 0, 1]
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1]
])
self.mesh = gl.GLMeshItem(vertexes=vertices / 10, faces=faces, faceColors=colors, smooth=False)
self.guiplt.addItem(self.mesh) # 和之前的添加组件一样
# 接下来是添加x y z 的轴显示
def addaxis(self, data):
self.axisx = gl.GLTextItem()
self.axisx.setData(pos=[0, -1, 0],
text='X轴',
color='EEEED1', font=QtGui.QFont('Helvetica', 10))
self.guiplt.addItem(self.axisx)
self.axisy = gl.GLTextItem()
self.axisy.setData(pos=[data[0] / 2 + 1, data[1] / 2, 0],
text='Y轴',
color='EEEED1', font=QtGui.QFont('Helvetica', 10))
self.guiplt.addItem(self.axisy)
self.axisz = gl.GLTextItem()
self.axisz.setData(pos=[-data[0] / 2 - 1, data[1] + 1, 0],
text='Z轴',
color='EEEED1', font=QtGui.QFont('Helvetica', 10))
self.guiplt.addItem(self.axisz)
在这里我只讲一些基本的简单操作具体的逻辑要看自己程序的结构 下面是我的一些记录代码供自己读用的
def paint3D(self):
self.guiplt = gl.GLViewWidget()
self.guiplt.setBackgroundColor('8B8682') # 总背景色
self.grid = gl.GLGridItem()
self.grid.setSpacing(x=1, y=1, z=1) # 刻度
self.grid.setColor('FFFAFA')
self.grid.setSize(10, 10, 10)
data = self.grid.size()
pos1 = QVector3D(0, data[1] / 2, 0)
self.guiplt.setCameraPosition(pos=pos1, distance=19, elevation=10, azimuth=-45)
self.dataflag = 0
self.guiplt.addItem(self.grid)
# 坐标轴'''
# ax = gl.GLAxisItem()
# ax.setSize(10, 10, 10)
# self.guiplt.addItem(ax)
self.scatter = gl.GLScatterPlotItem()
self.guiplt.addItem(self.scatter)
# 加个参照雷达
self.addradar(data)
# 加坐标轴指示
self.addaxis(data)
for k in range(60):
exec("self.text3d%s = gl.GLTextItem()" % k)
exec("self.guiplt.addItem(self.text3d%s)" % k)
return self.guiplt
def to_xy(self):
data = self.grid.size()
pos = QVector3D(0, data[1] / 2, data[2])
self.guiplt.setCameraPosition(pos=pos, distance=data[2]/2, elevation=90, azimuth=-90)
self.dataflag = 1
def to_xz(self):
data = self.grid.size()
pos = QVector3D(0, 0, 0)
self.guiplt.setCameraPosition(pos=pos, distance=data[2]/2, elevation=0, azimuth=-90)
self.dataflag = 2
def change3d(self): #轴长度选择修改后 需要修改相机位置需要修改参照坐标轴指示位置
if self.lineEdit.text().isdigit():
zrange = float(self.lineEdit_6.text())
yrange = float(self.lineEdit_2.text())
xrange = float(self.lineEdit.text())
self.grid.setSize(xrange, yrange, zrange)
data = self.grid.size()
pos1 = QVector3D(0, data[1] / 2, 0)
self.guiplt.setCameraPosition(pos=pos1, distance=19, elevation=10, azimuth=-45)
self.dataflag = 0
self.guiplt.removeItem(self.mesh)
self.guiplt.removeItem(self.axisx)
self.guiplt.removeItem(self.axisy)
self.guiplt.removeItem(self.axisz)
# 加个参照雷达
self.addradar(data)
# 加坐标轴指示
self.addaxis(data)
# 数据
def data_tlv1_4D(self, datain, num_in_tlv, q):
if self.checkBox_4.isChecked():
self.points_num = num_in_tlv
else:
self.points_num = 0
# print(self.points_num)
pos = np.empty((num_in_tlv, 3))
size = np.empty((num_in_tlv))
color = np.empty((num_in_tlv, 4))
zadd = float(self.lineEdit_6.text()) / 2.
yadd = float(self.lineEdit_2.text())
print(size)
for i in range(self.points_num):
m = i * 10 + 4
x = (struct.unpack('h', datain[m:m + 2])[0]) / (2 ** q)
y = (struct.unpack('h', datain[m + 2:m + 4])[0]) / (2 ** q)
z = (struct.unpack('h', datain[m + 4:m + 6])[0]) / (2 ** q)
pos[i] = ('%.2f' % x, '%.2f' % y, '%.2f' % z)
size[i] = 0.1
if z > 0:
color[i] = (0.0, 0.0, 255.0, 255) # 颜色(3) 加 透明度(1)
else:
color[i] = (255.0, 0.0, 0.0, 255) # 颜色(3) 加 透明度(1)
str1 = '%.2f' % x
str2 = '%.2f' % y
str3 = '%.2f' % z
if self.dataflag == 0:
exec(
"self.text3d%s.setData(pos=np.array([x, y, z]), text='('+str1+','+str2+','+str3+')', "
"color='EEEED1', "
"font=QtGui.QFont('Helvetica', 10)) " % i)
elif self.dataflag == 1:
exec(
"self.text3d%s.setData(pos=np.array([x, y, 0]), text='('+str1+','+str2+')', "
"color='EEEED1', "
"font=QtGui.QFont('Helvetica', 10)) " % i)
else:
exec(
"self.text3d%s.setData(pos=np.array([x, yadd, z]), text='('+str1+','+str3+')', "
"color='EEEED1', "
"font=QtGui.QFont('Helvetica', 10)) " % i)
for k in range(60 - self.points_num):
exec("self.text3d%s.setData(text='')" % (self.points_num + k))
if self.dataflag == 0:
self.scatter.setData(pos=pos, size=size, color=color, pxMode=False)
elif self.dataflag == 1:
pos[:, 2] = 0
self.scatter.setData(pos=pos, size=size, color=color, pxMode=False)
else:
pos[:, 1] = yadd
self.scatter.setData(pos=pos, size=size, color=color, pxMode=False)
# 当探测点数为空时 我们要做的就是清空之前的点
# 加载合适的地方
if PointTargetExistFlag == 0:
self.scatter.setData(pos=np.array([]), size=np.array([]), color=np.array([]))
for k in range(60):
exec("self.text3d%s.setData(text='')" % k)
最后看下动态效果图,我的数据帧是接近20ms的 所以我觉得它的刷新率完全可以满足
pyqtgraph.opengl