1+1=10

记记笔记,放松一下...

PySide6下VTK小记

接前面Qt与Python的绑定历史回顾PySide6下Matplotlib小记,继续梳理一点点...

vtk 是什么

VTK(Visualization Toolkit) 是一个开源的跨平台库,用于 3D 计算机图形学、图像处理 和 科学数据可视化。它由 Kitware 开发和维护,支持从简单的 2D/3D 图形渲染到复杂的可视化算法应用。它支持多种格式文件:

格式 文件扩展名 用途 VTK 支持的类 特点
VTK *.vtk VTK 原生格式,用于网格和几何数据 vtkDataSetReader / vtkDataSetWriter 支持结构化、非结构化网格及几何;ASCII 和二进制两种模式。
VTU *.vtu XML 格式的非结构化网格 vtkXMLUnstructuredGridReader / vtkXMLUnstructuredGridWriter XML 格式,支持大数据和并行处理。
STL *.stl 表面几何模型,常用于 3D 打印 vtkSTLReader / vtkSTLWriter 支持 ASCII 和二进制格式。
PLY *.ply 多边形网格和点云 vtkPLYReader / vtkPLYWriter 支持点、边、面以及附加属性(如颜色、法线)。
OBJ *.obj 表面几何和纹理数据 vtkOBJReader / vtkOBJExporter 可存储顶点、面、法线和纹理数据,与 MTL 文件结合支持材质。
3DS *.3ds 3D Studio 模型 vtk3DSImporter 早期的三维模型格式,支持材质和几何。
GLTF/GLB *.gltf, *.glb 高效传输和渲染的现代格式 外部库支持(如 PyVista 或 vtk.js) 支持几何、材质、纹理和动画;GLB 为二进制格式。
ExodusII *.exo 有限元分析的科学数据 vtkExodusIIReader 存储网格、模拟结果和元数据,常用于工程仿真。
XDMF *.xdmf 高性能科学数据格式 vtkXdmfReader 通常与 HDF5 结合使用,支持大规模并行处理。
NetCDF *.nc 气象和海洋科学网格数据 vtkNetCDFReader 存储多维数组数据,支持高效存储和访问。
Ensight *.case 流体力学和有限元分析数据 vtkEnsightReader 支持科学可视化,专为流体仿真设计。
HDF5 *.h5 高性能科学数据格式 vtkHDFReader 支持多维数据,提供快速存储和访问。
ParaView State *.pvd, *.pvtu 并行科学数据可视化 vtkPVDReader ParaView 支持的格式,适用于并行处理的网格数据。
LAS/LAZ *.las, *.laz 激光雷达点云数据 外部工具转换后支持 存储点云数据,包含高度、颜色和反射强度等信息。

东西太多,找个最简单的例子,先看看python下如何使用,找找感觉

例子1:使用 vtk

传统方式,直接导入 vtk 模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import vtk

cone = vtk.vtkConeSource()
cone.SetResolution(50)

# 创建一个 Mapper,将几何数据映射到图形
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(cone.GetOutputPort())

# 创建一个 Actor,表示绘制到屏幕上的对象
actor = vtk.vtkActor()
actor.SetMapper(mapper)

# 创建一个 Renderer,负责渲染场景
renderer = vtk.vtkRenderer()
renderer.SetBackground(0.1, 0.2, 0.4)  # 设置背景色

# 创建一个 RenderWindow 和 RenderWindowInteractor
render_window = vtk.vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetSize(800, 600)

interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(render_window)

# 将 Actor 添加到 Renderer 中
renderer.AddActor(actor)

# 开始渲染
render_window.Render()
interactor.Start()

注,vtk.py文件内部长这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
"""This is the vtk module"""

# this module has the same contents as vtkmodules.all
from vtkmodules.vtkCommonCore import *
from vtkmodules.vtkWebCore import *
from vtkmodules.vtkCommonMath import *
from vtkmodules.vtkCommonTransforms import *
from vtkmodules.vtkCommonDataModel import *
from vtkmodules.vtkCommonExecutionModel import *
from vtkmodules.vtkIOCore import *
from vtkmodules.vtkImagingCore import *
from vtkmodules.vtkIOImage import *
from vtkmodules.vtkIOXMLParser import *
from vtkmodules.vtkIOXML import *
from vtkmodules.vtkCommonMisc import *
from vtkmodules.vtkFiltersCore import *
from vtkmodules.vtkRenderingCore import *
from vtkmodules.vtkRenderingContext2D import *
from vtkmodules.vtkRenderingFreeType import *
...

