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条)