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

自制简单的网格可视化软件 — 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_())

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Echarts实现散点图

效果图如下&#xff1a; <html><head><meta charsetutf-8><link rel"stylesheet" href"js/leaflet1.7.1/dist/leaflet.css"/><script src"js/leaflet1.7.1/dist/leaflet.js"></script><script src"…

win10 eclipse安装教程

前言&#xff1a;安装eclipse之前必须安装JDK&#xff0c;JDK是编译环境&#xff0c;eclipse是集成开发平台。 一、JDK的安装 Java Development Kit 简称 JDK (一). 官方下载地址&#xff1a; Java Archive Downloads - Java SE 8u211 and later (oracle.com) 找到&#xf…

用Stable Diffusion帮助进行卡通风格渲染

用Stable Diffusion帮助进行卡通风格渲染 正常风格渲染卡通风格贴图增加涅斐尔边缘高光效果 正常风格渲染 正常的动物写实模型 卡通风格贴图 用Stable Diffusion可以帮助我们将写实贴图转化为卡通风格&#xff08;具体参数可以自己调试&#xff0c;总体上是将提示词强度和图…

《QT从基础到进阶·三十八》QWidget实现炫酷log日志打印界面

QWidget实现了log日志的打印功能&#xff0c;不仅可以在界面显示&#xff0c;还可以生成打印日志。先来看下效果&#xff0c;源码放在文章末尾&#xff1a; LogPlugin插件类管理log所有功能&#xff0c;它可以获取Log界面并能打印正常信息&#xff0c;警告信息和错误信息&…

【101011011序列检测_2023.11.20】

源文件 tb文件in输入&#xff1a;01010‘1011011’0 VCS仿真结果 当next_state为s9时&#xff0c;out置为1

软考中级哪个科目最简单?

那必须是系统集成项目管理工程师&#xff01; 系统集成项目管理工程师考试内容少&#xff0c;题型简&#xff0c;报考门槛低&#xff0c;零基础就能报考&#xff0c;学习内容比较简单&#xff0c;接近工作和生活。 系统集成项目管理工程师证书是中国计算机技术职业资格&#…

【React-Router】嵌套路由

1. 嵌套路由 在一级路由中又内嵌了其他路由&#xff0c;这种关系就叫做嵌套路由。 2. 嵌套路由配置 // /page/About/index.js const About () > {return (<div>二级路由 About 组件</div>) }export default About// /page/Layout/index.js import { Outlet, …

Ajax技

Ajax的特点 异步提交&#xff1a;Ajax采用异步通信方式&#xff0c;能够在页面无需重新加载的情况下向服务器发送请求并接收响应数据&#xff0c;提升了用户体验。无需插件&#xff1a;Ajax是基于标准浏览器的Javascript和XMLHttpRequest对象实现的&#xff0c;无需安装插件或…

【kubernetes】k8s架构之节点

文章目录 1、集群架构示意图2、概述3、管理3.1 节点名称唯一性3.2 节点自注册3.3 手动节点管理 4、节点状态4.1 地址&#xff08;Addresses&#xff09;4.2 状况&#xff08;Condition&#xff09;4.3 容量&#xff08;Capacity&#xff09;与可分配&#xff08;Allocatable&am…

关于一些bug的解决1、el-input的输入无效2、搜索之后发现数据不对3、el多选框、单选框点击无用4、

el-input输入无效 原来的代码是 var test null 但是我发现不能输入任何值 反倒修改test的初始值为123是可以的 于是我确定绑定没问题 就是修改的问题 于是改成 var test ref&#xff08;&#xff09; v-model绑定的值改成test.value就可以了 因为ref是相应式的 可以通过输入…

minio安装使用-linux

下载地址&#xff1a;MinIO | Code and downloads to create high performance object storage 选择 minio server 可以直接下载二进制文件。 将下载的文件传输到服务器的指定文件夹下&#xff0c;如 /opt/minio。 然后在&#xff0c;命令行启动minio&#xff1a; /opt/mini…

Nginx高级

Nginx高级 第一部分&#xff1a;扩容 通过扩容提升整体吞吐量 1.单机垂直扩容&#xff1a;硬件资源增加 云服务资源增加 整机&#xff1a;IBM、浪潮、DELL、HP等 CPU/主板&#xff1a;更新到主流 网卡&#xff1a;10G/40G网卡 磁盘&#xff1a;SAS(SCSI) HDD&#xff08;机械…

智慧社区建设管理方案,AI技术让小区更智能、更舒适

一、背景与需求分析 智慧社区是充分应用大数据、云计算、人工智能等信息技术手段&#xff0c;整合社区各类服务资源&#xff0c;打造基于信息化、智能化管理与服务的社区治理新形态。根据《关于深入推进智慧社区建设的意见》&#xff0c;到2025年&#xff0c;基本构建起网格化…

生活如果真能像队列一样的话

生活如果真能像队列一样&#xff0c;那该多好啊。 —————————————————————————————————————————— 背包&#xff0c;队列 可以先看他们的API&#xff1a;都含有一个无参构造函数&#xff0c;添加单个元素的方法&#xff0c;测试集合…

从零开始学习typescript——什么是typescript

什么是typescript typescript是javascript 类型的超级&#xff0c;他可以编译成纯javascript. TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行&#xff0c;并且是开源的。 这个是typescript 官网对 typescript的描述 背景及特点 TypeScript是微软开发的一个开源…

机器学习/sklearn 笔记:K-means,kmeans++

1 K-means介绍 1.0 方法介绍 KMeans算法通过尝试将样本分成n个方差相等的组来聚类&#xff0c;该算法要求指定群集的数量。它适用于大量样本&#xff0c;并已在许多不同领域的广泛应用领域中使用。KMeans算法将一组样本分成不相交的簇&#xff0c;每个簇由簇中样本的平均值描…

GIT实践与常用命令---回退

实践场景 场景1 回退提交 在日常工作中&#xff0c;我们可能会和多个同事在同一个分支进行开发&#xff0c;有时候我们可能会出现一些错误提交&#xff0c;这些错误提交如果想撤销&#xff0c;可以有两种解决办法:回退( reset )、反做(revert) keywords&#xff1a;reset、rev…

【计算方法与科学建模】矩阵特征值与特征向量的计算(二):Jacobi 过关法及其Python实现(Jacobi 旋转法的改进)

文章目录 一、Jacobi 旋转法1. 基本思想2. 注意事项 二、Jacobi 过关法1. 基本思想2. 注意事项 三、Python实现迭代过程&#xff08;调试&#xff09; 矩阵的特征值&#xff08;eigenvalue&#xff09;和特征向量&#xff08;eigenvector&#xff09;在很多应用中都具有重要的数…

Python 提高篇学习笔记(一):深拷贝和浅拷贝

文章目录 一、什么是对象的引用二、深拷贝和浅拷贝2.1 浅拷贝(Shallow Copy)2.2 深拷贝(Deep Copy)2.3 copy.copy和copy.deepcopy的区别 一、什么是对象的引用 在 Python 中&#xff0c;对象的引用是指变量指向内存中某个对象的地址或标识符。当你创建一个新的对象(比如一个整…