现已对python的基础知识和PYQT5基本掌握了,现准备进入三维绘图领域进行学习,学习过程中会将总结的一些代码陆续发布出来,同初学者一同进步提高。
参考网上的相关资源,作了一个较完整的可以结合QT5和vtk库进行绘制点、三角网线、网格线、面的三维绘图示例,运行界面如下:(如没有安装vtk包,需安装:pip install vtk)
代码如下:
#vkt示例:画一示例球体,学习用VKT画出点,线,面、网格线的方法,同时将vkt同QT5/6的窗体控件绑定显示
import sys
import numpy as np
import vtkmodules.all as vtk
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5 import QtWidgets, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle('QT5+vtk画点、线、面、网格')
# 创建VTK渲染窗口和相关组件
self.ren = vtk.vtkRenderer() #渲染器,所有类型的最终数据要加入同一渲染器中才行
self.actor_pan = vtk.vtkActor() #面的角色变量(如重新设置颜色时有用,应设置成类全局变量)
self.actor_line = vtk.vtkActor() #线的角色变量(如重新设置颜色时有用,应设置成类全局变量)
self.actor_point = vtk.vtkActor() #点的角色变量(如重新设置颜色时有用,应设置成类全局变量)
self.actor_grid = vtk.vtkActor() #网格线的角色变量(如重新设置颜色时有用,应设置成类全局变量)
self.vtkWidget = QVTKRenderWindowInteractor(self) #显示窗口绑定到QT的窗口中
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.setCentralWidget(self.vtkWidget)
# 创建一个球体示例
sphere_source = vtk.vtkSphereSource()
sphere_source.SetRadius(10.) # 设置球体半径
sphere_source.SetCenter(0.0, 0.0, 0.0) # 设置球体中心
sphere_source.Update() # 更新球体源以生成几何数据
self.bUpdate=False #开始时更改微调按纽时是否刷新窗体
bPan=True #是否显示面
bLine=True #是否显示面上的三角网
bPoint=True #是否显示随机画的点云
bgrid=True #是否显示指定范围画出的网络线
if(bPan):
mapper_pan = vtk.vtkPolyDataMapper() #面渲染器:应分别设置
mapper_pan.SetInputConnection(sphere_source.GetOutputPort())
actor_pan = vtk.vtkActor() #面角色,一般用类全局变量self.actor,仅作为示例
self.actor=actor_pan #类全局角色同面角色,好在类其他位置来处理颜色等
actor_pan.SetMapper(mapper_pan)
actor_pan.GetProperty().SetColor(1.0, 0.0, 0.0) # 设置球体颜色为红色
self.ren.AddActor(actor_pan) # 将球体角色(面)添加到渲染器中
#如有下面的一行代码可以显示网格线,但显示网格线就不能显示面,不能面和线同时显示
#self.actor.GetProperty().SetRepresentationToWireframe()
#显示球体三角网网格线(因重新定义了线角色,可以同面一起显示)OK
if(bLine):
mapper_line = vtk.vtkPolyDataMapper() #线渲染器:应分别设置
mapper_line.SetInputConnection(sphere_source.GetOutputPort())
self.actor_line = vtk.vtkActor()
self.actor_line.SetMapper(mapper_line)
extractEdges=vtk.vtkExtractEdges()
extractEdges.SetInputConnection(sphere_source.GetOutputPort())
mapper_line.SetInputConnection(extractEdges.GetOutputPort())
self.actor_line.SetMapper(mapper_line)
self.actor_line.GetProperty().SetColor(1.0,1.0,0) # 设置球体三角网为黄色
# 将球体角色(线)添加到渲染器中
self.ren.AddActor(self.actor_line)
#画网格线:OK
if(bgrid):
points = vtk.vtkPoints()
cells = vtk.vtkCellArray()
polydata = vtk.vtkPolyData()
mapper_grid = vtk.vtkPolyDataMapper() #网络线渲染器:应分别设置
rangeX = [-50,50] #x向网格范围
rangeY = [-50,50] #y向网格范围
rangeZ = [-10,10] #z向网格范围
intervalX = 10 #x向网格间距
intervalY = 10 #y向网格间距
intervalZ = 20 #z向网格间距
for gridZ in range(rangeZ[0],rangeZ[1] + intervalZ,intervalZ):
for gridX in range(rangeX[0],rangeX[1] + intervalX,intervalX):
lineStart = [gridX, rangeY[0],gridZ]
lineEnd = [gridX,rangeY[1],gridZ]
pointIdStart = points.InsertNextPoint(lineStart)
pointIdEnd = points.InsertNextPoint(lineEnd)
singleLineCell = [pointIdStart,pointIdEnd]
cells.InsertNextCell(2,singleLineCell)
for gridY in range(rangeY[0],rangeY[1] +intervalY,intervalY):
lineStart = [gridY, rangeX[0],gridZ]
lineEnd = [gridY,rangeX[1],gridZ]
pointIdStart = points.InsertNextPoint(lineStart)
pointIdEnd = points.InsertNextPoint(lineEnd)
singleLineCell = [pointIdStart,pointIdEnd]
cells.InsertNextCell(2,singleLineCell)
for gridY in range(rangeY[0], rangeY[1] + intervalY , intervalY):
for gridZ in range(rangeZ[0], rangeZ[1] + intervalZ, intervalZ):
lineStart = [rangeX[0], gridY, gridZ]
lineEnd = [rangeX[1], gridY, gridZ]
pointIdStart = points.InsertNextPoint(lineStart)
pointIdEnd = points.InsertNextPoint(lineEnd)
singleLineCell = [pointIdStart, pointIdEnd]
cells.InsertNextCell(2, singleLineCell)
for gridX in range(rangeX[0], rangeX[1] +intervalX, intervalX):
lineStart = [gridX, gridY, rangeZ[0]]
lineEnd = [gridX, gridY, rangeZ[1]]
pointIdStart = points.InsertNextPoint(lineStart)
pointIdEnd = points.InsertNextPoint(lineEnd)
singleLineCell = [pointIdStart, pointIdEnd]
cells.InsertNextCell(2, singleLineCell)
polydata.SetLines(cells)
polydata.SetPoints(points)
mapper_grid.SetInputData(polydata)
#self.actor_grid = vtk.vtkActor() #点角色:可分别设置,但为改颜色等应定义为类全局变量
self.actor_grid.SetMapper(mapper_grid)
self.actor_grid.GetProperty().SetColor(0.2,0.2,0.2) # 设置网格为灰色
self.ren.AddActor(self.actor_grid)
#画点:
if(bPoint):
# 设置点云大小
n_points = 10000
# 设置点云的x、y、z范围
x_range = (-50, 50)
y_range = (-50, 50)
z_range = (-10, 10)
# 生成随机点云
x = np.random.uniform(x_range[0], x_range[1], n_points)
y = np.random.uniform(y_range[0], y_range[1], n_points)
z = np.random.uniform(z_range[0], z_range[1], n_points)
# 将x, y, z合并为点云数组
points = np.stack([x, y, z], axis=-1)
# 创建vtkPoints对象
vtk_points = vtk.vtkPoints()
for point in points:
vtk_points.InsertNextPoint(point)
# 创建一个vtkPolyData对象
poly_data = vtk.vtkPolyData()
poly_data.SetPoints(vtk_points)
# 创建一个vtkVertexGlyphFilter对象
glyph_filter = vtk.vtkVertexGlyphFilter()
glyph_filter.SetInputData(poly_data)
glyph_filter.Update()
# 创建一个mapper
mapper_point = vtk.vtkPolyDataMapper() #点渲染器:应分别设置
mapper_point.SetInputConnection(glyph_filter.GetOutputPort())
# 创建一个actor
#actor_point = vtk.vtkActor() #点角色:可分别设置,但为改颜色等应定义为类全局变量
self.actor_point.SetMapper(mapper_point)
# 将actor添加到renderer
self.actor_point.GetProperty().SetColor(0,0,1.0) # 设置点为绿色
self.ren.AddActor(self.actor_point)
# 设置相机位置以更好地查看球体
camera = self.ren.GetActiveCamera()
camera.SetPosition(0, 0, 500) # 设置相机位置
camera.SetFocalPoint(0, 0, 0) # 设置相机焦点(观察点)
camera.SetViewUp(0, 1, 0) # 设置相机的上方向
# 创建控件来控制颜色和相机
self.createControlWidgets()
position = camera.GetPosition()
self.moveCameraXSpinBox.setValue(position[0])
self.moveCameraYSpinBox.setValue(position[1])
self.moveCameraZSpinBox.setValue(position[2])
self.bUpdate=True
# 设置定时器以更新VTK渲染窗口
self.timer = QtCore.QTimer(self)
#self.timer.timeout.connect(self.vtkWidget.GetRenderWindow().Render) #每指定秒刷新重画一次,不必要,改成每指定秒得到相机位置值
self.timer.timeout.connect(self.onTimer)
self.timer.start(100) #更新频率,例如每100毫秒更新一次
self.vtkWidget.GetRenderWindow().Render() #渲染窗口一次取代上面的计时器
# 布局设置
self.mainLayout = QtWidgets.QVBoxLayout()
self.mainLayout.addWidget(self.vtkWidget)
self.mainLayout.addWidget(self.controlFrame)
centralWidget = QtWidgets.QWidget()
centralWidget.setLayout(self.mainLayout)
self.setCentralWidget(centralWidget)
#窗体底部增加一些操作交互控件
def createControlWidgets(self):
# 创建一个框架来放置所有控件
self.controlFrame = QtWidgets.QFrame()
self.controlLayout = QtWidgets.QHBoxLayout(self.controlFrame)
# 添加颜色选择控件
self.colorPicker = QtWidgets.QColorDialog()
self.colorPicker.setOption(QtWidgets.QColorDialog.ShowAlphaChannel, False)
self.colorPickerButton = QtWidgets.QPushButton("改变面域颜色")
self.colorPickerButton.clicked.connect(self.changeColor)
self.controlLayout.addWidget(self.colorPickerButton)
# 添加相机控制控件
self.moveCameraLabel = QtWidgets.QLabel("移动相机沿XYZ轴方向:")
self.moveCameraXSpinBox = QtWidgets.QDoubleSpinBox()
self.moveCameraXSpinBox.setRange(-2000, 2000)
self.moveCameraXSpinBox.setSingleStep(5)
self.moveCameraYSpinBox = QtWidgets.QDoubleSpinBox()
self.moveCameraYSpinBox.setRange(-2000, 2000)
self.moveCameraYSpinBox.setSingleStep(5)
self.moveCameraZSpinBox = QtWidgets.QDoubleSpinBox()
self.moveCameraZSpinBox.setRange(-2000, 2000)
self.moveCameraZSpinBox.setSingleStep(5)
self.moveCameraXSpinBox.valueChanged.connect(self.moveCamera)
self.moveCameraYSpinBox.valueChanged.connect(self.moveCamera)
self.moveCameraZSpinBox.valueChanged.connect(self.moveCamera)
self.controlLayout.addWidget(self.moveCameraLabel)
self.controlLayout.addWidget(self.moveCameraXSpinBox)
self.controlLayout.addWidget(self.moveCameraYSpinBox)
self.controlLayout.addWidget(self.moveCameraZSpinBox)
#计时器槽函数
def onTimer(self):
self.bUpdate=False
camera = self.ren.GetActiveCamera()
position = camera.GetPosition()
self.moveCameraXSpinBox.setValue(position[0])
self.moveCameraYSpinBox.setValue(position[1])
self.moveCameraZSpinBox.setValue(position[2])
self.bUpdate=True
#点击变化颜色按纽槽函数
def changeColor(self):
color = self.colorPicker.getColor()
if color.isValid():
self.actor.GetProperty().SetColor(color.redF(), color.greenF(), color.blueF())
self.vtkWidget.GetRenderWindow().Render()
#三个微调按纽对应的同一个槽函数
def moveCamera(self):
if(True):
camera = self.ren.GetActiveCamera()
position = camera.GetPosition()
camera.SetPosition(self.moveCameraXSpinBox.value(), self.moveCameraYSpinBox.value(), self.moveCameraZSpinBox.value())
self.vtkWidget.GetRenderWindow().Render()
# 创建PyQt5应用程序和主窗口
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())