例子2:使用 vtkmodules

使用 模块化导入方式,理论上更快,但是细节...

例子中前两行代码中没有直接用到,但是必须导入,否则窗口中没有任何内容!??

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import vtkmodules.vtkInteractionStyle  # 渲染交互器
import vtkmodules.vtkRenderingOpenGL2  # 渲染窗口
from vtkmodules.vtkFiltersSources import vtkConeSource  # 圆锥体源
from vtkmodules.vtkRenderingCore import (
        vtkActor,
        vtkPolyDataMapper,
        vtkRenderer,
        vtkRenderWindow,
        vtkRenderWindowInteractor
)# 渲染相关类

# 创建一个圆锥体
cone = vtkConeSource()
cone.SetResolution(50)

# 创建一个 Mapper,将几何数据映射到图形
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(cone.GetOutputPort()) 

# 创建一个 Actor,表示绘制到屏幕上的对象
actor = vtkActor()
actor.SetMapper(mapper)r

# 创建一个 Renderer,负责渲染场景
renderer = vtkRenderer()
renderer.AddActor(actor) 
renderer.SetBackground(0.1, 0.2, 0.4)

# 创建一个 RenderWindow 和 RenderWindowInteractor
render_window = vtkRenderWindow()
render_window.AddRenderer(renderer)
render_window.SetSize(800, 600)

interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(render_window)

# 开始渲染
render_window.Render()
interactor.Start()

vtk类

和渲染直接相关的类:

  • vtkRenderer 渲染器,负责绘制一个场景。
  • vtkRenderWindow 渲染窗口,负责在屏幕上显示内容。
  • vtkRenderWindowInteractor 提供与用户的交互功能(缩放、旋转、平移等)。

vtkRenderervtkRenderWindow都很复杂。

vtkWindow

vtkWindow 类有点多,特别关注:

  • vtkXOpenGLRenderWindow (Linux默认)
  • vtkWin32OpenGLRenderWindow (Windows默认)
  • vtkEGLRenderWindow (VTK_OPENGL_HAS_EGL启用时的默认)

详细的类继承关系:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
vtkWindow
   ├── vtkRenderWindow
   │      ├── vtkOpenGLRenderWindow
   │      │      ├── vtkCocoaOpenGLRenderWindow
   │      │      ├── vtkEGLRenderWindow
   │      │      ├── vtkGenericOpenGLRenderWindow
   │      │      │      ├── vtkExternalOpenGLRenderWindow
   │      │      │      ├── vtkZSpaceGenericRenderWindow
   │      │      ├── vtkIOSRenderWindow
   │      │      ├── vtkOSOpenGLRenderWindow
   │      │      ├── vtkSDL2OpenGLRenderWindow
   │      │      ├── vtkVRRenderWindow
   │      │      │      ├── vtkOpenVRRenderWindow
   │      │      │      ├── vtkOpenXRRenderWindow
   │      │      ├── vtkWebAssemblyOpenGLRenderWindow
   │      │      ├── vtkWin32OpenGLRenderWindow
   │      │      │      ├── vtkWin32OpenGLDXRenderWindo
   │      │      │      ├── vtkZSpaceWin32RenderWindo
   │      │      ├── vtkXOpenGLRenderWindow
   │      ├── vtkWebGPURenderWindow
   │      │      ├── vtkSDL2WebGPURenderWindow
   │      │      ├── vtkWebAssemblyWebGPURenderWindow
   │      │      ├── vtkXWebGPURenderWindow

vtkRender

1
2
3
4
5
6
7
8
9
vtkViewport
   ├── vtkRender
   │      ├── vtkOpenGLRender
   │      │      ├── vtkExternalOpenGLRender
   │      │      ├── vtkVRRender
   │      │      │      ├── vtkOpenVRRender
   │      │      │      ├── vtkOpenXRRender
   │      │      ├── vtkZSpaceRender
   │      ├── vtkWebGPURender

Qt控件?

尽管这部分和PySide无关,但由于其对C++ Qt有用,简单列一下

源码目录vtk/GUISupport/Qt中有一些C++的类,用于提供和Qt的集成。其中有几个和QWidget/QWindow直接相关。

QVTKOpenGLNativeWidget

  • 别名:QVTKRenderWidget
  • 派生自:QOpenGLWidget
  • 内部使用 vtkGenericOpenGLRenderWindow

