pyqt5 - Python VTK Repeated Point Picking Unexpected Behavior - Stack Overflow

I am using pyQT5 and VTK to render meshes and select points. I have a custom interactor that I use to i

I am using pyQT5 and VTK to render meshes and select points. I have a custom interactor that I use to intercept the QT events and perform operations. For my application, I want to allow the user to use the standard CTRL+click behavior for selecting multiple points. I have implemented this to the best of my ability, however I have noticed that repeated clicking leads to unexpected behavior. All of a sudden the picker will start going in sequential order, regardless of the cursor position. In the below image, I clicked on the more center, red point (18), and, while holding CTRL, clicked again, and it selected point 0 without moving the cursor. Does the picker need to be reset?

The code in full:

import sys
import numpy as np
import vtk
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5 import QtCore, QtGui, QtWidgets

# Subclass QVTKRenderWindowInteractor. This uses PyQT event handling
#   rather than vtk's to avoid clashes.
class MyRenderWindowInteractor(QVTKRenderWindowInteractor):

    def __init__(self, parent, mygui):
        super().__init__(parent)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.mygui = mygui

        # Initialize the renderer and interactor
        self.renderer = vtk.vtkRenderer()
        self.interactor = self.GetRenderWindow().GetInteractor()

        # Booleans for mouse click events
        self.left_mouse_pressed = False
        self.middle_mouse_pressed = False
        self.right_mouse_pressed = False
        self.mouse_position = None
        self.mouse_pressed_position = None
        self.mouse_released_position = None

        # Initialize objects for selection
        self.selection_ids = vtk.vtkIdTypeArray()
        self.selection_ids.SetNumberOfComponents(1)
        self.selection_grid = vtk.vtkUnstructuredGrid()
        self.selection_node = vtk.vtkSelectionNode()
        self.selection_node.SetSelectionList(self.selection_ids)
        self.selection_node.SetFieldType(vtk.vtkSelectionNode.CELL)
        self.selection_node.SetContentType(vtk.vtkSelectionNode.INDICES)
        self.selection = vtk.vtkSelection()
        self.selection.AddNode(self.selection_node)
        self.selection_extractor = vtk.vtkExtractSelection()
        self.selection_data_mapper = vtk.vtkDataSetMapper()
        self.selection_data_mapper.SetInputData(self.selection_grid)

        # Initialize objects for point selection
        self.picked_point_id = -1
        self.point_picker = vtk.vtkPointPicker()
        self.point_picker.UseCellsOn()
        self.selection_node_actor = vtk.vtkActor()
        self.selection_node_actor.SetMapper(self.selection_data_mapper)
        self.selection_node_actor.GetProperty().SetPointSize(5)
        self.selection_node_actor.GetProperty().SetColor(vtk.vtkNamedColors().GetColor3d("Red"))
        self.renderer.AddActor(self.selection_node_actor)
        self.selection_node.SetFieldType(vtk.vtkSelectionNode.POINT)
        self.selection_node.SetContentType(vtk.vtkSelectionNode.INDICES)
        
    # Override
    def mousePressEvent(self, event):

        # Overall Try/Except
        try:

            # Update mouse pressed position
            self.mouse_pressed_position = event.localPos()
            self.mouse_position = self.mouse_pressed_position

            # If the left button is pressed 
            if event.button() == QtCore.Qt.LeftButton:

                # Update pressed boolean
                self.left_mouse_pressed = True

                # Get the position of the click
                world_pos = np.array(self.point_picker.GetPickPosition())

                # Extract the point
                self.point_picker.Pick(event.localPos().x(),
                                       self.height() - event.localPos().y(), 0, self.renderer)
                picked_point_id = self.point_picker.GetPointId()
                print(picked_point_id)

                # If a point has been selectioned
                if picked_point_id != -1 and picked_point_id != self.picked_point_id:

                    # Reset the list of selectioned points only if CTRL is not pressed
                    if event.modifiers() != QtCore.Qt.ControlModifier:
                        self.selection_ids.Initialize()
                        
                    # Reset items
                    self.picked_point_id = picked_point_id
                    
                    # Copy the selectioned point to a new unstructred grid
                    self.selection_ids.InsertNextValue(picked_point_id)
                    self.selection.Modified()
                    self.selection_extractor.SetInputData(0, self.mygui.mesh_grid)
                    self.selection_extractor.SetInputData(1, self.selection)
                    self.selection_extractor.Update()
                    self.selection_grid.ShallowCopy(self.selection_extractor.GetOutput())
                    self.selection_grid.Modified()
                    self.selection_data_mapper.Update()
                    self.interactor.Render()

            # If the middle button is pressed 
            elif event.button() == QtCore.Qt.MiddleButton:

                # Update pressed boolean
                self.middle_mouse_pressed = True

            # If the right button is pressed 
            elif event.button() == QtCore.Qt.RightButton:

                # Update pressed boolean
                self.right_mouse_pressed = True

        # Print Error
        except Exception as error:
            print('MOUSE PRESSED EVENT ERROR: '+str(error)+'.')

    # Override
    def mouseReleaseEvent(self, event):

        # Overall Try/Except
        try:

            # Update mouse released position
            self.mouse_released_position = event.localPos()
            self.mouse_position = self.mouse_released_position

            # If the left button is released
            if event.button() == QtCore.Qt.LeftButton:

                # Update pressed boolean
                self.left_mouse_pressed = False

            # If the middle button is released 
            elif event.button() == QtCore.Qt.MiddleButton:

                # Update pressed boolean
                self.middle_mouse_pressed = False

            # If the right button is released 
            elif event.button() == QtCore.Qt.RightButton:

                # Update pressed boolean
                self.right_mouse_pressed = False
                        
        # Print Error
        except Exception as error:
            print('MOUSE RELEASE EVENT ERROR: '+str(error)+'.')

    # Override
    def mouseMoveEvent(self, event):
            
        # If the middle mouse button is currently pressed, rotate
        if self.middle_mouse_pressed:

            # Get the old and new mouse positions on the display in vtk coordinates
            display_pos_old = QtCore.QPoint(int(self.mouse_position.x()),
                                            int(self.height() - self.mouse_position.y()))
            display_pos_new = QtCore.QPoint(int(event.localPos().x()),
                                            int(self.height() - event.localPos().y()))
            display_motion = display_pos_new - display_pos_old
            
            # Compute the new display focal position, and new world positions
            display_focal = np.array([0., 0., 0.])
            world_pos_old = np.array([0., 0., 0., 0.])
            world_pos_new = np.array([0., 0., 0., 0.])
            world_pos = np.array(self.renderer.GetActiveCamera().GetPosition())
            world_focal = np.array(self.renderer.GetActiveCamera().GetFocalPoint())
            self.interactor.GetInteractorStyle().ComputeWorldToDisplay(self.renderer,
                                                                 world_focal[0],
                                                                 world_focal[1],
                                                                 world_focal[2],
                                                                 display_focal)
            self.interactor.GetInteractorStyle().ComputeDisplayToWorld(self.renderer,
                                                                  display_pos_old.x(),
                                                                  display_pos_old.y(),
                                                                  display_focal[2],
                                                                  world_pos_old)
            self.interactor.GetInteractorStyle().ComputeDisplayToWorld(self.renderer,
                                                                  display_pos_new.x(),
                                                                  display_pos_new.y(),
                                                                  display_focal[2],
                                                                  world_pos_new)
            world_motion = world_pos_old - world_pos_new

            # Rotate as appropriate
            if np.abs(display_motion.x()) > np.abs(display_motion.y()):
                self.renderer.GetActiveCamera().Azimuth(-2.0 * display_motion.x())
            else:
                self.renderer.GetActiveCamera().Elevation(-1.0 * display_motion.y())
            self.renderer.GetActiveCamera().OrthogonalizeViewUp()
            self.renderer.ResetCameraClippingRange()
            self.interactor.Render()

            # Reset mouse position to the latest event
            self.mouse_position = event.localPos()

        # If the right mouse button is currently pressed, pan
        if self.right_mouse_pressed:

            # Get the old and new mouse positions on the display
            display_pos_old = QtCore.QPoint(int(self.mouse_position.x()),
                                            int(self.height() - self.mouse_position.y()))
            display_pos_new = QtCore.QPoint(int(event.localPos().x()),
                                            int(self.height() - event.localPos().y()))
            display_motion = display_pos_new - display_pos_old

            # Compute the new display focal position, and new world positions
            display_focal = np.array([0., 0., 0.])
            world_pos_old = np.array([0., 0., 0., 0.])
            world_pos_new = np.array([0., 0., 0., 0.])
            world_pos = np.array(self.renderer.GetActiveCamera().GetPosition())
            world_focal = np.array(self.renderer.GetActiveCamera().GetFocalPoint())
            self.interactor.GetInteractorStyle().ComputeWorldToDisplay(self.renderer,
                                                                 world_focal[0],
                                                                 world_focal[1],
                                                                 world_focal[2],
                                                                 display_focal)
            self.interactor.GetInteractorStyle().ComputeDisplayToWorld(self.renderer,
                                                                  display_pos_old.x(),
                                                                  display_pos_old.y(),
                                                                  display_focal[2],
                                                                  world_pos_old)
            self.interactor.GetInteractorStyle().ComputeDisplayToWorld(self.renderer,
                                                                  display_pos_new.x(),
                                                                  display_pos_new.y(),
                                                                  display_focal[2],
                                                                  world_pos_new)
            world_motion = world_pos_old - world_pos_new

            # Pan
            self.renderer.GetActiveCamera().SetFocalPoint(*(world_focal[0:3] + world_motion[0:3]))
            self.renderer.GetActiveCamera().SetPosition(*(world_pos[0:3] + world_motion[0:3]))
            self.renderer.ResetCameraClippingRange()
            self.interactor.Render()

            # Reset mouse pressed position to the latest event
            self.mouse_position = event.localPos()

