QT5+vtk画三维点、线、面、网格线示例

现已对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())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mr_LuoWei2009

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值