【三维几何学习】自制简单的网格可视化软件 — Mesh Visualization

news2025/1/27 19:38:53

自制简单的网格可视化软件 — Mesh Visualization

  • 引言
  • 一、整体框架
    • 1.1 三角形网格
    • 1.2 界面管理
    • 1.3 VTK可视化界面
  • 二、核心源码
    • 2.1 三角形网格:TriMesh类
    • 2.2 界面Widget_Mesh_Manager
    • 2.3 VTK可视化
    • 2.4 main

引言

使用PyQt自制简单的网格可视化软件 - 视频展示

本是为了方便输入模型分析、网格分割结果可视化,使用PyQt做的一款小软件,后续通过增删变成了Mesh Visualization,主要针对三角形网格。主要功能包括:读取网格(目前仅支持.obj),关于网格顶点、边、面的一些可视化小操作(是否显示、更改颜色等)、比较简单的灯光以及背景设置、一些基本功能(模型列表、操作日志等)。


一、整体框架

在这里插入图片描述

  • MainWindow:主窗体。包含缩小、放大、关闭、菜单栏等
  • Mesh:三角网格,包含网格的读取、保存、网格的一些基本属性
  • Resource:资源文件夹。包含界面样式和图片
  • Widget_Mesh_Manager:界面管理,内含多个子控件。包含模型列表、网格信息显示界面、日志界面
  • Widget_Vtk:模型渲染界面,就是VTK渲染窗口。包含VTK显示代码actor、mapper、light等
    在这里插入图片描述

关于界面 统一采用一个界面三个文件:

  1. xxx.ui (可编辑的界面,pyqt插件可打开)
  2. ui_xxx.py (ui界面转的py界面文件) 参数设置 $FileName$ -o ui_$FileNameWithoutExtension$.py -x
  3. xxx.py (主要在这里写代码)

1.1 三角形网格

三角形网格Mesh文件夹中含有两个文件:

  • load_and_save.py 读取保存三角形网格,目前只支持obj文件,可用现有的库代替 /todo
  • TriMesh.py 三角形网格类,保存三角形网格的文件名、格式、顶点、面片等信息,每个网格都有一个独立vtk actor,方便操作以及显示

1.2 界面管理

  • 模型列表:显示打开的每一个模型,可以进行模型间的切换显示
  • 模型信息:包括顶点和面片数量,可修改点、边、面以及灯光颜色
  • 日志显示:记录每一步操作,但目前只显示部分操作 /todo
    在这里插入图片描述

1.3 VTK可视化界面

主要用于模型渲染显示 (self.vtk_widget = QVTKRenderWindowInteractor(self)):

  • mapper 映射器,将数据转为图形数据
  • renderer 渲染器,将三维图形转为二维图片
  • light 灯光,只设置了一个灯光 /todo
  • 交互方式 vtk.vtkInteractorStyleTrackballCamera()

其中actor每个三角形网格一个,方便单独操作


二、核心源码

2.1 三角形网格:TriMesh类

import ntpath
import numpy as np
from scipy.sparse import csr_matrix
from Mesh.load_and_save import load_obj, load_obj_with_edges
import vtkmodules.all as vtk