class GUIForm(object):
    def setupUi(self, GUIForm):
        GUIForm.setObjectName("GUIForm")
        self.centralwidget = QtWidgets.QWidget(GUIForm)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        GUIForm.setCentralWidget(self.centralwidget)

        self.retranslateUi(GUIForm)
        QtCore.QMetaObject.connectSlotsByName(GUIForm)

        # Add vtk window, renderer, and interactor
        self.frame_vtk = QtWidgets.QFrame()
        self.vtk_window = MyRenderWindowInteractor(self.frame_vtk, self)
        self.vtk_window.setMinimumWidth(30)
        self.verticalLayout.addWidget(self.vtk_window)
        self.vtk_renderer = self.vtk_window.renderer
        self.vtk_window.GetRenderWindow().AddRenderer(self.vtk_renderer)
        self.vtk_interactor = self.vtk_window.GetRenderWindow().GetInteractor()
        self.vtk_interactor.Initialize()
        
    def retranslateUi(self, GUIForm):
        _translate = QtCore.QCoreApplication.translate

class MyGUI(QtWidgets.QMainWindow, GUIForm):

    # Initialize
    def __init__(self, parent=None):

        # Call inherited class functions
        super(MyGUI, self).__init__(parent)
        self.setupUi(self)

        # Add the user style to the interactor and observers for button controls
        self.vtk_interactor.SetInteractorStyle(vtk.vtkInteractorStyleUser())
        self.vtk_interactor.LightFollowCameraOff()

        # Create some points and cells
        self.mesh_grid = vtk.vtkUnstructuredGrid()
        self.mesh_grid.AllocateExact(25, 36)
        self.points = vtk.vtkPoints()
        self.points.SetNumberOfPoints(36)
        for i in range(0, 6):
            for j in range(0, 6):
                self.points.SetPoint(6*i+j, (float(i), float(j), 0.0))
        self.mesh_grid.SetPoints(self.points)
        for i in range(0, 5):
            for j in range(0, 5):
                self.mesh_grid.InsertNextCell(vtk.VTK_QUAD,4,(6*i+j, 6*i+j+1, 6*(i+1)+j+1, 6*(i+1)+j))

        # Setup mesh mapper and actor
        self.mesh_mapper = vtk.vtkDataSetMapper()
        if vtk.VTK_MAJOR_VERSION <= 5:
            self.mesh_mapper.SetInput(self.mesh_grid)
        else:
            self.mesh_mapper.SetInputData(self.mesh_grid)
        self.mesh_actor = vtk.vtkActor()
        self.mesh_actor.SetMapper(self.mesh_mapper)
        self.mesh_actor.GetProperty().EdgeVisibilityOn()
        self.vtk_renderer.AddActor(self.mesh_actor)
        self.vtk_renderer.ResetCamera()
        
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = MyGUI()
    w.resize(900, 900)
    w.show()
    sys.exit(app.exec_())

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744340598a4569379.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信