vtkPropPicker not working in OSX and Qt

Picker is not picking at the correct position - there seems to be a factor 2 somewhere which screws up the result:

import sys
import vtk
from PyQt5 import Qt
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

class MainWindow(Qt.QMainWindow):

    def __init__(self, parent = None):
        Qt.QMainWindow.__init__(self, parent)

        self.frame = Qt.QFrame()
        self.vl = Qt.QVBoxLayout()
        self.vtkWidget = QVTKRenderWindowInteractor(self.frame)

        self.ren = vtk.vtkRenderer()
        self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
        self.picker = vtk.vtkPropPicker()

        # Create source
        source = vtk.vtkSphereSource()
        # Create a mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(source.GetOutputPort())
        # Create an actor
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)

        self.ren.AddActor(actor)
        self.ren.ResetCamera()

        self.vl.addWidget(self.vtkWidget)
        self.frame.setLayout(self.vl)
        self.setCentralWidget(self.frame)
        self.show()

        self.iren.AddObserver("LeftButtonPressEvent", self.onclick)
        self.iren.Initialize()
        self.iren.Start()

    def onclick(self, iren, event):
        x, y = self.iren.GetEventPosition()
        print("Left button pressed at", x,y)

        self.picker.PickProp(x, y, self.ren)
        picked = self.picker.GetProp3D()
        if picked:
            picked3d = self.picker.GetPickPosition()
            print("  3d picked pt", picked3d) 
            source = vtk.vtkSphereSource()
            source.SetRadius(0.02)
            mapper = vtk.vtkPolyDataMapper()
            mapper.SetInputConnection(source.GetOutputPort())
            actor = vtk.vtkActor()
            actor.SetMapper(mapper)
            actor.GetProperty().SetColor(255,0,0)
            actor.SetPosition(picked3d)
            self.ren.AddActor(actor)


if __name__ == "__main__":
    app = Qt.QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())

(clicking the black empty area close to the bottom-left corner places a red sphere).

I’m also getting a warning message:

qt.qpa.window: <QNSWindow: 0x7fa833705d90; contentView=<QNSView: 0x7fa833704bd0; QCocoaWindow(0x60000167c790, window=QWidgetWindow(0x60000057d4a0, name="QFrameClassWindow"))>> has active key-value observers (KVO)! These will stop working now that the window is recreated, and will result in exceptions when the observers are removed. Break in QCocoaWindow::recreateWindowIfNeeded to debug.

System info:

vtk version       : 9.1.0
python version    : 3.9.7 (default, Sep 16 2021, 08:50:36) [Clang 10.0.0 ]
python interpreter: /Users/mmusy/Software/miniconda3/bin/python
system            : Darwin 21.3.0 posix x86_64
Screen: Color LCD
Size: 1280 x 800
1 Like

It seems that event position is not scaled with the correct device pixel ratio in QVTKRenderWindowInteractor. You can add logs or attach a debugger to see why that happens. Maybe using tolerance and other techniques that are used in QVTKInteractorAdapter will have to be implemented in the Python class.

You could have a look a this error report, too - maybe they are related problems:

Thanks for posting this problem. Same problem with PySide6 on Mac OSX (see code below). Picking does not work. The message “3d picked pt” never appears. Still having the problem and looking for a solution.

(comcab) NIWA-1019500~/comcab-master$ uname -a
Darwin NIWA-1019500 21.4.0 Darwin Kernel Version 21.4.0: Fri Mar 18 00:45:05 PDT 2022; root:xnu-8020.101.4~15/RELEASE_X86_64 x86_64

python --version; python -c “import vtk;print(f’{vtk.VTK_MAJOR_VERSION}.{vtk.VTK_MINOR_VERSION}’)”

Python 3.9.4

9.1

import sys
import vtk
from PySide6 import QtWidgets
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

class MainWindow(QtWidgets.QMainWindow):

def __init__(self, parent = None):
    QtWidgets.QMainWindow.__init__(self, parent)

    self.frame = QtWidgets.QFrame()
    self.vl = QtWidgets.QVBoxLayout()
    self.vtkWidget = QVTKRenderWindowInteractor(self.frame)

    self.ren = vtk.vtkRenderer()
    self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
    self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
    self.picker = vtk.vtkPropPicker()

    # Create source
    source = vtk.vtkSphereSource()
    # Create a mapper
    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputConnection(source.GetOutputPort())
    # Create an actor
    actor = vtk.vtkActor()
    actor.SetMapper(mapper)

    self.ren.AddActor(actor)
    self.ren.ResetCamera()

    self.vl.addWidget(self.vtkWidget)
    self.frame.setLayout(self.vl)
    self.setCentralWidget(self.frame)
    self.show()

    self.iren.AddObserver("LeftButtonPressEvent", self.onclick)
    self.iren.Initialize()
    self.iren.Start()

def onclick(self, iren, event):
    x, y = self.iren.GetEventPosition()
    print("Left button pressed at", x,y)

    self.picker.PickProp(x, y, self.ren)
    picked = self.picker.GetProp3D()
    if picked:
        picked3d = self.picker.GetPickPosition()
        print("  3d picked pt", picked3d) 
        source = vtk.vtkSphereSource()
        source.SetRadius(0.02)
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(source.GetOutputPort())
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)
        actor.GetProperty().SetColor(255,0,0)
        actor.SetPosition(picked3d)
        self.ren.AddActor(actor)

if name == “main”:
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())

It seems vtkCellPicker suffers from the same problem (OS X and VTK 9.1), see code below. The picker returns the wrong world position.

import sys
import math
import vtk
from PySide6 import QtWidgets
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, parent = None):
        QtWidgets.QMainWindow.__init__(self, parent)

        self.frame = QtWidgets.QFrame()
        self.vl = QtWidgets.QVBoxLayout()
        self.vtkWidget = QVTKRenderWindowInteractor(self.frame)

        self.ren = vtk.vtkRenderer()
        self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()

        self.picker = vtk.vtkCellPicker() #vtkPropPicker()

        # Create source
        source = vtk.vtkSphereSource()
        # Create a mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(source.GetOutputPort())
        # Create an actor
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)

        self.ren.AddActor(actor)
        self.ren.ResetCamera()

        # self.iren.SetPicker(self.picker)

        self.vl.addWidget(self.vtkWidget)
        self.frame.setLayout(self.vl)
        self.setCentralWidget(self.frame)
        self.show()

        self.iren.AddObserver("LeftButtonPressEvent", self.onclick)
        self.iren.Initialize()
        self.iren.Start()

    def onclick(self, iren, event):
        x, y = self.iren.GetEventPosition()
        print("Left button pressed at", x,y)

        cellId = self.picker.Pick(x, y, 0, self.ren)
        if cellId >= 0:
            xyz = self.picker.GetPickPosition()
            radius = math.sqrt(xyz[0]**2 + xyz[1]**2 + xyz[2]**2)
            print(f"  3d picked pt {xyz} radius: {radius} ") 
            source = vtk.vtkSphereSource()
            source.SetRadius(0.02)
            mapper = vtk.vtkPolyDataMapper()
            mapper.SetInputConnection(source.GetOutputPort())
            actor = vtk.vtkActor()
            actor.SetMapper(mapper)
            actor.GetProperty().SetColor(1,0,0)
            actor.SetPosition(xyz)
            self.ren.AddActor(actor)


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

Depending on where you pick, you might see something like

Left button pressed at 150 172
  3d picked pt (0.19066947142160784, -0.22312961309267365, -0.3825671026006565) radius: 0.48218177025198994 

(The radius should be close to 1.)