class TriMesh:
    # 0.文件
    filename = None  # 文件名
    path = None  # 路径
    file = None  # 完整路径
    format = None  # 格式

    # 1.基本属性
    vs = None     # 顶点
    faces = None  # 面片
    name = None   # 自定义名称
    actor = None  # 保存可视化的数据

    # 2.进阶属性
    point_adjacency_matrix = None  # 点的邻接矩阵
    edges = None
    edge_labels = None
    edge_actor = None

    def __init__(self, file=None, mode=None):
        # 赋值
        self.path = ntpath.split(file)[0]
        self.filename = ntpath.split(file)[1]
        self.file = file
        self.format = self.filename.split('.')[-1]
        self.actor = vtk.vtkActor()
        self.edge_actor = vtk.vtkActor()

        # 读取
        if self.format == 'obj':
            if mode == 1:
                self.vs, self.faces, self.edges, self.edge_labels = load_obj_with_edges(file)
            else:
                self.vs, self.faces = load_obj(file)
        else:
            print('Unsupported format')
            return

        # 计算点邻接矩阵
        self.point_adjacency_matrix = self.computer_point_adjacency_matrix()

    def computer_point_adjacency_matrix(self):
        num = len(self.vs)
        row = np.hstack([self.faces[:, 0], self.faces[:, 1], self.faces[:, 2]])
        col = np.hstack([self.faces[:, 1], self.faces[:, 2], self.faces[:, 0]])
        value = 0 * row + 1
        point_adjacency_matrix = csr_matrix((value, (row, col)), shape=(num, num)).toarray()
        return point_adjacency_matrix

    def boundary_edge(self):
        edge_cnt = self.point_adjacency_matrix + self.point_adjacency_matrix.T
        two_point = np.where(edge_cnt == 1)
        return two_point

    def creat_edges(self):
        edge2key = dict()
        edges = []
        for face_id, face in enumerate(self.faces):
            faces_edges = []
            for i in range(3):
                cur_edge = (face[i], face[(i + 1) % 3])
                faces_edges.append(cur_edge)
            for idx, edge in enumerate(faces_edges):
                edge = tuple(sorted(list(edge)))
                faces_edges[idx] = edge
                if edge not in edge2key:
                    edge2key[edge] = 1
                    edges.append(list(edge))
        self.edges = np.array(edges, dtype=np.int32)


if __name__ == '__main__':
    cs = TriMesh('../00ceshi/1.obj')
    # print(cs.point_adjacency_matrix)
    point = cs.boundary_edge()
    print(1)

2.2 界面Widget_Mesh_Manager

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from Widget_Mesh_Manager.ui_Widget_Mesh_Manager import Ui_Widget_Mesh_Manager
import datetime
from Mesh.TriMesh import TriMesh
import numpy as np


class Widget_Mesh_Manager(QWidget, Ui_Widget_Mesh_Manager):
    TriMesh_list = []
    mesh_show = pyqtSignal(int)

    def __init__(self, parent=None):
        super(Widget_Mesh_Manager, self).__init__(parent)
        self.setupUi(self)
        # 滚动条设置
        self.hSlider_pointSize.setMinimum(1)       # 点
        self.hSlider_pointSize.setMaximum(9)
        self.hSlider_pointSize.setSingleStep(1)
        # self.hSlider_pointSize.setTickInterval(2)     # 带有样式 不显示刻度
        # self.hSlider_pointSize.setTickPosition(QSlider.TicksBelow)
        self.hSlider_edgeSize.setMinimum(1)        # 边
        self.hSlider_edgeSize.setMaximum(9)
        self.hSlider_edgeSize.setSingleStep(1)
        self.hSlider_lightIntensity.setMinimum(0)  # 灯光
        self.hSlider_lightIntensity.setMaximum(10)
        self.hSlider_lightIntensity.setSingleStep(1)

        # 按钮点击函数
        self.initBtn()

        # UI布局
        self.initUI()

    def initBtn(self):
        pass

    def initUI(self):
        # tableWidget
        self.tableWidget.setColumnCount(1)  # 列数
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 所有列自动拉伸,充满界面
        self.tableWidget.setSelectionMode(QAbstractItemView.SingleSelection)           # 设置只能选中一行
        self.tableWidget.setEditTriggers(QTableView.NoEditTriggers)                    # 不可编辑
        self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows)            # 设置只有行选中
        self.tableWidget.verticalHeader().setVisible(False)         # 隐藏列表头
        self.tableWidget.horizontalHeader().setVisible(False)       # 隐藏行

    def addMesh(self, mesh: TriMesh):
        # 添加到list
        self.TriMesh_list.append(mesh)
        # 添加到ui
        row = self.tableWidget.rowCount()
        self.tableWidget.insertRow(row)
        item = QTableWidgetItem(mesh.filename)
        self.tableWidget.setItem(row, 0, item)
        self.tableWidget.clearFocus()
        self.tableWidget.selectRow(row)     # 新加入的行被选中
        # item.setSelected(True)
        # 刷新Info
        self.showMesh_Info(row)

    def addLog(self, info):
        time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S: ")
        self.textEdit.append(time + info)

    def showMesh_Info(self, mesh_id):
        mesh = self.TriMesh_list[mesh_id]
        self.groupBox.setTitle(mesh.filename)
        self.label_vs_num.setText('顶点个数:' + str(len(mesh.vs)))
        self.label_face_num.setText('面片个数:' + str(len(mesh.faces)))
        self.mesh_show.emit(mesh_id)

    # 重写tableWidget的点击事件
    def on_tableWidget_cellClicked(self, row, col):
        self.showMesh_Info(row)
        pass

