如何在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_())
关键修改说明
- 导入必要模块:新增
vtkCommand和vtkPointPicker的导入。 - 初始化拾取器:在
setup_canvas中创建vtkPointPicker并设置给交互器。 - 添加点击回调:定义
on_left_click函数,处理鼠标左键点击事件,调用Pick方法获取拾取结果,成功则输出点ID和坐标。 - 绑定事件:通过
AddObserver将LeftButtonPressEvent与回调函数关联。
内容的提问来源于stack exchange,提问作者OptimusPrime
相关产品推荐
相关产品推荐