QVTKOpenGLStereoWidget

  • 派生自:QWidget
  • 内部使用 vtkGenericOpenGLRenderWindow 或 vtkRenderWindow

QVTKOpenGLWindow

  • 派生自:QOpenGLWindow
  • 内部使用 vtkGenericOpenGLRenderWindow 或 vtkRenderWindow

手册中说,一般来说,QVTKOpenGLNativeWidget 可能是更好的选择,但对于需要四缓冲(quad-buffer)立体显示的应用程序,基于 QVTKOpenGLWindow 的 QVTKOpenGLStereoWidget 可能是更好的选择。需要注意,QVTKOpenGLStereoWidget 有所有 Qt 中 QWidget::createWindowContainer 造成的缺陷。

PySide等绑定的支持?

vtk是一个C++的库,它并没有使用sip或shiboken创建PyQt或PySide的绑定。而是直接尝试找到并使用(这个顺序比matplotlib采用的顺序更符合我的认知/喜好):

  • PySide6
  • PyQt6
  • PyQt5
  • PySide2
  • PyQt4
  • PySide

Python/vtkmodules/qt/__init__.py 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import importlib
import sys

# PyQtImpl can be set by the user
PyQtImpl = None

# Has an implementation has been imported yet?
for impl in ["PySide6", "PyQt6", "PyQt5", "PySide2", "PyQt4", "PySide"]:
    if impl in sys.modules:
        # Sometimes an attempted import can be crufty (e.g., unclean
        # uninstalls of PyQt5), so let's try to import the actual functionality
        try:
            importlib.import_module(impl + '.QtCore')
        except Exception:
            pass
        else:
            PyQtImpl = impl
            break

Qt控件 QVTKRenderWindowInteractor

vtk提供了一个 名为 QVTKRenderWindowInteractor 的类:

1
class QVTKRenderWindowInteractor(QVTKRWIBaseClass):

它是从QWidget(或者QOpenGLWidget,或者QGLWidet)派生出来的:

1
2
3
4
5
6
7
8
9
# Define types for base class, based on string
if QVTKRWIBase == "QWidget":
    QVTKRWIBaseClass = QWidget
elif QVTKRWIBase == "QGLWidget":
    QVTKRWIBaseClass = QGLWidget
elif QVTKRWIBase == "QOpenGLWidget":
    QVTKRWIBaseClass = QOpenGLWidget
else:
    raise ImportError("Unknown base class for QVTKRenderWindowInteractor " + QVTKRWIBase)

有了这个Widget,和Qt程序集成就不是问题了

  • 与Qt窗口集成
  • 事件交互

PySide6下使用

pyside6 vtk

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import sys

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QMainWindow

from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

# load implementations for rendering and interaction factory classes
import vtkmodules.vtkInteractionStyle
import vtkmodules.vtkRenderingOpenGL2

class VTKWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("VTK in PySide6 from 1+1=10")
        self.setGeometry(100, 100, 800, 600)

        # 创建VTK对应的 QWidget 控件
        widget = QVTKRenderWindowInteractor(self)
        self.setCentralWidget(widget)

        # 应用渲染器
        ren = vtkRenderer()
        widget.GetRenderWindow().AddRenderer(ren)

        # 创建 3D 锥体
        cone = vtkConeSource()
        cone.SetResolution(8)

        # 创建映射器并设置输入
        coneMapper = vtkPolyDataMapper()
        coneMapper.SetInputConnection(cone.GetOutputPort())

        # 创建演员并设置映射器
        coneActor = vtkActor()
        coneActor.SetMapper(coneMapper)

        # 将演员添加到渲染器
        ren.AddActor(coneActor)

        # 设置渲染器背景颜色
        ren.SetBackground(0.1, 0.2, 0.4)  # 深蓝色背景

        # 显示窗口
        self.show()

        # 初始化并开始交互
        widget.Initialize()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = VTKWindow()
    sys.exit(app.exec())

参考

  • https://gitlab.kitware.com/vtk/vtk/-/blob/master/Wrapping/Python/vtkmodules/qt/QVTKRenderWindowInteractor.py
  • https://gitlab.kitware.com/vtk/vtk/-/blob/master/Examples/GUI/Qt/MinimalQtVTKApp/
  • https://examples.vtk.org/site/Python/
  • https://docs.vtk.org/en/latest/build_instructions/index.html