2.3 VTK可视化

from PyQt5.QtWidgets import *
from Widget_Vtk.ui_Widget_Vtk import Ui_Widget_Vtk
from Mesh.TriMesh import TriMesh
import numpy as np
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtkmodules.all as vtk


class Widget_Vtk(QWidget, Ui_Widget_Vtk):
    mapper = None    # 映射器 数据
    actor = None     # 演员 get mapper
    renderer = None  # 渲染
    light = None     # 灯光 只有一个

    def __init__(self, parent=None):
        super(Widget_Vtk, self).__init__(parent)
        self.setupUi(self)
        #
        self.vtk_widget = QVTKRenderWindowInteractor(self)
        self.colors = np.array([[0, 0, 255], [0, 255, 255], [255, 0, 255], [0, 255, 0],
                                [255, 255, 0], [255, 0, 0], [100, 180, 51], [255, 150, 51]])
        self.colors = np.array([[80, 136, 240], [0, 255, 255], [255, 0, 255], [0, 255, 0],
                                [255, 255, 0], [255, 0, 0], [70, 70, 220], [255, 150, 51], [0,0,0]])
        self.colors1 = np.array([[180, 90, 90], [121, 185, 128], [90, 90, 180], [180, 180, 0],
                                [255, 255, 0], [255, 0, 0], [100, 255, 51], [255, 150, 51]])

        # 初始化render
        self.renderer = vtk.vtkRenderer()
        self.renderer.SetBackground(1, 1, 1)
        self.vtk_widget.GetRenderWindow().AddRenderer(self.renderer)
        self.interactor = self.vtk_widget.GetRenderWindow().GetInteractor()
        interactor_style = vtk.vtkInteractorStyleTrackballCamera()
        self.interactor.SetInteractorStyle(interactor_style)
        self.interactor.Initialize()

        # 初始化light
        self.light = vtk.vtkLight()
        self.light.SwitchOff()      # 默认关闭
        self.renderer.AddLight(self.light)
        self.vtk_widget.Render()
        self.vtk_widget.Start()
        self.renderer.ResetCamera()
        self.vtk_widget.update()

    def resizeEvent(self, e):
        """
            重写窗口移动事件
        """
        self.vtk_widget.resize(self.size())

    def removeAllActors(self):
        al = self.renderer.GetActors()
        n = al.GetNumberOfItems()
        al.InitTraversal()
        for i in range(n):
            actor = al.GetNextActor()
            self.renderer.RemoveActor(actor)
        self.vtk_widget.update()

    def showTrimesh(self, mesh: TriMesh):
        # 添加点
        points = vtk.vtkPoints()
        for v in mesh.vs:
            points.InsertNextPoint(v)

        # 添加面片
        polys = vtk.vtkCellArray()
        for f in mesh.faces:
            polys.InsertNextCell(len(f), f)

        # 创建PolyData
        cube = vtk.vtkPolyData()
        cube.SetPoints(points)
        cube.SetPolys(polys)

        # 创建 mapper 和 actor
        mapper = vtk.vtkPolyDataMapper()
        mapper.ScalarVisibilityOff()
        mapper.SetInputData(cube)

        mesh.actor.SetMapper(mapper)
        mesh.actor.GetProperty().SetColor([0.5, 0.5, 0.5])

        self.renderer.AddActor(mesh.actor)
        self.renderer.ResetCamera()
        self.vtk_widget.update()

    def show_boundary_edge(self, mesh: TriMesh):
        # 添加点
        points = vtk.vtkPoints()
        for v in mesh.vs:
            points.InsertNextPoint(v)

        pa, pb = mesh.boundary_edge()
        # 添加边
        edges = vtk.vtkCellArray()
        for i in range(len(pa)):
            edges.InsertNextCell(2, np.array([pa[i], pb[i]]))

        # 添加边界
        mapper2 = vtk.vtkPolyDataMapper()
        cube1 = vtk.vtkPolyData()
        cube1.SetPoints(points)
        cube1.SetLines(edges)

        mapper2.SetInputData(cube1)

        mesh.edge_actor.SetMapper(mapper2)
        # mesh.edge_actor.GetProperty().SetEdgeColor(0, 0, 1)
        # mesh.edge_actor.GetProperty().SetEdgeVisibility(1)
        mesh.edge_actor.GetProperty().SetLineWidth(2)
        self.renderer.AddActor(mesh.edge_actor)
        self.vtk_widget.update()

    def show_mesh_color(self, mesh: TriMesh):
        # 添加点
        points = vtk.vtkPoints()
        for v in mesh.vs:
            points.InsertNextPoint(v)

        # 添加面片
        polys = vtk.vtkCellArray()
        cellColor = vtk.vtkFloatArray()
        for f in mesh.faces:
            polys.InsertNextCell(len(f), f)
            cellColor.InsertNextValue(mesh.vs[f[0]][0])

        # 创建PolyData
        cube = vtk.vtkPolyData()
        cube.SetPoints(points)
        cube.SetPolys(polys)
        cube.GetCellData().SetScalars(cellColor)

        # 创建 mapper 和 actor
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetScalarRange(min(mesh.vs[:, 0]), max(mesh.vs[:, 0]))
        self.mapper.SetInputData(cube)
        mesh.actor.SetMapper(self.mapper)
        mesh.actor.GetProperty().SetEdgeVisibility(1)

        self.vtk_widget.update()

    def show_point_color(self, mesh: TriMesh, seg=[]):
        # 添加点
        points = vtk.vtkPoints()
        pColor = vtk.vtkFloatArray()

        for v in mesh.vs:
            points.InsertNextPoint(v)
            # id = np.random.randint(0, 2)
            pColor.InsertNextValue(v[0])
        if len(seg) > 0:
            pColor = vtk.vtkFloatArray()
            for s in seg:
                pColor.InsertNextValue(s)

        # 添加面片
        polys = vtk.vtkCellArray()
        for f in mesh.faces:
            polys.InsertNextCell(len(f), f)

        # 创建PolyData
        cube = vtk.vtkPolyData()
        cube.SetPoints(points)
        cube.SetPolys(polys)
        cube.GetPointData().SetScalars(pColor)

        mapper = vtk.vtkPolyDataMapper()
        mapper.SetScalarRange(min(mesh.vs[:, 2]), max(mesh.vs[:, 2]))
        mapper.SetInputData(cube)

        mesh.actor.SetMapper(mapper)
        mesh.actor.GetProperty().SetColor([0.5, 0.5, 0.5])

        self.vtk_widget.update()

    def show_mesh_seg(self, mesh: TriMesh, seg):
        # 添加点
        points = vtk.vtkPoints()
        for v in mesh.vs:
            points.InsertNextPoint(v)

        # 添加面片
        polys = vtk.vtkCellArray()
        cellColor = vtk.vtkUnsignedCharArray()
        cellColor.SetNumberOfComponents(3)
        for f in mesh.faces:
            polys.InsertNextCell(len(f), f)
        for s in seg:
            c = self.colors[s]
            cellColor.InsertNextTuple(c)


        # 创建PolyData
        cube = vtk.vtkPolyData()
        cube.SetPoints(points)
        cube.SetPolys(polys)
        cube.GetCellData().SetScalars(cellColor)

        # 创建 mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetColorModeToDefault()  # 需要设置为默认颜色Mode
        mapper.SetInputData(cube)
        mesh.actor.SetMapper(mapper)

        self.vtk_widget.update()

    def show_points(self, mesh: TriMesh):
        # 添加点
        points = vtk.vtkPoints()
        for v in mesh.vs:
            points.InsertNextPoint(v)

        vs = vtk.vtkPolyData()
        vs.SetPoints(points)

        # 生成顶点
        vertex = vtk.vtkVertexGlyphFilter()
        vertex.SetInputData(vs)

        # 创建 mapper 和 actor
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(vertex.GetOutputPort())

        mesh.actor.SetMapper(mapper)
        mesh.actor.GetProperty().SetColor([0, 0, 0])
        mesh.actor.GetProperty().SetPointSize(5)
        self.vtk_widget.update()

    def saveToimage(self):
        from vtkmodules.vtkRenderingCore import vtkWindowToImageFilter
        from vtkmodules.vtkIOImage import (
            vtkBMPWriter,
            vtkJPEGWriter,
            vtkPNGWriter,
            vtkPNMWriter,
            vtkPostScriptWriter,
            vtkTIFFWriter
        )

        im_filter = vtkWindowToImageFilter()                   #
        im_filter.SetInput(self.vtk_widget.GetRenderWindow())  # QVTKRenderWindowInteractor
        im_filter.SetScale(3)                                  #
        im_filter.SetInputBufferTypeToRGBA()

        writer = vtkBMPWriter()
        #writer = vtkPostScriptWriter()
        writer.SetFileName('cs.bmp')
        #writer.SetFileName('cs.ps')
        writer.SetInputConnection(im_filter.GetOutputPort())
        writer.Write()

    def showTrimesh_with_edge(self, mesh: TriMesh):
        # 添加点
        points = vtk.vtkPoints()
        for v in mesh.vs:
            points.InsertNextPoint(v)

        # 添加面片
        c = 255
        face_color = [c, c, c]
        polys = vtk.vtkCellArray()
        cellColor = vtk.vtkUnsignedCharArray()
        cellColor.SetNumberOfComponents(3)
        for f in mesh.faces:
            polys.InsertNextCell(len(f), f)
            cellColor.InsertNextTuple(face_color)

        # 添加边 和 边的颜色
        edges = vtk.vtkCellArray()
        edge_colors = vtk.vtkUnsignedCharArray()
        edge_colors.SetNumberOfComponents(3)
        for e in mesh.edges:
            edges.InsertNextCell(2, e)
        for edge_labels in mesh.edge_labels:
            # edge_colors.InsertNextTuple(self.colors[edge_labels])  # colors1
            edge_colors.InsertNextTuple(self.colors[edge_labels])

        # 创建PolyData
        cube = vtk.vtkPolyData()
        cube.SetPoints(points)
        cube.SetPolys(polys)
        # cube.SetLines(edges)
        cube.GetCellData().SetScalars(cellColor)

        # 细分
        loop = vtk.vtkLoopSubdivisionFilter()
        # loop = vtk.vtkButterflySubdivisionFilter()
        # loop = vtk.vtkLinearSubdivisionFilter()
        loop.SetInputData(cube)
        loop.SetNumberOfSubdivisions(0)
        loop.Update()
        print(loop.GetOutput().GetNumberOfPolys())

        # 平滑
        smoothFilter = vtk.vtkSmoothPolyDataFilter()
        # smoothFilter = vtk.vtkWindowedSincPolyDataFilter()
        smoothFilter.SetInputConnection(loop.GetOutputPort())
        # smoothFilter.SetInputData(cube)
        smoothFilter.SetNumberOfIterations(1)  # 控制平滑次数,次数越大平滑越厉害
        smoothFilter.Update()


        # 创建 mapper 和 actor
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetColorModeToDefault()     # 需要设置为默认颜色Mode
        # self.mapper.SetInputData(cube)
        # self.mapper.SetInputConnection(loop.GetOutputPort())
        self.mapper.SetInputConnection(smoothFilter.GetOutputPort())
        print(smoothFilter.GetOutput().GetNumberOfPolys())
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(self.mapper)

        # 添加边
        self.mapper2 = vtk.vtkPolyDataMapper()
        cube1 = vtk.vtkPolyData()
        cube1.SetPoints(points)
        # cube1.SetPolys(edges)
        cube1.SetLines(edges)
        cube1.GetCellData().SetScalars(edge_colors)
        self.mapper2.SetInputData(cube1)
        self.actor2 = vtk.vtkActor()
        self.actor2.SetMapper(self.mapper2)
        # self.actor2.GetProperty().SetEdgeColor(0, 0, 1)
        self.actor2.GetProperty().SetEdgeVisibility(1)
        self.actor2.GetProperty().SetLineWidth(4)

        # 显示
        self.vtk_widget.Render()
        self.vtk_widget.Start()



        # self.renderer.
        self.renderer = vtk.vtkRenderer()
        self.renderer.SetBackground(0.7, 0.7, 0.7)
        self.renderer.SetBackground2(1, 1, 1)
        self.renderer.SetGradientBackground(1)
        # 灯光
        myLight = vtk.vtkLight()
        # myLight.SetColor(1, 1, 1)  # 设置灯光的颜色,以RGB的形式指定颜色
        # myLight.SetPosition(100, 100, 100)  # 设灯光照位置
        myLight.SetLightType(vtk.VTK_LIGHT_TYPE_HEADLIGHT)
        #vtk.VTK_LIGHT_TYPE_SCENE_LIGHT
        #vtk.VTK_LIGHT_TYPE_CAMERA_LIGHT
        # myLight.SetFocalPoint(self.renderer.GetActiveCamera().GetFocalPoint())  # 设置灯光焦点
        myLight.SetIntensity(1)      # 可视化边的时候设置为0.9
        # myLight.SwitchOff()
        self.renderer.AddLight(myLight)  # 因为renderer里可以有多个灯光,所以是AddLight() 而不是 SetLight()
        # self.renderer.Set
        self.vtk_widget.GetRenderWindow().AddRenderer(self.renderer)
        self.renderer.AddActor(self.actor)
        self.renderer.AddActor(self.actor2)

        self.renderer.ResetCamera()
        self.vtk_widget.update()

    def cs(self):
        # 三角网格
        vs = np.array([[-1, 1, -0.5],
                       [-1, 0, 0],
                       [-1, -1, -0.5],
                       [0, 0.3, 0],
                       [0, -0.3, 0],
                       [0.5, 0, 0.5]], dtype=np.float32)  # 0.5 -0.5
        faces = np.array([[4, 1, 3], [4, 1, 2], [0, 3, 1], [3, 5, 4]], dtype=np.int16)
        # 颜色
        c = np.array([[0, 1, 1], [0.5, 1, 0.5], [0.5, 1, 0.5], [1, 1, 0.5]]) * 255

        # 添加点
        points = vtk.vtkPoints()
        for v in vs:
            points.InsertNextPoint(v)

        # 添加面片
        polys = vtk.vtkCellArray()
        for f in faces:
            polys.InsertNextCell(3, f)

        cellColor = vtk.vtkUnsignedCharArray()
        cellColor.SetNumberOfComponents(3)
        for tmp in c:
            cellColor.InsertNextTuple(tmp)

        # 创建PolyData
        cube = vtk.vtkPolyData()
        cube.SetPoints(points)
        cube.SetPolys(polys)
        cube.GetCellData().SetScalars(cellColor)

        # 细分
        l = vtk.vtkLinearSubdivisionFilter()
        l.SetInputData(cube)
        l.SetNumberOfSubdivisions(1)
        l.Update()


        loop = vtk.vtkLoopSubdivisionFilter()
        #loop.SetInputData(l.GetOutputPort())
        loop.SetInputConnection(l.GetOutputPort())
        loop.SetNumberOfSubdivisions(5)
        loop.Update()

        # 创建Mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetColorModeToDefault()
        mapper.SetInputData(cube)
        mapper.SetInputConnection(loop.GetOutputPort())

        # 创建actor
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)
        # actor.GetProperty().SetColor([1, 1, 1])
        # actor.GetProperty().SetEdgeColor(0, 0, 0)
        actor.GetProperty().SetEdgeVisibility(0)

        # 灯光
        myLight = vtk.vtkLight()
        myLight.SetColor(1, 1, 1)  # 设置灯光的颜色,以RGB的形式指定颜色
        myLight.SetPosition(0, 0, 1)
        myLight.SetLightType(vtk.VTK_LIGHT_TYPE_SCENE_LIGHT)
        # vtk.VTK_LIGHT_TYPE_SCENE_LIGHT
        # vtk.VTK_LIGHT_TYPE_CAMERA_LIGHT
        myLight.SetFocalPoint(self.renderer.GetActiveCamera().GetFocalPoint())  # 设置灯光焦点
        myLight.SetIntensity(0.5)  # 可视化边的时候设置为0.9
        # myLight.SwitchOff()
        self.renderer.AddLight(myLight)  # 因为renderer里可以有多个灯光,所以是AddLight() 而不是 SetLight()

        self.renderer.AddActor(actor)
        self.renderer.ResetCamera()
        self.vtk_widget.update()



