You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python的VTK PolyData中左键点击获取点坐标及ID

实现VTK点云左键点击获取坐标与ID

问题描述

使用QVTKRenderWindowInteractor在VTK渲染器中展示随机点,可通过Qt按钮更新点云,但无法实现左键点击点时获取其坐标和ID,尝试vtkPointPicker和vtkCellPicker未成功。

解决方案

要实现点的拾取,核心是通过vtkPointPicker绑定交互器,并监听鼠标左键点击事件处理拾取结果,具体步骤如下:

  • 初始化点拾取器:创建vtkPointPicker实例并绑定到交互器,它专门用于拾取点数据,适配当前点云场景。
  • 编写拾取回调函数:定义鼠标左键点击的处理逻辑,在回调中获取拾取到的点ID、坐标并输出或进一步处理。
  • 绑定事件与回调:将回调函数注册到交互器的LeftButtonPressEvent事件,触发时执行拾取逻辑。

修改后的完整代码

import sys
import numpy as np

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton
)

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from vtkmodules.vtkCommonCore import vtkPoints, vtkCommand
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkInteractionWidgets import vtkCameraOrientationWidget
from vtkmodules.vtkCommonDataModel import (
    vtkCellArray,
    vtkPolyData,
)
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderer,
    vtkPointPicker
)
import vtkmodules.util.numpy_support as vtk_np


class Ui_MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi()

    def setupUi(self):
        self.setWindowTitle('Visualization App')
        self.resize(800, 600)
        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout()

        self.colors = vtkNamedColors()

        # Interactor widget
        self.canvas = QVTKRenderWindowInteractor(self.centralwidget)
        self.verticalLayout.addWidget(self.canvas)
        
        # button
        self.pushButton = QPushButton(self.centralwidget)
        self.pushButton.setText('Update')
        self.verticalLayout.addWidget(self.pushButton)
        self.pushButton.clicked.connect(self.update_plot)

        self.centralwidget.setLayout(self.verticalLayout)
        self.setCentralWidget(self.centralwidget)

        self.setup_canvas()

        # enable user interface interactor
        self.show()
        self.ren_win.Render()
        self.interactor.Initialize()
        self.interactor.Start()

    def setup_canvas(self):
        # Renderer, render window and the interactor
        self.ren = vtkRenderer()
        self.ren_win = self.canvas.GetRenderWindow()
        self.interactor = self.ren_win.GetInteractor()

        self.ren.SetBackground(.2, .3, .4)
        self.ren_win.AddRenderer(self.ren)

        # Target
        self.target_actor = self.init_canvas_actor(np.random.normal(size=(np.random.randint(100), 3), scale=0.2))
        self.ren.AddActor(self.target_actor)

        # 初始化点拾取器并绑定到交互器
        self.picker = vtkPointPicker()
        self.interactor.SetPicker(self.picker)

        # 绑定鼠标左键点击事件到回调函数
        self.click_observer = self.interactor.AddObserver(vtkCommand.LeftButtonPressEvent, self.on_left_click)

        # Camera orientation widget
        # Important: The interactor must be set prior to enabling the widget
        self.interactor.SetRenderWindow(self.ren_win)
        self.cam_orient_manipulator = vtkCameraOrientationWidget()
        self.cam_orient_manipulator.SetParentRenderer(self.ren)
        self.cam_orient_manipulator.On()

        # Camera position
        self.ren.GetActiveCamera().Azimuth(0)
        self.ren.GetActiveCamera().Elevation(-80)
        self.ren.ResetCamera()

    def on_left_click(self, obj, event):
        # 获取鼠标点击位置
        x, y = obj.GetEventPosition()
        # 执行拾取
        self.picker.Pick(x, y, 0, self.ren)
        
        # 获取拾取结果
        picked_point_id = self.picker.GetPointId()
        if picked_point_id != -1:
            # 获取点坐标
            point_coords = [0.0, 0.0, 0.0]
            self.pd.GetPoint(picked_point_id, point_coords)
            print(f"拾取到点ID: {picked_point_id}, 坐标: {point_coords}")
        else:
            print("未拾取到任何点")

    def init_canvas_actor(self, nparray: np.ndarray):
        self.nparray = nparray
        nCoords = nparray.shape[0]

        self.points = vtkPoints()
        self.cells = vtkCellArray()
        self.pd = vtkPolyData()

        self.points.SetData(vtk_np.numpy_to_vtk(nparray))
        cells_npy = np.vstack([np.ones(nCoords, dtype=np.int64), np.arange(nCoords, dtype=np.int64)]).T.flatten()
        self.cells.SetCells(nCoords, vtk_np.numpy_to_vtkIdTypeArray(cells_npy))
        self.pd.SetPoints(self.points)
        self.pd.SetVerts(self.cells)

        mapper = vtkPolyDataMapper()
        mapper.SetInputDataObject(self.pd)

        actor = vtkActor()
        actor.SetMapper(mapper)
        actor.GetProperty().SetRepresentationToPoints()
        actor.GetProperty().SetColor(0.0, 1.0, 0.0)
        actor.GetProperty().SetPointSize(4)
        
        return actor

    def update_plot(self):
        nCoords = np.random.randint(100)
        pc = np.random.normal(size=(nCoords, 3), scale=0.2)
        points: vtkPoints = self.pd.GetPoints()
        points.SetData(vtk_np.numpy_to_vtk(pc))
        cells_npy = np.vstack([np.ones(nCoords, dtype=np.int64), np.arange(nCoords, dtype=np.int64)]).T.flatten()
        self.cells.SetCells(nCoords, vtk_np.numpy_to_vtkIdTypeArray(cells_npy))
        self.pd.SetPoints(points)
        
        points.Modified()
        self.cells.Modified()
        self.pd.Modified()
        self.show()
        self.ren_win.Render()
        

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Ui_MainWindow()
    sys.exit(app.exec_())

关键修改说明

  1. 导入必要模块:新增vtkCommand和vtkPointPicker的导入。
  2. 初始化拾取器:在setup_canvas中创建vtkPointPicker并设置给交互器。
  3. 添加点击回调:定义on_left_click函数,处理鼠标左键点击事件,调用Pick方法获取拾取结果,成功则输出点ID和坐标。
  4. 绑定事件:通过AddObserver将LeftButtonPressEvent与回调函数关联。

内容的提问来源于stack exchange,提问作者OptimusPrime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:25:59