2.4 main

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from MainWindow.MainWindow import MainWindow
import sys

if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 加载图标
    app.setWindowIcon(QIcon('./Resource/rabbit.ico'))
    # 加载样式
    s = './Resource/blue.css'
    with open(s, "r") as f:
        app.setPalette(QPalette(QColor('#EAF7FF')))
        app.setStyleSheet(f.read())
    # 显示
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1239724.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kubernetes+Gitlab+Jenkins+ArgoCD多集群部署

KubernetesGitlabJenkinsArgoCD多集群部署 文章目录 KubernetesGitlabJenkinsArgoCD多集群部署1. KubernetesGitlabJenkinsArgoCD多集群部署2. 添加WebHooks自动触发3. Jenkins-构建-执行Shell4. 制作镜像及修改Yaml文件4.1 Dockerfile4.2 Build-Shell 5.自动部署Demo测试5.1 推…

VMware 16 Pro 安装以及下载

1、下载地址: https://www.aliyundrive.com/s/nj3PSD4TN9G 2、安装文件 右击打开 下一步 密钥:ZF3R0-FHED2-M80TY-8QYGC-NPKYF 到此,安装完毕

Mysql 递归查询子类Id的所有父类Id

文章目录 问题描述先看结果表结构展示实现递归查询集合查询结果修复数据 问题描述 最近开发过程中遇到一个问题,每次添加代理关系都要去递归查询一下它在不在这个代理关系树上.很麻烦也很浪费资源.想着把代理关系的父类全部存起来 先看结果 表结构展示 表名(t_agent_user_rela…

MySQL数据库时间计算的用法

今天给大家分享如何通过MySQL内置函数实现时间的转换和计算,在工作当中,测试人员经常需要查询数据库表的日期时间,但发现开发人员存入数据库表的形式都是时间戳形式,不利于测试人员查看,测试人员只能利用工具对时间戳进…

visual studio 如何建立 C 语言项目

安装这个 模块。 新建 空项目 创建完成 写demo 点击运行:

数据结构与算法编程题9

将两个递增的有序链表合并为一个递增的有序链表。要求结果链表仍使用原来两个链表的存储空间, 不另外占用其它的存储空间。表中不允许有重复的数据 a: 1, 2, 4, 5, 7, 8, 9, 10 b: 1, 2, 3, 6, 7, 8 #include <iostream> using namespace std;typedef int Elemtype; #def…

试试MyBatis-Plus可视化代码生成器,太香了,你一定会感谢我

前言 在基于Mybatis的开发模式中&#xff0c;很多开发者还会选择Mybatis-Plus来辅助功能开发&#xff0c;以此提高开发的效率。虽然Mybatis也有代码生成的工具&#xff0c;但Mybatis-Plus由于在Mybatis基础上做了一些调整&#xff0c;因此&#xff0c;常规的生成工具生成的代码…

redis运维(十六) 有序集合

一 有序集合 把握一点&#xff1a; 各种redis 命令都提供各种语言对应的API 接口,后续API是关键 ① 概念 1、sorted set --> 有序集合2、redis有序集合也是集合类型的一部分&#xff0c;所以它保留了集合中元素不能重复的特性3、但是不同的是,有序集合给每个元素多设置…

【LeetCode刷题-链表】--25.K个一组翻转链表

25.K个一组翻转链表 思路&#xff1a; 把链表节点按照k个一组分组&#xff0c;可以使用一个指针head依次指向每组的头节点&#xff0c;这个指针每次向前移动k步&#xff0c;直至链表结尾&#xff0c;对于每个分组&#xff0c; 先判断它的长度是否大于等于k&#xff0c;若是&am…

微博头条文章开放接口报错 auth by Null spi

接口文档地址 https://open.weibo.com/wiki/Toutiao/api 接口说明 https://api.weibo.com/proxy/article/publish.json 请求方式 POST 请求参数 参数名称类型是否必需描述titlestring是文章标题&#xff0c;限定32个中英文字符以内contentstring是正文内容&#xff0c;限制9…

Servlet---HttpServlet、HttpServletRequest、HttpServletResponseAPI详解

文章目录 HttpServlet基础方法doXXX方法Servlet的生命周期 HttpServletRequest获取请求中的信息获取请求传递的参数获取 query string 里的数据获取form表单里的数据获取JSON里的数据如何解析JSON格式获取数据返回数据 HttpServletResponse设置响应的Header设置不同的状态码设置…

C++(模板进阶)

目录 前言&#xff1a; 本章学习目标&#xff1a; 1.非类型模版参数 1.1使用方法 1.2注意事项 1.3 实际引用 2.模版特化 2.1概念 2.2函数模板特化 2.3类模板特化 2.3.1全特化 2.3.2偏特化 3.模版分离编译 ​编辑 3.1失败原因 ​编辑 3.2解决方案 4 总结 前言&…

乐划锁屏插画大赏热度持续,进一步促进价值内容的创造与传播

锁屏,原本只是为了防止手机在口袋里“误触”而打造的功能,现如今逐渐成为文化传播领域的热门入口。乐划锁屏不断丰富锁屏内容和场景玩法,通过打造“乐划锁屏插画大赏”系列活动为广大内容创作者提供了更多展示自我的机会,丰富平台内容。 从2020年到2023年,乐划锁屏插画大赏已连…

[点云分割] 基于颜色的区域增长分割

效果&#xff1a; 代码&#xff1a; #include <iostream> #include <thread> #include <vector>#include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/search/search.h> #include <pcl/search/kdtree.h> #inclu…

Node.js入门指南(一)

目录 Node.js入门 什么是Node.js Node.js的作用 Node.js安装 Node.js编码注意事项 Buffer(缓冲器&#xff09; 定义 使用 fs模块 概念 文件写入 文件读取 文件移动与重命名 文件删除 文件夹操作 查看资源状态 路径问题 path模块 Node.js入门 什么是Node.js …

基于单片机声光控智能路灯系统仿真设计

**单片机设计介绍&#xff0c; 基于单片机声光控智能路灯系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的声光控智能路灯系统是一种利用单片机技术实现智能控制的路灯系统。它通过感知环境音量和光照强度…

vue中为什么data属性是一个函数而不是一个对象

面试官&#xff1a;为什么data属性是一个函数而不是一个对象&#xff1f; 一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象&#xff0c;也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:"foo"},// 函数格…

c语言编程(模考2)

简答题1 从键盘输入10个数&#xff0c;统计非正数的个数&#xff0c;并且计算非正数的和 #include<stdio.h> int main() {int i,n0,sum0;int a[10];printf("请输入10个数&#xff1a;");for(i0;i<10;i){scanf("%d",&a[i]);}for(i0;i<10…

【Python】数据类型和切片的零碎知识点

1. 数据类型 pow(a, b, c) # a^b % c print("happy {}".format(name))数字类型包括整数&#xff0c;浮点数&#xff0c;复数 0x9a表示十六进制数&#xff08;0x&#xff0c;0X开头表示十六进制&#xff09; 0b1010&#xff0c;-0B101表示二进制数&#xff08;0…

【endnote】如何将参考文献放到想放的位置

1. 方式 直接将生成的文献全选拖到想放的位置 注意&#xff1a;不要使用ctrlx这种操作。 2.具体操作 2.1 新建测试文档 如下图&#xff1a; 2.2 引用两篇文献】 如下图&#xff1a; 2.3 测试 如下图&#xff0c;选中所有已经引用的文献。 拖拽到想要防止的位置。 新…